From 13d509b40c2e9acc05831396222793545207cd35 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 24 Feb 2023 16:28:09 -0600 Subject: [PATCH 001/202] refactor layer actions to be more flexible --- x-pack/plugins/lens/common/constants.ts | 2 + .../layer_actions/clone_layer_action.tsx | 1 - .../layer_actions/open_layer_settings.tsx | 1 - .../layer_actions/remove_layer_action.tsx | 1 - .../editor_frame/config_panel/layer_panel.tsx | 15 ++-- x-pack/plugins/lens/public/types.ts | 14 ++-- .../visualizations/xy/annotations/actions.ts | 17 ++-- .../visualizations/xy/visualization.test.ts | 79 ++++++++++--------- .../visualizations/xy/visualization.tsx | 29 +------ 9 files changed, 65 insertions(+), 94 deletions(-) diff --git a/x-pack/plugins/lens/common/constants.ts b/x-pack/plugins/lens/common/constants.ts index 1495410cdb14c..4ea5f1e38032d 100644 --- a/x-pack/plugins/lens/common/constants.ts +++ b/x-pack/plugins/lens/common/constants.ts @@ -17,6 +17,8 @@ export const NOT_INTERNATIONALIZED_PRODUCT_NAME = 'Lens Visualizations'; export const BASE_API_URL = '/api/lens'; export const LENS_EDIT_BY_VALUE = 'edit_by_value'; +export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; + export const ENABLE_SQL = 'discover:enableSql'; export const PieChartTypes = { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx index 64a59df81c911..97d0cf73b80dd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx @@ -22,7 +22,6 @@ export const getCloneLayerAction = (props: CloneLayerAction): LayerAction => { }); return { - id: 'cloneLayerAction', execute: props.execute, displayName, isCompatible: Boolean(props.activeVisualization.cloneLayer && !props.isTextBasedLanguage), diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx index 13570df68c581..4047bb403e6eb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx @@ -17,7 +17,6 @@ export const getOpenLayerSettingsAction = (props: { }); return { - id: 'openLayerSettings', displayName, execute: props.openLayerSettings, icon: 'gear', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx index fa0a83d4484e8..2fae974229721 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx @@ -196,7 +196,6 @@ export const getRemoveLayerAction = (props: RemoveLayerAction): LayerAction => { ); return { - id: 'removeLayerAction', execute: async () => { const storage = new Storage(localStorage); const lensLocalStorage = storage.get(LOCAL_STORAGE_LENS_KEY) ?? {}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index d006ac539814d..c25aca30b0d0e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -325,16 +325,11 @@ export function LayerPanel( const compatibleActions = useMemo( () => [ - ...(activeVisualization - .getSupportedActionsForLayer?.(layerId, visualizationState) - .map((action) => ({ - ...action, - execute: () => { - updateVisualization( - activeVisualization.onLayerAction?.(layerId, action.id, visualizationState) - ); - }, - })) || []), + ...(activeVisualization.getSupportedActionsForLayer?.( + layerId, + visualizationState, + updateVisualization + ) || []), ...getSharedActions({ layerId, activeVisualization, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 0ab04eac70f90..0b78d968c25b8 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -612,7 +612,6 @@ export interface DatasourceDataPanelProps { /** @internal **/ export interface LayerAction { - id: string; displayName: string; description?: string; execute: () => void | Promise; @@ -622,8 +621,6 @@ export interface LayerAction { 'data-test-subj'?: string; } -export type LayerActionFromVisualization = Omit; - interface SharedDimensionProps { /** Visualizations can restrict operations based on their own rules. * For example, limiting to only bucketed or only numeric operations. @@ -1106,12 +1103,11 @@ export interface Visualization { * returns a list of custom actions supported by the visualization layer. * Default actions like delete/clear are not included in this list and are managed by the editor frame * */ - getSupportedActionsForLayer?: (layerId: string, state: T) => LayerActionFromVisualization[]; - - /** - * Perform state mutations in response to a layer action - */ - onLayerAction?: (layerId: string, actionId: string, state: T) => T; + getSupportedActionsForLayer?: ( + layerId: string, + state: T, + setState: StateSetter + ) => LayerAction[]; /** returns the type string of the given layer */ getLayerType: (layerId: string, state?: T) => LayerType | undefined; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts index 68938fbee5211..e3338d006c637 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts @@ -6,21 +6,20 @@ */ import { i18n } from '@kbn/i18n'; -import type { LayerActionFromVisualization } from '../../../types'; +import type { LayerAction, StateSetter } from '../../../types'; import type { XYState, XYAnnotationLayerConfig } from '../types'; -export const IGNORE_GLOBAL_FILTERS_ACTION_ID = 'ignoreGlobalFilters'; -export const KEEP_GLOBAL_FILTERS_ACTION_ID = 'keepGlobalFilters'; - export const createAnnotationActions = ({ state, layer, layerIndex, + setState, }: { state: XYState; layer: XYAnnotationLayerConfig; layerIndex: number; -}): LayerActionFromVisualization[] => { + setState: StateSetter; +}): LayerAction[] => { const label = !layer.ignoreGlobalFilters ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersLabel', { defaultMessage: 'Ignore global filters', @@ -30,9 +29,6 @@ export const createAnnotationActions = ({ }); return [ { - id: !layer.ignoreGlobalFilters - ? IGNORE_GLOBAL_FILTERS_ACTION_ID - : KEEP_GLOBAL_FILTERS_ACTION_ID, displayName: label, description: !layer.ignoreGlobalFilters ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersDescription', { @@ -43,6 +39,11 @@ export const createAnnotationActions = ({ defaultMessage: 'All the dimensions configured in this layer respect filters defined at kibana level.', }), + execute: () => { + const newLayers = [...state.layers]; + newLayers[layerIndex] = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; + return setState({ ...state, layers: newLayers }); + }, icon: !layer.ignoreGlobalFilters ? 'filterIgnore' : 'filter', isCompatible: true, 'data-test-subj': !layer.ignoreGlobalFilters diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 8dd7d9b1f2dbe..f146165bf4bd8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -37,7 +37,6 @@ import { DataViewsState } from '../../state_management'; import { createMockedIndexPattern } from '../../datasources/form_based/mocks'; import { createMockDataViewsState } from '../../data_views_service/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; -import { KEEP_GLOBAL_FILTERS_ACTION_ID } from './annotations/actions'; import { layerTypes, Visualization } from '../..'; const exampleAnnotation: EventAnnotationConfig = { @@ -2963,29 +2962,33 @@ describe('xy_visualization', () => { }); }); - describe('layer actions', () => { + describe('getSupportedActionsForLayer', () => { it('should return no actions for a data layer', () => { - expect(xyVisualization.getSupportedActionsForLayer?.('first', exampleState())).toHaveLength( - 0 - ); + expect( + xyVisualization.getSupportedActionsForLayer?.('first', exampleState(), jest.fn()) + ).toHaveLength(0); }); it('should return one action for an annotation layer', () => { const baseState = exampleState(); expect( - xyVisualization.getSupportedActionsForLayer?.('annotation', { - ...baseState, - layers: [ - ...baseState.layers, - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - indexPatternId: 'myIndexPattern', - }, - ], - }) + xyVisualization.getSupportedActionsForLayer?.( + 'annotation', + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + indexPatternId: 'myIndexPattern', + }, + ], + }, + jest.fn() + ) ).toEqual([ expect.objectContaining({ displayName: 'Keep global filters', @@ -2998,29 +3001,29 @@ describe('xy_visualization', () => { ]); }); - it('should handle an annotation action', () => { + it('should return an action that performs a state update on click', () => { const baseState = exampleState(); - const state = { - ...baseState, - layers: [ - ...baseState.layers, - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - indexPatternId: 'myIndexPattern', - }, - ], - }; - - const newState = xyVisualization.onLayerAction!( + const setState = jest.fn(); + const [action] = xyVisualization.getSupportedActionsForLayer?.( 'annotation', - KEEP_GLOBAL_FILTERS_ACTION_ID, - state - ); + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + indexPatternId: 'myIndexPattern', + }, + ], + }, + setState + )!; + action.execute(); - expect(newState).toEqual( + expect(setState).toHaveBeenCalledWith( expect.objectContaining({ layers: expect.arrayContaining([ { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index a019f9c6b1988..f8978cd1fa0e3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -49,7 +49,6 @@ import { type XYDataLayerConfig, type SeriesType, type PersistedState, - type XYAnnotationLayerConfig, visualizationTypes, } from './types'; import { @@ -102,11 +101,7 @@ import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; -import { - createAnnotationActions, - IGNORE_GLOBAL_FILTERS_ACTION_ID, - KEEP_GLOBAL_FILTERS_ACTION_ID, -} from './annotations/actions'; +import { createAnnotationActions } from './annotations/actions'; const XY_ID = 'lnsXY'; export const getXyVisualization = ({ @@ -261,34 +256,16 @@ export const getXyVisualization = ({ ]; }, - getSupportedActionsForLayer(layerId, state) { + getSupportedActionsForLayer(layerId, state, setState) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; const actions = []; if (isAnnotationsLayer(layer)) { - actions.push(...createAnnotationActions({ state, layerIndex, layer })); + actions.push(...createAnnotationActions({ state, layerIndex, layer, setState })); } return actions; }, - onLayerAction(layerId, actionId, state) { - if ([IGNORE_GLOBAL_FILTERS_ACTION_ID, KEEP_GLOBAL_FILTERS_ACTION_ID].includes(actionId)) { - return { - ...state, - layers: state.layers.map((layer) => - layer.layerId === layerId - ? { - ...layer, - ignoreGlobalFilters: !(layer as XYAnnotationLayerConfig).ignoreGlobalFilters, - } - : layer - ), - }; - } - - return state; - }, - onIndexPatternChange(state, indexPatternId, layerId) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; From 22fa4b52d357f39c65f1efc955b8836868708814 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 28 Feb 2023 11:44:56 -0600 Subject: [PATCH 002/202] merge martas work --- .../group3/type_registrations.test.ts | 2 + .../expression_xy/public/plugin.ts | 2 +- .../event_annotation/common/constants.ts | 3 + src/plugins/event_annotation/common/index.ts | 3 + src/plugins/event_annotation/common/types.ts | 10 + ...t_annotation_group_saved_object_finder.tsx | 64 ++++ .../public/event_annotation_service/index.tsx | 6 +- .../event_annotation_service/service.test.ts | 305 +++++++++++++++- .../event_annotation_service/service.tsx | 336 +++++++++++++----- .../public/event_annotation_service/types.ts | 21 ++ src/plugins/event_annotation/public/mocks.ts | 3 +- src/plugins/event_annotation/public/plugin.ts | 2 +- src/plugins/event_annotation/server/plugin.ts | 2 + .../event_annotation/server/saved_objects.ts | 178 ++++++++++ src/plugins/event_annotation/tsconfig.json | 1 + .../config_panel/config_panel.test.tsx | 2 +- .../config_panel/config_panel.tsx | 17 +- .../config_panel/dimension_container.tsx | 2 +- .../layer_actions/layer_actions.tsx | 5 +- .../editor_frame/config_panel/layer_panel.tsx | 33 +- .../editor_frame/suggestion_helpers.ts | 3 +- .../flyout_container.scss | 2 +- .../flyout_container.tsx | 42 ++- .../lens/public/state_management/index.ts | 1 + .../public/state_management/lens_slice.ts | 7 +- x-pack/plugins/lens/public/types.ts | 11 +- x-pack/plugins/lens/public/utils.ts | 1 + .../public/visualizations/xy/add_layer.tsx | 219 ++++++++++++ .../visualizations/xy/annotations/actions.ts | 54 --- .../actions/edit_details_action.tsx | 165 +++++++++ .../actions/ignore_filters_action.ts | 54 +++ .../xy/annotations/actions/index.ts | 110 ++++++ .../actions/revert_changes_action.ts | 38 ++ .../xy/annotations/actions/save_action.tsx | 73 ++++ .../xy/annotations/actions/unlink_action.tsx | 184 ++++++++++ .../lens/public/visualizations/xy/index.ts | 2 +- .../xy/load_annotation_library_flyout.tsx | 134 +++++++ .../visualizations/xy/visualization.test.ts | 6 +- .../visualizations/xy/visualization.tsx | 14 +- 39 files changed, 1926 insertions(+), 191 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx create mode 100644 src/plugins/event_annotation/server/saved_objects.ts rename x-pack/plugins/lens/public/{editor_frame_service/editor_frame/config_panel => shared_components}/flyout_container.scss (97%) rename x-pack/plugins/lens/public/{editor_frame_service/editor_frame/config_panel => shared_components}/flyout_container.tsx (82%) create mode 100644 x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx create mode 100644 x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index 7f4ca3fb70dc2..43870e8b58b1e 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -42,6 +42,8 @@ const previouslyRegisteredTypes = [ 'csp-rule-template', 'csp_rule', 'dashboard', + 'event-annotation', + 'event-annotation-group', 'endpoint:user-artifact', 'endpoint:user-artifact-manifest', 'enterprise_search_telemetry', diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 9c76a0d59395f..3a6af23cf0b67 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -83,7 +83,7 @@ export class ExpressionXyPlugin { const paletteService = await palettes.getPalettes(); const { theme: kibanaTheme } = coreStart; - const eventAnnotationService = await eventAnnotation.getService(); + const eventAnnotationService = await eventAnnotation.getService(coreStart); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); return { diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts index 3f3f9877b9786..1a7a889d6bc9c 100644 --- a/src/plugins/event_annotation/common/constants.ts +++ b/src/plugins/event_annotation/common/constants.ts @@ -23,3 +23,6 @@ export const AvailableAnnotationIcons = { TAG: 'tag', TRIANGLE: 'triangle', } as const; + +export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; +export const EVENT_ANNOTATION_TYPE = 'event-annotation'; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index 779d0e13e7813..7ff2740489340 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -33,4 +33,7 @@ export type { QueryPointEventAnnotationConfig, AvailableAnnotationIcon, EventAnnotationOutput, + EventAnnotationGroupAttributes, } from './types'; + +export { EVENT_ANNOTATION_TYPE, EVENT_ANNOTATION_GROUP_TYPE } from './constants'; diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 5f869cbee5520..d63b09d7dce97 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -82,10 +82,20 @@ export type EventAnnotationConfig = | RangeEventAnnotationConfig | QueryPointEventAnnotationConfig; +export interface EventAnnotationGroupAttributes { + title: string; + description?: string; + tags?: string[]; + ignoreGlobalFilters?: boolean; +} + export interface EventAnnotationGroupConfig { annotations: EventAnnotationConfig[]; indexPatternId: string; ignoreGlobalFilters?: boolean; + title?: string; + description?: string; + tags?: string[]; } export type EventAnnotationArgs = diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx new file mode 100644 index 0000000000000..2a82ef8f7aaff --- /dev/null +++ b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx @@ -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 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 { i18n } from '@kbn/i18n'; +import { SavedObjectFinderUi } from '@kbn/saved-objects-plugin/public'; +import { CoreStart } from '@kbn/core/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; +import { EVENT_ANNOTATION_GROUP_TYPE } from '../../common'; + +export const EventAnnotationGroupSavedObjectFinder = ({ + uiSettings, + http, + fixedPageSize = 10, + onChoose, +}: { + uiSettings: IUiSettingsClient; + http: CoreStart['http']; + fixedPageSize: number; + onChoose: (value: { + id: string; + type: string; + fullName: string; + savedObject: SavedObjectCommon; + }) => void; +}) => { + return ( + { + onChoose({ id, type, fullName, savedObject }); + }} + showFilter={false} + noItemsMessage={ + + } + savedObjectMetaData={savedObjectMetaData} + uiSettings={uiSettings} + http={http} + /> + ); +}; + +const savedObjectMetaData = [ + { + type: EVENT_ANNOTATION_GROUP_TYPE, + getIconForSavedObject: () => 'annotation', + name: i18n.translate('eventAnnotation.eventAnnotationGroup.metadata.name', { + defaultMessage: 'Annotations Groups', + }), + includeFields: ['*'], + }, +]; diff --git a/src/plugins/event_annotation/public/event_annotation_service/index.tsx b/src/plugins/event_annotation/public/event_annotation_service/index.tsx index e967a7cb0f0a2..bed211c5a6f64 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/index.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/index.tsx @@ -6,14 +6,16 @@ * Side Public License, v 1. */ +import { CoreStart } from '@kbn/core/public'; +// import { EventAnnotationGroupConfig } from '../../common'; import { EventAnnotationServiceType } from './types'; export class EventAnnotationService { private eventAnnotationService?: EventAnnotationServiceType; - public async getService() { + public async getService(core: CoreStart) { if (!this.eventAnnotationService) { const { getEventAnnotationService } = await import('./service'); - this.eventAnnotationService = getEventAnnotationService(); + this.eventAnnotationService = getEventAnnotationService(core); } return this.eventAnnotationService; } diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 5df25bc69d308..27dce04642a11 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -6,14 +6,138 @@ * Side Public License, v 1. */ +import { CoreStart } from '@kbn/core/public'; +import { coreMock } from '@kbn/core/public/mocks'; +import { EventAnnotationConfig } from '../../common'; import { getEventAnnotationService } from './service'; import { EventAnnotationServiceType } from './types'; +const annotationGroupResolveMocks = { + nonExistingGroup: { + saved_object: { + attributes: {}, + references: [], + id: 'nonExistingGroup', + }, + outcome: 'exactMatch', + }, + noAnnotations: { + saved_object: { + attributes: { + title: 'groupTitle', + }, + type: 'event-annotation-group', + references: [ + { + id: 'ipid', + name: 'ipid', + type: 'index-pattern', + }, + ], + }, + outcome: 'exactMatch', + }, + multiAnnotations: { + saved_object: { + attributes: { + title: 'groupTitle', + }, + id: 'multiAnnotations', + type: 'event-annotation-group', + references: [ + { + id: 'ipid', + name: 'ipid', + type: 'index-pattern', + }, + ], + }, + outcome: 'exactMatch', + }, +}; + +const annotationResolveMocks = { + nonExistingGroup: { savedObjects: [] }, + noAnnotations: { savedObjects: [] }, + multiAnnotations: { + savedObjects: [ + { + id: 'annotation1', + attributes: { + id: 'annotation1', + type: 'manual', + key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, + label: 'Event', + icon: 'triangle' as const, + color: 'red', + lineStyle: 'dashed' as const, + lineWidth: 3, + } as EventAnnotationConfig, + type: 'event-annotation', + references: [ + { + id: 'multiAnnotations', + name: 'event_annotation_group_ref', + type: 'event-annotation-group', + }, + ], + }, + { + id: 'annotation2', + attributes: { + id: 'ann2', + label: 'Query based event', + icon: 'triangle', + color: 'red', + type: 'query', + timeField: 'timestamp', + key: { + type: 'point_in_time', + }, + lineStyle: 'dashed', + lineWidth: 3, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + } as EventAnnotationConfig, + type: 'event-annotation', + references: [ + { + id: 'multiAnnotations', + name: 'event_annotation_group_ref', + type: 'event-annotation-group', + }, + ], + }, + ], + }, +}; + +let core: CoreStart; + describe('Event Annotation Service', () => { let eventAnnotationService: EventAnnotationServiceType; - beforeAll(() => { - eventAnnotationService = getEventAnnotationService(); + beforeEach(() => { + core = coreMock.createStart(); + (core.savedObjects.client.resolve as jest.Mock).mockImplementation( + (_type, id: 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup') => { + return annotationGroupResolveMocks[id]; + } + ); + (core.savedObjects.client.create as jest.Mock).mockImplementation(() => { + return annotationGroupResolveMocks.multiAnnotations.saved_object; + }); + (core.savedObjects.client.bulkCreate as jest.Mock).mockImplementation(() => { + return annotationResolveMocks.multiAnnotations; + }); + (core.savedObjects.client.find as jest.Mock).mockImplementation(({ hasReference: { id } }) => { + const typedId = id as 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup'; + return annotationResolveMocks[typedId]; + }); + eventAnnotationService = getEventAnnotationService(core); }); + afterEach(() => { + jest.clearAllMocks(); + }); + describe('toExpression', () => { it('should work for an empty list', () => { expect(eventAnnotationService.toExpression([])).toEqual([]); @@ -318,4 +442,181 @@ describe('Event Annotation Service', () => { } ); }); + describe('loadAnnotationGroup', () => { + it('should not error when loading group doesnt exit', async () => { + expect(await eventAnnotationService.loadAnnotationGroup('nonExistingGroup')).toEqual({ + annotations: [], + indexPatternId: undefined, + title: undefined, + }); + }); + it('should properly load an annotation group with no annotation', async () => { + expect(await eventAnnotationService.loadAnnotationGroup('noAnnotations')).toEqual({ + annotations: [], + indexPatternId: 'ipid', + title: 'groupTitle', + }); + }); + it('should properly load an annotation group with a multiple annotation', async () => { + expect(await eventAnnotationService.loadAnnotationGroup('multiAnnotations')).toEqual({ + annotations: [ + annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, + annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, + ], + indexPatternId: 'ipid', + title: 'groupTitle', + }); + }); + }); + describe('deleteAnnotationGroup', () => { + it('deletes annotation group along with annotations that reference them', async () => { + await eventAnnotationService.deleteAnnotationGroup('multiAnnotations'); + expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ + { id: 'multiAnnotations', type: 'event-annotation-group' }, + { id: 'annotation1', type: 'event-annotation' }, + { id: 'annotation2', type: 'event-annotation' }, + ]); + }); + }); + describe('createAnnotationGroup', () => { + it('creates annotation group along with annotations', async () => { + await eventAnnotationService.createAnnotationGroup({ + title: 'newGroupTitle', + indexPatternId: 'ipid', + annotations: [ + annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, + annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, + ], + }); + expect(core.savedObjects.client.create).toHaveBeenCalledWith( + 'event-annotation-group', + { title: 'newGroupTitle' }, + { + references: [ + { + id: 'ipid', + name: 'event-annotation-group_dataView-ref-ipid', + type: 'index-pattern', + }, + ], + } + ); + expect(core.savedObjects.client.bulkCreate).toHaveBeenCalledWith([ + { + attributes: annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, + id: 'annotation1', + references: [ + { + id: 'multiAnnotations', + name: 'event-annotation_group-ref-annotation1', + type: 'event-annotation-group', + }, + ], + type: 'event-annotation', + }, + { + attributes: annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, + id: 'ann2', + references: [ + { + id: 'multiAnnotations', + name: 'event-annotation_group-ref-ann2', + type: 'event-annotation-group', + }, + ], + type: 'event-annotation', + }, + ]); + }); + }); + describe('updateAnnotationGroupAttributes', () => { + it('updates annotation group attributes', async () => { + await eventAnnotationService.updateAnnotationGroup( + { title: 'newTitle', indexPatternId: 'newId', annotations: [] }, + 'multiAnnotations' + ); + expect(core.savedObjects.client.update).toHaveBeenCalledWith( + 'event-annotation-group', + 'multiAnnotations', + { title: 'newTitle' }, + { + references: [ + { + id: 'newId', + name: 'event-annotation-group_dataView-ref-newId', + type: 'index-pattern', + }, + ], + } + ); + }); + }); + describe('updateAnnotations', () => { + const upsert = [ + { + id: 'annotation2', + label: 'Query based event', + icon: 'triangle', + color: 'red', + type: 'query', + timeField: 'timestamp', + key: { + type: 'point_in_time', + }, + lineStyle: 'dashed', + lineWidth: 3, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }, + { + id: 'annotation4', + label: 'Query based event', + type: 'query', + timeField: 'timestamp', + key: { + type: 'point_in_time', + }, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }, + ] as EventAnnotationConfig[]; + it('updates annotations - deletes annotations', async () => { + await eventAnnotationService.updateAnnotations('multiAnnotations', { + delete: ['annotation1', 'annotation2'], + }); + expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ + { id: 'annotation1', type: 'event-annotation' }, + { id: 'annotation2', type: 'event-annotation' }, + ]); + }); + it('updates annotations - inserts new annotations', async () => { + await eventAnnotationService.updateAnnotations('multiAnnotations', { upsert }); + expect(core.savedObjects.client.bulkCreate).toHaveBeenCalledWith([ + { + id: 'annotation2', + type: 'event-annotation', + attributes: upsert[0], + overwrite: true, + references: [ + { + id: 'multiAnnotations', + name: 'event-annotation-group-ref-annotation2', + type: 'event-annotation-group', + }, + ], + }, + { + id: 'annotation4', + type: 'event-annotation', + attributes: upsert[1], + overwrite: true, + references: [ + { + id: 'multiAnnotations', + name: 'event-annotation-group-ref-annotation4', + type: 'event-annotation-group', + }, + ], + }, + ]); + }); + }); }); diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 1b2bdbc9611cc..efa6a2a3a2145 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -6,10 +6,19 @@ * Side Public License, v 1. */ +import React from 'react'; import { partition } from 'lodash'; import { queryToAst } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; -import { EventAnnotationConfig } from '../../common'; +import { CoreStart, SavedObjectsClientContract } from '@kbn/core/public'; +import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; +import { + EventAnnotationConfig, + EventAnnotationGroupConfig, + EventAnnotationGroupAttributes, + EVENT_ANNOTATION_GROUP_TYPE, + EVENT_ANNOTATION_TYPE, +} from '../../common'; import { EventAnnotationServiceType } from './types'; import { defaultAnnotationColor, @@ -18,108 +27,179 @@ import { isRangeAnnotationConfig, isQueryAnnotationConfig, } from './helpers'; +import { EventAnnotationGroupSavedObjectFinder } from '../components/event_annotation_group_saved_object_finder'; export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; } -export function getEventAnnotationService(): EventAnnotationServiceType { - const annotationsToExpression = (annotations: EventAnnotationConfig[]) => { - const [queryBasedAnnotations, manualBasedAnnotations] = partition( - annotations, - isQueryAnnotationConfig +export function getEventAnnotationService(core: CoreStart): EventAnnotationServiceType { + const client: SavedObjectsClientContract = core.savedObjects.client; + + const loadAnnotationGroup = async ( + savedObjectId: string + ): Promise => { + const groupResponse = await client.resolve( + EVENT_ANNOTATION_GROUP_TYPE, + savedObjectId ); - const expressions = []; + if (groupResponse.saved_object.error) { + throw groupResponse.saved_object.error; + } + + const annotations = ( + await client.find({ + type: EVENT_ANNOTATION_TYPE, + hasReference: { + type: EVENT_ANNOTATION_GROUP_TYPE, + id: savedObjectId, + }, + }) + ).savedObjects + .filter(({ error }) => !error) + .map((annotation) => annotation.attributes); + + return { + title: groupResponse.saved_object.attributes.title, + description: groupResponse.saved_object.attributes.description, + tags: groupResponse.saved_object.attributes.tags, + ignoreGlobalFilters: groupResponse.saved_object.attributes.ignoreGlobalFilters, + indexPatternId: groupResponse.saved_object.references.find( + (ref) => ref.type === 'index-pattern' + )?.id!, + annotations, + }; + }; + + const deleteAnnotationGroup = async (savedObjectId: string): Promise => { + const annotationsSOs = ( + await client.find({ + type: EVENT_ANNOTATION_TYPE, + hasReference: { + type: EVENT_ANNOTATION_GROUP_TYPE, + id: savedObjectId, + }, + }) + ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); + await client.bulkDelete([ + { type: EVENT_ANNOTATION_GROUP_TYPE, id: savedObjectId }, + ...annotationsSOs, + ]); + }; + + const createAnnotationGroup = async ( + group: EventAnnotationGroupConfig + ): Promise<{ id: string }> => { + const { title, description, tags, ignoreGlobalFilters, indexPatternId, annotations } = group; - for (const annotation of manualBasedAnnotations) { - if (isRangeAnnotationConfig(annotation)) { - const { label, color, key, outside, id } = annotation; - const { timestamp: time, endTimestamp: endTime } = key; - expressions.push({ - type: 'expression' as const, - chain: [ + const groupSavedObjectId = ( + await client.create( + EVENT_ANNOTATION_GROUP_TYPE, + { title, description, tags, ignoreGlobalFilters }, + { + references: [ { - type: 'function' as const, - function: 'manual_range_event_annotation', - arguments: { - id: [id], - time: [time], - endTime: [endTime], - label: [label || defaultAnnotationLabel], - color: [color || defaultAnnotationRangeColor], - outside: [Boolean(outside)], - isHidden: [Boolean(annotation.isHidden)], - }, + type: 'index-pattern', + id: indexPatternId, + name: `event-annotation-group_dataView-ref-${indexPatternId}`, }, ], - }); - } else { - const { label, color, lineStyle, lineWidth, icon, key, textVisibility, id } = annotation; - expressions.push({ - type: 'expression' as const, - chain: [ + } + ) + ).id; + + if (annotations && annotations.length) { + await client.bulkCreate( + annotations.map((annotation) => ({ + type: EVENT_ANNOTATION_TYPE, + id: annotation.id, + attributes: annotation, + references: [ { - type: 'function' as const, - function: 'manual_point_event_annotation', - arguments: { - id: [id], - time: [key.timestamp], - label: [label || defaultAnnotationLabel], - color: [color || defaultAnnotationColor], - lineWidth: [lineWidth || 1], - lineStyle: [lineStyle || 'solid'], - icon: hasIcon(icon) ? [icon] : ['triangle'], - textVisibility: [textVisibility || false], - isHidden: [Boolean(annotation.isHidden)], - }, + type: EVENT_ANNOTATION_GROUP_TYPE, + id: groupSavedObjectId, + name: `event-annotation_group-ref-${annotation.id}`, // do name have to be unique with id? }, ], - }); + })) + ); + } + return { id: groupSavedObjectId }; + }; + + const updateAnnotationGroup = async ( + group: EventAnnotationGroupConfig, + savedObjectId: string + ): Promise => { + const { title, description, tags, ignoreGlobalFilters, indexPatternId } = group; + await client.update( + EVENT_ANNOTATION_GROUP_TYPE, + savedObjectId, + { title, description, tags, ignoreGlobalFilters }, + { + references: [ + { + type: 'index-pattern', + id: indexPatternId, + name: `event-annotation-group_dataView-ref-${indexPatternId}`, + }, + ], } + ); + }; + + const updateAnnotations = async ( + savedObjectId: string, + modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } + ): Promise => { + if (modifications.delete && modifications.delete.length > 0) { + await client.bulkDelete( + modifications.delete.map((id) => ({ type: EVENT_ANNOTATION_TYPE, id })) + ); } - for (const annotation of queryBasedAnnotations) { - const { - id, - label, - color, - lineStyle, - lineWidth, - icon, - timeField, - textVisibility, - textField, - filter, - extraFields, - } = annotation; - expressions.push({ - type: 'expression' as const, - chain: [ + if (modifications.upsert && modifications.upsert.length > 0) { + const annotationsToUpdate = modifications.upsert.map((a) => ({ + type: EVENT_ANNOTATION_TYPE, + attributes: a, + id: a.id, + overwrite: true, + references: [ { - type: 'function' as const, - function: 'query_point_event_annotation', - arguments: { - id: [id], - timeField: timeField ? [timeField] : [], - label: [label || defaultAnnotationLabel], - color: [color || defaultAnnotationColor], - lineWidth: [lineWidth || 1], - lineStyle: [lineStyle || 'solid'], - icon: hasIcon(icon) ? [icon] : ['triangle'], - textVisibility: [textVisibility || false], - textField: textVisibility && textField ? [textField] : [], - filter: filter ? [queryToAst(filter)] : [], - extraFields: extraFields || [], - isHidden: [Boolean(annotation.isHidden)], - }, + type: EVENT_ANNOTATION_GROUP_TYPE, + id: savedObjectId, + name: `event-annotation-group-ref-${a.id}`, }, ], - }); + })); + await client.bulkCreate(annotationsToUpdate); } - return expressions; }; + return { + loadAnnotationGroup, + updateAnnotations, + updateAnnotationGroup, + createAnnotationGroup, + deleteAnnotationGroup, + renderEventAnnotationGroupSavedObjectFinder: (props: { + fixedPageSize: number; + onChoose: (value: { + id: string; + type: string; + fullName: string; + savedObject: SavedObjectCommon; + }) => void; + }) => { + return ( + + ); + }, toExpression: annotationsToExpression, toFetchExpression: ({ interval, groups }) => { if (groups.length === 0) { @@ -177,3 +257,99 @@ export function getEventAnnotationService(): EventAnnotationServiceType { }, }; } + +const annotationsToExpression = (annotations: EventAnnotationConfig[]) => { + const [queryBasedAnnotations, manualBasedAnnotations] = partition( + annotations, + isQueryAnnotationConfig + ); + + const expressions = []; + + for (const annotation of manualBasedAnnotations) { + if (isRangeAnnotationConfig(annotation)) { + const { label, color, key, outside, id } = annotation; + const { timestamp: time, endTimestamp: endTime } = key; + expressions.push({ + type: 'expression' as const, + chain: [ + { + type: 'function' as const, + function: 'manual_range_event_annotation', + arguments: { + id: [id], + time: [time], + endTime: [endTime], + label: [label || defaultAnnotationLabel], + color: [color || defaultAnnotationRangeColor], + outside: [Boolean(outside)], + isHidden: [Boolean(annotation.isHidden)], + }, + }, + ], + }); + } else { + const { label, color, lineStyle, lineWidth, icon, key, textVisibility, id } = annotation; + expressions.push({ + type: 'expression' as const, + chain: [ + { + type: 'function' as const, + function: 'manual_point_event_annotation', + arguments: { + id: [id], + time: [key.timestamp], + label: [label || defaultAnnotationLabel], + color: [color || defaultAnnotationColor], + lineWidth: [lineWidth || 1], + lineStyle: [lineStyle || 'solid'], + icon: hasIcon(icon) ? [icon] : ['triangle'], + textVisibility: [textVisibility || false], + isHidden: [Boolean(annotation.isHidden)], + }, + }, + ], + }); + } + } + + for (const annotation of queryBasedAnnotations) { + const { + id, + label, + color, + lineStyle, + lineWidth, + icon, + timeField, + textVisibility, + textField, + filter, + extraFields, + } = annotation; + expressions.push({ + type: 'expression' as const, + chain: [ + { + type: 'function' as const, + function: 'query_point_event_annotation', + arguments: { + id: [id], + timeField: timeField ? [timeField] : [], + label: [label || defaultAnnotationLabel], + color: [color || defaultAnnotationColor], + lineWidth: [lineWidth || 1], + lineStyle: [lineStyle || 'solid'], + icon: hasIcon(icon) ? [icon] : ['triangle'], + textVisibility: [textVisibility || false], + textField: textVisibility && textField ? [textField] : [], + filter: filter ? [queryToAst(filter)] : [], + extraFields: extraFields || [], + isHidden: [Boolean(annotation.isHidden)], + }, + }, + ], + }); + } + return expressions; +}; diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index cf3d759b7a324..68f4c8556594f 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -7,12 +7,33 @@ */ import { ExpressionAstExpression } from '@kbn/expressions-plugin/common/ast'; +import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; export interface EventAnnotationServiceType { + loadAnnotationGroup: (savedObjectId: string) => Promise; + deleteAnnotationGroup: (savedObjectId: string) => Promise; + createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; + updateAnnotations: ( + savedObjectId: string, + modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } + ) => Promise; + updateAnnotationGroup: ( + group: EventAnnotationGroupConfig, + savedObjectId: string + ) => Promise; toExpression: (props: EventAnnotationConfig[]) => ExpressionAstExpression[]; toFetchExpression: (props: { interval: string; groups: EventAnnotationGroupConfig[]; }) => ExpressionAstExpression[]; + renderEventAnnotationGroupSavedObjectFinder: (props: { + fixedPageSize: number; + onChoose: (value: { + id: string; + type: string; + fullName: string; + savedObject: SavedObjectCommon; + }) => void; + }) => JSX.Element; } diff --git a/src/plugins/event_annotation/public/mocks.ts b/src/plugins/event_annotation/public/mocks.ts index e78d4e8f75de7..a2c236d9d593e 100644 --- a/src/plugins/event_annotation/public/mocks.ts +++ b/src/plugins/event_annotation/public/mocks.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ +import { coreMock } from '@kbn/core/public/mocks'; import { getEventAnnotationService } from './event_annotation_service/service'; // not really mocking but avoiding async loading -export const eventAnnotationServiceMock = getEventAnnotationService(); +export const eventAnnotationServiceMock = getEventAnnotationService(coreMock.createStart()); diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 47df586e061d7..4aaed8dab788a 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Plugin, CoreSetup, CoreStart, IUiSettingsClient } from '@kbn/core/public'; +import { Plugin, CoreSetup, IUiSettingsClient, CoreStart } from '@kbn/core/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { EventAnnotationService } from './event_annotation_service'; diff --git a/src/plugins/event_annotation/server/plugin.ts b/src/plugins/event_annotation/server/plugin.ts index 4d59f0e6800dd..c53eb128e4fba 100644 --- a/src/plugins/event_annotation/server/plugin.ts +++ b/src/plugins/event_annotation/server/plugin.ts @@ -15,6 +15,7 @@ import { manualRangeEventAnnotation, queryPointEventAnnotation, } from '../common'; +import { setupSavedObjects } from './saved_objects'; // import { getFetchEventAnnotations } from './fetch_event_annotations'; interface SetupDependencies { @@ -36,6 +37,7 @@ export class EventAnnotationServerPlugin implements Plugin { // dependencies.expressions.registerFunction( // getFetchEventAnnotations({ getStartServices: core.getStartServices }) // ); + setupSavedObjects(core); return {}; } diff --git a/src/plugins/event_annotation/server/saved_objects.ts b/src/plugins/event_annotation/server/saved_objects.ts new file mode 100644 index 0000000000000..d228bc7ea0072 --- /dev/null +++ b/src/plugins/event_annotation/server/saved_objects.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 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 { + CoreSetup, + mergeSavedObjectMigrationMaps, + SavedObject, + SavedObjectMigrationMap, +} from '@kbn/core/server'; + +import type { + SavedObjectsClientContract, + SavedObjectsExportTransformContext, +} from '@kbn/core/server'; +import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; +import { EventAnnotationConfig } from '../common'; +import { EVENT_ANNOTATION_GROUP_TYPE, EVENT_ANNOTATION_TYPE } from '../common/constants'; +import { EventAnnotationGroupAttributes } from '../common/types'; + +type EventAnnotationAttributes = EventAnnotationConfig; + +export const EVENT_ANNOTATIONS_INDEX = '.kibana_event_annotations'; + +export function setupSavedObjects(coreSetup: CoreSetup) { + coreSetup.savedObjects.registerType({ + name: EVENT_ANNOTATION_GROUP_TYPE, + hidden: false, + namespaceType: 'multiple-isolated', + indexPattern: EVENT_ANNOTATIONS_INDEX, + management: { + icon: 'questionInCircle', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj: { attributes: EventAnnotationGroupAttributes }) => obj.attributes.title, + onExport: async (context, objects: Array>) => + handleExport({ context, objects, coreSetup }), + }, + migrations: () => { + const dataViewMigrations = DataViewPersistableStateService.getAllMigrations(); + return mergeSavedObjectMigrationMaps(eventAnnotationGroupMigrations, dataViewMigrations); + }, + mappings: { + properties: { + title: { + type: 'text', + }, + description: { + type: 'text', + }, + tags: { + type: 'keyword', + }, + ignoreGlobalFilters: { + type: 'boolean', + }, + }, + }, + }); + + coreSetup.savedObjects.registerType({ + name: EVENT_ANNOTATION_TYPE, + indexPattern: EVENT_ANNOTATIONS_INDEX, + hidden: false, + namespaceType: 'multiple-isolated', + management: { + visibleInManagement: false, + importableAndExportable: true, + }, + migrations: () => eventAnnotationMigrations, + mappings: { + properties: { + id: { + type: 'text', + }, + type: { + type: 'text', + }, + key: { + type: 'flattened', + }, + color: { + type: 'text', + }, + lineStyle: { + type: 'text', + }, + icon: { + type: 'text', + }, + label: { + type: 'text', + }, + lineWidth: { + type: 'long', + }, + extraFields: { + type: 'flattened', + }, + filter: { + type: 'flattened', + }, + outside: { + type: 'boolean', + }, + textVisibility: { + type: 'boolean', + }, + isHidden: { + type: 'boolean', + }, + timeField: { + type: 'text', + }, + textField: { + type: 'text', + }, + }, + }, + }); +} + +export async function handleExport({ + context, + objects, + coreSetup, +}: { + context: SavedObjectsExportTransformContext; + objects: Array>; + coreSetup: CoreSetup; +}): Promise>> { + try { + if (objects.length <= 0) { + return []; + } + const [{ savedObjects }] = await coreSetup.getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(context.request); + + const annotationsSavedObjects = await getAnnotationsSavedObjects({ + savedObjectsClient, + groupIds: objects.map((o) => o.id), + }); + + return [...objects, ...annotationsSavedObjects.flat()]; + } catch (error) { + throw new Error('Error exporting annotations'); + } +} + +async function getAnnotationsSavedObjects({ + savedObjectsClient, + groupIds, +}: { + savedObjectsClient: SavedObjectsClientContract; + groupIds: string[]; +}): Promise>> { + const references = groupIds.map((id) => ({ type: EVENT_ANNOTATION_GROUP_TYPE, id })); + + const finder = savedObjectsClient.createPointInTimeFinder({ + type: EVENT_ANNOTATION_TYPE, + hasReference: references, + }); + + let result: Array> = []; + for await (const findResults of finder.find()) { + result = result.concat(findResults.saved_objects); + } + + return result; +} + +const eventAnnotationGroupMigrations: SavedObjectMigrationMap = {}; + +const eventAnnotationMigrations: SavedObjectMigrationMap = {}; diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 0b5e98d364fed..d5ff92a20ffdc 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -19,6 +19,7 @@ "@kbn/core-ui-settings-browser", "@kbn/datemath", "@kbn/ui-theme", + "@kbn/saved-objects-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index 7f509c9f921b4..e3a64a6eb2e0b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -28,8 +28,8 @@ import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { LayerType } from '../../../../common'; import { ReactWrapper } from 'enzyme'; import { addLayer } from '../../../state_management'; -import { AddLayerButton } from './add_layer'; import { createIndexPatternServiceMock } from '../../../mocks/data_views_service_mock'; +import { AddLayerButton } from '../../../visualizations/xy/add_layer'; jest.mock('../../../id_generator'); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 41ac0e3359ef6..3c394910561dd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -33,7 +33,6 @@ import { useLensSelector, selectVisualization, } from '../../../state_management'; -import { AddLayerButton } from './add_layer'; import { getRemoveOperation } from '../../../utils'; export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: ConfigPanelWrapperProps) { @@ -314,14 +313,14 @@ export function LayerPanels( ) ); })} - {!hideAddLayerButton && ( - addLayer(layerType)} - /> - )} + {!hideAddLayerButton && + activeVisualization.getAddLayerButtonComponent && + activeVisualization.getAddLayerButtonComponent({ + visualization: activeVisualization, + visualizationState: visualization.state, + layersMeta: props.framePublicAPI, + onAddLayerClick: addLayer, + })} ); } 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 13ba032f9b902..5a109a6ad36a9 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 @@ -8,7 +8,7 @@ import './dimension_container.scss'; import React from 'react'; -import { FlyoutContainer } from './flyout_container'; +import { FlyoutContainer } from '../../../shared_components/flyout_container'; export function DimensionContainer({ panel, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx index 0bfd515f91bf7..b1fbee9a55de5 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx @@ -27,6 +27,7 @@ import { getOpenLayerSettingsAction } from './open_layer_settings'; export interface LayerActionsProps { layerIndex: number; actions: LayerAction[]; + mountingPoint?: HTMLDivElement | null | undefined; } /** @internal **/ @@ -128,7 +129,7 @@ const InContextMenuActions = (props: LayerActionsProps) => { title={i.displayName} onClick={() => { closePopover(); - i.execute(); + i.execute(props.mountingPoint); }} > @@ -160,7 +161,7 @@ export const LayerActions = (props: LayerActionsProps) => { data-test-subj={dataTestSubj} aria-label={displayName} title={displayName} - onClick={execute} + onClick={() => execute?.(props.mountingPoint)} /> ); }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index c25aca30b0d0e..82c58960934b3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -50,7 +50,7 @@ import { } from '../../../state_management'; import { onDropForVisualization, shouldRemoveSource } from './buttons/drop_targets_utils'; import { getSharedActions } from './layer_actions/layer_actions'; -import { FlyoutContainer } from './flyout_container'; +import { FlyoutContainer } from '../../../shared_components/flyout_container'; // hide the random sampling settings from the UI const DISPLAY_RANDOM_SAMPLING_SETTINGS = false; @@ -122,6 +122,8 @@ export function LayerPanel( core, } = props; + const isSaveable = useLensSelector((state) => state.lens.isSaveable); + const datasourceStates = useLensSelector(selectDatasourceStates); const isFullscreen = useLensSelector(selectIsFullscreenDatasource); const dateRange = useLensSelector(selectResolvedDateRange); @@ -132,6 +134,7 @@ export function LayerPanel( const panelRef = useRef(null); const settingsPanelRef = useRef(null); + const registerLayerRef = useCallback( (el) => registerNewLayerRef(layerId, el), [layerId, registerNewLayerRef] @@ -325,11 +328,20 @@ export function LayerPanel( const compatibleActions = useMemo( () => [ - ...(activeVisualization.getSupportedActionsForLayer?.( - layerId, - visualizationState, - updateVisualization - ) || []), + ...(activeVisualization + .getSupportedActionsForLayer?.( + layerId, + visualizationState, + updateVisualization, + isSaveable + ) + .map((action) => ({ + ...action, + execute: () => { + action.execute(layerActionsFlyoutRef.current); + }, + })) || []), + ...getSharedActions({ layerId, activeVisualization, @@ -366,8 +378,10 @@ export function LayerPanel( props.framePublicAPI, updateVisualization, visualizationState, + isSaveable, ] ); + const layerActionsFlyoutRef = useRef(null); return ( <> @@ -391,7 +405,12 @@ export function LayerPanel( /> - + +
{(layerDatasource || activeVisualization.renderLayerPanel) && } 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 81298a97f650e..5a4c976764774 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 @@ -23,7 +23,6 @@ import type { } from '../../types'; import type { DragDropIdentifier } from '../../drag_drop'; import type { LayerType } from '../../../common'; -import { getLayerType } from './config_panel/add_layer'; import { LensDispatch, switchVisualization, @@ -77,7 +76,7 @@ export function getSuggestions({ } const layers = datasource.getLayers(datasourceState); for (const layerId of layers) { - const type = getLayerType(activeVisualization, visualizationState, layerId); + const type = activeVisualization.getLayerType(layerId, visualizationState) || LayerTypes.DATA; memo[layerId] = type; } return memo; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/flyout_container.scss b/x-pack/plugins/lens/public/shared_components/flyout_container.scss similarity index 97% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/flyout_container.scss rename to x-pack/plugins/lens/public/shared_components/flyout_container.scss index 9c8ee858c761e..c76b64c16097d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/flyout_container.scss +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.scss @@ -1,4 +1,4 @@ -@import '../../../mixins'; +@import '../mixins'; .lnsDimensionContainer { // Use the EuiFlyout style diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx similarity index 82% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/flyout_container.tsx rename to x-pack/plugins/lens/public/shared_components/flyout_container.tsx index 041f59df332f4..e71bee4eb08ee 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -19,7 +19,7 @@ import { EuiFocusTrap, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../utils'; +import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../utils'; function fromExcludedClickTarget(event: Event) { for ( @@ -44,14 +44,20 @@ export function FlyoutContainer({ handleClose, isFullscreen, panelRef, + panelContainerRef, children, + customFooter, + onClickInside, }: { isOpen: boolean; handleClose: () => boolean; children: React.ReactElement | null; groupLabel: string; - isFullscreen: boolean; - panelRef: (el: HTMLDivElement) => void; + isFullscreen?: boolean; + panelRef?: (el: HTMLDivElement) => void; + panelContainerRef?: (el: HTMLDivElement) => void; + customFooter?: React.ReactElement; + onClickInside?: () => void; }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); @@ -91,6 +97,8 @@ export function FlyoutContainer({ onEscapeKey={closeFlyout} >
onClickInside?.()} + ref={panelContainerRef} role="dialog" aria-labelledby="lnsDimensionContainerTitle" className="lnsDimensionContainer euiFlyout" @@ -137,19 +145,21 @@ export function FlyoutContainer({
{children}
- - - {i18n.translate('xpack.lens.dimensionContainer.close', { - defaultMessage: 'Close', - })} - - + {customFooter || ( + + + {i18n.translate('xpack.lens.dimensionContainer.close', { + defaultMessage: 'Close', + })} + + + )}
diff --git a/x-pack/plugins/lens/public/state_management/index.ts b/x-pack/plugins/lens/public/state_management/index.ts index bfb922397e3da..36031871d38b6 100644 --- a/x-pack/plugins/lens/public/state_management/index.ts +++ b/x-pack/plugins/lens/public/state_management/index.ts @@ -45,6 +45,7 @@ export const { addLayer, setLayerDefaultDimension, removeDimension, + setIsLoadLibraryVisible, } = lensActions; export const makeConfigureStore = ( diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index 5f00eed48005f..2f19b3eadfb23 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -10,6 +10,7 @@ import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { mapValues, uniq } from 'lodash'; import { Query } from '@kbn/es-query'; import { History } from 'history'; +import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { LensEmbeddableInput } from '..'; import { TableInspectorAdapter } from '../editor_frame_service/types'; import type { @@ -24,7 +25,6 @@ import type { DataViewsState, LensAppState, LensStoreDeps, VisualizationState } import type { Datasource, Visualization } from '../types'; import { generateId } from '../id_generator'; import type { LayerType } from '../../common/types'; -import { getLayerType } from '../editor_frame_service/editor_frame/config_panel/add_layer'; import { getVisualizeFieldSuggestions } from '../editor_frame_service/editor_frame/suggestion_helpers'; import type { FramePublicAPI, LensEditContextMapping, LensEditEvent } from '../types'; import { selectDataViews, selectFramePublicAPI } from './selectors'; @@ -161,6 +161,7 @@ export const switchVisualization = createAction<{ }>('lens/switchVisualization'); export const rollbackSuggestion = createAction('lens/rollbackSuggestion'); export const setToggleFullscreen = createAction('lens/setToggleFullscreen'); +export const setIsLoadLibraryVisible = createAction('lens/setIsLoadLibraryVisible'); export const submitSuggestion = createAction('lens/submitSuggestion'); export const switchDatasource = createAction<{ newDatasourceId: string; @@ -254,6 +255,7 @@ export const lensActions = { switchVisualization, rollbackSuggestion, setToggleFullscreen, + setIsLoadLibraryVisible, submitSuggestion, switchDatasource, switchAndCleanDatasource, @@ -1115,7 +1117,8 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { const activeDatasource = datasourceMap[state.activeDatasourceId]; const activeVisualization = visualizationMap[state.visualization.activeId]; - const layerType = getLayerType(activeVisualization, state.visualization.state, layerId); + const layerType = + activeVisualization.getLayerType(layerId, state.visualization.state) || LayerTypes.DATA; const { activeDatasourceState, activeVisualizationState } = addInitialValueIfAvailable({ datasourceState: state.datasourceStates[state.activeDatasourceId].state, visualizationState: state.visualization.state, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 0b78d968c25b8..6198ded54d4bf 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -614,7 +614,7 @@ export interface DatasourceDataPanelProps { export interface LayerAction { displayName: string; description?: string; - execute: () => void | Promise; + execute: (mountingPoint: HTMLDivElement | null | undefined) => void | Promise; icon: IconType; color?: EuiButtonIconProps['color']; isCompatible: boolean; @@ -1106,7 +1106,8 @@ export interface Visualization { getSupportedActionsForLayer?: ( layerId: string, state: T, - setState: StateSetter + setState: StateSetter, + isSaveable?: boolean ) => LayerAction[]; /** returns the type string of the given layer */ @@ -1229,6 +1230,12 @@ export interface Visualization { label: string; hideTooltip?: boolean; }) => JSX.Element | null; + getAddLayerButtonComponent?: (props: { + visualization: Visualization; + visualizationState: T; + onAddLayerClick: (layerType: LayerType) => void; + layersMeta: Pick; + }) => JSX.Element | null; /** * Creates map of columns ids and unique lables. Used only for noDatasource layers */ diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 4592bf5bfddcd..2c490e5c25db5 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -175,6 +175,7 @@ export function getIndexPatternsIds({ } return currentId; }, undefined); + const referencesIds = references .filter(({ type }) => type === 'index-pattern') .map(({ id }) => id); diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx new file mode 100644 index 0000000000000..b456ff7ca682e --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -0,0 +1,219 @@ +/* + * 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, useMemo } from 'react'; +import { + EuiToolTip, + EuiButton, + EuiPopover, + EuiIcon, + EuiContextMenu, + EuiBadge, + EuiFlexItem, + EuiFlexGroup, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { LayerType, Visualization } from '../..'; +import { FramePublicAPI } from '../../types'; +import { LoadAnnotationLibraryFlyout } from './load_annotation_library_flyout'; + +interface AddLayerButtonProps { + visualization: Visualization; + visualizationState: unknown; + onAddLayerClick: (layerType: LayerType) => void; + layersMeta: Pick; + eventAnnotationService: EventAnnotationServiceType; +} + +// this is a very generic implementation and some of the edgecases don't apply to the xy chart... We might want to clean up +export function AddLayerButton({ + visualization, + visualizationState, + onAddLayerClick, + layersMeta, + eventAnnotationService, +}: AddLayerButtonProps) { + const [showLayersChoice, toggleLayersChoice] = useState(false); + + const [isLoadLibraryVisible, setLoadLibraryFlyoutVisible] = useState(false); + + const supportedLayers = useMemo(() => { + if (!visualization.appendLayer || !visualizationState) { + return null; + } + return visualization + .getSupportedLayers?.(visualizationState, layersMeta) + ?.filter(({ canAddViaMenu: hideFromMenu }) => !hideFromMenu); + }, [visualization, visualizationState, layersMeta]); + + if (supportedLayers == null || !supportedLayers.length) { + return null; + } + const annotationPanel = ({ + type, + label, + icon, + disabled, + toolTipContent, + }: typeof supportedLayers[0]) => { + return { + panel: 1, + toolTipContent, + disabled, + name: ( + + + {label} + + + + + {i18n.translate('xpack.lens.configPanel.experimentalLabel', { + defaultMessage: 'Technical preview', + })} + + + + ), + className: 'lnsLayerAddButton', + icon: icon && , + ['data-test-subj']: `lnsLayerAddButton-${type}`, + }; + }; + if (supportedLayers.length === 1) { + return ( + + onAddLayerClick(supportedLayers[0].type)} + iconType="layers" + > + {i18n.translate('xpack.lens.configPanel.addLayerButton', { + defaultMessage: 'Add layer', + })} + + + ); + } + return ( + <> + toggleLayersChoice(!showLayersChoice)} + iconType="layers" + > + {i18n.translate('xpack.lens.configPanel.addLayerButton', { + defaultMessage: 'Add layer', + })} + + } + isOpen={showLayersChoice} + closePopover={() => toggleLayersChoice(false)} + panelPaddingSize="none" + > + { + const { type, label, icon, disabled, toolTipContent } = props; + if (type === LayerTypes.ANNOTATIONS) { + return annotationPanel(props); + } + return { + toolTipContent, + disabled, + name: {label}, + className: 'lnsLayerAddButton', + icon: icon && , + ['data-test-subj']: `lnsLayerAddButton-${type}`, + onClick: () => { + onAddLayerClick(type); + toggleLayersChoice(false); + }, + }; + }), + }, + { + id: 1, + initialFocusedItemIndex: 0, + title: i18n.translate('xpack.lens.configPanel.selectAnnotationMethod', { + defaultMessage: 'Select annotation method', + }), + items: [ + { + name: i18n.translate('xpack.lens.configPanel.newAnnotation', { + defaultMessage: 'New annotation', + }), + icon: 'plusInCircleFilled', + onClick: () => { + onAddLayerClick(LayerTypes.ANNOTATIONS); + toggleLayersChoice(false); + }, + }, + { + name: i18n.translate('xpack.lens.configPanel.loadFromLibrary', { + defaultMessage: 'Load from library', + }), + icon: 'folderOpen', + onClick: () => { + setLoadLibraryFlyoutVisible(true); + toggleLayersChoice(false); + }, + }, + ], + }, + ]} + /> + + {isLoadLibraryVisible && ( + { + // todo: expand to be able to initiate adding a layer from the library with data (not empty) + onAddLayerClick(LayerTypes.ANNOTATIONS); + }} + /> + )} + + ); +} diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts deleted file mode 100644 index e3338d006c637..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts +++ /dev/null @@ -1,54 +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'; -import type { LayerAction, StateSetter } from '../../../types'; -import type { XYState, XYAnnotationLayerConfig } from '../types'; - -export const createAnnotationActions = ({ - state, - layer, - layerIndex, - setState, -}: { - state: XYState; - layer: XYAnnotationLayerConfig; - layerIndex: number; - setState: StateSetter; -}): LayerAction[] => { - const label = !layer.ignoreGlobalFilters - ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersLabel', { - defaultMessage: 'Ignore global filters', - }) - : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersLabel', { - defaultMessage: 'Keep global filters', - }); - return [ - { - displayName: label, - description: !layer.ignoreGlobalFilters - ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersDescription', { - defaultMessage: - 'All the dimensions configured in this layer ignore filters defined at kibana level.', - }) - : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersDescription', { - defaultMessage: - 'All the dimensions configured in this layer respect filters defined at kibana level.', - }), - execute: () => { - const newLayers = [...state.layers]; - newLayers[layerIndex] = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; - return setState({ ...state, layers: newLayers }); - }, - icon: !layer.ignoreGlobalFilters ? 'filterIgnore' : 'filter', - isCompatible: true, - 'data-test-subj': !layer.ignoreGlobalFilters - ? 'lnsXY_annotationLayer_ignoreFilters' - : 'lnsXY_annotationLayer_keepFilters', - }, - ]; -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx new file mode 100644 index 0000000000000..c31536508d17b --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.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 React from 'react'; +import { CoreStart } from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { + EuiButton, + EuiButtonEmpty, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutFooter, + EuiForm, + EuiFormRow, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { LayerAction, StateSetter } from '../../../../types'; +import { FlyoutContainer } from '../../../../shared_components/flyout_container'; +import type { XYState, XYAnnotationLayerConfig } from '../../types'; + +export const EditDetailsFlyout = ({ + domElement, + groupLabel, + title, + isNew, + onConfirm, +}: { + domElement: Element; + groupLabel: string; + title?: string; + isNew?: boolean; + onConfirm: () => void; +}) => { + const [newTitle, setNewTitle] = React.useState(title); + // TODO: debounce title change to set in higher level state to persist when closing the flyout + return ( + + + + unmountComponentAtNode(domElement)} + data-test-subj="lns-indexPattern-loadLibraryCancel" + > + {i18n.translate('xpack.lens.annotations.cancel', { + defaultMessage: 'Cancel', + })} + + + + { + onConfirm(); + // todo: notification? + unmountComponentAtNode(domElement); + }} + iconType="plusInCircleFilled" + fill + color="success" + // disabled={!selectedItem} // TODO: disable if no title + > + {i18n.translate('xpack.lens.annotations.addToLibrary', { + defaultMessage: 'Add to library', + })} + + + + + ) : undefined + } + groupLabel={groupLabel} + handleClose={() => { + unmountComponentAtNode(domElement); + return true; + }} + > +
+
+ + + } + > + { + setNewTitle(e.target.value); + }} + isInvalid={!'isDuplicate' || newTitle?.length === 0} + /> + + +
+
+
+ ); +}; + +export const getEditDetailsAction = ({ + state, + layer, + layerIndex, + setState, + core, + isNew, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + layerIndex: number; + setState: StateSetter; + core: CoreStart; + isNew?: boolean; +}): LayerAction => { + const displayName = i18n.translate('xpack.lens.xyChart.annotations.editAnnotationGroupDetails', { + defaultMessage: 'Edit annotation group details', + }); + return { + displayName, + description: i18n.translate( + 'xpack.lens.xyChart.annotations.editAnnotationGroupDetailsDescription', + { defaultMessage: 'Edit title, description and tags of the annotation group' } + ), + execute: async (domElement) => { + console.log('what', domElement); + if (domElement) { + render( + {}} + />, + domElement + ); + } + }, + icon: 'pencil', + isCompatible: true, + 'data-test-subj': 'lnsXY_annotationLayer_editAnnotationDetails', + }; +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts new file mode 100644 index 0000000000000..6cbd7f8cbbdd6 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts @@ -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 { i18n } from '@kbn/i18n'; +import type { LayerAction, StateSetter } from '../../../../types'; +import type { XYState, XYAnnotationLayerConfig } from '../../types'; + +export const IGNORE_GLOBAL_FILTERS_ACTION_ID = 'ignoreGlobalFilters'; +export const KEEP_GLOBAL_FILTERS_ACTION_ID = 'keepGlobalFilters'; + +export const getIgnoreFilterAction = ({ + state, + layer, + layerIndex, + setState, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + layerIndex: number; + setState: StateSetter; +}): LayerAction => { + return { + displayName: !layer.ignoreGlobalFilters + ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersLabel', { + defaultMessage: 'Ignore global filters', + }) + : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersLabel', { + defaultMessage: 'Keep global filters', + }), + description: !layer.ignoreGlobalFilters + ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersDescription', { + defaultMessage: + 'All the dimensions configured in this layer ignore filters defined at kibana level.', + }) + : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersDescription', { + defaultMessage: + 'All the dimensions configured in this layer respect filters defined at kibana level.', + }), + execute: () => { + const newLayers = [...state.layers]; + newLayers[layerIndex] = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; + return setState({ ...state, layers: newLayers }); + }, + icon: !layer.ignoreGlobalFilters ? 'filterIgnore' : 'filter', + isCompatible: true, + 'data-test-subj': !layer.ignoreGlobalFilters + ? 'lnsXY_annotationLayer_ignoreFilters' + : 'lnsXY_annotationLayer_keepFilters', + }; +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts new file mode 100644 index 0000000000000..abc2a2e713f22 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -0,0 +1,110 @@ +/* + * 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 { CoreStart } from '@kbn/core/public'; +import type { LayerAction, StateSetter } from '../../../../types'; +import type { XYState, XYAnnotationLayerConfig } from '../../types'; +import { getUnlinkLayerAction } from './unlink_action'; +import { getIgnoreFilterAction } from './ignore_filters_action'; +import { getEditDetailsAction } from './edit_details_action'; +// import { getRevertAction } from './revert_changes_action'; +import { getSaveLayerAction } from './save_action'; +export { + IGNORE_GLOBAL_FILTERS_ACTION_ID, + KEEP_GLOBAL_FILTERS_ACTION_ID, +} from './ignore_filters_action'; + +export const createAnnotationActions = ({ + state, + layer, + layerIndex, + setState, + core, + isSaveable, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + layerIndex: number; + setState: StateSetter; + core: CoreStart; + isSaveable?: boolean; +}): LayerAction[] => { + const actions = []; + + const savingToLibraryPermitted = Boolean( + core.application.capabilities.visualize.save && isSaveable + ); + + if (savingToLibraryPermitted) { + // check if the annotation is saved as a saved object or in inline - same as we check for save modal for visualization + const isAnnotationGroupSO = true; + + if (isAnnotationGroupSO) { + // check if Annotation group hasUnsavedChanges to know if we should allow reverting and saving - similar to how we do it for persistedDoc vs currentDoc on app level + const hasUnsavedChanges = true; + + if (hasUnsavedChanges) { + const saveAction = getSaveLayerAction({ + state, + layer, + layerIndex, + setState, + core, + execute: () => { + // save the annotation group + // update state + }, + }); + actions.push(saveAction); + } + + const editDetailsAction = getEditDetailsAction({ state, layer, layerIndex, setState, core }); + + const unlinkAction = getUnlinkLayerAction({ + execute: () => { + const title = 'Annotation group name'; // TODO: pass the title from Annotation group state + // save to Lens Saved object state - there's nothing we should do with the Annotation group Saved Object + // update Lens state + core.notifications.toasts.addSuccess( + i18n.translate('xpack.lens.xyChart.annotations.notificationUnlinked', { + defaultMessage: `Unlinked “{title}“ from library`, + values: { title }, + }) + ); + }, + core, + }); + actions.push(editDetailsAction, unlinkAction); + + // revert can be implemented later + // if (hasUnsavedChanges) { + // const revertAction = getRevertAction({ state, layer, layerIndex, setState }); + // actions.push(revertAction); + // } + } else { + actions.push( + getSaveLayerAction({ + isNew: true, + state, + layer, + layerIndex, + setState, + core, + execute: () => { + // Save to library + // update state + }, + }) + ); + } + } + + const ignoreGlobalFiltersAction = getIgnoreFilterAction({ state, layer, layerIndex, setState }); + actions.push(ignoreGlobalFiltersAction); + return actions; +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts new file mode 100644 index 0000000000000..ce171af26fb20 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.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 { i18n } from '@kbn/i18n'; +import type { LayerAction, StateSetter } from '../../../../types'; +import type { XYState, XYAnnotationLayerConfig } from '../../types'; + +export const getRevertAction = ({ + state, + layer, + layerIndex, + setState, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + layerIndex: number; + setState: StateSetter; +}): LayerAction => { + return { + displayName: i18n.translate('xpack.lens.xyChart.annotations.revertChanges', { + defaultMessage: 'Revert changes', + }), + description: i18n.translate('xpack.lens.xyChart.annotations.revertChangesDescription', { + defaultMessage: 'Restores annotation group to the last saved state.', + }), + execute: () => { + // TODO: open flyout ? + // console.log('TODO: Revert changes action!'); + }, + icon: 'editorUndo', + isCompatible: true, + 'data-test-subj': 'lnsXY_annotationLayer_revertChanges', + }; +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx new file mode 100644 index 0000000000000..9f31bf0c2f59e --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.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 React from 'react'; +import type { CoreStart } from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import { render } from 'react-dom'; +import type { LayerAction, StateSetter } from '../../../../types'; +import { XYAnnotationLayerConfig, XYState } from '../../types'; +import { EditDetailsFlyout } from './edit_details_action'; + +export const getSaveLayerAction = ({ + state, + layer, + layerIndex, + setState, + core, + isNew, + execute, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + layerIndex: number; + setState: StateSetter; + core: CoreStart; + isNew?: boolean; + execute: () => void; +}): LayerAction => { + const displayName = isNew + ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { + defaultMessage: 'Add to library', + }) + : i18n.translate('xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary', { + defaultMessage: 'Save', + }); + return { + displayName, + description: i18n.translate( + 'xpack.lens.xyChart.annotations.addAnnotationGroupToLibraryDescription', + { defaultMessage: 'Saves annotation group as separate saved object' } + ), + execute: async (domElement) => { + console.log('execute', domElement); + if (domElement && isNew) { + render( + { + execute(); + }} + />, + domElement + ); + } else { + execute(); + } + }, + icon: 'save', + isCompatible: true, + 'data-test-subj': 'lnsXY_annotationLayer_saveToLibrary', + }; +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx new file mode 100644 index 0000000000000..d52840b862e27 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useState } from 'react'; +import type { CoreStart } from '@kbn/core/public'; +import { + EuiButton, + EuiButtonEmpty, + EuiCheckbox, + EuiCheckboxProps, + EuiFlexGroup, + EuiFlexItem, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { LayerAction } from '../../../../types'; +import { LOCAL_STORAGE_LENS_KEY } from '../../../../settings_storage'; + +export const getUnlinkLayerAction = (props: { + execute: () => void; + core: Pick; +}): LayerAction => { + const { modalTitle, modalDesc } = getButtonCopy('title'); + + return { + execute: async () => { + const storage = new Storage(localStorage); + const lensLocalStorage = storage.get(LOCAL_STORAGE_LENS_KEY) ?? {}; + + const updateLensLocalStorage = (partial: Record) => { + storage.set(LOCAL_STORAGE_LENS_KEY, { + ...lensLocalStorage, + ...partial, + }); + }; + + if (!lensLocalStorage.skipUnlinkFromLibraryModal) { + const modal = props.core.overlays.openModal( + toMountPoint( + modal.close()} + updateLensLocalStorage={updateLensLocalStorage} + />, + { theme$: props.core.theme.theme$ } + ), + { + 'data-test-subj': 'lnsLayerUnlinkModal', + } + ); + await modal.onClose; + } else { + props.execute(); + } + }, + + description: i18n.translate('xpack.lens.xyChart.annotations.unlinksFromLibrary', { + defaultMessage: 'Saves the annotation group as a part of the Lens Saved Object', + }), + displayName: i18n.translate('xpack.lens.xyChart.annotations.unlinkFromLibrary', { + defaultMessage: 'Unlink from library', + }), + isCompatible: true, + icon: 'unlink', + 'data-test-subj': 'lnsXY_annotationLayer_unlinkFromLibrary', + }; +}; + +const SKIP_UNLINK_FROM_LIBRARY_KEY = 'skipUnlinkFromLibraryModal'; + +const getButtonCopy = (title: string) => { + const modalTitle = i18n.translate('xpack.lens.modalTitle.unlinkAnnotationGroupTitle', { + defaultMessage: 'Unlink "{title}" from library?', + values: { title }, + }); + const modalDesc = i18n.translate( + 'xpack.lens.layer.unlinkModal.unlinkAnnotationGroupDescription', + { + defaultMessage: `Unlinking from the library will cause this annotation group to be saved locally to this visualization. Any changes made will no longer be shared with the originating annotation library group.`, + } + ); + + return { + modalTitle, + modalDesc, + }; +}; + +export const UnlinkConfirmModal = ({ + modalTitle, + modalDesc, + skipUnlinkFromLibraryModal, + execute, + closeModal, + updateLensLocalStorage, +}: { + modalTitle: string; + modalDesc: string; + skipUnlinkFromLibraryModal: boolean; + execute: () => void; + closeModal: () => void; + updateLensLocalStorage: (partial: Record) => void; +}) => { + const [skipDeleteModalLocal, setSkipDeleteModalLocal] = useState(skipUnlinkFromLibraryModal); + const onChangeShouldShowModal: EuiCheckboxProps['onChange'] = useCallback( + ({ target }) => setSkipDeleteModalLocal(target.checked), + [] + ); + + const onUnlink = useCallback(() => { + updateLensLocalStorage({ + [SKIP_UNLINK_FROM_LIBRARY_KEY]: skipDeleteModalLocal, + }); + closeModal(); + execute(); + }, [closeModal, execute, skipDeleteModalLocal, updateLensLocalStorage]); + + return ( + <> + + {modalTitle} + + +

+ {modalDesc} + {i18n.translate('xpack.lens.layer.unlinkModal.cannotUndo', { + defaultMessage: `You can't undo this action.`, + })} +

+
+ + + + + + + + + + + {i18n.translate('xpack.lens.layer.cancelDelete', { + defaultMessage: `Cancel`, + })} + + + + + {i18n.translate('xpack.lens.layer.unlinkConfirm', { + defaultMessage: `Unlink`, + })} + + + + + + + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/index.ts b/x-pack/plugins/lens/public/visualizations/xy/index.ts index 26001e7245fef..800c2227baadd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/index.ts @@ -34,7 +34,7 @@ export class XyVisualization { await core.getStartServices(); const [palettes, eventAnnotationService] = await Promise.all([ charts.palettes.getPalettes(), - eventAnnotation.getService(), + eventAnnotation.getService(coreStart), ]); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); return getXyVisualization({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx new file mode 100644 index 0000000000000..a14e487e529d6 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -0,0 +1,134 @@ +/* + * 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, { useRef } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutFooter, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; +import { FlyoutContainer } from '../../shared_components/flyout_container'; + +export function LoadAnnotationLibraryFlyout({ + eventAnnotationService, + isLoadLibraryVisible, + setLoadLibraryFlyoutVisible, + addLayer, +}: { + isLoadLibraryVisible: boolean; + setLoadLibraryFlyoutVisible: (visible: boolean) => void; + eventAnnotationService: EventAnnotationServiceType; + addLayer: () => void; +}) { + const containerPanelRef = useRef(null); + const otherElementsHeight = 250; + const singleEntryHeight = 40; + const numberOfElements = Math.floor( + ((containerPanelRef?.current?.clientHeight || 800) - otherElementsHeight) / singleEntryHeight + ); + + const [selectedItem, setSelectedItem] = React.useState<{ + id: string; + type: string; + fullName: string; + savedObject: SavedObjectCommon; + } | null>(null); + + // needed to clean the state when clicking not on the item on the list + const hasBeenClicked = useRef(false); + + React.useEffect(() => { + hasBeenClicked.current = false; + }, [selectedItem]); + + const { + renderEventAnnotationGroupSavedObjectFinder: EventAnnotationGroupSavedObjectFinder, + loadAnnotationGroup, + } = eventAnnotationService || {}; + + return ( + { + if (!hasBeenClicked.current) { + setSelectedItem(null); + } + }} + panelContainerRef={(el) => (containerPanelRef.current = el)} + customFooter={ + + + + { + setLoadLibraryFlyoutVisible(false); + setSelectedItem(null); + }} + data-test-subj="lns-indexPattern-loadLibraryCancel" + > + {i18n.translate('xpack.lens.loadAnnotationsLibrary.cancel', { + defaultMessage: 'Cancel', + })} + + + + { + setLoadLibraryFlyoutVisible(false); + if (selectedItem) { + loadAnnotationGroup(selectedItem?.id).then((loadedGroup) => { + console.log('loadedGroup:', loadedGroup); + addLayer(); + }); + } + }} + iconType="folderOpen" + fill + disabled={!selectedItem} + > + {i18n.translate('xpack.lens.loadAnnotationsLibrary.loadSelected', { + defaultMessage: 'Load selected', + })} + + + + + } + isOpen={Boolean(isLoadLibraryVisible)} + groupLabel={i18n.translate('xpack.lens.editorFrame.loadFromLibrary', { + defaultMessage: 'Select annotations from library', + })} + handleClose={() => { + setLoadLibraryFlyoutVisible(false); + setSelectedItem(null); + return true; + }} + > +
+ { + hasBeenClicked.current = true; + setSelectedItem({ id, type, fullName, savedObject }); + }} + fixedPageSize={numberOfElements} + /> +
+
+ ); +} diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index f146165bf4bd8..ad7db64deae31 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -80,13 +80,15 @@ function exampleState(): XYState { const paletteServiceMock = chartPluginMock.createPaletteRegistry(); const fieldFormatsMock = fieldFormatsServiceMock.createStartContract(); +const core = coreMock.createStart(); + const xyVisualization = getXyVisualization({ paletteService: paletteServiceMock, fieldFormats: fieldFormatsMock, useLegacyTimeAxis: false, kibanaTheme: themeServiceMock.createStartContract(), eventAnnotationService: eventAnnotationServiceMock, - core: coreMock.createStart(), + core, storage: {} as IStorageWrapper, data: dataPluginMock.createStartContract(), unifiedSearch: unifiedSearchPluginMock.createStartContract(), @@ -3021,7 +3023,7 @@ describe('xy_visualization', () => { }, setState )!; - action.execute(); + action.execute(null); expect(setState).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index f8978cd1fa0e3..dd0409bdbdec2 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -102,6 +102,7 @@ import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; import { createAnnotationActions } from './annotations/actions'; +import { AddLayerButton } from './add_layer'; const XY_ID = 'lnsXY'; export const getXyVisualization = ({ @@ -201,10 +202,11 @@ export const getXyVisualization = ({ }; }, + // how to implement this for saved object annotations? getPersistableState(state) { return extractReferences(state); }, - + // how to implement this for saved object annotations? fromPersistableState(state, references, initialContext) { return injectReferences(state, references, initialContext); }, @@ -256,12 +258,14 @@ export const getXyVisualization = ({ ]; }, - getSupportedActionsForLayer(layerId, state, setState) { + getSupportedActionsForLayer(layerId, state, setState, isSaveable) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; const actions = []; if (isAnnotationsLayer(layer)) { - actions.push(...createAnnotationActions({ state, layerIndex, layer, setState })); + actions.push( + ...createAnnotationActions({ state, layerIndex, layer, setState, core, isSaveable }) + ); } return actions; }, @@ -656,7 +660,9 @@ export const getXyVisualization = ({ domElement ); }, - + getAddLayerButtonComponent: (props) => { + return ; + }, toExpression: (state, layers, attributes, datasourceExpressionsByLayers = {}) => toExpression( state, From f32c013ec9b21d72f0ddffa8d9fa2e639e89f98e Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 28 Feb 2023 16:23:18 -0600 Subject: [PATCH 003/202] saving annotation group to library --- .../event_annotation/common/constants.ts | 1 - src/plugins/event_annotation/common/types.ts | 2 - src/plugins/event_annotation/kibana.jsonc | 3 +- .../event_annotation_service/service.test.ts | 45 +--- .../event_annotation_service/service.tsx | 216 ++++++++---------- .../public/event_annotation_service/types.ts | 20 +- src/plugins/event_annotation/server/plugin.ts | 4 +- .../event_annotation/server/saved_objects.ts | 136 +---------- x-pack/plugins/lens/common/constants.ts | 2 - .../actions/edit_details_action.tsx | 10 +- .../xy/annotations/actions/index.ts | 32 +-- .../xy/annotations/actions/save_action.tsx | 73 ++++-- .../lens/public/visualizations/xy/types.ts | 24 +- .../visualizations/xy/visualization.tsx | 10 +- 14 files changed, 230 insertions(+), 348 deletions(-) diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts index 1a7a889d6bc9c..04255cee00c22 100644 --- a/src/plugins/event_annotation/common/constants.ts +++ b/src/plugins/event_annotation/common/constants.ts @@ -25,4 +25,3 @@ export const AvailableAnnotationIcons = { } as const; export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; -export const EVENT_ANNOTATION_TYPE = 'event-annotation'; diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index d63b09d7dce97..2001935629c9e 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -94,8 +94,6 @@ export interface EventAnnotationGroupConfig { indexPatternId: string; ignoreGlobalFilters?: boolean; title?: string; - description?: string; - tags?: string[]; } export type EventAnnotationArgs = diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index f4f2a4791cfbb..a1e7cf29d3688 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -9,7 +9,8 @@ "browser": true, "requiredPlugins": [ "expressions", - "data" + "data", + "savedObjects" ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 27dce04642a11..ba411761f6c87 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -442,7 +442,7 @@ describe('Event Annotation Service', () => { } ); }); - describe('loadAnnotationGroup', () => { + describe.skip('loadAnnotationGroup', () => { it('should not error when loading group doesnt exit', async () => { expect(await eventAnnotationService.loadAnnotationGroup('nonExistingGroup')).toEqual({ annotations: [], @@ -468,7 +468,7 @@ describe('Event Annotation Service', () => { }); }); }); - describe('deleteAnnotationGroup', () => { + describe.skip('deleteAnnotationGroup', () => { it('deletes annotation group along with annotations that reference them', async () => { await eventAnnotationService.deleteAnnotationGroup('multiAnnotations'); expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ @@ -480,17 +480,18 @@ describe('Event Annotation Service', () => { }); describe('createAnnotationGroup', () => { it('creates annotation group along with annotations', async () => { + const annotations = [ + annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, + annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, + ]; await eventAnnotationService.createAnnotationGroup({ title: 'newGroupTitle', indexPatternId: 'ipid', - annotations: [ - annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, - annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, - ], + annotations, }); expect(core.savedObjects.client.create).toHaveBeenCalledWith( 'event-annotation-group', - { title: 'newGroupTitle' }, + { title: 'newGroupTitle', annotations }, { references: [ { @@ -501,35 +502,9 @@ describe('Event Annotation Service', () => { ], } ); - expect(core.savedObjects.client.bulkCreate).toHaveBeenCalledWith([ - { - attributes: annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, - id: 'annotation1', - references: [ - { - id: 'multiAnnotations', - name: 'event-annotation_group-ref-annotation1', - type: 'event-annotation-group', - }, - ], - type: 'event-annotation', - }, - { - attributes: annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, - id: 'ann2', - references: [ - { - id: 'multiAnnotations', - name: 'event-annotation_group-ref-ann2', - type: 'event-annotation-group', - }, - ], - type: 'event-annotation', - }, - ]); }); }); - describe('updateAnnotationGroupAttributes', () => { + describe.skip('updateAnnotationGroupAttributes', () => { it('updates annotation group attributes', async () => { await eventAnnotationService.updateAnnotationGroup( { title: 'newTitle', indexPatternId: 'newId', annotations: [] }, @@ -551,7 +526,7 @@ describe('Event Annotation Service', () => { ); }); }); - describe('updateAnnotations', () => { + describe.skip('updateAnnotations', () => { const upsert = [ { id: 'annotation2', diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index efa6a2a3a2145..07629fa693887 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -15,9 +15,7 @@ import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { EventAnnotationConfig, EventAnnotationGroupConfig, - EventAnnotationGroupAttributes, EVENT_ANNOTATION_GROUP_TYPE, - EVENT_ANNOTATION_TYPE, } from '../../common'; import { EventAnnotationServiceType } from './types'; import { @@ -36,67 +34,67 @@ export function hasIcon(icon: string | undefined): icon is string { export function getEventAnnotationService(core: CoreStart): EventAnnotationServiceType { const client: SavedObjectsClientContract = core.savedObjects.client; - const loadAnnotationGroup = async ( - savedObjectId: string - ): Promise => { - const groupResponse = await client.resolve( - EVENT_ANNOTATION_GROUP_TYPE, - savedObjectId - ); + // const loadAnnotationGroup = async ( + // savedObjectId: string + // ): Promise => { + // const groupResponse = await client.resolve( + // EVENT_ANNOTATION_GROUP_TYPE, + // savedObjectId + // ); - if (groupResponse.saved_object.error) { - throw groupResponse.saved_object.error; - } + // if (groupResponse.saved_object.error) { + // throw groupResponse.saved_object.error; + // } - const annotations = ( - await client.find({ - type: EVENT_ANNOTATION_TYPE, - hasReference: { - type: EVENT_ANNOTATION_GROUP_TYPE, - id: savedObjectId, - }, - }) - ).savedObjects - .filter(({ error }) => !error) - .map((annotation) => annotation.attributes); + // const annotations = ( + // await client.find({ + // type: EVENT_ANNOTATION_TYPE, + // hasReference: { + // type: EVENT_ANNOTATION_GROUP_TYPE, + // id: savedObjectId, + // }, + // }) + // ).savedObjects + // .filter(({ error }) => !error) + // .map((annotation) => annotation.attributes); - return { - title: groupResponse.saved_object.attributes.title, - description: groupResponse.saved_object.attributes.description, - tags: groupResponse.saved_object.attributes.tags, - ignoreGlobalFilters: groupResponse.saved_object.attributes.ignoreGlobalFilters, - indexPatternId: groupResponse.saved_object.references.find( - (ref) => ref.type === 'index-pattern' - )?.id!, - annotations, - }; - }; + // return { + // title: groupResponse.saved_object.attributes.title, + // description: groupResponse.saved_object.attributes.description, + // tags: groupResponse.saved_object.attributes.tags, + // ignoreGlobalFilters: groupResponse.saved_object.attributes.ignoreGlobalFilters, + // indexPatternId: groupResponse.saved_object.references.find( + // (ref) => ref.type === 'index-pattern' + // )?.id!, + // annotations, + // }; + // }; - const deleteAnnotationGroup = async (savedObjectId: string): Promise => { - const annotationsSOs = ( - await client.find({ - type: EVENT_ANNOTATION_TYPE, - hasReference: { - type: EVENT_ANNOTATION_GROUP_TYPE, - id: savedObjectId, - }, - }) - ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); - await client.bulkDelete([ - { type: EVENT_ANNOTATION_GROUP_TYPE, id: savedObjectId }, - ...annotationsSOs, - ]); - }; + // const deleteAnnotationGroup = async (savedObjectId: string): Promise => { + // const annotationsSOs = ( + // await client.find({ + // type: EVENT_ANNOTATION_TYPE, + // hasReference: { + // type: EVENT_ANNOTATION_GROUP_TYPE, + // id: savedObjectId, + // }, + // }) + // ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); + // await client.bulkDelete([ + // { type: EVENT_ANNOTATION_GROUP_TYPE, id: savedObjectId }, + // ...annotationsSOs, + // ]); + // }; const createAnnotationGroup = async ( group: EventAnnotationGroupConfig ): Promise<{ id: string }> => { - const { title, description, tags, ignoreGlobalFilters, indexPatternId, annotations } = group; + const { title, ignoreGlobalFilters, indexPatternId, annotations } = group; const groupSavedObjectId = ( await client.create( EVENT_ANNOTATION_GROUP_TYPE, - { title, description, tags, ignoreGlobalFilters }, + { title, ignoreGlobalFilters, annotations }, { references: [ { @@ -109,80 +107,64 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi ) ).id; - if (annotations && annotations.length) { - await client.bulkCreate( - annotations.map((annotation) => ({ - type: EVENT_ANNOTATION_TYPE, - id: annotation.id, - attributes: annotation, - references: [ - { - type: EVENT_ANNOTATION_GROUP_TYPE, - id: groupSavedObjectId, - name: `event-annotation_group-ref-${annotation.id}`, // do name have to be unique with id? - }, - ], - })) - ); - } return { id: groupSavedObjectId }; }; - const updateAnnotationGroup = async ( - group: EventAnnotationGroupConfig, - savedObjectId: string - ): Promise => { - const { title, description, tags, ignoreGlobalFilters, indexPatternId } = group; - await client.update( - EVENT_ANNOTATION_GROUP_TYPE, - savedObjectId, - { title, description, tags, ignoreGlobalFilters }, - { - references: [ - { - type: 'index-pattern', - id: indexPatternId, - name: `event-annotation-group_dataView-ref-${indexPatternId}`, - }, - ], - } - ); - }; + // const updateAnnotationGroup = async ( + // group: EventAnnotationGroupConfig, + // savedObjectId: string + // ): Promise => { + // const { title, description, tags, ignoreGlobalFilters, indexPatternId } = group; + // await client.update( + // EVENT_ANNOTATION_GROUP_TYPE, + // savedObjectId, + // { title, description, tags, ignoreGlobalFilters }, + // { + // references: [ + // { + // type: 'index-pattern', + // id: indexPatternId, + // name: `event-annotation-group_dataView-ref-${indexPatternId}`, + // }, + // ], + // } + // ); + // }; - const updateAnnotations = async ( - savedObjectId: string, - modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } - ): Promise => { - if (modifications.delete && modifications.delete.length > 0) { - await client.bulkDelete( - modifications.delete.map((id) => ({ type: EVENT_ANNOTATION_TYPE, id })) - ); - } + // const updateAnnotations = async ( + // savedObjectId: string, + // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } + // ): Promise => { + // if (modifications.delete && modifications.delete.length > 0) { + // await client.bulkDelete( + // modifications.delete.map((id) => ({ type: EVENT_ANNOTATION_TYPE, id })) + // ); + // } - if (modifications.upsert && modifications.upsert.length > 0) { - const annotationsToUpdate = modifications.upsert.map((a) => ({ - type: EVENT_ANNOTATION_TYPE, - attributes: a, - id: a.id, - overwrite: true, - references: [ - { - type: EVENT_ANNOTATION_GROUP_TYPE, - id: savedObjectId, - name: `event-annotation-group-ref-${a.id}`, - }, - ], - })); - await client.bulkCreate(annotationsToUpdate); - } - }; + // if (modifications.upsert && modifications.upsert.length > 0) { + // const annotationsToUpdate = modifications.upsert.map((a) => ({ + // type: EVENT_ANNOTATION_TYPE, + // attributes: a, + // id: a.id, + // overwrite: true, + // references: [ + // { + // type: EVENT_ANNOTATION_GROUP_TYPE, + // id: savedObjectId, + // name: `event-annotation-group-ref-${a.id}`, + // }, + // ], + // })); + // await client.bulkCreate(annotationsToUpdate); + // } + // }; return { - loadAnnotationGroup, - updateAnnotations, - updateAnnotationGroup, + // loadAnnotationGroup, + // updateAnnotations, + // updateAnnotationGroup, createAnnotationGroup, - deleteAnnotationGroup, + // deleteAnnotationGroup, renderEventAnnotationGroupSavedObjectFinder: (props: { fixedPageSize: number; onChoose: (value: { diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index 68f4c8556594f..9ea5c9b625bd3 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -11,17 +11,17 @@ import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; export interface EventAnnotationServiceType { - loadAnnotationGroup: (savedObjectId: string) => Promise; - deleteAnnotationGroup: (savedObjectId: string) => Promise; + // loadAnnotationGroup: (savedObjectId: string) => Promise; + // deleteAnnotationGroup: (savedObjectId: string) => Promise; createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; - updateAnnotations: ( - savedObjectId: string, - modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } - ) => Promise; - updateAnnotationGroup: ( - group: EventAnnotationGroupConfig, - savedObjectId: string - ) => Promise; + // updateAnnotations: ( + // savedObjectId: string, + // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } + // ) => Promise; + // updateAnnotationGroup: ( + // group: EventAnnotationGroupConfig, + // savedObjectId: string + // ) => Promise; toExpression: (props: EventAnnotationConfig[]) => ExpressionAstExpression[]; toFetchExpression: (props: { interval: string; diff --git a/src/plugins/event_annotation/server/plugin.ts b/src/plugins/event_annotation/server/plugin.ts index c53eb128e4fba..0ae55744016e6 100644 --- a/src/plugins/event_annotation/server/plugin.ts +++ b/src/plugins/event_annotation/server/plugin.ts @@ -34,9 +34,7 @@ export class EventAnnotationServerPlugin implements Plugin { dependencies.expressions.registerFunction(manualRangeEventAnnotation); dependencies.expressions.registerFunction(queryPointEventAnnotation); dependencies.expressions.registerFunction(eventAnnotationGroup); - // dependencies.expressions.registerFunction( - // getFetchEventAnnotations({ getStartServices: core.getStartServices }) - // ); + setupSavedObjects(core); return {}; diff --git a/src/plugins/event_annotation/server/saved_objects.ts b/src/plugins/event_annotation/server/saved_objects.ts index d228bc7ea0072..443df50aaa0c9 100644 --- a/src/plugins/event_annotation/server/saved_objects.ts +++ b/src/plugins/event_annotation/server/saved_objects.ts @@ -9,42 +9,30 @@ import { CoreSetup, mergeSavedObjectMigrationMaps, - SavedObject, SavedObjectMigrationMap, } from '@kbn/core/server'; -import type { - SavedObjectsClientContract, - SavedObjectsExportTransformContext, -} from '@kbn/core/server'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; -import { EventAnnotationConfig } from '../common'; -import { EVENT_ANNOTATION_GROUP_TYPE, EVENT_ANNOTATION_TYPE } from '../common/constants'; +import { EVENT_ANNOTATION_GROUP_TYPE } from '../common/constants'; import { EventAnnotationGroupAttributes } from '../common/types'; -type EventAnnotationAttributes = EventAnnotationConfig; - -export const EVENT_ANNOTATIONS_INDEX = '.kibana_event_annotations'; - export function setupSavedObjects(coreSetup: CoreSetup) { coreSetup.savedObjects.registerType({ name: EVENT_ANNOTATION_GROUP_TYPE, hidden: false, - namespaceType: 'multiple-isolated', - indexPattern: EVENT_ANNOTATIONS_INDEX, + namespaceType: 'multiple', // TODO double-check type management: { icon: 'questionInCircle', defaultSearchField: 'title', importableAndExportable: true, getTitle: (obj: { attributes: EventAnnotationGroupAttributes }) => obj.attributes.title, - onExport: async (context, objects: Array>) => - handleExport({ context, objects, coreSetup }), }, migrations: () => { const dataViewMigrations = DataViewPersistableStateService.getAllMigrations(); return mergeSavedObjectMigrationMaps(eventAnnotationGroupMigrations, dataViewMigrations); }, mappings: { + dynamic: false, properties: { title: { type: 'text', @@ -52,127 +40,9 @@ export function setupSavedObjects(coreSetup: CoreSetup) { description: { type: 'text', }, - tags: { - type: 'keyword', - }, - ignoreGlobalFilters: { - type: 'boolean', - }, - }, - }, - }); - - coreSetup.savedObjects.registerType({ - name: EVENT_ANNOTATION_TYPE, - indexPattern: EVENT_ANNOTATIONS_INDEX, - hidden: false, - namespaceType: 'multiple-isolated', - management: { - visibleInManagement: false, - importableAndExportable: true, - }, - migrations: () => eventAnnotationMigrations, - mappings: { - properties: { - id: { - type: 'text', - }, - type: { - type: 'text', - }, - key: { - type: 'flattened', - }, - color: { - type: 'text', - }, - lineStyle: { - type: 'text', - }, - icon: { - type: 'text', - }, - label: { - type: 'text', - }, - lineWidth: { - type: 'long', - }, - extraFields: { - type: 'flattened', - }, - filter: { - type: 'flattened', - }, - outside: { - type: 'boolean', - }, - textVisibility: { - type: 'boolean', - }, - isHidden: { - type: 'boolean', - }, - timeField: { - type: 'text', - }, - textField: { - type: 'text', - }, }, }, }); } -export async function handleExport({ - context, - objects, - coreSetup, -}: { - context: SavedObjectsExportTransformContext; - objects: Array>; - coreSetup: CoreSetup; -}): Promise>> { - try { - if (objects.length <= 0) { - return []; - } - const [{ savedObjects }] = await coreSetup.getStartServices(); - const savedObjectsClient = savedObjects.getScopedClient(context.request); - - const annotationsSavedObjects = await getAnnotationsSavedObjects({ - savedObjectsClient, - groupIds: objects.map((o) => o.id), - }); - - return [...objects, ...annotationsSavedObjects.flat()]; - } catch (error) { - throw new Error('Error exporting annotations'); - } -} - -async function getAnnotationsSavedObjects({ - savedObjectsClient, - groupIds, -}: { - savedObjectsClient: SavedObjectsClientContract; - groupIds: string[]; -}): Promise>> { - const references = groupIds.map((id) => ({ type: EVENT_ANNOTATION_GROUP_TYPE, id })); - - const finder = savedObjectsClient.createPointInTimeFinder({ - type: EVENT_ANNOTATION_TYPE, - hasReference: references, - }); - - let result: Array> = []; - for await (const findResults of finder.find()) { - result = result.concat(findResults.saved_objects); - } - - return result; -} - const eventAnnotationGroupMigrations: SavedObjectMigrationMap = {}; - -const eventAnnotationMigrations: SavedObjectMigrationMap = {}; diff --git a/x-pack/plugins/lens/common/constants.ts b/x-pack/plugins/lens/common/constants.ts index 4ea5f1e38032d..1495410cdb14c 100644 --- a/x-pack/plugins/lens/common/constants.ts +++ b/x-pack/plugins/lens/common/constants.ts @@ -17,8 +17,6 @@ export const NOT_INTERNATIONALIZED_PRODUCT_NAME = 'Lens Visualizations'; export const BASE_API_URL = '/api/lens'; export const LENS_EDIT_BY_VALUE = 'edit_by_value'; -export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; - export const ENABLE_SQL = 'discover:enableSql'; export const PieChartTypes = { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx index c31536508d17b..2962ea33bdb07 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx @@ -35,7 +35,7 @@ export const EditDetailsFlyout = ({ groupLabel: string; title?: string; isNew?: boolean; - onConfirm: () => void; + onConfirm: (title: string) => void; }) => { const [newTitle, setNewTitle] = React.useState(title); // TODO: debounce title change to set in higher level state to persist when closing the flyout @@ -67,9 +67,11 @@ export const EditDetailsFlyout = ({ { - onConfirm(); - // todo: notification? - unmountComponentAtNode(domElement); + if (newTitle) { + onConfirm(newTitle); + // todo: notification? + unmountComponentAtNode(domElement); + } }} iconType="plusInCircleFilled" fill diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index abc2a2e713f22..411b3fd0cf930 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -7,12 +7,12 @@ import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; -import type { XYState, XYAnnotationLayerConfig } from '../../types'; +import { XYState, XYAnnotationLayerConfig, isByReferenceXyAnnotationLayer } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; import { getIgnoreFilterAction } from './ignore_filters_action'; import { getEditDetailsAction } from './edit_details_action'; -// import { getRevertAction } from './revert_changes_action'; import { getSaveLayerAction } from './save_action'; export { IGNORE_GLOBAL_FILTERS_ACTION_ID, @@ -26,6 +26,7 @@ export const createAnnotationActions = ({ setState, core, isSaveable, + eventAnnotationService, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -33,6 +34,7 @@ export const createAnnotationActions = ({ setState: StateSetter; core: CoreStart; isSaveable?: boolean; + eventAnnotationService: EventAnnotationServiceType; }): LayerAction[] => { const actions = []; @@ -42,9 +44,7 @@ export const createAnnotationActions = ({ if (savingToLibraryPermitted) { // check if the annotation is saved as a saved object or in inline - same as we check for save modal for visualization - const isAnnotationGroupSO = true; - - if (isAnnotationGroupSO) { + if (isByReferenceXyAnnotationLayer(layer)) { // check if Annotation group hasUnsavedChanges to know if we should allow reverting and saving - similar to how we do it for persistedDoc vs currentDoc on app level const hasUnsavedChanges = true; @@ -52,13 +52,9 @@ export const createAnnotationActions = ({ const saveAction = getSaveLayerAction({ state, layer, - layerIndex, setState, - core, - execute: () => { - // save the annotation group - // update state - }, + eventAnnotationService, + toasts: core.notifications.toasts, }); actions.push(saveAction); } @@ -80,25 +76,15 @@ export const createAnnotationActions = ({ core, }); actions.push(editDetailsAction, unlinkAction); - - // revert can be implemented later - // if (hasUnsavedChanges) { - // const revertAction = getRevertAction({ state, layer, layerIndex, setState }); - // actions.push(revertAction); - // } } else { actions.push( getSaveLayerAction({ isNew: true, state, layer, - layerIndex, setState, - core, - execute: () => { - // Save to library - // update state - }, + eventAnnotationService, + toasts: core.notifications.toasts, }) ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 9f31bf0c2f59e..38f32af7e813f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -6,36 +6,37 @@ */ import React from 'react'; -import type { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { render } from 'react-dom'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { ToastsStart } from '@kbn/core-notifications-browser'; +import { MountPoint } from '@kbn/core-mount-utils-browser'; +import { FormattedMessage } from '@kbn/i18n-react'; import type { LayerAction, StateSetter } from '../../../../types'; -import { XYAnnotationLayerConfig, XYState } from '../../types'; +import { ByReferenceXYAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; import { EditDetailsFlyout } from './edit_details_action'; export const getSaveLayerAction = ({ state, layer, - layerIndex, setState, - core, + eventAnnotationService, isNew, - execute, + toasts, }: { state: XYState; layer: XYAnnotationLayerConfig; - layerIndex: number; setState: StateSetter; - core: CoreStart; + eventAnnotationService: EventAnnotationServiceType; isNew?: boolean; - execute: () => void; + toasts: ToastsStart; }): LayerAction => { const displayName = isNew ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { defaultMessage: 'Add to library', }) : i18n.translate('xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary', { - defaultMessage: 'Save', + defaultMessage: 'Save to library', }); return { displayName, @@ -44,7 +45,6 @@ export const getSaveLayerAction = ({ { defaultMessage: 'Saves annotation group as separate saved object' } ), execute: async (domElement) => { - console.log('execute', domElement); if (domElement && isNew) { render( { - execute(); + isNew={isNew} + onConfirm={async (title) => { + // TODO - error handling + const { id } = await eventAnnotationService.createAnnotationGroup({ + ...layer, + title, + }); + + const newLayer: ByReferenceXYAnnotationLayerConfig = { + ...layer, + annotationGroupId: id, + __lastSaved: { + title, + ...layer, + }, + }; + + setState({ + ...state, + layers: state.layers.map((existingLayer) => + existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer + ), + }); + + toasts.addSuccess({ + title: i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', + { + defaultMessage: 'Saved "{title}"', + values: { + title, + }, + } + ), + text: ((element) => + render( +
+ annotation library, + }} + /> +
, + element + )) as MountPoint, + }); }} />, domElement ); } else { - execute(); + return eventAnnotationService.createAnnotationGroup(layer).then(); } }, icon: 'save', diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 5530ea928fc68..a40a8a7c243b4 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -21,7 +21,10 @@ import type { FillStyle, YAxisConfig, } from '@kbn/expression-xy-plugin/common'; -import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { + EventAnnotationConfig, + EventAnnotationGroupAttributes, +} from '@kbn/event-annotation-plugin/common'; import { IconChartArea, IconChartLine, @@ -113,7 +116,7 @@ export interface XYReferenceLineLayerConfig { layerType: 'referenceLine'; } -export interface XYAnnotationLayerConfig { +export interface ByValueXYAnnotationLayerConfig { layerId: string; layerType: 'annotations'; annotations: EventAnnotationConfig[]; @@ -123,6 +126,23 @@ export interface XYAnnotationLayerConfig { ignoreGlobalFilters: boolean; } +export type ByReferenceXYAnnotationLayerConfig = ByValueXYAnnotationLayerConfig & { + annotationGroupId: string; + __lastSaved: ByValueXYAnnotationLayerConfig & { + title: EventAnnotationGroupAttributes['title']; + }; +}; + +export type XYAnnotationLayerConfig = + | ByReferenceXYAnnotationLayerConfig + | ByValueXYAnnotationLayerConfig; + +export const isByReferenceXyAnnotationLayer = ( + layer: ByReferenceXYAnnotationLayerConfig | ByValueXYAnnotationLayerConfig +): layer is ByReferenceXYAnnotationLayerConfig => { + return 'annotationGroupId' in layer; +}; + export type XYLayerConfig = | XYDataLayerConfig | XYReferenceLineLayerConfig diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index dd0409bdbdec2..7cdc034165610 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -264,7 +264,15 @@ export const getXyVisualization = ({ const actions = []; if (isAnnotationsLayer(layer)) { actions.push( - ...createAnnotationActions({ state, layerIndex, layer, setState, core, isSaveable }) + ...createAnnotationActions({ + state, + layerIndex, + layer, + setState, + core, + isSaveable, + eventAnnotationService, + }) ); } return actions; From d398395ad6c4c9ba00d8f755747ee79fd41dd9da Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 1 Mar 2023 10:28:19 -0600 Subject: [PATCH 004/202] unit test save action --- .../__snapshots__/save_action.test.tsx.snap | 51 ++++++ .../actions/edit_details_action.tsx | 87 +++++----- .../annotations/actions/save_action.test.tsx | 106 ++++++++++++ .../xy/annotations/actions/save_action.tsx | 156 ++++++++++-------- 4 files changed, 288 insertions(+), 112 deletions(-) create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap new file mode 100644 index 0000000000000..26ae9bef5d706 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`annotation group save action successful initial save 1`] = ` +Array [ + Array [ + Object { + "layers": Array [ + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "layerId": "mylayerid", + "layerType": "annotations", + "title": "title", + }, + "annotationGroupId": "saved-id-123", + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "layerId": "mylayerid", + "layerType": "annotations", + }, + ], + "legend": Object { + "isVisible": true, + "position": "bottom", + }, + "preferredSeriesType": "area", + }, + ], +] +`; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx index 2962ea33bdb07..11fb3188899ea 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx @@ -28,13 +28,11 @@ export const EditDetailsFlyout = ({ domElement, groupLabel, title, - isNew, onConfirm, }: { domElement: Element; groupLabel: string; title?: string; - isNew?: boolean; onConfirm: (title: string) => void; }) => { const [newTitle, setNewTitle] = React.useState(title); @@ -43,49 +41,47 @@ export const EditDetailsFlyout = ({ - - - unmountComponentAtNode(domElement)} - data-test-subj="lns-indexPattern-loadLibraryCancel" - > - {i18n.translate('xpack.lens.annotations.cancel', { - defaultMessage: 'Cancel', - })} - - - - { - if (newTitle) { - onConfirm(newTitle); - // todo: notification? - unmountComponentAtNode(domElement); - } - }} - iconType="plusInCircleFilled" - fill - color="success" - // disabled={!selectedItem} // TODO: disable if no title - > - {i18n.translate('xpack.lens.annotations.addToLibrary', { - defaultMessage: 'Add to library', - })} - - - - - ) : undefined + + + + unmountComponentAtNode(domElement)} + data-test-subj="lns-indexPattern-loadLibraryCancel" + > + {i18n.translate('xpack.lens.annotations.cancel', { + defaultMessage: 'Cancel', + })} + + + + { + if (newTitle) { + onConfirm(newTitle); + // todo: notification? + unmountComponentAtNode(domElement); + } + }} + iconType="plusInCircleFilled" + fill + color="success" + // disabled={!selectedItem} // TODO: disable if no title + > + {i18n.translate('xpack.lens.annotations.addToLibrary', { + defaultMessage: 'Add to library', + })} + + + + } groupLabel={groupLabel} handleClose={() => { @@ -147,7 +143,6 @@ export const getEditDetailsAction = ({ { defaultMessage: 'Edit title, description and tags of the annotation group' } ), execute: async (domElement) => { - console.log('what', domElement); if (domElement) { render( { + const layerId = 'mylayerid'; + + const layer: ByValueXYAnnotationLayerConfig = { + layerId, + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }; + + const savedId = 'saved-id-123'; + + const getProps = () => ({ + state: { + preferredSeriesType: 'area', + legend: { isVisible: true, position: 'bottom' }, + layers: [{ layerId } as XYAnnotationLayerConfig], + } as XYState, + layer, + setState: jest.fn(), + eventAnnotationService: { + createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), + toExpression: jest.fn(), + toFetchExpression: jest.fn(), + renderEventAnnotationGroupSavedObjectFinder: jest.fn(), + } as EventAnnotationServiceType, + toasts: toastsServiceMock.createStartContract(), + }); + + let props: ReturnType; + beforeEach(() => { + props = getProps(); + }); + + test('successful initial save', async () => { + const wrapper = shallowWithIntl( + + ); + + const newTitle = 'title'; + + // ignore the linter, you need this await statement + await wrapper.find(EditDetailsFlyout).prop('onConfirm')(newTitle); + + expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ + ...props.layer, + title: newTitle, + }); + + expect(props.setState.mock.calls).toMatchSnapshot(); + + expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); + }); + + test('failed initial save', async () => { + (props.eventAnnotationService.createAnnotationGroup as jest.Mock).mockRejectedValue( + new Error('oh noooooo') + ); + + const wrapper = shallowWithIntl( + + ); + + const newTitle = 'title'; + + // ignore the linter, you need this await statement + await wrapper.find(EditDetailsFlyout).prop('onConfirm')(newTitle); + + expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ + ...props.layer, + title: newTitle, + }); + + expect(props.toasts.addError).toHaveBeenCalledTimes(1); + + expect(props.setState).not.toHaveBeenCalled(); + + expect(props.toasts.addSuccess).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 38f32af7e813f..f7abac1ff177d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -16,14 +16,98 @@ import type { LayerAction, StateSetter } from '../../../../types'; import { ByReferenceXYAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; import { EditDetailsFlyout } from './edit_details_action'; -export const getSaveLayerAction = ({ +// exported for testing only +export const Flyout = ({ + domElement, state, layer, setState, eventAnnotationService, - isNew, toasts, }: { + domElement: HTMLDivElement; + state: XYState; + layer: XYAnnotationLayerConfig; + setState: StateSetter; + eventAnnotationService: EventAnnotationServiceType; + toasts: ToastsStart; +}) => ( + { + let savedId: string; + + try { + const { id } = await eventAnnotationService.createAnnotationGroup({ + ...layer, + title, + }); + + savedId = id; + } catch (err) { + toasts.addError(err, { + title: i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.errorToastTitle', + { + defaultMessage: 'Failed to save "{title}"', + values: { + title, + }, + } + ), + }); + + return; + } + + const newLayer: ByReferenceXYAnnotationLayerConfig = { + ...layer, + annotationGroupId: savedId, + __lastSaved: { + title, + ...layer, + }, + }; + + setState({ + ...state, + layers: state.layers.map((existingLayer) => + existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer + ), + }); + + toasts.addSuccess({ + title: i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', + { + defaultMessage: 'Saved "{title}"', + values: { + title, + }, + } + ), + text: ((element) => + render( +
+ annotation library, + }} + /> +
, + element + )) as MountPoint, + }); + }} + /> +); + +export const getSaveLayerAction = (props: { state: XYState; layer: XYAnnotationLayerConfig; setState: StateSetter; @@ -31,7 +115,7 @@ export const getSaveLayerAction = ({ isNew?: boolean; toasts: ToastsStart; }): LayerAction => { - const displayName = isNew + const displayName = props.isNew ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { defaultMessage: 'Add to library', }) @@ -45,70 +129,10 @@ export const getSaveLayerAction = ({ { defaultMessage: 'Saves annotation group as separate saved object' } ), execute: async (domElement) => { - if (domElement && isNew) { - render( - { - // TODO - error handling - const { id } = await eventAnnotationService.createAnnotationGroup({ - ...layer, - title, - }); - - const newLayer: ByReferenceXYAnnotationLayerConfig = { - ...layer, - annotationGroupId: id, - __lastSaved: { - title, - ...layer, - }, - }; - - setState({ - ...state, - layers: state.layers.map((existingLayer) => - existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer - ), - }); - - toasts.addSuccess({ - title: i18n.translate( - 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', - { - defaultMessage: 'Saved "{title}"', - values: { - title, - }, - } - ), - text: ((element) => - render( -
- annotation library, - }} - /> -
, - element - )) as MountPoint, - }); - }} - />, - domElement - ); + if (props.isNew && domElement) { + render(, domElement); } else { - return eventAnnotationService.createAnnotationGroup(layer).then(); + return props.eventAnnotationService.createAnnotationGroup(props.layer).then(); } }, icon: 'save', From 89b38a788c532fb36d9e3c3d278f68acac9d0bf9 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 1 Mar 2023 13:14:12 -0600 Subject: [PATCH 005/202] extract annotation group references for persistence --- .../src/capabilities_service.mock.ts | 5 +- .../xy/annotations/actions/save_action.tsx | 4 +- .../public/visualizations/xy/state_helpers.ts | 39 ++++-- .../lens/public/visualizations/xy/types.ts | 33 +++-- .../visualizations/xy/visualization.test.ts | 116 +++++++++++++++++- .../visualizations/xy/visualization.tsx | 5 +- 6 files changed, 174 insertions(+), 28 deletions(-) diff --git a/packages/core/capabilities/core-capabilities-browser-mocks/src/capabilities_service.mock.ts b/packages/core/capabilities/core-capabilities-browser-mocks/src/capabilities_service.mock.ts index fa1636b57dedb..0f2d12390a6ae 100644 --- a/packages/core/capabilities/core-capabilities-browser-mocks/src/capabilities_service.mock.ts +++ b/packages/core/capabilities/core-capabilities-browser-mocks/src/capabilities_service.mock.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { deepFreeze } from '@kbn/std'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { CapabilitiesStart, @@ -14,11 +13,11 @@ import type { } from '@kbn/core-capabilities-browser-internal'; const createStartContractMock = (): jest.Mocked => ({ - capabilities: deepFreeze({ + capabilities: { catalogue: {}, management: {}, navLinks: {}, - }), + }, }); const createMock = (): jest.Mocked> => ({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index f7abac1ff177d..5f4b8f2214e21 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -13,7 +13,7 @@ import { ToastsStart } from '@kbn/core-notifications-browser'; import { MountPoint } from '@kbn/core-mount-utils-browser'; import { FormattedMessage } from '@kbn/i18n-react'; import type { LayerAction, StateSetter } from '../../../../types'; -import { ByReferenceXYAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; +import { XYByReferenceAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; import { EditDetailsFlyout } from './edit_details_action'; // exported for testing only @@ -63,7 +63,7 @@ export const Flyout = ({ return; } - const newLayer: ByReferenceXYAnnotationLayerConfig = { + const newLayer: XYByReferenceAnnotationLayerConfig = { ...layer, annotationGroupId: savedId, __lastSaved: { diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 236cd25dd2e2d..c7cec5d96e9de 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { DistributiveOmit } from '@elastic/eui'; import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { SavedObjectReference } from '@kbn/core/public'; +import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-plugin/common'; import { isQueryAnnotationConfig } from '@kbn/event-annotation-plugin/public'; import { i18n } from '@kbn/i18n'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; @@ -24,6 +24,8 @@ import { XYState, XYPersistedState, XYAnnotationLayerConfig, + isByReferenceXyAnnotationLayer, + XYPersistedLayerConfig, } from './types'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; @@ -117,20 +119,33 @@ function getLayerReferenceName(layerId: string) { return `xy-visualization-layer-${layerId}`; } -export function extractReferences(state: XYState) { +export function getPersistableState(state: XYState) { const savedObjectReferences: SavedObjectReference[] = []; - const persistableLayers: Array> = []; + const persistableLayers: XYPersistedLayerConfig[] = []; state.layers.forEach((layer) => { - if (isAnnotationsLayer(layer)) { - const { indexPatternId, ...persistableLayer } = layer; - savedObjectReferences.push({ - type: 'index-pattern', - id: indexPatternId, - name: getLayerReferenceName(layer.layerId), - }); - persistableLayers.push(persistableLayer); - } else { + if (!isAnnotationsLayer(layer)) { persistableLayers.push(layer); + } else { + if (isByReferenceXyAnnotationLayer(layer)) { + savedObjectReferences.push({ + type: EVENT_ANNOTATION_GROUP_TYPE, + id: layer.annotationGroupId, + name: getLayerReferenceName(layer.layerId), + }); + persistableLayers.push({ + layerId: layer.layerId, + layerType: layer.layerType, + annotationGroupId: layer.annotationGroupId, + }); + } else { + const { indexPatternId, ...persistableLayer } = layer; + savedObjectReferences.push({ + type: 'index-pattern', + id: indexPatternId, + name: getLayerReferenceName(layer.layerId), + }); + persistableLayers.push(persistableLayer); + } } }); return { savedObjectReferences, state: { ...state, layers: persistableLayers } }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index a40a8a7c243b4..e51e01f0897be 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -38,7 +38,6 @@ import { IconChartBarHorizontal, } from '@kbn/chart-icons'; -import { DistributiveOmit } from '@elastic/eui'; import { CollapseFunction } from '../../../common/expressions'; import type { VisualizationType } from '../../types'; import type { ValueLabelConfig } from '../../../common/types'; @@ -126,23 +125,43 @@ export interface ByValueXYAnnotationLayerConfig { ignoreGlobalFilters: boolean; } -export type ByReferenceXYAnnotationLayerConfig = ByValueXYAnnotationLayerConfig & { +export type XYPersistedByValueAnnotationLayerConfig = Omit< + ByValueXYAnnotationLayerConfig, + 'indexPatternId' +>; + +export type XYByReferenceAnnotationLayerConfig = ByValueXYAnnotationLayerConfig & { annotationGroupId: string; __lastSaved: ByValueXYAnnotationLayerConfig & { title: EventAnnotationGroupAttributes['title']; }; }; +export interface XYPersistedByReferenceAnnotationLayerConfig { + layerId: string; + layerType: 'annotations'; + annotationGroupId: string; +} + export type XYAnnotationLayerConfig = - | ByReferenceXYAnnotationLayerConfig + | XYByReferenceAnnotationLayerConfig | ByValueXYAnnotationLayerConfig; +export type XYPersistedAnnotationLayerConfig = + | XYPersistedByReferenceAnnotationLayerConfig + | XYPersistedByValueAnnotationLayerConfig; + export const isByReferenceXyAnnotationLayer = ( - layer: ByReferenceXYAnnotationLayerConfig | ByValueXYAnnotationLayerConfig -): layer is ByReferenceXYAnnotationLayerConfig => { - return 'annotationGroupId' in layer; + layer: XYByReferenceAnnotationLayerConfig | ByValueXYAnnotationLayerConfig +): layer is XYByReferenceAnnotationLayerConfig => { + return 'annotationGroupId' in layer && '__lastSaved' in layer; }; +export type XYPersistedLayerConfig = + | XYDataLayerConfig + | XYReferenceLineLayerConfig + | XYPersistedAnnotationLayerConfig; + export type XYLayerConfig = | XYDataLayerConfig | XYReferenceLineLayerConfig @@ -186,7 +205,7 @@ export interface XYState { export type State = XYState; export type XYPersistedState = Omit & { - layers: Array>; + layers: XYPersistedLayerConfig[]; }; export type PersistedState = XYPersistedState; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index ad7db64deae31..211a4fd865b74 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -22,6 +22,8 @@ import type { XYReferenceLineLayerConfig, SeriesType, XYPersistedState, + ByValueXYAnnotationLayerConfig, + XYByReferenceAnnotationLayerConfig, } from './types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { IconChartBar, IconCircle } from '@kbn/chart-icons'; @@ -30,7 +32,10 @@ import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks' import { Datatable } from '@kbn/expressions-plugin/common'; import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; -import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { + EventAnnotationConfig, + PointInTimeEventAnnotationConfig, +} from '@kbn/event-annotation-plugin/common'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { DataViewsState } from '../../state_management'; @@ -38,6 +43,7 @@ import { createMockedIndexPattern } from '../../datasources/form_based/mocks'; import { createMockDataViewsState } from '../../data_views_service/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { layerTypes, Visualization } from '../..'; +import { set } from '@kbn/safer-lodash-set'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', @@ -81,6 +87,7 @@ const paletteServiceMock = chartPluginMock.createPaletteRegistry(); const fieldFormatsMock = fieldFormatsServiceMock.createStartContract(); const core = coreMock.createStart(); +set(core, 'application.capabilities.visualize.save', true); const xyVisualization = getXyVisualization({ paletteService: paletteServiceMock, @@ -2884,6 +2891,113 @@ describe('xy_visualization', () => { }); }); + describe('#getPersistableState', () => { + it('should extract index pattern ids from by-value annotation layers', () => { + const state = exampleState(); + const layer: ByValueXYAnnotationLayerConfig = { + layerId: 'layer-id', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }; + state.layers = [layer]; + + const { state: persistableState, savedObjectReferences } = + xyVisualization.getPersistableState!(state); + + expect(persistableState.layers).toMatchInlineSnapshot(` + Array [ + Object { + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "ignoreGlobalFilters": false, + "layerId": "layer-id", + "layerType": "annotations", + }, + ] + `); + + expect(savedObjectReferences).toMatchInlineSnapshot(` + Array [ + Object { + "id": "some-index-pattern", + "name": "xy-visualization-layer-layer-id", + "type": "index-pattern", + }, + ] + `); + }); + + it('should extract annotation group ids from by-reference annotation layers', () => { + const state = exampleState(); + const byValueLayer: ByValueXYAnnotationLayerConfig = { + layerId: 'layer-id', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }; + + const layer: XYByReferenceAnnotationLayerConfig = { + ...byValueLayer, + annotationGroupId: 'annotation-group-id', + __lastSaved: { ...byValueLayer, title: 'My saved object title' }, + }; + + state.layers = [layer]; + + const { state: persistableState, savedObjectReferences } = + xyVisualization.getPersistableState!(state); + + expect(persistableState.layers).toMatchInlineSnapshot(` + Array [ + Object { + "annotationGroupId": "annotation-group-id", + "layerId": "layer-id", + "layerType": "annotations", + }, + ] + `); + + expect(savedObjectReferences).toMatchInlineSnapshot(` + Array [ + Object { + "id": "annotation-group-id", + "name": "xy-visualization-layer-layer-id", + "type": "event-annotation-group", + }, + ] + `); + }); + }); + describe('#fromPersistableState', () => { it('should inject references on annotation layers', () => { const baseState = exampleState(); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 7cdc034165610..824cfb475803e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -52,7 +52,7 @@ import { visualizationTypes, } from './types'; import { - extractReferences, + getPersistableState, getAnnotationLayerErrors, injectReferences, isHorizontalChart, @@ -202,9 +202,8 @@ export const getXyVisualization = ({ }; }, - // how to implement this for saved object annotations? getPersistableState(state) { - return extractReferences(state); + return getPersistableState(state); }, // how to implement this for saved object annotations? fromPersistableState(state, references, initialContext) { From a47d143ed6c1574ce38bf01af82fd13e902a81f8 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 1 Mar 2023 19:38:19 -0600 Subject: [PATCH 006/202] library annotation groups removed from lens saved object and loaded at bootstrap --- src/plugins/event_annotation/common/types.ts | 1 + .../event_annotation_service/service.tsx | 63 ++++++++--------- .../public/event_annotation_service/types.ts | 2 +- .../lens/public/app_plugin/mounter.tsx | 3 + .../plugins/lens/public/app_plugin/types.ts | 2 + .../editor_frame/state_helpers.ts | 37 ++++++++++ .../init_middleware/load_initial.ts | 2 + x-pack/plugins/lens/public/types.ts | 2 + .../xy/annotations/actions/index.ts | 5 +- .../annotations/actions/save_action.test.tsx | 5 +- .../public/visualizations/xy/state_helpers.ts | 69 ++++++++++++++----- .../lens/public/visualizations/xy/types.ts | 20 ++---- .../visualizations/xy/visualization.test.ts | 6 +- .../visualizations/xy/visualization.tsx | 10 ++- .../xy/visualization_helpers.tsx | 13 ++++ 15 files changed, 165 insertions(+), 75 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 2001935629c9e..a7389e7f6dbdc 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -87,6 +87,7 @@ export interface EventAnnotationGroupAttributes { description?: string; tags?: string[]; ignoreGlobalFilters?: boolean; + annotations: EventAnnotationConfig[]; } export interface EventAnnotationGroupConfig { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 07629fa693887..cbaaff7a19e3e 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -14,6 +14,7 @@ import { CoreStart, SavedObjectsClientContract } from '@kbn/core/public'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { EventAnnotationConfig, + EventAnnotationGroupAttributes, EventAnnotationGroupConfig, EVENT_ANNOTATION_GROUP_TYPE, } from '../../common'; @@ -34,41 +35,35 @@ export function hasIcon(icon: string | undefined): icon is string { export function getEventAnnotationService(core: CoreStart): EventAnnotationServiceType { const client: SavedObjectsClientContract = core.savedObjects.client; - // const loadAnnotationGroup = async ( - // savedObjectId: string - // ): Promise => { - // const groupResponse = await client.resolve( - // EVENT_ANNOTATION_GROUP_TYPE, - // savedObjectId - // ); + const loadAnnotationGroup = async ( + savedObjectId: string + ): Promise => { + const savedObject = await client.get( + EVENT_ANNOTATION_GROUP_TYPE, + savedObjectId + ); - // if (groupResponse.saved_object.error) { - // throw groupResponse.saved_object.error; - // } + // const annotations = ( + // await client.find({ + // type: EVENT_ANNOTATION_TYPE, + // hasReference: { + // type: EVENT_ANNOTATION_GROUP_TYPE, + // id: savedObjectId, + // }, + // }) + // ).savedObjects + // .filter(({ error }) => !error) + // .map((annotation) => annotation.attributes); - // const annotations = ( - // await client.find({ - // type: EVENT_ANNOTATION_TYPE, - // hasReference: { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // }, - // }) - // ).savedObjects - // .filter(({ error }) => !error) - // .map((annotation) => annotation.attributes); - - // return { - // title: groupResponse.saved_object.attributes.title, - // description: groupResponse.saved_object.attributes.description, - // tags: groupResponse.saved_object.attributes.tags, - // ignoreGlobalFilters: groupResponse.saved_object.attributes.ignoreGlobalFilters, - // indexPatternId: groupResponse.saved_object.references.find( - // (ref) => ref.type === 'index-pattern' - // )?.id!, - // annotations, - // }; - // }; + return { + title: savedObject.attributes.title, + // description: groupResponse.saved_object.attributes.description, + // tags: groupResponse.saved_object.attributes.tags, + ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters, + indexPatternId: savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!, + annotations: savedObject.attributes.annotations, + }; + }; // const deleteAnnotationGroup = async (savedObjectId: string): Promise => { // const annotationsSOs = ( @@ -160,7 +155,7 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi // }; return { - // loadAnnotationGroup, + loadAnnotationGroup, // updateAnnotations, // updateAnnotationGroup, createAnnotationGroup, diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index 9ea5c9b625bd3..988e6102c6f20 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -11,7 +11,7 @@ import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; export interface EventAnnotationServiceType { - // loadAnnotationGroup: (savedObjectId: string) => Promise; + loadAnnotationGroup: (savedObjectId: string) => Promise; // deleteAnnotationGroup: (savedObjectId: string) => Promise; createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; // updateAnnotations: ( diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 070a336623c9e..921a791e81273 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -95,6 +95,7 @@ export async function getLensServices( inspector, navigation, embeddable, + eventAnnotation, savedObjectsTagging, usageCollection, fieldFormats, @@ -106,6 +107,7 @@ export async function getLensServices( const storage = new Storage(localStorage); const stateTransfer = embeddable?.getStateTransfer(); const embeddableEditorIncomingState = stateTransfer?.getIncomingEditorState(APP_ID); + const eventAnnotationService = await eventAnnotation.getService(coreStart); return { data, @@ -117,6 +119,7 @@ export async function getLensServices( usageCollection, savedObjectsTagging, attributeService, + eventAnnotationService, executionContext: coreStart.executionContext, http: coreStart.http, uiActions: startDependencies.uiActions, diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 193ba130e02ef..cc18a3ac51f35 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -45,6 +45,7 @@ import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { DocLinksStart } from '@kbn/core-doc-links-browser'; import type { SharePluginStart } from '@kbn/share-plugin/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import type { DatasourceMap, EditorFrameInstance, @@ -145,6 +146,7 @@ export interface LensAppServices { dataViews: DataViewsPublicPluginStart; fieldFormats: FieldFormatsStart; data: DataPublicPluginStart; + eventAnnotationService: EventAnnotationServiceType; inspector: LensInspector; uiSettings: IUiSettingsClient; uiActions: UiActionsStart; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 02410b6994c34..d9c15ed6ccee1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -14,6 +14,11 @@ import type { DataViewsContract, DataViewSpec } from '@kbn/data-views-plugin/pub import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import type { TimefilterContract } from '@kbn/data-plugin/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { + EventAnnotationGroupConfig, + EVENT_ANNOTATION_GROUP_TYPE, +} from '@kbn/event-annotation-plugin/common'; import { Datasource, DatasourceLayers, @@ -164,12 +169,33 @@ export async function initializeDataViews( }; } +const initializeEventAnnotationGroups = async ( + eventAnnotationService: EventAnnotationServiceType, + references?: SavedObjectReference[] +) => { + const annotationGroups: Record = {}; + + await Promise.all( + // TODO error handling + references + ?.filter((ref) => ref.type === EVENT_ANNOTATION_GROUP_TYPE) + .map(({ id }) => + eventAnnotationService + .loadAnnotationGroup(id) + .then((group) => (annotationGroups[id] = group)) + ) || [] + ); + + return annotationGroups; +}; + /** * This function composes both initializeDataViews & initializeDatasources into a single call */ export async function initializeSources( { dataViews, + eventAnnotationService, datasourceMap, visualizationMap, visualizationState, @@ -181,6 +207,7 @@ export async function initializeSources( adHocDataViews, }: { dataViews: DataViewsContract; + eventAnnotationService: EventAnnotationServiceType; datasourceMap: DatasourceMap; visualizationMap: VisualizationMap; visualizationState: VisualizationState; @@ -206,6 +233,12 @@ export async function initializeSources( }, options ); + + const annotationGroups = await initializeEventAnnotationGroups( + eventAnnotationService, + references + ); + return { indexPatterns, indexPatternRefs, @@ -222,6 +255,7 @@ export async function initializeSources( visualizationState, references, initialContext, + annotationGroups, }), }; } @@ -231,16 +265,19 @@ export function initializeVisualization({ visualizationState, references, initialContext, + annotationGroups, }: { visualizationState: VisualizationState; visualizationMap: VisualizationMap; references?: SavedObjectReference[]; initialContext?: VisualizeFieldContext | VisualizeEditorContext; + annotationGroups: Record; }) { if (visualizationState?.activeId) { return ( visualizationMap[visualizationState.activeId]?.fromPersistableState?.( visualizationState.state, + annotationGroups, references, initialContext ) ?? visualizationState.state diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index 97c08a1ad3258..a00ad9581769e 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -114,6 +114,7 @@ export function loadInitial( const loaderSharedArgs = { dataViews: lensServices.dataViews, storage: lensServices.storage, + eventAnnotationService: lensServices.eventAnnotationService, defaultIndexPatternId: lensServices.uiSettings.get('defaultIndex'), }; @@ -302,6 +303,7 @@ export function loadInitial( references: [...doc.references, ...(doc.state.internalReferences || [])], initialContext, dataViews: lensServices.dataViews, + eventAnnotationService: lensServices.eventAnnotationService, storage: lensServices.storage, adHocDataViews: doc.state.adHocDataViews, defaultIndexPatternId: lensServices.uiSettings.get('defaultIndex'), diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 6198ded54d4bf..0eadc982f4ee8 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -39,6 +39,7 @@ import { SearchRequest } from '@kbn/data-plugin/public'; import { estypes } from '@elastic/elasticsearch'; import React from 'react'; import { CellValueContext } from '@kbn/embeddable-plugin/public'; +import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; import type { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import type { DateRange, LayerType, SortingHint } from '../common'; import type { @@ -1060,6 +1061,7 @@ export interface Visualization { /** Hydrate from persistable state and references to final state */ fromPersistableState?: ( state: P, + annotationGroups: Record, references?: SavedObjectReference[], initialContext?: VisualizeFieldContext | VisualizeEditorContext ) => T; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index 411b3fd0cf930..6c3cc41702537 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -9,11 +9,12 @@ import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; -import { XYState, XYAnnotationLayerConfig, isByReferenceXyAnnotationLayer } from '../../types'; +import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; import { getIgnoreFilterAction } from './ignore_filters_action'; import { getEditDetailsAction } from './edit_details_action'; import { getSaveLayerAction } from './save_action'; +import { isByReferenceAnnotationLayer } from '../../visualization_helpers'; export { IGNORE_GLOBAL_FILTERS_ACTION_ID, KEEP_GLOBAL_FILTERS_ACTION_ID, @@ -44,7 +45,7 @@ export const createAnnotationActions = ({ if (savingToLibraryPermitted) { // check if the annotation is saved as a saved object or in inline - same as we check for save modal for visualization - if (isByReferenceXyAnnotationLayer(layer)) { + if (isByReferenceAnnotationLayer(layer)) { // check if Annotation group hasUnsavedChanges to know if we should allow reverting and saving - similar to how we do it for persistedDoc vs currentDoc on app level const hasUnsavedChanges = true; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index 34c3709b87344..c961fb1a6922a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import { ByValueXYAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; +import { XYByValueAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; import { Flyout } from './save_action'; import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { EditDetailsFlyout } from './edit_details_action'; @@ -17,7 +17,7 @@ import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/c describe('annotation group save action', () => { const layerId = 'mylayerid'; - const layer: ByValueXYAnnotationLayerConfig = { + const layer: XYByValueAnnotationLayerConfig = { layerId, layerType: 'annotations', indexPatternId: 'some-index-pattern', @@ -46,6 +46,7 @@ describe('annotation group save action', () => { setState: jest.fn(), eventAnnotationService: { createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), + loadAnnotationGroup: jest.fn(), toExpression: jest.fn(), toFetchExpression: jest.fn(), renderEventAnnotationGroupSavedObjectFinder: jest.fn(), diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index c7cec5d96e9de..6b275728073f5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -7,7 +7,10 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { SavedObjectReference } from '@kbn/core/public'; -import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-plugin/common'; +import { + EventAnnotationGroupConfig, + EVENT_ANNOTATION_GROUP_TYPE, +} from '@kbn/event-annotation-plugin/common'; import { isQueryAnnotationConfig } from '@kbn/event-annotation-plugin/public'; import { i18n } from '@kbn/i18n'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; @@ -24,10 +27,16 @@ import { XYState, XYPersistedState, XYAnnotationLayerConfig, - isByReferenceXyAnnotationLayer, XYPersistedLayerConfig, + XYByReferenceAnnotationLayerConfig, } from './types'; -import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; +import { + getDataLayers, + isAnnotationsLayer, + isDataLayer, + isPersistedByReferenceAnnotationLayer, + isByReferenceAnnotationLayer, +} from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { return ( @@ -126,7 +135,7 @@ export function getPersistableState(state: XYState) { if (!isAnnotationsLayer(layer)) { persistableLayers.push(layer); } else { - if (isByReferenceXyAnnotationLayer(layer)) { + if (isByReferenceAnnotationLayer(layer)) { savedObjectReferences.push({ type: EVENT_ANNOTATION_GROUP_TYPE, id: layer.annotationGroupId, @@ -151,15 +160,14 @@ export function getPersistableState(state: XYState) { return { savedObjectReferences, state: { ...state, layers: persistableLayers } }; } +export const REF_NOT_FOUND = 'ref-not-found'; + export function injectReferences( state: XYPersistedState, - references?: SavedObjectReference[], + annotationGroups: Record, + references: SavedObjectReference[], initialContext?: VisualizeFieldContext | VisualizeEditorContext ): XYState { - if (!references || !references.length) { - return state as XYState; - } - const fallbackIndexPatternId = references.find(({ type }) => type === 'index-pattern')!.id; return { ...state, @@ -167,13 +175,42 @@ export function injectReferences( if (!isAnnotationsLayer(layer)) { return layer as XYLayerConfig; } - return { - ...layer, - indexPatternId: - getIndexPatternIdFromInitialContext(layer, initialContext) || - references.find(({ name }) => name === getLayerReferenceName(layer.layerId))?.id || - fallbackIndexPatternId, - }; + + let injectedLayer: XYAnnotationLayerConfig; + if (isPersistedByReferenceAnnotationLayer(layer)) { + const annotationGroupId = references?.find( + ({ name }) => name === getLayerReferenceName(layer.layerId) + )?.id; + + const annotationGroup = annotationGroupId ? annotationGroups[annotationGroupId] : undefined; + + if (!annotationGroupId || !annotationGroup) { + throw new Error("can't find annotation group!"); // TODO - better handling + } + + // declared as a separate variable for type checking + const newLayer: XYByReferenceAnnotationLayerConfig = { + layerId: layer.layerId, + layerType: layer.layerType, + annotationGroupId, + indexPatternId: annotationGroup.indexPatternId, + ignoreGlobalFilters: Boolean(annotationGroup.ignoreGlobalFilters), + annotations: annotationGroup.annotations, + __lastSaved: annotationGroup, + }; + + injectedLayer = newLayer; + } else { + injectedLayer = { + ...layer, + indexPatternId: + getIndexPatternIdFromInitialContext(layer, initialContext) || + references.find(({ name }) => name === getLayerReferenceName(layer.layerId))?.id || + fallbackIndexPatternId, + }; + } + + return injectedLayer; }), }; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index e51e01f0897be..67f94061553be 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -23,7 +23,7 @@ import type { } from '@kbn/expression-xy-plugin/common'; import { EventAnnotationConfig, - EventAnnotationGroupAttributes, + EventAnnotationGroupConfig, } from '@kbn/event-annotation-plugin/common'; import { IconChartArea, @@ -115,7 +115,7 @@ export interface XYReferenceLineLayerConfig { layerType: 'referenceLine'; } -export interface ByValueXYAnnotationLayerConfig { +export interface XYByValueAnnotationLayerConfig { layerId: string; layerType: 'annotations'; annotations: EventAnnotationConfig[]; @@ -126,15 +126,13 @@ export interface ByValueXYAnnotationLayerConfig { } export type XYPersistedByValueAnnotationLayerConfig = Omit< - ByValueXYAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, 'indexPatternId' >; -export type XYByReferenceAnnotationLayerConfig = ByValueXYAnnotationLayerConfig & { +export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig & { annotationGroupId: string; - __lastSaved: ByValueXYAnnotationLayerConfig & { - title: EventAnnotationGroupAttributes['title']; - }; + __lastSaved: EventAnnotationGroupConfig; }; export interface XYPersistedByReferenceAnnotationLayerConfig { @@ -145,18 +143,12 @@ export interface XYPersistedByReferenceAnnotationLayerConfig { export type XYAnnotationLayerConfig = | XYByReferenceAnnotationLayerConfig - | ByValueXYAnnotationLayerConfig; + | XYByValueAnnotationLayerConfig; export type XYPersistedAnnotationLayerConfig = | XYPersistedByReferenceAnnotationLayerConfig | XYPersistedByValueAnnotationLayerConfig; -export const isByReferenceXyAnnotationLayer = ( - layer: XYByReferenceAnnotationLayerConfig | ByValueXYAnnotationLayerConfig -): layer is XYByReferenceAnnotationLayerConfig => { - return 'annotationGroupId' in layer && '__lastSaved' in layer; -}; - export type XYPersistedLayerConfig = | XYDataLayerConfig | XYReferenceLineLayerConfig diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 211a4fd865b74..f966cd6411912 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -22,7 +22,7 @@ import type { XYReferenceLineLayerConfig, SeriesType, XYPersistedState, - ByValueXYAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, XYByReferenceAnnotationLayerConfig, } from './types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; @@ -2894,7 +2894,7 @@ describe('xy_visualization', () => { describe('#getPersistableState', () => { it('should extract index pattern ids from by-value annotation layers', () => { const state = exampleState(); - const layer: ByValueXYAnnotationLayerConfig = { + const layer: XYByValueAnnotationLayerConfig = { layerId: 'layer-id', layerType: 'annotations', indexPatternId: 'some-index-pattern', @@ -2948,7 +2948,7 @@ describe('xy_visualization', () => { it('should extract annotation group ids from by-reference annotation layers', () => { const state = exampleState(); - const byValueLayer: ByValueXYAnnotationLayerConfig = { + const byValueLayer: XYByValueAnnotationLayerConfig = { layerId: 'layer-id', layerType: 'annotations', indexPatternId: 'some-index-pattern', diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 824cfb475803e..107037be11acd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -205,9 +205,13 @@ export const getXyVisualization = ({ getPersistableState(state) { return getPersistableState(state); }, - // how to implement this for saved object annotations? - fromPersistableState(state, references, initialContext) { - return injectReferences(state, references, initialContext); + + fromPersistableState(state, annotationGroups, references, initialContext) { + if (!references || !references.length) { + return state as XYState; + } + + return injectReferences(state, annotationGroups, references, initialContext); }, getDescription, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index e7580fe3b9d6a..5282a5e51d9a8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -19,6 +19,9 @@ import { XYDataLayerConfig, XYReferenceLineLayerConfig, SeriesType, + XYByReferenceAnnotationLayerConfig, + XYPersistedAnnotationLayerConfig, + XYPersistedByReferenceAnnotationLayerConfig, } from './types'; import { isHorizontalChart } from './state_helpers'; import { layerTypes } from '../..'; @@ -140,6 +143,16 @@ export const isAnnotationsLayer = ( layer: Pick ): layer is XYAnnotationLayerConfig => layer.layerType === layerTypes.ANNOTATIONS; +export const isByReferenceAnnotationLayer = ( + layer: XYAnnotationLayerConfig +): layer is XYByReferenceAnnotationLayerConfig => + 'annotationGroupId' in layer && '__lastSaved' in layer; + +export const isPersistedByReferenceAnnotationLayer = ( + layer: XYPersistedAnnotationLayerConfig +): layer is XYPersistedByReferenceAnnotationLayerConfig => + 'annotationGroupId' in layer && !('__lastSaved' in layer); + export const getAnnotationsLayers = (layers: Array>) => (layers || []).filter((layer): layer is XYAnnotationLayerConfig => isAnnotationsLayer(layer)); From e47c17b8c0ec74cf3db3e4450294999c02d20d16 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 2 Mar 2023 13:16:12 -0600 Subject: [PATCH 007/202] report data views used by annotation groups --- .../editor_frame/state_helpers.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index d9c15ed6ccee1..13f35c82b8916 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -38,12 +38,13 @@ import { readFromStorage } from '../../settings_storage'; import { loadIndexPatternRefs, loadIndexPatterns } from '../../data_views_service/loader'; function getIndexPatterns( + annotationGroupDataviewIds: string[], references?: SavedObjectReference[], initialContext?: VisualizeFieldContext | VisualizeEditorContext, initialId?: string, adHocDataviews?: string[] ) { - const indexPatternIds = []; + const indexPatternIds = [...annotationGroupDataviewIds]; // use the initialId only when no context is passed over if (!initialContext && initialId) { @@ -102,6 +103,7 @@ export async function initializeDataViews( references, initialContext, adHocDataViews: persistedAdHocDataViews, + annotationGroups, }: { dataViews: DataViewsContract; datasourceMap: DatasourceMap; @@ -111,6 +113,7 @@ export async function initializeDataViews( references?: SavedObjectReference[]; initialContext?: VisualizeFieldContext | VisualizeEditorContext; adHocDataViews?: Record; + annotationGroups: Record; }, options?: InitializationOptions ) { @@ -139,6 +142,7 @@ export async function initializeDataViews( const adHocDataviewsIds: string[] = Object.keys(adHocDataViews || {}); const usedIndexPatternsIds = getIndexPatterns( + Object.values(annotationGroups).map((group) => group.indexPatternId), references, initialContext, initialId, @@ -220,6 +224,11 @@ export async function initializeSources( }, options?: InitializationOptions ) { + const annotationGroups = await initializeEventAnnotationGroups( + eventAnnotationService, + references + ); + const { indexPatternRefs, indexPatterns } = await initializeDataViews( { datasourceMap, @@ -230,15 +239,11 @@ export async function initializeSources( defaultIndexPatternId, references, adHocDataViews, + annotationGroups, // TODO - is this necessary? }, options ); - const annotationGroups = await initializeEventAnnotationGroups( - eventAnnotationService, - references - ); - return { indexPatterns, indexPatternRefs, From 968cb740d84e873e4c22dd591e0facb6a5149c84 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 2 Mar 2023 16:07:34 -0600 Subject: [PATCH 008/202] support by-ref annotation groups in embeddable --- .../editor_frame/state_helpers.ts | 30 ++++++++++---- .../public/editor_frame_service/service.tsx | 2 + .../public/embeddable/embeddable.test.tsx | 23 +++++++++++ .../lens/public/embeddable/embeddable.tsx | 40 +++++++++---------- x-pack/plugins/lens/public/plugin.ts | 9 ++++- 5 files changed, 72 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 1a78461fd07b4..c0739297f2a1f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -349,6 +349,13 @@ export const getDatasourceLayers = memoizeOne(function getDatasourceLayers( return datasourceLayers; }); +export interface DocumentToExpressionReturnType { + ast: Ast | null; + indexPatterns: IndexPatternMap; + indexPatternRefs: IndexPatternRef[]; + activeVisualizationState: unknown; +} + export async function persistedStateToExpression( datasourceMap: DatasourceMap, visualizations: VisualizationMap, @@ -358,12 +365,9 @@ export async function persistedStateToExpression( storage: IStorageWrapper; dataViews: DataViewsContract; timefilter: TimefilterContract; + eventAnnotationService: EventAnnotationServiceType; } -): Promise<{ - ast: Ast | null; - indexPatterns: IndexPatternMap; - indexPatternRefs: IndexPatternRef[]; -}> { +): Promise { const { state: { visualization: persistedVisualizationState, @@ -377,15 +381,22 @@ export async function persistedStateToExpression( description, } = doc; if (!visualizationType) { - return { ast: null, indexPatterns: {}, indexPatternRefs: [] }; + return { ast: null, indexPatterns: {}, indexPatternRefs: [], activeVisualizationState: null }; } + + const annotationGroups = await initializeEventAnnotationGroups( + services.eventAnnotationService, + references + ); + const visualization = visualizations[visualizationType!]; - const visualizationState = initializeVisualization({ + const activeVisualizationState = initializeVisualization({ visualizationMap: visualizations, visualizationState: { state: persistedVisualizationState, activeId: visualizationType, }, + annotationGroups, references: [...references, ...(internalReferences || [])], }); const datasourceStatesFromSO = Object.fromEntries( @@ -403,6 +414,7 @@ export async function persistedStateToExpression( storage: services.storage, defaultIndexPatternId: services.uiSettings.get('defaultIndex'), adHocDataViews, + annotationGroups, }, { isFullEditor: false } ); @@ -422,6 +434,7 @@ export async function persistedStateToExpression( ast: null, indexPatterns, indexPatternRefs, + activeVisualizationState, }; } @@ -432,13 +445,14 @@ export async function persistedStateToExpression( title, description, visualization, - visualizationState, + visualizationState: activeVisualizationState, datasourceMap, datasourceStates, datasourceLayers, indexPatterns, dateRange: { fromDate: currentTimeRange.from, toDate: currentTimeRange.to }, }), + activeVisualizationState, indexPatterns, indexPatternRefs, }; 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 80a30ab9325e0..1d183fbe196c2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -24,6 +24,7 @@ import { DataViewsPublicPluginSetup, DataViewsPublicPluginStart, } from '@kbn/data-views-plugin/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { Document } from '../persistence/saved_object_store'; import { Datasource, @@ -57,6 +58,7 @@ export interface EditorFramePlugins { uiSettings: IUiSettingsClient; storage: IStorageWrapper; timefilter: TimefilterContract; + eventAnnotationService: EventAnnotationServiceType; } async function collectAsyncDefinitions( diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index 92bfa95eb2aef..e2058e0fdf3f0 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -182,6 +182,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, }, @@ -235,6 +236,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, }, @@ -296,6 +298,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { @@ -350,6 +353,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, {} as LensEmbeddableInput @@ -426,6 +430,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, {} as LensEmbeddableInput @@ -480,6 +485,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, {} as LensEmbeddableInput @@ -533,6 +539,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, {} as LensEmbeddableInput @@ -582,6 +589,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123' } as LensEmbeddableInput @@ -637,6 +645,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123' } as LensEmbeddableInput @@ -696,6 +705,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123' } as LensEmbeddableInput @@ -753,6 +763,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123' } as LensEmbeddableInput @@ -817,6 +828,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, input @@ -882,6 +894,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, input @@ -950,6 +963,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, input @@ -1003,6 +1017,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123' } as LensEmbeddableInput @@ -1058,6 +1073,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123' } as LensEmbeddableInput @@ -1110,6 +1126,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123', timeRange, query, filters } as LensEmbeddableInput @@ -1178,6 +1195,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123', onLoad } as unknown as LensEmbeddableInput @@ -1264,6 +1282,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123', onFilter } as unknown as LensEmbeddableInput @@ -1325,6 +1344,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123', onBrushEnd } as unknown as LensEmbeddableInput @@ -1383,6 +1403,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), }, { id: '123', onTableRowClick } as unknown as LensEmbeddableInput @@ -1415,6 +1436,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }; }); @@ -1526,6 +1548,7 @@ describe('embeddable', () => { }, indexPatterns: {}, indexPatternRefs: [], + activeVisualizationState: null, }), uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, }, diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 2f6d57bc86942..cd8729df6863a 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -31,7 +31,7 @@ import { import type { Start as InspectorStart } from '@kbn/inspector-plugin/public'; import { Subscription } from 'rxjs'; -import { toExpression, Ast } from '@kbn/interpreter'; +import { toExpression } from '@kbn/interpreter'; import { DefaultInspectorAdapters, ErrorLike, RenderMode } from '@kbn/expressions-plugin/common'; import { map, distinctUntilChanged, skip } from 'rxjs/operators'; import fastIsEqual from 'fast-deep-equal'; @@ -120,6 +120,7 @@ import { getApplicationUserMessages, } from '../app_plugin/get_application_user_messages'; import { MessageList } from '../editor_frame_service/editor_frame/workspace_panel/message_list'; +import type { DocumentToExpressionReturnType } from '../editor_frame_service/editor_frame'; export type LensSavedObjectAttributes = Omit; @@ -162,11 +163,7 @@ export interface LensEmbeddableOutput extends EmbeddableOutput { export interface LensEmbeddableDeps { attributeService: LensAttributeService; data: DataPublicPluginStart; - documentToExpression: (doc: Document) => Promise<{ - ast: Ast | null; - indexPatterns: IndexPatternMap; - indexPatternRefs: IndexPatternRef[]; - }>; + documentToExpression: (doc: Document) => Promise; injectFilterReferences: FilterManager['inject']; visualizationMap: VisualizationMap; datasourceMap: DatasourceMap; @@ -248,8 +245,14 @@ const getExpressionFromDocument = async ( document: Document, documentToExpression: LensEmbeddableDeps['documentToExpression'] ) => { - const { ast, indexPatterns, indexPatternRefs } = await documentToExpression(document); - return { ast: ast ? toExpression(ast) : null, indexPatterns, indexPatternRefs }; + const { ast, indexPatterns, indexPatternRefs, activeVisualizationState } = + await documentToExpression(document); + return { + ast: ast ? toExpression(ast) : null, + indexPatterns, + indexPatternRefs, + activeVisualizationState, + }; }; function getViewUnderlyingDataArgs({ @@ -409,6 +412,8 @@ export class Embeddable private viewUnderlyingDataArgs?: ViewUnderlyingDataArgs; + private activeVisualizationState?: unknown; + constructor( private deps: LensEmbeddableDeps, initialInput: LensEmbeddableInput, @@ -531,20 +536,12 @@ export class Embeddable return this.deps.visualizationMap[this.activeVisualizationId]; } - private get activeVisualizationState() { - if (!this.activeVisualization) return; - return this.activeVisualization.initialize( - () => '', - this.savedVis?.state.visualization, - undefined, - this.savedVis?.references - ); - } - private indexPatterns: IndexPatternMap = {}; private indexPatternRefs: IndexPatternRef[] = []; + // TODO - consider getting this from the persistedStateToExpression function + // where it is already computed private get activeDatasourceState(): undefined | unknown { if (!this.activeDatasourceId || !this.activeDatasource) return; @@ -688,14 +685,13 @@ export class Embeddable }; try { - const { ast, indexPatterns, indexPatternRefs } = await getExpressionFromDocument( - this.savedVis, - this.deps.documentToExpression - ); + const { ast, indexPatterns, indexPatternRefs, activeVisualizationState } = + await getExpressionFromDocument(this.savedVis, this.deps.documentToExpression); this.expression = ast; this.indexPatterns = indexPatterns; this.indexPatternRefs = indexPatternRefs; + this.activeVisualizationState = activeVisualizationState; } catch { // nothing, errors should be reported via getUserMessages } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 2e269f37e7f8c..6871c1ec0eb95 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -273,7 +273,7 @@ export class LensPlugin { ) { const startServices = createStartServicesGetter(core.getStartServices); - const getStartServices = async (): Promise => { + const getStartServicesForEmbeddable = async (): Promise => { const { getLensAttributeService, setUsageCollectionStart, initMemoizedErrorNotification } = await import('./async_services'); const { core: coreStart, plugins } = startServices(); @@ -289,6 +289,7 @@ export class LensPlugin { ); const visualizationMap = await this.editorFrameService!.loadVisualizations(); const datasourceMap = await this.editorFrameService!.loadDatasources(); + const eventAnnotationService = await eventAnnotation.getService(coreStart); if (plugins.usageCollection) { setUsageCollectionStart(plugins.usageCollection); @@ -310,6 +311,7 @@ export class LensPlugin { storage: new Storage(localStorage), uiSettings: core.uiSettings, timefilter: plugins.data.query.timefilter.timefilter, + eventAnnotationService, }), injectFilterReferences: data.query.filterManager.inject.bind(data.query.filterManager), visualizationMap, @@ -325,7 +327,10 @@ export class LensPlugin { }; if (embeddable) { - embeddable.registerEmbeddableFactory('lens', new EmbeddableFactory(getStartServices)); + embeddable.registerEmbeddableFactory( + 'lens', + new EmbeddableFactory(getStartServicesForEmbeddable) + ); } if (share) { From c27a6a3d87a863eabe8304241561ec502742bdd8 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 2 Mar 2023 16:55:28 -0600 Subject: [PATCH 009/202] improve saved object reference names and schema --- src/plugins/event_annotation/common/types.ts | 4 +- .../__snapshots__/visualization.test.ts.snap | 78 ++++++++ .../public/visualizations/xy/state_helpers.ts | 8 +- .../lens/public/visualizations/xy/types.ts | 2 +- .../visualizations/xy/visualization.test.ts | 178 ++++++++++++++---- .../xy/visualization_helpers.tsx | 3 +- 6 files changed, 225 insertions(+), 48 deletions(-) create mode 100644 x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index a7389e7f6dbdc..923723fe4e8c2 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -93,8 +93,8 @@ export interface EventAnnotationGroupAttributes { export interface EventAnnotationGroupConfig { annotations: EventAnnotationConfig[]; indexPatternId: string; - ignoreGlobalFilters?: boolean; - title?: string; + ignoreGlobalFilters?: boolean; // TODO - can this be a required attribute? + title?: string; // TODO - can this be a required attribute? } export type EventAnnotationArgs = diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap new file mode 100644 index 0000000000000..2864bbdc0a2e6 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap @@ -0,0 +1,78 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`xy_visualization #initialize should hydrate by-reference annotation groups 1`] = ` +Array [ + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "icon": "circle", + "id": "an1", + "key": Object { + "timestamp": "2022-03-18T08:25:17.140Z", + "type": "point_in_time", + }, + "label": "Event 1", + "type": "manual", + }, + ], + "ignoreGlobalFilters": true, + "indexPatternId": "data-view-123", + "title": "my title!", + }, + "annotationGroupId": "my-annotation-group-id1", + "annotations": Array [ + Object { + "icon": "circle", + "id": "an1", + "key": Object { + "timestamp": "2022-03-18T08:25:17.140Z", + "type": "point_in_time", + }, + "label": "Event 1", + "type": "manual", + }, + ], + "ignoreGlobalFilters": true, + "indexPatternId": "data-view-123", + "layerId": "annotation", + "layerType": "annotations", + }, + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "icon": "circle", + "id": "an2", + "key": Object { + "timestamp": "2022-04-18T11:01:59.135Z", + "type": "point_in_time", + }, + "label": "Annotation2", + "type": "manual", + }, + ], + "ignoreGlobalFilters": true, + "indexPatternId": "data-view-773203", + "title": "my other title!", + }, + "annotationGroupId": "my-annotation-group-id2", + "annotations": Array [ + Object { + "icon": "circle", + "id": "an2", + "key": Object { + "timestamp": "2022-04-18T11:01:59.135Z", + "type": "point_in_time", + }, + "label": "Annotation2", + "type": "manual", + }, + ], + "ignoreGlobalFilters": true, + "indexPatternId": "data-view-773203", + "layerId": "annotation", + "layerType": "annotations", + }, +] +`; diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 6ab4c5d1166a3..e2a5a5cd72ea1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -11,6 +11,7 @@ import { EventAnnotationGroupConfig, EVENT_ANNOTATION_GROUP_TYPE, } from '@kbn/event-annotation-plugin/common'; +import { v4 as uuidv4 } from 'uuid'; import { isQueryAnnotationConfig } from '@kbn/event-annotation-plugin/public'; import { i18n } from '@kbn/i18n'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; @@ -138,15 +139,16 @@ export function getPersistableState(state: XYState) { persistableLayers.push(layer); } else { if (isByReferenceAnnotationLayer(layer)) { + const referenceName = `ref-${uuidv4()}`; savedObjectReferences.push({ type: EVENT_ANNOTATION_GROUP_TYPE, id: layer.annotationGroupId, - name: getLayerReferenceName(layer.layerId), + name: referenceName, }); persistableLayers.push({ layerId: layer.layerId, layerType: layer.layerType, - annotationGroupId: layer.annotationGroupId, + annotationGroupRef: referenceName, }); } else { const { indexPatternId, ...persistableLayer } = layer; @@ -190,7 +192,7 @@ export function injectReferences( let injectedLayer: XYAnnotationLayerConfig; if (isPersistedByReferenceAnnotationLayer(layer)) { const annotationGroupId = references?.find( - ({ name }) => name === getLayerReferenceName(layer.layerId) + ({ name }) => name === layer.annotationGroupRef )?.id; const annotationGroup = annotationGroupId ? annotationGroups[annotationGroupId] : undefined; diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 67f94061553be..652f533388ba4 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -138,7 +138,7 @@ export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig export interface XYPersistedByReferenceAnnotationLayerConfig { layerId: string; layerType: 'annotations'; - annotationGroupId: string; + annotationGroupRef: string; } export type XYAnnotationLayerConfig = diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 3f9da5e7edd7a..1aa2de54b6838 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -24,6 +24,7 @@ import type { XYPersistedState, XYByValueAnnotationLayerConfig, XYByReferenceAnnotationLayerConfig, + XYPersistedByReferenceAnnotationLayerConfig, } from './types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { IconChartBar, IconCircle } from '@kbn/chart-icons'; @@ -44,6 +45,8 @@ import { createMockDataViewsState } from '../../data_views_service/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { layerTypes, Visualization } from '../..'; import { set } from '@kbn/safer-lodash-set'; +import { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; +import { getAnnotationLayers } from './visualization_helpers'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', @@ -195,7 +198,7 @@ describe('xy_visualization', () => { describe('#initialize', () => { it('loads default state', () => { - const initialState = xyVisualization.initialize(() => 'l1'); + const initialState = xyVisualization.initialize(() => 'l1', {}); expect(initialState.layers).toHaveLength(1); expect((initialState.layers[0] as XYDataLayerConfig).xAccessor).not.toBeDefined(); @@ -228,7 +231,7 @@ describe('xy_visualization', () => { expect(xyVisualization.initialize(() => 'first', {}, exampleState())).toEqual(exampleState()); }); - it('should inject references on annotation layers', () => { + it('should inject data view references on by-value annotation layers', () => { const baseState = exampleState(); expect( xyVisualization.initialize!( @@ -311,6 +314,73 @@ describe('xy_visualization', () => { ], }); }); + + it('should hydrate by-reference annotation groups', () => { + const annotationGroupId1 = 'my-annotation-group-id1'; + const annotationGroupId2 = 'my-annotation-group-id2'; + + const refName1 = 'my-reference'; + const refName2 = 'my-other-reference'; + + const references: SavedObjectReference[] = [ + { + name: refName1, + id: annotationGroupId1, + type: 'event-annotation-group', + }, + { + name: refName2, + id: annotationGroupId2, + type: 'event-annotation-group', + }, + { + name: 'some-name', + id: 'some-index-pattern-*', + type: 'index-pattern', + }, + ]; + + const baseState = exampleState(); + expect( + getAnnotationLayers( + xyVisualization.initialize!( + () => 'first', + { + [annotationGroupId1]: { + annotations: [exampleAnnotation], + indexPatternId: 'data-view-123', + ignoreGlobalFilters: true, + title: 'my title!', + }, + [annotationGroupId2]: { + annotations: [exampleAnnotation2], + indexPatternId: 'data-view-773203', + ignoreGlobalFilters: true, + title: 'my other title!', + }, + }, + { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotationGroupRef: refName1, + } as XYPersistedByReferenceAnnotationLayerConfig, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotationGroupRef: refName2, + } as XYPersistedByReferenceAnnotationLayerConfig, + ], + } as XYPersistedState, + undefined, + references + ).layers + ) + ).toMatchSnapshot(); + }); }); describe('#removeLayer', () => { @@ -3032,53 +3102,81 @@ describe('xy_visualization', () => { it('should extract annotation group ids from by-reference annotation layers', () => { const state = exampleState(); - const byValueLayer: XYByValueAnnotationLayerConfig = { - layerId: 'layer-id', - layerType: 'annotations', - indexPatternId: 'some-index-pattern', - ignoreGlobalFilters: false, - annotations: [ + const layers: XYByReferenceAnnotationLayerConfig[] = ( + [ { - id: 'some-annotation-id', - type: 'manual', - key: { - type: 'point_in_time', - timestamp: 'timestamp', - }, - } as PointInTimeEventAnnotationConfig, - ], - }; - - const layer: XYByReferenceAnnotationLayerConfig = { + layerId: 'layer-id', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }, + { + layerId: 'layer-id2', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id2', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }, + ] as XYByValueAnnotationLayerConfig[] + ).map((byValueLayer, index) => ({ ...byValueLayer, - annotationGroupId: 'annotation-group-id', + annotationGroupId: `annotation-group-id-${index}`, __lastSaved: { ...byValueLayer, title: 'My saved object title' }, - }; + })); - state.layers = [layer]; + state.layers = layers; const { state: persistableState, savedObjectReferences } = xyVisualization.getPersistableState!(state); - expect(persistableState.layers).toMatchInlineSnapshot(` - Array [ - Object { - "annotationGroupId": "annotation-group-id", - "layerId": "layer-id", - "layerType": "annotations", - }, - ] - `); + expect(persistableState.layers).toEqual([ + { + annotationGroupRef: savedObjectReferences[0].name, + layerId: 'layer-id', + layerType: 'annotations', + }, + { + annotationGroupRef: savedObjectReferences[1].name, + layerId: 'layer-id2', + layerType: 'annotations', + }, + ]); - expect(savedObjectReferences).toMatchInlineSnapshot(` - Array [ - Object { - "id": "annotation-group-id", - "name": "xy-visualization-layer-layer-id", - "type": "event-annotation-group", - }, - ] - `); + expect(savedObjectReferences).toEqual([ + { + name: (persistableState.layers[0] as XYPersistedByReferenceAnnotationLayerConfig) + .annotationGroupRef, + id: 'annotation-group-id-0', + type: 'event-annotation-group', + }, + { + name: (persistableState.layers[1] as XYPersistedByReferenceAnnotationLayerConfig) + .annotationGroupRef, + id: 'annotation-group-id-1', + type: 'event-annotation-group', + }, + ]); + + expect(savedObjectReferences[0].name).not.toBe(savedObjectReferences[1].name); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index 364a35981187e..f1f0160737217 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -154,8 +154,7 @@ export const isByReferenceAnnotationLayer = ( export const isPersistedByReferenceAnnotationLayer = ( layer: XYPersistedAnnotationLayerConfig -): layer is XYPersistedByReferenceAnnotationLayerConfig => - 'annotationGroupId' in layer && !('__lastSaved' in layer); +): layer is XYPersistedByReferenceAnnotationLayerConfig => 'annotationGroupRef' in layer; export const getAnnotationLayers = (layers: Array>) => (layers || []).filter((layer): layer is XYAnnotationLayerConfig => From 568b89c2c7d1bd0c0f060aba26f457a617b2401a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 2 Mar 2023 17:17:25 -0600 Subject: [PATCH 010/202] reverting a few name changes --- .../xy/annotations/actions/index.ts | 4 +-- .../visualizations/xy/annotations/helpers.tsx | 14 ++++----- .../visualizations/xy/color_assignment.ts | 6 ++-- .../public/visualizations/xy/state_helpers.ts | 24 +++++++------- .../public/visualizations/xy/to_expression.ts | 4 +-- .../visualizations/xy/visualization.test.ts | 4 +-- .../visualizations/xy/visualization.tsx | 31 +++++++++---------- .../xy/visualization_helpers.tsx | 14 ++++----- .../xy/xy_config_panel/dimension_editor.tsx | 8 ++--- .../xy/xy_config_panel/layer_header.tsx | 6 ++-- 10 files changed, 54 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index 6c3cc41702537..c289c4d168c34 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -14,7 +14,7 @@ import { getUnlinkLayerAction } from './unlink_action'; import { getIgnoreFilterAction } from './ignore_filters_action'; import { getEditDetailsAction } from './edit_details_action'; import { getSaveLayerAction } from './save_action'; -import { isByReferenceAnnotationLayer } from '../../visualization_helpers'; +import { isByReferenceAnnotationsLayer } from '../../visualization_helpers'; export { IGNORE_GLOBAL_FILTERS_ACTION_ID, KEEP_GLOBAL_FILTERS_ACTION_ID, @@ -45,7 +45,7 @@ export const createAnnotationActions = ({ if (savingToLibraryPermitted) { // check if the annotation is saved as a saved object or in inline - same as we check for save modal for visualization - if (isByReferenceAnnotationLayer(layer)) { + if (isByReferenceAnnotationsLayer(layer)) { // check if Annotation group hasUnsavedChanges to know if we should allow reverting and saving - similar to how we do it for persistedDoc vs currentDoc on app level const hasUnsavedChanges = true; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index de7dec5bc618a..c63fc226907c5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -23,10 +23,10 @@ import { annotationsIconSet } from '../xy_config_panel/annotations_config_panel/ import type { XYState, XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../types'; import { checkScaleOperation, - getAnnotationLayers, + getAnnotationsLayers, getAxisName, getDataLayers, - isByValueAnnotationLayer, + isAnnotationsLayer, } from '../visualization_helpers'; import { generateId } from '../../../id_generator'; @@ -180,7 +180,7 @@ export const onAnnotationDrop: Visualization['onDrop'] = ({ dropType, }) => { const targetLayer = prevState.layers.find((l) => l.layerId === target.layerId); - if (!targetLayer || !isByValueAnnotationLayer(targetLayer)) { + if (!targetLayer || !isAnnotationsLayer(targetLayer)) { return prevState; } const targetAnnotation = targetLayer.annotations.find(({ id }) => id === target.columnId); @@ -244,7 +244,7 @@ export const onAnnotationDrop: Visualization['onDrop'] = ({ } const sourceLayer = prevState.layers.find((l) => l.layerId === source.layerId); - if (!sourceLayer || !isByValueAnnotationLayer(sourceLayer)) { + if (!sourceLayer || !isAnnotationsLayer(sourceLayer)) { return prevState; } const sourceAnnotation = sourceLayer.annotations.find(({ id }) => id === source.columnId); @@ -279,7 +279,7 @@ export const onAnnotationDrop: Visualization['onDrop'] = ({ return { ...prevState, layers: prevState.layers.map((l): XYLayerConfig => { - if (!isByValueAnnotationLayer(l) || !isByValueAnnotationLayer(targetLayer)) { + if (!isAnnotationsLayer(l) || !isAnnotationsLayer(targetLayer)) { return l; } if (l.layerId === target.layerId) { @@ -418,7 +418,7 @@ export const setAnnotationsDimension: Visualization['setDimension'] = ( frame, }) => { const targetLayer = prevState.layers.find((l) => l.layerId === layerId); - if (!targetLayer || !isByValueAnnotationLayer(targetLayer)) { + if (!targetLayer || !isAnnotationsLayer(targetLayer)) { return prevState; } const sourceAnnotation = previousColumn @@ -513,7 +513,7 @@ export const getAnnotationsConfiguration = ({ }; export const getUniqueLabels = (layers: XYLayerConfig[]) => { - const annotationLayers = getAnnotationLayers(layers); + const annotationLayers = getAnnotationsLayers(layers); const columnLabelMap = {} as Record; const counts = {} as Record; diff --git a/x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts b/x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts index fe0b3c178516b..45742a72ca9de 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/color_assignment.ts @@ -17,7 +17,7 @@ import { import type { AccessorConfig, FramePublicAPI } from '../../types'; import { getColumnToLabelMap } from './state_helpers'; import { FormatFactory } from '../../../common'; -import { isDataLayer, isReferenceLayer, isByValueAnnotationLayer } from './visualization_helpers'; +import { isDataLayer, isReferenceLayer, isAnnotationsLayer } from './visualization_helpers'; import { getAnnotationsAccessorColorConfig } from './annotations/helpers'; import { getReferenceLineAccessorColorConfig, @@ -121,7 +121,7 @@ export function getAssignedColorConfig( if (isReferenceLayer(layer)) { return getSingleColorConfig(accessor); } - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { const annotation = layer.annotations.find((a) => a.id === accessor); return { columnId: accessor, @@ -175,7 +175,7 @@ export function getAccessorColorConfigs( if (isReferenceLayer(layer)) { return getReferenceLineAccessorColorConfig(layer); } - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { return getAnnotationsAccessorColorConfig(layer); } const layerContainsSplits = !layer.collapseFn && layer.splitAccessor; diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index e2a5a5cd72ea1..62aba32697d8b 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -33,12 +33,12 @@ import { } from './types'; import { getDataLayers, - isByValueAnnotationLayer, + isAnnotationsLayer, isDataLayer, - isPersistedByReferenceAnnotationLayer, - isByReferenceAnnotationLayer, - isPersistedByValueAnnotationLayer, - getAnnotationLayers, + isPersistedByReferenceAnnotationsLayer, + isByReferenceAnnotationsLayer, + isPersistedByValueAnnotationsLayer, + getAnnotationsLayers, } from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -76,7 +76,7 @@ export function getIconForSeries(type: SeriesType): EuiIconType { } export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { return layer?.annotations?.find((ann) => ann.id === accessor)?.color || null; } if (isDataLayer(layer) && layer.splitAccessor) { @@ -135,10 +135,10 @@ export function getPersistableState(state: XYState) { const savedObjectReferences: SavedObjectReference[] = []; const persistableLayers: XYPersistedLayerConfig[] = []; state.layers.forEach((layer) => { - if (!isByValueAnnotationLayer(layer)) { + if (!isAnnotationsLayer(layer)) { persistableLayers.push(layer); } else { - if (isByReferenceAnnotationLayer(layer)) { + if (isByReferenceAnnotationsLayer(layer)) { const referenceName = `ref-${uuidv4()}`; savedObjectReferences.push({ type: EVENT_ANNOTATION_GROUP_TYPE, @@ -165,9 +165,9 @@ export function getPersistableState(state: XYState) { } export function isPersistedState(state: XYPersistedState | XYState): state is XYPersistedState { - return getAnnotationLayers(state.layers).some( + return getAnnotationsLayers(state.layers).some( (layer) => - isPersistedByValueAnnotationLayer(layer) || isPersistedByReferenceAnnotationLayer(layer) + isPersistedByValueAnnotationsLayer(layer) || isPersistedByReferenceAnnotationsLayer(layer) ); } @@ -185,12 +185,12 @@ export function injectReferences( return { ...state, layers: state.layers.map((layer) => { - if (!isByValueAnnotationLayer(layer)) { + if (!isAnnotationsLayer(layer)) { return layer as XYLayerConfig; } let injectedLayer: XYAnnotationLayerConfig; - if (isPersistedByReferenceAnnotationLayer(layer)) { + if (isPersistedByReferenceAnnotationsLayer(layer)) { const annotationGroupId = references?.find( ({ name }) => name === layer.annotationGroupRef )?.id; diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 204639185f27f..4e33e35371246 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -56,7 +56,7 @@ import { getLayerTypeOptions, getDataLayers, getReferenceLayers, - getAnnotationLayers, + getAnnotationsLayers, } from './visualization_helpers'; import { getUniqueLabels } from './annotations/helpers'; import { @@ -207,7 +207,7 @@ export const buildXYExpression = ( ); const uniqueLabels = getUniqueLabels(state.layers); - const validAnnotationsLayers = getAnnotationLayers(state.layers) + const validAnnotationsLayers = getAnnotationsLayers(state.layers) .filter((layer) => Boolean(layer.annotations.length)) .map((layer) => { return { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 1aa2de54b6838..75348512c6c10 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -46,7 +46,7 @@ import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks import { layerTypes, Visualization } from '../..'; import { set } from '@kbn/safer-lodash-set'; import { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; -import { getAnnotationLayers } from './visualization_helpers'; +import { getAnnotationsLayers } from './visualization_helpers'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', @@ -342,7 +342,7 @@ describe('xy_visualization', () => { const baseState = exampleState(); expect( - getAnnotationLayers( + getAnnotationsLayers( xyVisualization.initialize!( () => 'first', { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 48b5d6b9f2816..06894916640f3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -78,7 +78,7 @@ import { import { checkXAccessorCompatibility, defaultSeriesType, - getAnnotationLayers, + getAnnotationsLayers, getAxisName, getDataLayers, getDescription, @@ -86,7 +86,7 @@ import { getLayersByType, getReferenceLayers, getVisualizationType, - isByValueAnnotationLayer, + isAnnotationsLayer, isBucketed, isDataLayer, isNumericDynamicMetric, @@ -153,7 +153,7 @@ export const getXyVisualization = ({ cloneLayer(state, layerId, newLayerId, clonedIDsMap) { const toCopyLayer = state.layers.find((l) => l.layerId === layerId); if (toCopyLayer) { - if (isByValueAnnotationLayer(toCopyLayer)) { + if (isAnnotationsLayer(toCopyLayer)) { toCopyLayer.annotations.forEach((i) => clonedIDsMap.set(i.id, generateId())); } const newLayer = renewIDs(toCopyLayer, [...clonedIDsMap.keys()], (id: string) => @@ -262,7 +262,7 @@ export const getXyVisualization = ({ const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; const actions = []; - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { actions.push( ...createAnnotationActions({ state, @@ -281,7 +281,7 @@ export const getXyVisualization = ({ onIndexPatternChange(state, indexPatternId, layerId) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; - if (!layer || !isByValueAnnotationLayer(layer)) { + if (!layer || !isAnnotationsLayer(layer)) { return state; } const newLayers = [...state.layers]; @@ -298,7 +298,7 @@ export const getXyVisualization = ({ return { groups: [] }; } - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { return getAnnotationsConfiguration({ state, frame, layer }); } @@ -451,7 +451,7 @@ export const getXyVisualization = ({ throw new Error('target layer should exist'); } - if (isByValueAnnotationLayer(targetLayer)) { + if (isAnnotationsLayer(targetLayer)) { return onAnnotationDrop?.(props) || props.prevState; } return onDropForVisualization(props, this); @@ -470,7 +470,7 @@ export const getXyVisualization = ({ if (isReferenceLayer(foundLayer)) { return setReferenceDimension(props); } - if (isByValueAnnotationLayer(foundLayer)) { + if (isAnnotationsLayer(foundLayer)) { return setAnnotationsDimension(props); } @@ -495,7 +495,7 @@ export const getXyVisualization = ({ if (!foundLayer) { return prevState; } - if (isByValueAnnotationLayer(foundLayer)) { + if (isAnnotationsLayer(foundLayer)) { const newLayer = { ...foundLayer, annotations: foundLayer.annotations.filter(({ id }) => id !== columnId), @@ -600,7 +600,7 @@ export const getXyVisualization = ({ const layer = props.state.layers.find((l) => l.layerId === props.layerId)!; const dimensionEditor = isReferenceLayer(layer) ? ( - ) : isByValueAnnotationLayer(layer) ? ( + ) : isAnnotationsLayer(layer) ? ( ) : ( @@ -641,7 +641,7 @@ export const getXyVisualization = ({ if (isReferenceLayer(layer)) { return; } - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { return; } @@ -692,7 +692,7 @@ export const getXyVisualization = ({ getUserMessages(state, { frame }) { const { datasourceLayers, dataViews, activeData } = frame; - const annotationLayers = getAnnotationLayers(state.layers); + const annotationLayers = getAnnotationsLayers(state.layers); const errors: UserMessage[] = []; const hasDateHistogram = isDateHistogram(getDataLayers(state.layers), frame); @@ -873,12 +873,11 @@ export const getXyVisualization = ({ return getUniqueLabels(state.layers); }, getUsedDataView(state, layerId) { - return getAnnotationLayers(state.layers).find((l) => l.layerId === layerId)?.indexPatternId; + return getAnnotationsLayers(state.layers).find((l) => l.layerId === layerId)?.indexPatternId; }, getUsedDataViews(state) { return ( - state?.layers.filter(isByValueAnnotationLayer).map(({ indexPatternId }) => indexPatternId) ?? - [] + state?.layers.filter(isAnnotationsLayer).map(({ indexPatternId }) => indexPatternId) ?? [] ); }, renderDimensionTrigger({ columnId, label }) { @@ -963,7 +962,7 @@ export const getXyVisualization = ({ }); icon = IconChartBarReferenceLine; } - if (isByValueAnnotationLayer(layer) && layer.annotations && layer.annotations.length) { + if (isAnnotationsLayer(layer) && layer.annotations && layer.annotations.length) { layer.annotations.forEach((annotation) => { dimensions.push({ name: i18n.translate('xpack.lens.xyChart.layerAnnotation', { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index f1f0160737217..991567be5b9c3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -139,27 +139,25 @@ export const isReferenceLayer = ( export const getReferenceLayers = (layers: Array>) => (layers || []).filter((layer): layer is XYReferenceLineLayerConfig => isReferenceLayer(layer)); -export const isByValueAnnotationLayer = ( +export const isAnnotationsLayer = ( layer: Pick ): layer is XYAnnotationLayerConfig => layer.layerType === layerTypes.ANNOTATIONS; -export const isPersistedByValueAnnotationLayer = ( +export const isPersistedByValueAnnotationsLayer = ( layer: Pick ): layer is XYAnnotationLayerConfig => layer.layerType === layerTypes.ANNOTATIONS; -export const isByReferenceAnnotationLayer = ( +export const isByReferenceAnnotationsLayer = ( layer: XYAnnotationLayerConfig ): layer is XYByReferenceAnnotationLayerConfig => 'annotationGroupId' in layer && '__lastSaved' in layer; -export const isPersistedByReferenceAnnotationLayer = ( +export const isPersistedByReferenceAnnotationsLayer = ( layer: XYPersistedAnnotationLayerConfig ): layer is XYPersistedByReferenceAnnotationLayerConfig => 'annotationGroupRef' in layer; -export const getAnnotationLayers = (layers: Array>) => - (layers || []).filter((layer): layer is XYAnnotationLayerConfig => - isByValueAnnotationLayer(layer) - ); +export const getAnnotationsLayers = (layers: Array>) => + (layers || []).filter((layer): layer is XYAnnotationLayerConfig => isAnnotationsLayer(layer)); export interface LayerTypeToLayer { [layerTypes.DATA]: (layer: XYDataLayerConfig) => XYDataLayerConfig; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index a5207b8e32028..d7a1a5b1fc105 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -16,11 +16,7 @@ import { FormatFactory } from '../../../../common'; import { getSeriesColor, isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; import { PalettePicker, useDebouncedValue } from '../../../shared_components'; -import { - getDataLayers, - isByValueAnnotationLayer, - isReferenceLayer, -} from '../visualization_helpers'; +import { getDataLayers, isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; import { ReferenceLinePanel } from './reference_line_config_panel'; import { AnnotationsPanel } from './annotations_config_panel'; import { CollapseSetting } from '../../../shared_components/collapse_setting'; @@ -55,7 +51,7 @@ export function DimensionEditor( const { state, layerId } = props; const index = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[index]; - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { return ; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx index 62fbf942084e8..819dfe13c2ba2 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx @@ -19,7 +19,7 @@ import { State, visualizationTypes, SeriesType, XYAnnotationLayerConfig } from ' import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { ChangeIndexPattern, StaticHeader } from '../../../shared_components'; import { updateLayer } from '.'; -import { isByValueAnnotationLayer, isDataLayer, isReferenceLayer } from '../visualization_helpers'; +import { isAnnotationsLayer, isDataLayer, isReferenceLayer } from '../visualization_helpers'; export function LayerHeader(props: VisualizationLayerWidgetProps) { const layer = props.state.layers.find((l) => l.layerId === props.layerId); @@ -29,7 +29,7 @@ export function LayerHeader(props: VisualizationLayerWidgetProps) { if (isReferenceLayer(layer)) { return ; } - if (isByValueAnnotationLayer(layer)) { + if (isAnnotationsLayer(layer)) { return ; } return ; @@ -37,7 +37,7 @@ export function LayerHeader(props: VisualizationLayerWidgetProps) { export function LayerHeaderContent(props: VisualizationLayerHeaderContentProps) { const layer = props.state.layers.find((l) => l.layerId === props.layerId); - if (layer && isByValueAnnotationLayer(layer)) { + if (layer && isAnnotationsLayer(layer)) { return ; } return null; From 10bb53d242d8ad1e34cb18a7666f89e48df353f5 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 2 Mar 2023 18:10:48 -0600 Subject: [PATCH 011/202] show saved title in layer panel header --- .../xy/xy_config_panel/layer_header.test.tsx | 73 +++++++++++++++++++ .../xy/xy_config_panel/layer_header.tsx | 24 ++++-- 2 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx new file mode 100644 index 0000000000000..96820dacc4d9e --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.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 React from 'react'; + +import { FramePublicAPI } from '../../../types'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { LayerHeader } from './layer_header'; +import { + XYByReferenceAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, + XYLayerConfig, + XYState, +} from '../types'; + +describe('layer header', () => { + describe('annotation layer header', () => { + it('should inject annotation title for by-reference layer', () => { + const byRefGroupTitle = 'My group title!'; + + const byRefLayer: XYByReferenceAnnotationLayerConfig = { + layerId: 'layer-123', + layerType: 'annotations', + annotationGroupId: 'some-group', + annotations: [], + indexPatternId: '', + ignoreGlobalFilters: false, + __lastSaved: { + title: byRefGroupTitle, + annotations: [], + indexPatternId: '', + }, + }; + + const byValueLayer: XYByValueAnnotationLayerConfig = { + layerId: 'layer-123', + layerType: 'annotations', + annotations: [], + indexPatternId: '', + ignoreGlobalFilters: false, + }; + + const getStateWithLayers = (layers: XYLayerConfig[]): XYState => ({ + preferredSeriesType: 'area', + legend: { isVisible: false, position: 'left' }, + layers, + }); + + const props: Omit[0], 'state'> = { + layerId: 'layer-123', + frame: {} as FramePublicAPI, + onChangeIndexPattern: () => {}, + setState: () => {}, + }; + + expect( + mountWithIntl() + .text() + .trim() + ).toBe('Annotations'); + + expect( + mountWithIntl() + .text() + .trim() + ).toBe(byRefGroupTitle); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx index 819dfe13c2ba2..14115d58ff376 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx @@ -19,7 +19,12 @@ import { State, visualizationTypes, SeriesType, XYAnnotationLayerConfig } from ' import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { ChangeIndexPattern, StaticHeader } from '../../../shared_components'; import { updateLayer } from '.'; -import { isAnnotationsLayer, isDataLayer, isReferenceLayer } from '../visualization_helpers'; +import { + isAnnotationsLayer, + isByReferenceAnnotationsLayer, + isDataLayer, + isReferenceLayer, +} from '../visualization_helpers'; export function LayerHeader(props: VisualizationLayerWidgetProps) { const layer = props.state.layers.find((l) => l.layerId === props.layerId); @@ -30,7 +35,11 @@ export function LayerHeader(props: VisualizationLayerWidgetProps) { return ; } if (isAnnotationsLayer(layer)) { - return ; + return ( + + ); } return ; } @@ -54,13 +63,16 @@ function ReferenceLayerHeader() { ); } -function AnnotationsLayerHeader() { +function AnnotationsLayerHeader({ title }: { title: string | undefined }) { return ( ); } From 3a87ab7e85878bef00ff643d6acd8f8afdea0145 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 7 Mar 2023 08:59:04 -0600 Subject: [PATCH 012/202] save annotations in modal instead of flyout --- src/plugins/event_annotation/common/types.ts | 2 + .../event_annotation_service/service.test.ts | 9 +- .../event_annotation_service/service.tsx | 4 +- .../__snapshots__/save_action.test.tsx.snap | 6 + .../xy/annotations/actions/index.ts | 5 + .../annotations/actions/save_action.test.tsx | 48 +++-- .../xy/annotations/actions/save_action.tsx | 178 +++++++++++------- .../lens/public/visualizations/xy/index.ts | 7 +- .../visualizations/xy/visualization.tsx | 4 + 9 files changed, 178 insertions(+), 85 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 923723fe4e8c2..7224a2b7ca1d0 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -95,6 +95,8 @@ export interface EventAnnotationGroupConfig { indexPatternId: string; ignoreGlobalFilters?: boolean; // TODO - can this be a required attribute? title?: string; // TODO - can this be a required attribute? + description?: string; // TODO - can this be a required attribute? + tags?: string[]; // TODO - can this be a required attribute? } export type EventAnnotationArgs = diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index ba411761f6c87..8420e621dc493 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -486,12 +486,19 @@ describe('Event Annotation Service', () => { ]; await eventAnnotationService.createAnnotationGroup({ title: 'newGroupTitle', + description: 'my description', + tags: ['my', 'many', 'tags'], indexPatternId: 'ipid', annotations, }); expect(core.savedObjects.client.create).toHaveBeenCalledWith( 'event-annotation-group', - { title: 'newGroupTitle', annotations }, + { + title: 'newGroupTitle', + description: 'my description', + tags: ['my', 'many', 'tags'], + annotations, + }, { references: [ { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index cbaaff7a19e3e..059b04f69c1a5 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -84,12 +84,12 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi const createAnnotationGroup = async ( group: EventAnnotationGroupConfig ): Promise<{ id: string }> => { - const { title, ignoreGlobalFilters, indexPatternId, annotations } = group; + const { title, description, tags, ignoreGlobalFilters, indexPatternId, annotations } = group; const groupSavedObjectId = ( await client.create( EVENT_ANNOTATION_GROUP_TYPE, - { title, ignoreGlobalFilters, annotations }, + { title, description, tags, ignoreGlobalFilters, annotations }, { references: [ { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap index 26ae9bef5d706..cd2b22d251b57 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -17,10 +17,16 @@ Array [ "type": "manual", }, ], + "description": "description", "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", + "tags": Array [ + "my", + "many", + "tags", + ], "title": "title", }, "annotationGroupId": "saved-id-123", diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index c289c4d168c34..c74d8d4c73f18 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; @@ -28,6 +29,7 @@ export const createAnnotationActions = ({ core, isSaveable, eventAnnotationService, + savedObjectsTagging, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -36,6 +38,7 @@ export const createAnnotationActions = ({ core: CoreStart; isSaveable?: boolean; eventAnnotationService: EventAnnotationServiceType; + savedObjectsTagging?: SavedObjectTaggingPluginStart; }): LayerAction[] => { const actions = []; @@ -56,6 +59,7 @@ export const createAnnotationActions = ({ setState, eventAnnotationService, toasts: core.notifications.toasts, + savedObjectsTagging, }); actions.push(saveAction); } @@ -86,6 +90,7 @@ export const createAnnotationActions = ({ setState, eventAnnotationService, toasts: core.notifications.toasts, + savedObjectsTagging, }) ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index c961fb1a6922a..10a7e666d12cf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -9,10 +9,11 @@ import React from 'react'; import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { XYByValueAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; -import { Flyout } from './save_action'; +import { SaveModal } from './save_action'; import { shallowWithIntl } from '@kbn/test-jest-helpers'; -import { EditDetailsFlyout } from './edit_details_action'; import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { SavedObjectSaveModal } from '@kbn/saved-objects-plugin/public'; +import { taggingApiMock } from '@kbn/saved-objects-tagging-plugin/public/mocks'; describe('annotation group save action', () => { const layerId = 'mylayerid'; @@ -36,7 +37,7 @@ describe('annotation group save action', () => { const savedId = 'saved-id-123'; - const getProps = () => ({ + const getProps: () => Parameters[0] = () => ({ state: { preferredSeriesType: 'area', legend: { isVisible: true, position: 'bottom' }, @@ -52,6 +53,8 @@ describe('annotation group save action', () => { renderEventAnnotationGroupSavedObjectFinder: jest.fn(), } as EventAnnotationServiceType, toasts: toastsServiceMock.createStartContract(), + domElement: document.createElement('div'), + savedObjectsTagging: taggingApiMock.create(), }); let props: ReturnType; @@ -59,22 +62,38 @@ describe('annotation group save action', () => { props = getProps(); }); + const modalSaveArgs = { + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + onTitleDuplicate: () => {}, + }; + test('successful initial save', async () => { - const wrapper = shallowWithIntl( - - ); + const wrapper = shallowWithIntl(); const newTitle = 'title'; + const newDescription = 'description'; + const myTags = ['my', 'many', 'tags']; + + (wrapper + .find(SavedObjectSaveModal) + .prop('options') as React.ReactElement)!.props.onTagsSelected(myTags); // ignore the linter, you need this await statement - await wrapper.find(EditDetailsFlyout).prop('onConfirm')(newTitle); + await wrapper.find(SavedObjectSaveModal).prop('onSave')({ + newTitle, + newDescription, + ...modalSaveArgs, + }); expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ ...props.layer, title: newTitle, + description: newDescription, + tags: myTags, }); - expect(props.setState.mock.calls).toMatchSnapshot(); + expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); }); @@ -84,18 +103,23 @@ describe('annotation group save action', () => { new Error('oh noooooo') ); - const wrapper = shallowWithIntl( - - ); + const wrapper = shallowWithIntl(); const newTitle = 'title'; + const newDescription = 'new description'; // ignore the linter, you need this await statement - await wrapper.find(EditDetailsFlyout).prop('onConfirm')(newTitle); + await wrapper.find(SavedObjectSaveModal).prop('onSave')({ + newTitle, + newDescription, + ...modalSaveArgs, + }); expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ ...props.layer, title: newTitle, + description: newDescription, + tags: [], }); expect(props.toasts.addError).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 5f4b8f2214e21..6beecbbfb0b92 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -5,25 +5,29 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { render } from 'react-dom'; +import { render, unmountComponentAtNode } from 'react-dom'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { ToastsStart } from '@kbn/core-notifications-browser'; import { MountPoint } from '@kbn/core-mount-utils-browser'; import { FormattedMessage } from '@kbn/i18n-react'; +import { SavedObjectSaveModal } from '@kbn/saved-objects-plugin/public'; +import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-plugin/common'; +import { EuiIcon } from '@elastic/eui'; +import { type SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYByReferenceAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; -import { EditDetailsFlyout } from './edit_details_action'; // exported for testing only -export const Flyout = ({ +export const SaveModal = ({ domElement, state, layer, setState, eventAnnotationService, toasts, + savedObjectsTagging, }: { domElement: HTMLDivElement; state: XYState; @@ -31,81 +35,118 @@ export const Flyout = ({ setState: StateSetter; eventAnnotationService: EventAnnotationServiceType; toasts: ToastsStart; -}) => ( - { - let savedId: string; + savedObjectsTagging?: SavedObjectTaggingPluginStart; +}) => { + const initialTags: string[] = []; - try { - const { id } = await eventAnnotationService.createAnnotationGroup({ + const [selectedTags, setSelectedTags] = useState(initialTags); + + const closeModal = () => unmountComponentAtNode(domElement); + + return ( + { + let savedId: string; + + try { + const { id } = await eventAnnotationService.createAnnotationGroup({ + ...layer, + title: newTitle, + description: newDescription, + tags: selectedTags, + }); + + savedId = id; + } catch (err) { + toasts.addError(err, { + title: i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.errorToastTitle', + { + defaultMessage: 'Failed to save "{title}"', + values: { + title: newTitle, + }, + } + ), + }); + + return; + } + + const newLayer: XYByReferenceAnnotationLayerConfig = { ...layer, - title, + annotationGroupId: savedId, + __lastSaved: { + ...layer, + title: newTitle, + description: newDescription, + tags: selectedTags, + }, + }; + + setState({ + ...state, + layers: state.layers.map((existingLayer) => + existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer + ), }); - savedId = id; - } catch (err) { - toasts.addError(err, { + closeModal(); + + toasts.addSuccess({ title: i18n.translate( - 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.errorToastTitle', + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', { - defaultMessage: 'Failed to save "{title}"', + defaultMessage: 'Saved "{title}"', values: { - title, + title: newTitle, }, } ), + text: ((element) => + render( +
+ annotation library, + }} + /> +
, + element + )) as MountPoint, }); - - return; + }} + onClose={closeModal} + title="" + showCopyOnSave={false} + objectType={EVENT_ANNOTATION_GROUP_TYPE} + showDescription={true} + confirmButtonLabel={ + <> +
+ +
+
+ {i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.confirmButton', + { defaultMessage: 'Save annotation group' } + )} +
+ } - - const newLayer: XYByReferenceAnnotationLayerConfig = { - ...layer, - annotationGroupId: savedId, - __lastSaved: { - title, - ...layer, - }, - }; - - setState({ - ...state, - layers: state.layers.map((existingLayer) => - existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer - ), - }); - - toasts.addSuccess({ - title: i18n.translate( - 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', - { - defaultMessage: 'Saved "{title}"', - values: { - title, - }, - } - ), - text: ((element) => - render( -
- annotation library, - }} - /> -
, - element - )) as MountPoint, - }); - }} - /> -); + options={ + savedObjectsTagging ? ( + + ) : undefined + } + /> + ); +}; export const getSaveLayerAction = (props: { state: XYState; @@ -114,6 +155,7 @@ export const getSaveLayerAction = (props: { eventAnnotationService: EventAnnotationServiceType; isNew?: boolean; toasts: ToastsStart; + savedObjectsTagging?: SavedObjectTaggingPluginStart; }): LayerAction => { const displayName = props.isNew ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { @@ -130,7 +172,7 @@ export const getSaveLayerAction = (props: { ), execute: async (domElement) => { if (props.isNew && domElement) { - render(, domElement); + render(, domElement); } else { return props.eventAnnotationService.createAnnotationGroup(props.layer).then(); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/index.ts b/x-pack/plugins/lens/public/visualizations/xy/index.ts index 800c2227baadd..ce99700d456d3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/index.ts @@ -30,8 +30,10 @@ export class XyVisualization { ) { editorFrame.registerVisualization(async () => { const { getXyVisualization } = await import('../../async_services'); - const [coreStart, { charts, data, fieldFormats, eventAnnotation, unifiedSearch }] = - await core.getStartServices(); + const [ + coreStart, + { charts, data, fieldFormats, eventAnnotation, unifiedSearch, savedObjectsTagging }, + ] = await core.getStartServices(); const [palettes, eventAnnotationService] = await Promise.all([ charts.palettes.getPalettes(), eventAnnotation.getService(coreStart), @@ -47,6 +49,7 @@ export class XyVisualization { useLegacyTimeAxis, kibanaTheme: core.theme, unifiedSearch, + savedObjectsTagging, }); }); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 06894916640f3..9c8ae74f2c7cd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -21,6 +21,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -116,6 +117,7 @@ export const getXyVisualization = ({ kibanaTheme, eventAnnotationService, unifiedSearch, + savedObjectsTagging, }: { core: CoreStart; storage: IStorageWrapper; @@ -126,6 +128,7 @@ export const getXyVisualization = ({ useLegacyTimeAxis: boolean; kibanaTheme: ThemeServiceStart; unifiedSearch: UnifiedSearchPublicPluginStart; + savedObjectsTagging?: SavedObjectTaggingPluginStart; }): Visualization => ({ id: XY_ID, visualizationTypes, @@ -272,6 +275,7 @@ export const getXyVisualization = ({ core, isSaveable, eventAnnotationService, + savedObjectsTagging, }) ); } From dd6ef5932d4ea5cfd9380d1806d25b18df263c34 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 7 Mar 2023 19:56:10 -0600 Subject: [PATCH 013/202] functional test: saving an annotation group --- .../functional/page_objects/visualize_page.ts | 13 ++++- .../public/visualizations/xy/add_layer.tsx | 2 + .../apps/lens/group3/annotations.ts | 49 ++++++++++++++++++- .../test/functional/page_objects/lens_page.ts | 13 +++++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index be870ce24e127..bcfbc8caa9ce1 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -16,6 +16,7 @@ interface VisualizeSaveModalArgs { redirectToOrigin?: boolean; addToDashboard?: boolean; dashboardId?: string; + description?: string; } type DashboardPickerOption = @@ -393,10 +394,20 @@ export class VisualizePageObject extends FtrService { public async setSaveModalValues( vizName: string, - { saveAsNew, redirectToOrigin, addToDashboard, dashboardId }: VisualizeSaveModalArgs = {} + { + saveAsNew, + redirectToOrigin, + addToDashboard, + dashboardId, + description, + }: VisualizeSaveModalArgs = {} ) { await this.testSubjects.setValue('savedObjectTitle', vizName); + if (description) { + await this.testSubjects.setValue('viewDescription', description); + } + const saveAsNewCheckboxExists = await this.testSubjects.exists('saveAsNewCheckbox'); if (saveAsNewCheckboxExists) { const state = saveAsNew ? 'check' : 'uncheck'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index b456ff7ca682e..0727abcb0b8c1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -187,6 +187,7 @@ export function AddLayerButton({ onAddLayerClick(LayerTypes.ANNOTATIONS); toggleLayersChoice(false); }, + 'data-test-subj': 'lnsAnnotationLayer_new', }, { name: i18n.translate('xpack.lens.configPanel.loadFromLibrary', { @@ -197,6 +198,7 @@ export function AddLayerButton({ setLoadLibraryFlyoutVisible(true); toggleLayersChoice(false); }, + 'data-test-subj': 'lnsAnnotationLayer_addFromLibrary', }, ], }, diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index 373b04284fb2f..5975a81704da1 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -9,9 +9,10 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header', 'tagManagement']); const find = getService('find'); const retry = getService('retry'); + const toastsService = getService('toasts'); const testSubjects = getService('testSubjects'); const from = 'Sep 19, 2015 @ 06:31:44.000'; const to = 'Sep 23, 2015 @ 18:31:44.000'; @@ -101,5 +102,51 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('xyVisGroupedAnnotationIcon'); }); + + it('should save annotation group to library', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + + await PageObjects.lens.goToTimeRange(); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', 'xyVisChart'); + + await PageObjects.lens.createLayer('annotations'); + + await PageObjects.lens.performLayerAction('lnsXY_annotationLayer_saveToLibrary', 1); + + await PageObjects.visualize.setSaveModalValues('new annotation group', { + description: 'my description', + }); + + await testSubjects.click('savedObjectTagSelector'); + await testSubjects.click(`tagSelectorOption-action__create`); + + const { tagModal } = PageObjects.tagManagement; + + expect(await tagModal.isOpened()).to.be(true); + + await tagModal.fillForm( + { + name: 'my-new-tag', + color: '#FFCC33', + description: '', + }, + { + submit: true, + } + ); + + expect(await tagModal.isOpened()).to.be(false); + + await testSubjects.click('confirmSaveSavedObjectButton'); + + const toastContents = await toastsService.getToastContent(1); + + expect(toastContents).to.be( + 'Saved "new annotation group"\nView or manage in the annotation library' + ); + + // TODO check annotation library + }); }); } diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 0742e3cba9bb4..374737a1bc354 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -562,6 +562,16 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, + /** + * Perform the specified layer action + */ + async performLayerAction(testSubject: string, layerIndex = 0) { + await retry.try(async () => { + await testSubjects.click(`lnsLayerSplitButton--${layerIndex}`); + await testSubjects.click(testSubject); + }); + }, + async isDimensionEditorOpen() { return await testSubjects.exists('lns-indexPattern-dimensionContainerBack'); }, @@ -916,6 +926,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); if (await testSubjects.exists(`lnsLayerAddButton-${layerType}`)) { await testSubjects.click(`lnsLayerAddButton-${layerType}`); + if (layerType === 'annotations') { + await testSubjects.click('lnsAnnotationLayer_new'); + } } }, From b47030c8cbf4423b94426ce85934db37a03bf5c0 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 8 Mar 2023 12:53:36 -0600 Subject: [PATCH 014/202] better abstractions in the save action modal setup --- .../__snapshots__/save_action.test.tsx.snap | 10 +- .../annotations/actions/save_action.test.tsx | 226 ++++++++++-------- .../xy/annotations/actions/save_action.tsx | 223 ++++++++++------- 3 files changed, 263 insertions(+), 196 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap index cd2b22d251b57..f759df1f37382 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`annotation group save action successful initial save 1`] = ` +exports[`annotation group save action save routine successful initial save 1`] = ` Array [ Array [ Object { @@ -17,17 +17,15 @@ Array [ "type": "manual", }, ], - "description": "description", + "description": "my description", "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", "tags": Array [ - "my", - "many", - "tags", + "my-tag", ], - "title": "title", + "title": "my title", }, "annotationGroupId": "saved-id-123", "annotations": Array [ diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index 10a7e666d12cf..14e7472d8fa3c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -9,123 +9,155 @@ import React from 'react'; import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { XYByValueAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; -import { SaveModal } from './save_action'; +import { onSave, SaveModal } from './save_action'; import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { SavedObjectSaveModal } from '@kbn/saved-objects-plugin/public'; import { taggingApiMock } from '@kbn/saved-objects-tagging-plugin/public/mocks'; describe('annotation group save action', () => { - const layerId = 'mylayerid'; - - const layer: XYByValueAnnotationLayerConfig = { - layerId, - layerType: 'annotations', - indexPatternId: 'some-index-pattern', - ignoreGlobalFilters: false, - annotations: [ - { - id: 'some-annotation-id', - type: 'manual', - key: { - type: 'point_in_time', - timestamp: 'timestamp', - }, - } as PointInTimeEventAnnotationConfig, - ], - }; - - const savedId = 'saved-id-123'; - - const getProps: () => Parameters[0] = () => ({ - state: { - preferredSeriesType: 'area', - legend: { isVisible: true, position: 'bottom' }, - layers: [{ layerId } as XYAnnotationLayerConfig], - } as XYState, - layer, - setState: jest.fn(), - eventAnnotationService: { - createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), - loadAnnotationGroup: jest.fn(), - toExpression: jest.fn(), - toFetchExpression: jest.fn(), - renderEventAnnotationGroupSavedObjectFinder: jest.fn(), - } as EventAnnotationServiceType, - toasts: toastsServiceMock.createStartContract(), - domElement: document.createElement('div'), - savedObjectsTagging: taggingApiMock.create(), - }); - - let props: ReturnType; - beforeEach(() => { - props = getProps(); + describe('save modal', () => { + const modalSaveArgs = { + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + onTitleDuplicate: () => {}, + }; + + it('reports new saved object attributes', async () => { + const onSaveMock = jest.fn(); + const savedObjectsTagging = taggingApiMock.create(); + const wrapper = shallowWithIntl( + + ); + + const newTitle = 'title'; + const newDescription = 'description'; + const myTags = ['my', 'many', 'tags']; + + (wrapper + .find(SavedObjectSaveModal) + .prop('options') as React.ReactElement)!.props.onTagsSelected(myTags); + + // ignore the linter, you need this await statement + await wrapper.find(SavedObjectSaveModal).prop('onSave')({ + newTitle, + newDescription, + ...modalSaveArgs, + }); + + expect(onSaveMock).toHaveBeenCalledTimes(1); + expect(onSaveMock.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "closeModal": [Function], + "isTitleDuplicateConfirmed": false, + "newCopyOnSave": false, + "newDescription": "description", + "newTags": Array [ + "my", + "many", + "tags", + ], + "newTitle": "title", + "onTitleDuplicate": [Function], + }, + ] + `); + }); }); - const modalSaveArgs = { - newCopyOnSave: false, - isTitleDuplicateConfirmed: false, - onTitleDuplicate: () => {}, - }; - - test('successful initial save', async () => { - const wrapper = shallowWithIntl(); - - const newTitle = 'title'; - const newDescription = 'description'; - const myTags = ['my', 'many', 'tags']; - - (wrapper - .find(SavedObjectSaveModal) - .prop('options') as React.ReactElement)!.props.onTagsSelected(myTags); - - // ignore the linter, you need this await statement - await wrapper.find(SavedObjectSaveModal).prop('onSave')({ - newTitle, - newDescription, - ...modalSaveArgs, + describe('save routine', () => { + const layerId = 'mylayerid'; + + const layer: XYByValueAnnotationLayerConfig = { + layerId, + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }; + + const savedId = 'saved-id-123'; + + const getProps: () => Parameters[0] = () => ({ + state: { + preferredSeriesType: 'area', + legend: { isVisible: true, position: 'bottom' }, + layers: [{ layerId } as XYAnnotationLayerConfig], + } as XYState, + layer, + setState: jest.fn(), + eventAnnotationService: { + createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), + loadAnnotationGroup: jest.fn(), + toExpression: jest.fn(), + toFetchExpression: jest.fn(), + renderEventAnnotationGroupSavedObjectFinder: jest.fn(), + } as EventAnnotationServiceType, + toasts: toastsServiceMock.createStartContract(), + modalOnSaveProps: { + newTitle: 'my title', + newDescription: 'my description', + closeModal: jest.fn(), + newTags: ['my-tag'], + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + onTitleDuplicate: () => {}, + }, }); - expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ - ...props.layer, - title: newTitle, - description: newDescription, - tags: myTags, + let props: ReturnType; + beforeEach(() => { + props = getProps(); }); - expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); + test('successful initial save', async () => { + await onSave(props); - expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); - }); + expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ + ...props.layer, + title: props.modalOnSaveProps.newTitle, + description: props.modalOnSaveProps.newDescription, + tags: props.modalOnSaveProps.newTags, + }); - test('failed initial save', async () => { - (props.eventAnnotationService.createAnnotationGroup as jest.Mock).mockRejectedValue( - new Error('oh noooooo') - ); + expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); - const wrapper = shallowWithIntl(); + expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); + }); - const newTitle = 'title'; - const newDescription = 'new description'; + test('failed initial save', async () => { + (props.eventAnnotationService.createAnnotationGroup as jest.Mock).mockRejectedValue( + new Error('oh noooooo') + ); - // ignore the linter, you need this await statement - await wrapper.find(SavedObjectSaveModal).prop('onSave')({ - newTitle, - newDescription, - ...modalSaveArgs, - }); + await onSave(props); - expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ - ...props.layer, - title: newTitle, - description: newDescription, - tags: [], - }); + expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ + ...props.layer, + title: props.modalOnSaveProps.newTitle, + description: props.modalOnSaveProps.newDescription, + tags: props.modalOnSaveProps.newTags, + }); - expect(props.toasts.addError).toHaveBeenCalledTimes(1); + expect(props.toasts.addError).toHaveBeenCalledTimes(1); - expect(props.setState).not.toHaveBeenCalled(); + expect(props.setState).not.toHaveBeenCalled(); - expect(props.toasts.addSuccess).not.toHaveBeenCalled(); + expect(props.toasts.addSuccess).not.toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 6beecbbfb0b92..06d498164489a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -12,30 +12,27 @@ import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public' import { ToastsStart } from '@kbn/core-notifications-browser'; import { MountPoint } from '@kbn/core-mount-utils-browser'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SavedObjectSaveModal } from '@kbn/saved-objects-plugin/public'; +import { + OnSaveProps as SavedObjectOnSaveProps, + SavedObjectSaveModal, +} from '@kbn/saved-objects-plugin/public'; import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-plugin/common'; import { EuiIcon } from '@elastic/eui'; import { type SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYByReferenceAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; -// exported for testing only +type ModalOnSaveProps = SavedObjectOnSaveProps & { newTags: string[]; closeModal: () => void }; + +/** @internal exported for testing only */ export const SaveModal = ({ domElement, - state, - layer, - setState, - eventAnnotationService, - toasts, savedObjectsTagging, + onSave, }: { domElement: HTMLDivElement; - state: XYState; - layer: XYAnnotationLayerConfig; - setState: StateSetter; - eventAnnotationService: EventAnnotationServiceType; - toasts: ToastsStart; - savedObjectsTagging?: SavedObjectTaggingPluginStart; + savedObjectsTagging: SavedObjectTaggingPluginStart | undefined; + onSave: (props: ModalOnSaveProps) => void; }) => { const initialTags: string[] = []; @@ -45,81 +42,10 @@ export const SaveModal = ({ return ( { - let savedId: string; - - try { - const { id } = await eventAnnotationService.createAnnotationGroup({ - ...layer, - title: newTitle, - description: newDescription, - tags: selectedTags, - }); - - savedId = id; - } catch (err) { - toasts.addError(err, { - title: i18n.translate( - 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.errorToastTitle', - { - defaultMessage: 'Failed to save "{title}"', - values: { - title: newTitle, - }, - } - ), - }); - - return; - } - - const newLayer: XYByReferenceAnnotationLayerConfig = { - ...layer, - annotationGroupId: savedId, - __lastSaved: { - ...layer, - title: newTitle, - description: newDescription, - tags: selectedTags, - }, - }; - - setState({ - ...state, - layers: state.layers.map((existingLayer) => - existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer - ), - }); - - closeModal(); - - toasts.addSuccess({ - title: i18n.translate( - 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', - { - defaultMessage: 'Saved "{title}"', - values: { - title: newTitle, - }, - } - ), - text: ((element) => - render( -
- annotation library, - }} - /> -
, - element - )) as MountPoint, - }); - }} + onSave={async (props) => onSave({ ...props, closeModal, newTags: selectedTags })} onClose={closeModal} - title="" + title={''} + description={''} showCopyOnSave={false} objectType={EVENT_ANNOTATION_GROUP_TYPE} showDescription={true} @@ -148,7 +74,103 @@ export const SaveModal = ({ ); }; -export const getSaveLayerAction = (props: { +/** @internal exported for testing only */ +export const onSave = async ({ + state, + layer, + setState, + eventAnnotationService, + toasts, + modalOnSaveProps: { newTitle, newDescription, newTags: selectedTags, closeModal }, +}: { + state: XYState; + layer: XYAnnotationLayerConfig; + setState: StateSetter; + eventAnnotationService: EventAnnotationServiceType; + toasts: ToastsStart; + modalOnSaveProps: ModalOnSaveProps; +}) => { + let savedId: string; + + try { + const { id } = await eventAnnotationService.createAnnotationGroup({ + ...layer, + title: newTitle, + description: newDescription, + tags: selectedTags, + }); + + savedId = id; + } catch (err) { + toasts.addError(err, { + title: i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.errorToastTitle', + { + defaultMessage: 'Failed to save "{title}"', + values: { + title: newTitle, + }, + } + ), + }); + + return; + } + + const newLayer: XYByReferenceAnnotationLayerConfig = { + ...layer, + annotationGroupId: savedId, + __lastSaved: { + ...layer, + title: newTitle, + description: newDescription, + tags: selectedTags, + }, + }; + + setState({ + ...state, + layers: state.layers.map((existingLayer) => + existingLayer.layerId === newLayer.layerId ? newLayer : existingLayer + ), + }); + + closeModal(); + + toasts.addSuccess({ + title: i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.successToastTitle', + { + defaultMessage: 'Saved "{title}"', + values: { + title: newTitle, + }, + } + ), + text: ((element) => + render( +
+ annotation library, + }} + /> +
, + element + )) as MountPoint, + }); +}; + +export const getSaveLayerAction = ({ + state, + layer, + setState, + eventAnnotationService, + toasts, + savedObjectsTagging, +}: { state: XYState; layer: XYAnnotationLayerConfig; setState: StateSetter; @@ -157,13 +179,14 @@ export const getSaveLayerAction = (props: { toasts: ToastsStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; }): LayerAction => { - const displayName = props.isNew + const displayName = false ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { defaultMessage: 'Add to library', }) : i18n.translate('xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary', { defaultMessage: 'Save to library', }); + return { displayName, description: i18n.translate( @@ -171,10 +194,24 @@ export const getSaveLayerAction = (props: { { defaultMessage: 'Saves annotation group as separate saved object' } ), execute: async (domElement) => { - if (props.isNew && domElement) { - render(, domElement); - } else { - return props.eventAnnotationService.createAnnotationGroup(props.layer).then(); + if (domElement) { + render( + { + await onSave({ + state, + layer, + setState, + eventAnnotationService, + toasts, + modalOnSaveProps: props, + }); + }} + />, + domElement + ); } }, icon: 'save', From b6c7c02e41a6a616d9e4de42d0bb7f130b776ecf Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 8 Mar 2023 15:45:40 -0600 Subject: [PATCH 015/202] updating and copying existing annotation group --- src/plugins/event_annotation/common/types.ts | 14 +- .../event_annotation_service/service.test.ts | 14 +- .../event_annotation_service/service.tsx | 46 +++---- .../public/event_annotation_service/types.ts | 8 +- .../__snapshots__/save_action.test.tsx.snap | 106 ++++++++++++++- .../xy/annotations/actions/index.ts | 61 ++++----- .../annotations/actions/save_action.test.tsx | 128 +++++++++++++++++- .../xy/annotations/actions/save_action.tsx | 97 +++++++++---- 8 files changed, 367 insertions(+), 107 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 7224a2b7ca1d0..0f50b3687ca12 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -84,19 +84,19 @@ export type EventAnnotationConfig = export interface EventAnnotationGroupAttributes { title: string; - description?: string; - tags?: string[]; - ignoreGlobalFilters?: boolean; + description: string; + tags: string[]; + ignoreGlobalFilters: boolean; annotations: EventAnnotationConfig[]; } export interface EventAnnotationGroupConfig { annotations: EventAnnotationConfig[]; indexPatternId: string; - ignoreGlobalFilters?: boolean; // TODO - can this be a required attribute? - title?: string; // TODO - can this be a required attribute? - description?: string; // TODO - can this be a required attribute? - tags?: string[]; // TODO - can this be a required attribute? + ignoreGlobalFilters: boolean; + title: string; + description: string; + tags: string[]; } export type EventAnnotationArgs = diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 8420e621dc493..b9ea9b28a07db 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -489,6 +489,7 @@ describe('Event Annotation Service', () => { description: 'my description', tags: ['my', 'many', 'tags'], indexPatternId: 'ipid', + ignoreGlobalFilters: false, annotations, }); expect(core.savedObjects.client.create).toHaveBeenCalledWith( @@ -511,16 +512,23 @@ describe('Event Annotation Service', () => { ); }); }); - describe.skip('updateAnnotationGroupAttributes', () => { + describe('updateAnnotationGroup', () => { it('updates annotation group attributes', async () => { await eventAnnotationService.updateAnnotationGroup( - { title: 'newTitle', indexPatternId: 'newId', annotations: [] }, + { + title: 'newTitle', + description: '', + tags: [], + indexPatternId: 'newId', + annotations: [], + ignoreGlobalFilters: false, + }, 'multiAnnotations' ); expect(core.savedObjects.client.update).toHaveBeenCalledWith( 'event-annotation-group', 'multiAnnotations', - { title: 'newTitle' }, + { title: 'newTitle', description: '', tags: [], annotations: [] }, { references: [ { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 059b04f69c1a5..28f34f0e902b0 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -57,8 +57,8 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi return { title: savedObject.attributes.title, - // description: groupResponse.saved_object.attributes.description, - // tags: groupResponse.saved_object.attributes.tags, + description: savedObject.attributes.description, + tags: savedObject.attributes.tags, ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters, indexPatternId: savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!, annotations: savedObject.attributes.annotations, @@ -105,26 +105,26 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi return { id: groupSavedObjectId }; }; - // const updateAnnotationGroup = async ( - // group: EventAnnotationGroupConfig, - // savedObjectId: string - // ): Promise => { - // const { title, description, tags, ignoreGlobalFilters, indexPatternId } = group; - // await client.update( - // EVENT_ANNOTATION_GROUP_TYPE, - // savedObjectId, - // { title, description, tags, ignoreGlobalFilters }, - // { - // references: [ - // { - // type: 'index-pattern', - // id: indexPatternId, - // name: `event-annotation-group_dataView-ref-${indexPatternId}`, - // }, - // ], - // } - // ); - // }; + const updateAnnotationGroup = async ( + group: EventAnnotationGroupConfig, + annotationGroupId: string + ): Promise => { + const { title, description, tags, ignoreGlobalFilters, indexPatternId, annotations } = group; + await client.update( + EVENT_ANNOTATION_GROUP_TYPE, + annotationGroupId, + { title, description, tags, ignoreGlobalFilters, annotations }, + { + references: [ + { + type: 'index-pattern', + id: indexPatternId, + name: `event-annotation-group_dataView-ref-${indexPatternId}`, + }, + ], + } + ); + }; // const updateAnnotations = async ( // savedObjectId: string, @@ -157,7 +157,7 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi return { loadAnnotationGroup, // updateAnnotations, - // updateAnnotationGroup, + updateAnnotationGroup, createAnnotationGroup, // deleteAnnotationGroup, renderEventAnnotationGroupSavedObjectFinder: (props: { diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index 988e6102c6f20..aaeba1d3dbaa0 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -18,10 +18,10 @@ export interface EventAnnotationServiceType { // savedObjectId: string, // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } // ) => Promise; - // updateAnnotationGroup: ( - // group: EventAnnotationGroupConfig, - // savedObjectId: string - // ) => Promise; + updateAnnotationGroup: ( + group: EventAnnotationGroupConfig, + savedObjectId: string + ) => Promise; toExpression: (props: EventAnnotationConfig[]) => ExpressionAstExpression[]; toFetchExpression: (props: { interval: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap index f759df1f37382..d0e4e1c7dbc70 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -1,5 +1,57 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`annotation group save action save routine saving an existing group as new 1`] = ` +Array [ + Array [ + Object { + "layers": Array [ + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "description": "my description", + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "tags": Array [ + "my-tag", + ], + "title": "my title", + }, + "annotationGroupId": "saved-id-123", + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "layerId": "mylayerid", + "layerType": "annotations", + }, + ], + "legend": Object { + "isVisible": true, + "position": "bottom", + }, + "preferredSeriesType": "area", + }, + ], +] +`; + exports[`annotation group save action save routine successful initial save 1`] = ` Array [ Array [ @@ -20,8 +72,6 @@ Array [ "description": "my description", "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", - "layerId": "mylayerid", - "layerType": "annotations", "tags": Array [ "my-tag", ], @@ -53,3 +103,55 @@ Array [ ], ] `; + +exports[`annotation group save action save routine updating an existing group 1`] = ` +Array [ + Array [ + Object { + "layers": Array [ + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "description": "my description", + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "tags": Array [ + "my-tag", + ], + "title": "my title", + }, + "annotationGroupId": "my-group-id", + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "layerId": "mylayerid", + "layerType": "annotations", + }, + ], + "legend": Object { + "isVisible": true, + "position": "bottom", + }, + "preferredSeriesType": "area", + }, + ], +] +`; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index c74d8d4c73f18..cfa36255e2828 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -46,27 +46,27 @@ export const createAnnotationActions = ({ core.application.capabilities.visualize.save && isSaveable ); - if (savingToLibraryPermitted) { - // check if the annotation is saved as a saved object or in inline - same as we check for save modal for visualization - if (isByReferenceAnnotationsLayer(layer)) { - // check if Annotation group hasUnsavedChanges to know if we should allow reverting and saving - similar to how we do it for persistedDoc vs currentDoc on app level - const hasUnsavedChanges = true; - - if (hasUnsavedChanges) { - const saveAction = getSaveLayerAction({ - state, - layer, - setState, - eventAnnotationService, - toasts: core.notifications.toasts, - savedObjectsTagging, - }); - actions.push(saveAction); - } - - const editDetailsAction = getEditDetailsAction({ state, layer, layerIndex, setState, core }); + const hasUnsavedChanges = true; + if ( + !isByReferenceAnnotationsLayer(layer) || + (savingToLibraryPermitted && isByReferenceAnnotationsLayer(layer) && hasUnsavedChanges) + ) { + actions.push( + getSaveLayerAction({ + state, + layer, + setState, + eventAnnotationService, + toasts: core.notifications.toasts, + savedObjectsTagging, + }) + ); + } - const unlinkAction = getUnlinkLayerAction({ + if (isByReferenceAnnotationsLayer(layer)) { + actions.push(getEditDetailsAction({ state, layer, layerIndex, setState, core })); + actions.push( + getUnlinkLayerAction({ execute: () => { const title = 'Annotation group name'; // TODO: pass the title from Annotation group state // save to Lens Saved object state - there's nothing we should do with the Annotation group Saved Object @@ -79,24 +79,11 @@ export const createAnnotationActions = ({ ); }, core, - }); - actions.push(editDetailsAction, unlinkAction); - } else { - actions.push( - getSaveLayerAction({ - isNew: true, - state, - layer, - setState, - eventAnnotationService, - toasts: core.notifications.toasts, - savedObjectsTagging, - }) - ); - } + }) + ); } - const ignoreGlobalFiltersAction = getIgnoreFilterAction({ state, layer, layerIndex, setState }); - actions.push(ignoreGlobalFiltersAction); + actions.push(getIgnoreFilterAction({ state, layer, layerIndex, setState })); + return actions; }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index 14e7472d8fa3c..f418320e35d3f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -8,7 +8,12 @@ import React from 'react'; import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import { XYByValueAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; +import { + XYByValueAnnotationLayerConfig, + XYAnnotationLayerConfig, + XYState, + XYByReferenceAnnotationLayerConfig, +} from '../../types'; import { onSave, SaveModal } from './save_action'; import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; @@ -31,6 +36,10 @@ describe('annotation group save action', () => { domElement={document.createElement('div')} onSave={onSaveMock} savedObjectsTagging={savedObjectsTagging} + title="" + description="" + tags={[]} + showCopyOnSave={false} /> ); @@ -68,6 +77,35 @@ describe('annotation group save action', () => { ] `); }); + + it('shows existing saved object attributes', async () => { + const savedObjectsTagging = taggingApiMock.create(); + + const title = 'my title'; + const description = 'my description'; + const tags = ['my', 'tags']; + + const wrapper = shallowWithIntl( + {}} + savedObjectsTagging={savedObjectsTagging} + title={title} + description={description} + tags={tags} + showCopyOnSave={true} + /> + ); + + const saveModal = wrapper.find(SavedObjectSaveModal); + + expect(saveModal.prop('title')).toBe(title); + expect(saveModal.prop('description')).toBe(description); + expect(saveModal.prop('showCopyOnSave')).toBe(true); + expect((saveModal.prop('options') as React.ReactElement).props.initialSelection).toEqual( + tags + ); + }); }); describe('save routine', () => { @@ -102,6 +140,7 @@ describe('annotation group save action', () => { setState: jest.fn(), eventAnnotationService: { createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), + updateAnnotationGroup: jest.fn(), loadAnnotationGroup: jest.fn(), toExpression: jest.fn(), toFetchExpression: jest.fn(), @@ -128,12 +167,16 @@ describe('annotation group save action', () => { await onSave(props); expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ - ...props.layer, + annotations: props.layer.annotations, + indexPatternId: props.layer.indexPatternId, + ignoreGlobalFilters: props.layer.ignoreGlobalFilters, title: props.modalOnSaveProps.newTitle, description: props.modalOnSaveProps.newDescription, tags: props.modalOnSaveProps.newTags, }); + expect(props.modalOnSaveProps.closeModal).toHaveBeenCalled(); + expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); @@ -147,7 +190,9 @@ describe('annotation group save action', () => { await onSave(props); expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ - ...props.layer, + annotations: props.layer.annotations, + indexPatternId: props.layer.indexPatternId, + ignoreGlobalFilters: props.layer.ignoreGlobalFilters, title: props.modalOnSaveProps.newTitle, description: props.modalOnSaveProps.newDescription, tags: props.modalOnSaveProps.newTags, @@ -155,9 +200,86 @@ describe('annotation group save action', () => { expect(props.toasts.addError).toHaveBeenCalledTimes(1); + expect(props.modalOnSaveProps.closeModal).not.toHaveBeenCalled(); + expect(props.setState).not.toHaveBeenCalled(); expect(props.toasts.addSuccess).not.toHaveBeenCalled(); }); + + test('updating an existing group', async () => { + const annotationGroupId = 'my-group-id'; + + const byReferenceLayer: XYByReferenceAnnotationLayerConfig = { + ...props.layer, + annotationGroupId, + __lastSaved: { + ...props.layer, + title: 'old title', + description: 'old description', + tags: [], + }, + }; + + await onSave({ ...props, layer: byReferenceLayer }); + + expect(props.eventAnnotationService.createAnnotationGroup).not.toHaveBeenCalled(); + + expect(props.eventAnnotationService.updateAnnotationGroup).toHaveBeenCalledWith( + { + annotations: props.layer.annotations, + indexPatternId: props.layer.indexPatternId, + ignoreGlobalFilters: props.layer.ignoreGlobalFilters, + title: props.modalOnSaveProps.newTitle, + description: props.modalOnSaveProps.newDescription, + tags: props.modalOnSaveProps.newTags, + }, + annotationGroupId + ); + + expect(props.modalOnSaveProps.closeModal).toHaveBeenCalled(); + + expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); + + expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); + }); + + test('saving an existing group as new', async () => { + const annotationGroupId = 'my-group-id'; + + const byReferenceLayer: XYByReferenceAnnotationLayerConfig = { + ...props.layer, + annotationGroupId, + __lastSaved: { + ...props.layer, + title: 'old title', + description: 'old description', + tags: [], + }, + }; + + await onSave({ + ...props, + layer: byReferenceLayer, + modalOnSaveProps: { ...props.modalOnSaveProps, newCopyOnSave: true }, + }); + + expect(props.eventAnnotationService.updateAnnotationGroup).not.toHaveBeenCalled(); + + expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ + annotations: props.layer.annotations, + indexPatternId: props.layer.indexPatternId, + ignoreGlobalFilters: props.layer.ignoreGlobalFilters, + title: props.modalOnSaveProps.newTitle, + description: props.modalOnSaveProps.newDescription, + tags: props.modalOnSaveProps.newTags, + }); + + expect(props.modalOnSaveProps.closeModal).toHaveBeenCalled(); + + expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); + + expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 06d498164489a..5bb5f29df7e31 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -16,11 +16,15 @@ import { OnSaveProps as SavedObjectOnSaveProps, SavedObjectSaveModal, } from '@kbn/saved-objects-plugin/public'; -import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-plugin/common'; +import { + EventAnnotationGroupConfig, + EVENT_ANNOTATION_GROUP_TYPE, +} from '@kbn/event-annotation-plugin/common'; import { EuiIcon } from '@elastic/eui'; import { type SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYByReferenceAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; +import { isByReferenceAnnotationsLayer } from '../../visualization_helpers'; type ModalOnSaveProps = SavedObjectOnSaveProps & { newTags: string[]; closeModal: () => void }; @@ -29,14 +33,20 @@ export const SaveModal = ({ domElement, savedObjectsTagging, onSave, + title, + description, + tags, + showCopyOnSave, }: { domElement: HTMLDivElement; savedObjectsTagging: SavedObjectTaggingPluginStart | undefined; onSave: (props: ModalOnSaveProps) => void; + title: string; + description: string; + tags: string[]; + showCopyOnSave: boolean; }) => { - const initialTags: string[] = []; - - const [selectedTags, setSelectedTags] = useState(initialTags); + const [selectedTags, setSelectedTags] = useState(tags); const closeModal = () => unmountComponentAtNode(domElement); @@ -44,9 +54,9 @@ export const SaveModal = ({ onSave({ ...props, closeModal, newTags: selectedTags })} onClose={closeModal} - title={''} - description={''} - showCopyOnSave={false} + title={title} + description={description} + showCopyOnSave={showCopyOnSave} objectType={EVENT_ANNOTATION_GROUP_TYPE} showDescription={true} confirmButtonLabel={ @@ -65,7 +75,7 @@ export const SaveModal = ({ options={ savedObjectsTagging ? ( ) : undefined @@ -74,6 +84,41 @@ export const SaveModal = ({ ); }; +const saveAnnotationGroupToLibrary = async ( + layer: XYAnnotationLayerConfig, + { + newTitle, + newDescription, + newTags, + newCopyOnSave, + }: Pick, + eventAnnotationService: EventAnnotationServiceType +): Promise<{ id: string; config: EventAnnotationGroupConfig }> => { + let savedId: string; + + const saveAsNew = !isByReferenceAnnotationsLayer(layer) || newCopyOnSave; + + const groupConfig: EventAnnotationGroupConfig = { + annotations: layer.annotations, + indexPatternId: layer.indexPatternId, + ignoreGlobalFilters: layer.ignoreGlobalFilters, + title: newTitle, + description: newDescription, + tags: newTags, + }; + + if (saveAsNew) { + const { id } = await eventAnnotationService.createAnnotationGroup(groupConfig); + savedId = id; + } else { + await eventAnnotationService.updateAnnotationGroup(groupConfig, layer.annotationGroupId); + + savedId = layer.annotationGroupId; + } + + return { id: savedId, config: groupConfig }; +}; + /** @internal exported for testing only */ export const onSave = async ({ state, @@ -81,7 +126,7 @@ export const onSave = async ({ setState, eventAnnotationService, toasts, - modalOnSaveProps: { newTitle, newDescription, newTags: selectedTags, closeModal }, + modalOnSaveProps: { newTitle, newDescription, newTags, closeModal, newCopyOnSave }, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -90,17 +135,13 @@ export const onSave = async ({ toasts: ToastsStart; modalOnSaveProps: ModalOnSaveProps; }) => { - let savedId: string; - + let savedInfo: Awaited>; try { - const { id } = await eventAnnotationService.createAnnotationGroup({ - ...layer, - title: newTitle, - description: newDescription, - tags: selectedTags, - }); - - savedId = id; + savedInfo = await saveAnnotationGroupToLibrary( + layer, + { newTitle, newDescription, newTags, newCopyOnSave }, + eventAnnotationService + ); } catch (err) { toasts.addError(err, { title: i18n.translate( @@ -119,13 +160,8 @@ export const onSave = async ({ const newLayer: XYByReferenceAnnotationLayerConfig = { ...layer, - annotationGroupId: savedId, - __lastSaved: { - ...layer, - title: newTitle, - description: newDescription, - tags: selectedTags, - }, + annotationGroupId: savedInfo.id, + __lastSaved: savedInfo.config, }; setState({ @@ -175,11 +211,12 @@ export const getSaveLayerAction = ({ layer: XYAnnotationLayerConfig; setState: StateSetter; eventAnnotationService: EventAnnotationServiceType; - isNew?: boolean; toasts: ToastsStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; }): LayerAction => { - const displayName = false + const neverSaved = !isByReferenceAnnotationsLayer(layer); + + const displayName = neverSaved ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { defaultMessage: 'Add to library', }) @@ -209,6 +246,10 @@ export const getSaveLayerAction = ({ modalOnSaveProps: props, }); }} + title={neverSaved ? '' : layer.__lastSaved.title} + description={neverSaved ? '' : layer.__lastSaved.description} + tags={neverSaved ? [] : layer.__lastSaved.tags} + showCopyOnSave={!neverSaved} />, domElement ); From 45c6ca2345a69ab28f40805a7d4cae07e5e4f069 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 9 Mar 2023 12:41:24 -0600 Subject: [PATCH 016/202] improve object type message --- .../xy/annotations/actions/save_action.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 5bb5f29df7e31..d800ee7889066 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -16,10 +16,7 @@ import { OnSaveProps as SavedObjectOnSaveProps, SavedObjectSaveModal, } from '@kbn/saved-objects-plugin/public'; -import { - EventAnnotationGroupConfig, - EVENT_ANNOTATION_GROUP_TYPE, -} from '@kbn/event-annotation-plugin/common'; +import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; import { EuiIcon } from '@elastic/eui'; import { type SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; @@ -57,7 +54,10 @@ export const SaveModal = ({ title={title} description={description} showCopyOnSave={showCopyOnSave} - objectType={EVENT_ANNOTATION_GROUP_TYPE} + objectType={i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.objectType', + { defaultMessage: 'annotation group' } + )} showDescription={true} confirmButtonLabel={ <> From eb4ba124c08bc9ecd8b58be2414b4d8e087dfd66 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 9 Mar 2023 14:43:42 -0600 Subject: [PATCH 017/202] simplifying annotation layer actions logic --- .../actions/edit_details_action.tsx | 162 ------------------ .../xy/annotations/actions/index.ts | 8 +- .../visualizations/xy/visualization.tsx | 26 +++ 3 files changed, 27 insertions(+), 169 deletions(-) delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx deleted file mode 100644 index 11fb3188899ea..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/edit_details_action.tsx +++ /dev/null @@ -1,162 +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 { CoreStart } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { - EuiButton, - EuiButtonEmpty, - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFlyoutFooter, - EuiForm, - EuiFormRow, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { LayerAction, StateSetter } from '../../../../types'; -import { FlyoutContainer } from '../../../../shared_components/flyout_container'; -import type { XYState, XYAnnotationLayerConfig } from '../../types'; - -export const EditDetailsFlyout = ({ - domElement, - groupLabel, - title, - onConfirm, -}: { - domElement: Element; - groupLabel: string; - title?: string; - onConfirm: (title: string) => void; -}) => { - const [newTitle, setNewTitle] = React.useState(title); - // TODO: debounce title change to set in higher level state to persist when closing the flyout - return ( - - - - unmountComponentAtNode(domElement)} - data-test-subj="lns-indexPattern-loadLibraryCancel" - > - {i18n.translate('xpack.lens.annotations.cancel', { - defaultMessage: 'Cancel', - })} - - - - { - if (newTitle) { - onConfirm(newTitle); - // todo: notification? - unmountComponentAtNode(domElement); - } - }} - iconType="plusInCircleFilled" - fill - color="success" - // disabled={!selectedItem} // TODO: disable if no title - > - {i18n.translate('xpack.lens.annotations.addToLibrary', { - defaultMessage: 'Add to library', - })} - - - - - } - groupLabel={groupLabel} - handleClose={() => { - unmountComponentAtNode(domElement); - return true; - }} - > -
-
- - - } - > - { - setNewTitle(e.target.value); - }} - isInvalid={!'isDuplicate' || newTitle?.length === 0} - /> - - -
-
-
- ); -}; - -export const getEditDetailsAction = ({ - state, - layer, - layerIndex, - setState, - core, - isNew, -}: { - state: XYState; - layer: XYAnnotationLayerConfig; - layerIndex: number; - setState: StateSetter; - core: CoreStart; - isNew?: boolean; -}): LayerAction => { - const displayName = i18n.translate('xpack.lens.xyChart.annotations.editAnnotationGroupDetails', { - defaultMessage: 'Edit annotation group details', - }); - return { - displayName, - description: i18n.translate( - 'xpack.lens.xyChart.annotations.editAnnotationGroupDetailsDescription', - { defaultMessage: 'Edit title, description and tags of the annotation group' } - ), - execute: async (domElement) => { - if (domElement) { - render( - {}} - />, - domElement - ); - } - }, - icon: 'pencil', - isCompatible: true, - 'data-test-subj': 'lnsXY_annotationLayer_editAnnotationDetails', - }; -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index cfa36255e2828..5fb596515a7bb 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -13,7 +13,6 @@ import type { LayerAction, StateSetter } from '../../../../types'; import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; import { getIgnoreFilterAction } from './ignore_filters_action'; -import { getEditDetailsAction } from './edit_details_action'; import { getSaveLayerAction } from './save_action'; import { isByReferenceAnnotationsLayer } from '../../visualization_helpers'; export { @@ -46,11 +45,7 @@ export const createAnnotationActions = ({ core.application.capabilities.visualize.save && isSaveable ); - const hasUnsavedChanges = true; - if ( - !isByReferenceAnnotationsLayer(layer) || - (savingToLibraryPermitted && isByReferenceAnnotationsLayer(layer) && hasUnsavedChanges) - ) { + if (savingToLibraryPermitted) { actions.push( getSaveLayerAction({ state, @@ -64,7 +59,6 @@ export const createAnnotationActions = ({ } if (isByReferenceAnnotationsLayer(layer)) { - actions.push(getEditDetailsAction({ state, layer, layerIndex, setState, core })); actions.push( getUnlinkLayerAction({ execute: () => { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 9c8ae74f2c7cd..fd37d4eb7cc14 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -106,6 +106,32 @@ import { onDropForVisualization } from '../../editor_frame_service/editor_frame/ import { createAnnotationActions } from './annotations/actions'; import { AddLayerButton } from './add_layer'; +// Will be used later for dirty checking +// const hasUnsavedChanges = (layer: XYAnnotationLayerConfig) => { +// if (!isByReferenceAnnotationsLayer(layer)) { +// return false; +// } + +// type PropsToCompare = Pick< +// EventAnnotationGroupConfig, +// 'annotations' | 'ignoreGlobalFilters' | 'indexPatternId' +// >; + +// const currentConfig: PropsToCompare = { +// annotations: layer.annotations, +// ignoreGlobalFilters: layer.ignoreGlobalFilters, +// indexPatternId: layer.indexPatternId, +// }; + +// const savedConfig: PropsToCompare = { +// annotations: layer.__lastSaved.annotations, +// ignoreGlobalFilters: layer.__lastSaved.ignoreGlobalFilters, +// indexPatternId: layer.__lastSaved.indexPatternId, +// }; + +// return !fastIsEqual(currentConfig, savedConfig); +// }; + const XY_ID = 'lnsXY'; export const getXyVisualization = ({ core, From 685645d600345df557bf88ff35c8bd8f2a2e8452 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 9 Mar 2023 15:37:27 -0600 Subject: [PATCH 018/202] introduce linked by-value annotation layers --- .../__snapshots__/visualization.test.ts.snap | 8 + .../public/visualizations/xy/state_helpers.ts | 149 ++++++-- .../lens/public/visualizations/xy/types.ts | 39 +- .../visualizations/xy/visualization.test.ts | 351 +++++++++++++++--- .../visualizations/xy/visualization.tsx | 26 -- .../xy/visualization_helpers.tsx | 25 +- 6 files changed, 462 insertions(+), 136 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap index 2864bbdc0a2e6..c988b1fda4837 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap @@ -16,8 +16,10 @@ Array [ "type": "manual", }, ], + "description": "", "ignoreGlobalFilters": true, "indexPatternId": "data-view-123", + "tags": Array [], "title": "my title!", }, "annotationGroupId": "my-annotation-group-id1", @@ -33,10 +35,12 @@ Array [ "type": "manual", }, ], + "hide": undefined, "ignoreGlobalFilters": true, "indexPatternId": "data-view-123", "layerId": "annotation", "layerType": "annotations", + "simpleView": undefined, }, Object { "__lastSaved": Object { @@ -52,8 +56,10 @@ Array [ "type": "manual", }, ], + "description": "", "ignoreGlobalFilters": true, "indexPatternId": "data-view-773203", + "tags": Array [], "title": "my other title!", }, "annotationGroupId": "my-annotation-group-id2", @@ -69,10 +75,12 @@ Array [ "type": "manual", }, ], + "hide": undefined, "ignoreGlobalFilters": true, "indexPatternId": "data-view-773203", "layerId": "annotation", "layerType": "annotations", + "simpleView": undefined, }, ] `; diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 62aba32697d8b..7d20bc1582456 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -15,6 +15,8 @@ import { v4 as uuidv4 } from 'uuid'; import { isQueryAnnotationConfig } from '@kbn/event-annotation-plugin/public'; import { i18n } from '@kbn/i18n'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import fastIsEqual from 'fast-deep-equal'; +import { cloneDeep } from 'lodash'; import { validateQuery } from '../../shared_components'; import { DataViewsState } from '../../state_management'; import type { FramePublicAPI, DatasourcePublicAPI, VisualizeEditorContext } from '../../types'; @@ -30,6 +32,8 @@ import { XYAnnotationLayerConfig, XYPersistedLayerConfig, XYByReferenceAnnotationLayerConfig, + XYPersistedByReferenceAnnotationLayerConfig, + XYPersistedLinkedByValueAnnotationLayerConfig, } from './types'; import { getDataLayers, @@ -38,7 +42,7 @@ import { isPersistedByReferenceAnnotationsLayer, isByReferenceAnnotationsLayer, isPersistedByValueAnnotationsLayer, - getAnnotationsLayers, + isPersistedAnnotationsLayer, } from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -131,6 +135,31 @@ function getLayerReferenceName(layerId: string) { return `xy-visualization-layer-${layerId}`; } +const annotationLayerHasUnsavedChanges = (layer: XYAnnotationLayerConfig) => { + if (!isByReferenceAnnotationsLayer(layer)) { + return false; + } + + type PropsToCompare = Pick< + EventAnnotationGroupConfig, + 'annotations' | 'ignoreGlobalFilters' | 'indexPatternId' + >; + + const currentConfig: PropsToCompare = { + annotations: layer.annotations, + ignoreGlobalFilters: layer.ignoreGlobalFilters, + indexPatternId: layer.indexPatternId, + }; + + const savedConfig: PropsToCompare = { + annotations: layer.__lastSaved.annotations, + ignoreGlobalFilters: layer.__lastSaved.ignoreGlobalFilters, + indexPatternId: layer.__lastSaved.indexPatternId, + }; + + return !fastIsEqual(currentConfig, savedConfig); +}; + export function getPersistableState(state: XYState) { const savedObjectReferences: SavedObjectReference[] = []; const persistableLayers: XYPersistedLayerConfig[] = []; @@ -145,11 +174,32 @@ export function getPersistableState(state: XYState) { id: layer.annotationGroupId, name: referenceName, }); - persistableLayers.push({ - layerId: layer.layerId, - layerType: layer.layerType, - annotationGroupRef: referenceName, - }); + + if (!annotationLayerHasUnsavedChanges(layer)) { + const persistableLayer: XYPersistedByReferenceAnnotationLayerConfig = { + persistanceType: 'byReference', + layerId: layer.layerId, + layerType: layer.layerType, + annotationGroupRef: referenceName, + hide: layer.hide, + simpleView: layer.simpleView, + }; + + persistableLayers.push(persistableLayer); + } else { + const persistableLayer: XYPersistedLinkedByValueAnnotationLayerConfig = { + persistanceType: 'linked', + layerId: layer.layerId, + layerType: layer.layerType, + annotationGroupRef: referenceName, + hide: layer.hide, + simpleView: layer.simpleView, + annotations: layer.annotations, + ignoreGlobalFilters: layer.ignoreGlobalFilters, + }; + + persistableLayers.push(persistableLayer); + } } else { const { indexPatternId, ...persistableLayer } = layer; savedObjectReferences.push({ @@ -157,7 +207,7 @@ export function getPersistableState(state: XYState) { id: indexPatternId, name: getLayerReferenceName(layer.layerId), }); - persistableLayers.push(persistableLayer); + persistableLayers.push({ ...persistableLayer, persistanceType: 'byValue' }); } } }); @@ -165,10 +215,7 @@ export function getPersistableState(state: XYState) { } export function isPersistedState(state: XYPersistedState | XYState): state is XYPersistedState { - return getAnnotationsLayers(state.layers).some( - (layer) => - isPersistedByValueAnnotationsLayer(layer) || isPersistedByReferenceAnnotationsLayer(layer) - ); + return state.layers.some(isPersistedAnnotationsLayer); } export function injectReferences( @@ -184,15 +231,27 @@ export function injectReferences( const fallbackIndexPatternId = references.find(({ type }) => type === 'index-pattern')!.id; return { ...state, - layers: state.layers.map((layer) => { - if (!isAnnotationsLayer(layer)) { - return layer as XYLayerConfig; + layers: state.layers.map((persistedLayer) => { + if (!isPersistedAnnotationsLayer(persistedLayer)) { + return persistedLayer as XYLayerConfig; } + const indexPatternIdFromReferences = + references.find(({ name }) => name === getLayerReferenceName(persistedLayer.layerId))?.id || + fallbackIndexPatternId; + let injectedLayer: XYAnnotationLayerConfig; - if (isPersistedByReferenceAnnotationsLayer(layer)) { + + if (isPersistedByValueAnnotationsLayer(persistedLayer)) { + injectedLayer = { + ...persistedLayer, + indexPatternId: + // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? + indexPatternIdFromReferences, + }; + } else { const annotationGroupId = references?.find( - ({ name }) => name === layer.annotationGroupRef + ({ name }) => name === persistedLayer.annotationGroupRef )?.id; const annotationGroup = annotationGroupId ? annotationGroups[annotationGroupId] : undefined; @@ -202,25 +261,36 @@ export function injectReferences( } // declared as a separate variable for type checking - const newLayer: XYByReferenceAnnotationLayerConfig = { - layerId: layer.layerId, - layerType: layer.layerType, + const commonProps: Pick< + XYByReferenceAnnotationLayerConfig, + 'layerId' | 'layerType' | 'annotationGroupId' | 'hide' | 'simpleView' | '__lastSaved' + > = { + layerId: persistedLayer.layerId, + layerType: persistedLayer.layerType, annotationGroupId, - indexPatternId: annotationGroup.indexPatternId, - ignoreGlobalFilters: Boolean(annotationGroup.ignoreGlobalFilters), - annotations: annotationGroup.annotations, + hide: persistedLayer.hide, + simpleView: persistedLayer.simpleView, __lastSaved: annotationGroup, }; - injectedLayer = newLayer; - } else { - injectedLayer = { - ...layer, - indexPatternId: - getIndexPatternIdFromInitialContext(layer, initialContext) || - references.find(({ name }) => name === getLayerReferenceName(layer.layerId))?.id || - fallbackIndexPatternId, - }; + if (isPersistedByReferenceAnnotationsLayer(persistedLayer)) { + // a clean by-reference layer inherits from the library annotation group + injectedLayer = { + ...commonProps, + ignoreGlobalFilters: annotationGroup.ignoreGlobalFilters, + indexPatternId: annotationGroup.indexPatternId, + annotations: cloneDeep(annotationGroup.annotations), + }; + } else { + // a linked by-value layer gets settings from visualization state while + // still maintaining the reference to the library annotation group + injectedLayer = { + ...commonProps, + ignoreGlobalFilters: persistedLayer.ignoreGlobalFilters, + indexPatternId: indexPatternIdFromReferences, + annotations: cloneDeep(persistedLayer.annotations), + }; + } } return injectedLayer; @@ -228,14 +298,15 @@ export function injectReferences( }; } -function getIndexPatternIdFromInitialContext( - layer: XYAnnotationLayerConfig, - initialContext?: VisualizeFieldContext | VisualizeEditorContext -) { - if (initialContext && 'isVisualizeAction' in initialContext) { - return layer && 'indexPatternId' in layer ? layer.indexPatternId : undefined; - } -} +// TODO - was this doing anything? +// function getIndexPatternIdFromInitialContext( +// layer: XYPersistedByValueAnnotationLayerConfig, +// initialContext?: VisualizeFieldContext | VisualizeEditorContext +// ) { +// if (initialContext && 'isVisualizeAction' in initialContext) { +// return layer && 'indexPatternId' in layer ? layer.indexPatternId : undefined; +// } +// } export function getAnnotationLayerErrors( layer: XYAnnotationLayerConfig, diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 652f533388ba4..6b0e3cd696fa0 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -119,27 +119,49 @@ export interface XYByValueAnnotationLayerConfig { layerId: string; layerType: 'annotations'; annotations: EventAnnotationConfig[]; - hide?: boolean; + hide: boolean | undefined; indexPatternId: string; - simpleView?: boolean; + simpleView: boolean | undefined; ignoreGlobalFilters: boolean; } export type XYPersistedByValueAnnotationLayerConfig = Omit< XYByValueAnnotationLayerConfig, 'indexPatternId' ->; +> & { persistanceType?: 'byValue' }; // optional for backwards compatibility export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig & { annotationGroupId: string; __lastSaved: EventAnnotationGroupConfig; }; -export interface XYPersistedByReferenceAnnotationLayerConfig { - layerId: string; - layerType: 'annotations'; +export type XYPersistedByReferenceAnnotationLayerConfig = Pick< + XYPersistedByValueAnnotationLayerConfig, + 'layerId' | 'layerType' | 'hide' | 'simpleView' +> & { + persistanceType: 'byReference'; annotationGroupRef: string; -} +}; + +/** + * This is the type of hybrid layer we get after the user has made a change to + * a by-reference annotation layer and saved the visualization without + * first saving the changes to the library annotation layer. + * + * We maintain the link to the library annotation group, but allow the users + * changes (persisted in the visualization state) to override the attributes in + * the library version until the user + * - saves the changes to the library annotation group + * - reverts the changes + * - unlinks the layer from the library annotation group + */ +export type XYPersistedLinkedByValueAnnotationLayerConfig = Omit< + XYPersistedByValueAnnotationLayerConfig, + 'persistanceType' +> & + Omit & { + persistanceType: 'linked'; + }; export type XYAnnotationLayerConfig = | XYByReferenceAnnotationLayerConfig @@ -147,7 +169,8 @@ export type XYAnnotationLayerConfig = export type XYPersistedAnnotationLayerConfig = | XYPersistedByReferenceAnnotationLayerConfig - | XYPersistedByValueAnnotationLayerConfig; + | XYPersistedByValueAnnotationLayerConfig + | XYPersistedLinkedByValueAnnotationLayerConfig; export type XYPersistedLayerConfig = | XYDataLayerConfig diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 75348512c6c10..4486cdb7f8968 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -25,6 +25,9 @@ import type { XYByValueAnnotationLayerConfig, XYByReferenceAnnotationLayerConfig, XYPersistedByReferenceAnnotationLayerConfig, + XYPersistedByValueAnnotationLayerConfig, + XYAnnotationLayerConfig, + XYPersistedLinkedByValueAnnotationLayerConfig, } from './types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { IconChartBar, IconCircle } from '@kbn/chart-icons'; @@ -47,6 +50,7 @@ import { layerTypes, Visualization } from '../..'; import { set } from '@kbn/safer-lodash-set'; import { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; import { getAnnotationsLayers } from './visualization_helpers'; +import { cloneDeep } from 'lodash'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', @@ -246,7 +250,7 @@ describe('xy_visualization', () => { layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - }, + } as XYPersistedByValueAnnotationLayerConfig, ], } as XYPersistedState, undefined, @@ -288,6 +292,8 @@ describe('xy_visualization', () => { layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: undefined, + simpleView: undefined, }, ], }, @@ -351,12 +357,16 @@ describe('xy_visualization', () => { indexPatternId: 'data-view-123', ignoreGlobalFilters: true, title: 'my title!', + description: '', + tags: [], }, [annotationGroupId2]: { annotations: [exampleAnnotation2], indexPatternId: 'data-view-773203', ignoreGlobalFilters: true, title: 'my other title!', + description: '', + tags: [], }, }, { @@ -366,11 +376,13 @@ describe('xy_visualization', () => { { layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + persistanceType: 'byReference', annotationGroupRef: refName1, } as XYPersistedByReferenceAnnotationLayerConfig, { layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, + persistanceType: 'byReference', annotationGroupRef: refName2, } as XYPersistedByReferenceAnnotationLayerConfig, ], @@ -381,6 +393,117 @@ describe('xy_visualization', () => { ) ).toMatchSnapshot(); }); + + it('should hydrate linked by-value annotation groups', () => { + const annotationGroupId1 = 'my-annotation-group-id1'; + const annotationGroupId2 = 'my-annotation-group-id2'; + + const refName1 = 'my-reference'; + const refName2 = 'my-other-reference'; + + const dataViewId = 'some-index-pattern-*'; + + const references: SavedObjectReference[] = [ + { + name: refName1, + id: annotationGroupId1, + type: 'event-annotation-group', + }, + { + name: refName2, + id: annotationGroupId2, + type: 'event-annotation-group', + }, + { + name: 'some-name', + id: dataViewId, + type: 'index-pattern', + }, + ]; + + const persistedAnnotationLayers: XYPersistedLinkedByValueAnnotationLayerConfig[] = [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + persistanceType: 'linked', + annotationGroupRef: refName1, + ignoreGlobalFilters: false, // different from the persisted group + annotations: [], // different from the persisted group + hide: undefined, + simpleView: undefined, + }, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + persistanceType: 'linked', + annotationGroupRef: refName2, + ignoreGlobalFilters: false, // different from the persisted group + annotations: [], // different from the persisted group + hide: undefined, + simpleView: undefined, + }, + ]; + + const libraryAnnotationGroups = { + [annotationGroupId1]: { + annotations: [exampleAnnotation], + indexPatternId: 'data-view-123', + ignoreGlobalFilters: true, + title: 'my title!', + description: '', + tags: [], + }, + [annotationGroupId2]: { + annotations: [exampleAnnotation2], + indexPatternId: 'data-view-773203', + ignoreGlobalFilters: true, + title: 'my other title!', + description: '', + tags: [], + }, + }; + + const expectedAnnotationLayers: XYAnnotationLayerConfig[] = [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotationGroupId: annotationGroupId1, + ignoreGlobalFilters: persistedAnnotationLayers[0].ignoreGlobalFilters, + annotations: persistedAnnotationLayers[0].annotations, + hide: persistedAnnotationLayers[0].hide, + simpleView: persistedAnnotationLayers[0].simpleView, + indexPatternId: dataViewId, + __lastSaved: libraryAnnotationGroups[annotationGroupId1], + }, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotationGroupId: annotationGroupId2, + ignoreGlobalFilters: persistedAnnotationLayers[1].ignoreGlobalFilters, + annotations: persistedAnnotationLayers[1].annotations, + hide: persistedAnnotationLayers[1].hide, + simpleView: persistedAnnotationLayers[1].simpleView, + indexPatternId: dataViewId, + __lastSaved: libraryAnnotationGroups[annotationGroupId2], + }, + ]; + + const baseState = exampleState(); + expect( + getAnnotationsLayers( + xyVisualization.initialize!( + () => 'first', + libraryAnnotationGroups, + { + ...baseState, + layers: [...baseState.layers, ...persistedAnnotationLayers], + } as XYPersistedState, + undefined, + references + ).layers + ) + ).toEqual(expectedAnnotationLayers); + }); }); describe('#removeLayer', () => { @@ -2963,27 +3086,30 @@ describe('xy_visualization', () => { describe('#getUniqueLabels', () => { it('creates unique labels for single annotations layer with repeating labels', async () => { - const xyState = { - layers: [ + const annotationLayer: XYAnnotationLayerConfig = { + layerId: 'layerId', + layerType: 'annotations', + indexPatternId: 'some-id', + ignoreGlobalFilters: true, + simpleView: undefined, + hide: undefined, + annotations: [ { - layerId: 'layerId', - layerType: 'annotations', - annotations: [ - { - label: 'Event', - id: '1', - }, - { - label: 'Event', - id: '2', - }, - { - label: 'Custom', - id: '3', - }, - ], + label: 'Event', + id: '1', }, - ], + { + label: 'Event', + id: '2', + }, + { + label: 'Custom', + id: '3', + }, + ] as XYAnnotationLayerConfig['annotations'], + }; + const xyState = { + layers: [annotationLayer], } as XYState; expect(xyVisualization.getUniqueLabels!(xyState)).toEqual({ @@ -2993,45 +3119,54 @@ describe('xy_visualization', () => { }); }); it('creates unique labels for multiple annotations layers with repeating labels', async () => { + const annotationLayers: XYAnnotationLayerConfig[] = [ + { + layerId: 'layer1', + layerType: 'annotations', + indexPatternId: 'some-id', + ignoreGlobalFilters: true, + simpleView: undefined, + hide: undefined, + annotations: [ + { + label: 'Event', + id: '1', + }, + { + label: 'Event', + id: '2', + }, + { + label: 'Custom', + id: '3', + }, + ] as XYAnnotationLayerConfig['annotations'], + }, + { + layerId: 'layer2', + layerType: 'annotations', + indexPatternId: 'some-id', + ignoreGlobalFilters: true, + simpleView: undefined, + hide: undefined, + annotations: [ + { + label: 'Event', + id: '4', + }, + { + label: 'Event [1]', + id: '5', + }, + { + label: 'Custom', + id: '6', + }, + ] as XYAnnotationLayerConfig['annotations'], + }, + ]; const xyState = { - layers: [ - { - layerId: 'layer1', - layerType: 'annotations', - annotations: [ - { - label: 'Event', - id: '1', - }, - { - label: 'Event', - id: '2', - }, - { - label: 'Custom', - id: '3', - }, - ], - }, - { - layerId: 'layer2', - layerType: 'annotations', - annotations: [ - { - label: 'Event', - id: '4', - }, - { - label: 'Event [1]', - id: '5', - }, - { - label: 'Custom', - id: '6', - }, - ], - }, - ], + layers: annotationLayers, } as XYState; expect(xyVisualization.getUniqueLabels!(xyState)).toEqual({ @@ -3063,6 +3198,8 @@ describe('xy_visualization', () => { }, } as PointInTimeEventAnnotationConfig, ], + hide: undefined, + simpleView: undefined, }; state.layers = [layer]; @@ -3082,9 +3219,12 @@ describe('xy_visualization', () => { "type": "manual", }, ], + "hide": undefined, "ignoreGlobalFilters": false, "layerId": "layer-id", "layerType": "annotations", + "persistanceType": "byValue", + "simpleView": undefined, }, ] `); @@ -3140,7 +3280,12 @@ describe('xy_visualization', () => { ).map((byValueLayer, index) => ({ ...byValueLayer, annotationGroupId: `annotation-group-id-${index}`, - __lastSaved: { ...byValueLayer, title: 'My saved object title' }, + __lastSaved: { + ...byValueLayer, + title: 'My saved object title', + description: 'some description', + tags: [], + }, })); state.layers = layers; @@ -3153,11 +3298,17 @@ describe('xy_visualization', () => { annotationGroupRef: savedObjectReferences[0].name, layerId: 'layer-id', layerType: 'annotations', + persistanceType: 'byReference', + hide: undefined, + simpleView: undefined, }, { annotationGroupRef: savedObjectReferences[1].name, layerId: 'layer-id2', layerType: 'annotations', + persistanceType: 'byReference', + hide: undefined, + simpleView: undefined, }, ]); @@ -3178,6 +3329,88 @@ describe('xy_visualization', () => { expect(savedObjectReferences[0].name).not.toBe(savedObjectReferences[1].name); }); + + it('should persist unsaved changes to by-reference annotation layers', () => { + const state = exampleState(); + const layers: XYByReferenceAnnotationLayerConfig[] = ( + [ + { + layerId: 'layer-id', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }, + { + layerId: 'layer-id2', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + annotations: [ + { + id: 'some-annotation-id2', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }, + ] as XYByValueAnnotationLayerConfig[] + ).map((byValueLayer, index) => ({ + ...byValueLayer, + annotationGroupId: `annotation-group-id-${index}`, + __lastSaved: { + ...byValueLayer, + annotations: cloneDeep(byValueLayer.annotations), + title: 'My saved object title', + description: 'some description', + tags: [], + }, + })); + + // introduce some changes + layers[0].ignoreGlobalFilters = true; + layers[1].annotations[0].color = '#new-color'; + + state.layers = layers; + + const { state: persistableState, savedObjectReferences } = + xyVisualization.getPersistableState!(state); + + expect(persistableState.layers).toEqual([ + { + annotationGroupRef: savedObjectReferences[0].name, + layerId: 'layer-id', + layerType: 'annotations', + persistanceType: 'linked', + annotations: layers[0].annotations, + hide: layers[0].hide, + ignoreGlobalFilters: layers[0].ignoreGlobalFilters, + simpleView: layers[0].simpleView, + }, + { + annotationGroupRef: savedObjectReferences[1].name, + layerId: 'layer-id2', + layerType: 'annotations', + persistanceType: 'linked', + annotations: layers[1].annotations, + hide: layers[1].hide, + ignoreGlobalFilters: layers[1].ignoreGlobalFilters, + simpleView: layers[1].simpleView, + }, + ]); + }); }); describe('getSupportedActionsForLayer', () => { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index fd37d4eb7cc14..9c8ae74f2c7cd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -106,32 +106,6 @@ import { onDropForVisualization } from '../../editor_frame_service/editor_frame/ import { createAnnotationActions } from './annotations/actions'; import { AddLayerButton } from './add_layer'; -// Will be used later for dirty checking -// const hasUnsavedChanges = (layer: XYAnnotationLayerConfig) => { -// if (!isByReferenceAnnotationsLayer(layer)) { -// return false; -// } - -// type PropsToCompare = Pick< -// EventAnnotationGroupConfig, -// 'annotations' | 'ignoreGlobalFilters' | 'indexPatternId' -// >; - -// const currentConfig: PropsToCompare = { -// annotations: layer.annotations, -// ignoreGlobalFilters: layer.ignoreGlobalFilters, -// indexPatternId: layer.indexPatternId, -// }; - -// const savedConfig: PropsToCompare = { -// annotations: layer.__lastSaved.annotations, -// ignoreGlobalFilters: layer.__lastSaved.ignoreGlobalFilters, -// indexPatternId: layer.__lastSaved.indexPatternId, -// }; - -// return !fastIsEqual(currentConfig, savedConfig); -// }; - const XY_ID = 'lnsXY'; export const getXyVisualization = ({ core, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index 991567be5b9c3..e3fed18ae8ae2 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -22,6 +22,9 @@ import { XYByReferenceAnnotationLayerConfig, XYPersistedAnnotationLayerConfig, XYPersistedByReferenceAnnotationLayerConfig, + XYPersistedLinkedByValueAnnotationLayerConfig, + XYPersistedLayerConfig, + XYPersistedByValueAnnotationLayerConfig, } from './types'; import { isHorizontalChart } from './state_helpers'; import { layerTypes } from '../..'; @@ -141,11 +144,19 @@ export const getReferenceLayers = (layers: Array -): layer is XYAnnotationLayerConfig => layer.layerType === layerTypes.ANNOTATIONS; +): layer is XYAnnotationLayerConfig => + layer.layerType === layerTypes.ANNOTATIONS && 'indexPatternId' in layer; + +export const isPersistedAnnotationsLayer = ( + layer: XYPersistedLayerConfig +): layer is XYPersistedAnnotationLayerConfig => + layer.layerType === layerTypes.ANNOTATIONS && !('indexPatternId' in layer); export const isPersistedByValueAnnotationsLayer = ( - layer: Pick -): layer is XYAnnotationLayerConfig => layer.layerType === layerTypes.ANNOTATIONS; + layer: XYPersistedLayerConfig +): layer is XYPersistedByValueAnnotationLayerConfig => + isPersistedAnnotationsLayer(layer) && + (layer.persistanceType === 'byValue' || !layer.persistanceType); export const isByReferenceAnnotationsLayer = ( layer: XYAnnotationLayerConfig @@ -154,7 +165,13 @@ export const isByReferenceAnnotationsLayer = ( export const isPersistedByReferenceAnnotationsLayer = ( layer: XYPersistedAnnotationLayerConfig -): layer is XYPersistedByReferenceAnnotationLayerConfig => 'annotationGroupRef' in layer; +): layer is XYPersistedByReferenceAnnotationLayerConfig => + isPersistedAnnotationsLayer(layer) && layer.persistanceType === 'byReference'; + +export const isPersistedLinkedByValueAnnotationsLayer = ( + layer: XYPersistedAnnotationLayerConfig +): layer is XYPersistedLinkedByValueAnnotationLayerConfig => + isPersistedAnnotationsLayer(layer) && layer.persistanceType === 'linked'; export const getAnnotationsLayers = (layers: Array>) => (layers || []).filter((layer): layer is XYAnnotationLayerConfig => isAnnotationsLayer(layer)); From b10e5a7a676bce0f0300309e8bbf686f63c427c1 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 10 Mar 2023 12:20:03 -0600 Subject: [PATCH 019/202] more accurate typings --- .../public/visualizations/xy/state_helpers.ts | 2 + .../lens/public/visualizations/xy/types.ts | 4 +- .../visualizations/xy/visualization.test.ts | 61 +++++++++++++++---- .../xy/visualization_helpers.tsx | 2 + 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 7d20bc1582456..c43a42a346adb 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -245,6 +245,8 @@ export function injectReferences( if (isPersistedByValueAnnotationsLayer(persistedLayer)) { injectedLayer = { ...persistedLayer, + hide: persistedLayer.hide, + simpleView: persistedLayer.simpleView, indexPatternId: // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? indexPatternIdFromReferences, diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index 6b0e3cd696fa0..ace5f88c87fc1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -127,8 +127,8 @@ export interface XYByValueAnnotationLayerConfig { export type XYPersistedByValueAnnotationLayerConfig = Omit< XYByValueAnnotationLayerConfig, - 'indexPatternId' -> & { persistanceType?: 'byValue' }; // optional for backwards compatibility + 'indexPatternId' | 'hide' | 'simpleView' +> & { persistanceType?: 'byValue'; hide?: boolean; simpleView?: boolean }; // props made optional for backwards compatibility since this is how the existing saved objects are export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig & { annotationGroupId: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 4486cdb7f8968..2371846bae5c7 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -470,8 +470,8 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId1, ignoreGlobalFilters: persistedAnnotationLayers[0].ignoreGlobalFilters, annotations: persistedAnnotationLayers[0].annotations, - hide: persistedAnnotationLayers[0].hide, - simpleView: persistedAnnotationLayers[0].simpleView, + hide: Boolean(persistedAnnotationLayers[0].hide), + simpleView: Boolean(persistedAnnotationLayers[0].simpleView), indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId1], }, @@ -481,8 +481,8 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId2, ignoreGlobalFilters: persistedAnnotationLayers[1].ignoreGlobalFilters, annotations: persistedAnnotationLayers[1].annotations, - hide: persistedAnnotationLayers[1].hide, - simpleView: persistedAnnotationLayers[1].simpleView, + hide: Boolean(persistedAnnotationLayers[1].hide), + simpleView: Boolean(persistedAnnotationLayers[1].simpleView), indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId2], }, @@ -765,6 +765,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1014,6 +1016,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1074,6 +1078,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1134,6 +1140,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1175,6 +1183,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1218,6 +1228,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1225,6 +1237,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1276,6 +1290,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1283,6 +1299,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1334,6 +1352,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1341,6 +1361,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1391,13 +1413,18 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, + { layerId: 'second', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -1515,6 +1542,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, { ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, @@ -2262,6 +2291,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }; @@ -2897,6 +2928,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }; @@ -3091,8 +3124,8 @@ describe('xy_visualization', () => { layerType: 'annotations', indexPatternId: 'some-id', ignoreGlobalFilters: true, - simpleView: undefined, - hide: undefined, + simpleView: false, + hide: false, annotations: [ { label: 'Event', @@ -3125,8 +3158,8 @@ describe('xy_visualization', () => { layerType: 'annotations', indexPatternId: 'some-id', ignoreGlobalFilters: true, - simpleView: undefined, - hide: undefined, + simpleView: false, + hide: false, annotations: [ { label: 'Event', @@ -3147,8 +3180,8 @@ describe('xy_visualization', () => { layerType: 'annotations', indexPatternId: 'some-id', ignoreGlobalFilters: true, - simpleView: undefined, - hide: undefined, + simpleView: false, + hide: false, annotations: [ { label: 'Event', @@ -3198,8 +3231,8 @@ describe('xy_visualization', () => { }, } as PointInTimeEventAnnotationConfig, ], - hide: undefined, - simpleView: undefined, + hide: false, + simpleView: false, }; state.layers = [layer]; @@ -3435,6 +3468,8 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: true, indexPatternId: 'myIndexPattern', + hide: false, + simpleView: false, }, ], }, @@ -3467,6 +3502,8 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: true, indexPatternId: 'myIndexPattern', + hide: false, + simpleView: false, }, ], }, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index e3fed18ae8ae2..6901b6b1cf7d4 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -315,6 +315,8 @@ const newLayerFn = { annotations: [], indexPatternId, ignoreGlobalFilters: true, + hide: undefined, + simpleView: undefined, }), }; From ec2f54427278e6f04ae55d566ea72b8a133e9a4e Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 10 Mar 2023 12:39:33 -0600 Subject: [PATCH 020/202] leave functional testing note --- x-pack/test/functional/apps/lens/group3/annotations.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index 5975a81704da1..baaa557212b01 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -146,6 +146,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'Saved "new annotation group"\nView or manage in the annotation library' ); + // TODO check various saving configurations (linked layer, clean by-ref, revert) + // TODO check annotation library }); }); From 899f6ee9dd067cc42d178fbb4414b1d5175d3ab8 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 10 Mar 2023 17:42:43 -0600 Subject: [PATCH 021/202] refactor components to use SavedObjectsFinder --- src/plugins/event_annotation/kibana.jsonc | 5 ++++- ...t_annotation_group_saved_object_finder.tsx | 14 +++++++++---- .../public/event_annotation_service/index.tsx | 17 +++++++++++++-- .../event_annotation_service/service.tsx | 7 ++++++- src/plugins/event_annotation/public/plugin.ts | 12 +++++------ x-pack/plugins/lens/public/plugin.ts | 21 +++++++------------ .../lens/public/visualizations/xy/index.ts | 4 +--- 7 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index a1e7cf29d3688..c72cc150d869d 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -9,8 +9,11 @@ "browser": true, "requiredPlugins": [ "expressions", + "savedObjectsManagement", "data", - "savedObjects" + ], + "requiredBundles": [ + "savedObjectsFinder" ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx index 2a82ef8f7aaff..9e33505a49374 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx +++ b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx @@ -8,21 +8,24 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { SavedObjectFinderUi } from '@kbn/saved-objects-plugin/public'; import { CoreStart } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; +import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { EVENT_ANNOTATION_GROUP_TYPE } from '../../common'; export const EventAnnotationGroupSavedObjectFinder = ({ uiSettings, http, + savedObjectsManagement, fixedPageSize = 10, onChoose, }: { uiSettings: IUiSettingsClient; http: CoreStart['http']; + savedObjectsManagement: SavedObjectsManagementPluginStart; fixedPageSize: number; onChoose: (value: { id: string; @@ -32,7 +35,7 @@ export const EventAnnotationGroupSavedObjectFinder = ({ }) => void; }) => { return ( - { @@ -46,8 +49,11 @@ export const EventAnnotationGroupSavedObjectFinder = ({ /> } savedObjectMetaData={savedObjectMetaData} - uiSettings={uiSettings} - http={http} + services={{ + uiSettings, + http, + savedObjectsManagement, + }} /> ); }; diff --git a/src/plugins/event_annotation/public/event_annotation_service/index.tsx b/src/plugins/event_annotation/public/event_annotation_service/index.tsx index bed211c5a6f64..f648dcbad7fd9 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/index.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/index.tsx @@ -7,15 +7,28 @@ */ import { CoreStart } from '@kbn/core/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; // import { EventAnnotationGroupConfig } from '../../common'; import { EventAnnotationServiceType } from './types'; export class EventAnnotationService { private eventAnnotationService?: EventAnnotationServiceType; - public async getService(core: CoreStart) { + + private core: CoreStart; + private savedObjectsManagement: SavedObjectsManagementPluginStart; + + constructor(core: CoreStart, savedObjectsManagement: SavedObjectsManagementPluginStart) { + this.core = core; + this.savedObjectsManagement = savedObjectsManagement; + } + + public async getService() { if (!this.eventAnnotationService) { const { getEventAnnotationService } = await import('./service'); - this.eventAnnotationService = getEventAnnotationService(core); + this.eventAnnotationService = getEventAnnotationService( + this.core, + this.savedObjectsManagement + ); } return this.eventAnnotationService; } diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 28f34f0e902b0..1741ce0ae904d 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -12,6 +12,7 @@ import { queryToAst } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import { CoreStart, SavedObjectsClientContract } from '@kbn/core/public'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { EventAnnotationConfig, EventAnnotationGroupAttributes, @@ -32,7 +33,10 @@ export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; } -export function getEventAnnotationService(core: CoreStart): EventAnnotationServiceType { +export function getEventAnnotationService( + core: CoreStart, + savedObjectsManagement: SavedObjectsManagementPluginStart +): EventAnnotationServiceType { const client: SavedObjectsClientContract = core.savedObjects.client; const loadAnnotationGroup = async ( @@ -173,6 +177,7 @@ export function getEventAnnotationService(core: CoreStart): EventAnnotationServi ); diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 4aaed8dab788a..4d390f308a474 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { Plugin, CoreSetup, IUiSettingsClient, CoreStart } from '@kbn/core/public'; +import { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { EventAnnotationService } from './event_annotation_service'; import { @@ -19,8 +20,8 @@ import { import { getFetchEventAnnotations } from './fetch_event_annotations'; export interface EventAnnotationStartDependencies { + savedObjectsManagement: SavedObjectsManagementPluginStart; data: DataPublicPluginStart; - uiSettings: IUiSettingsClient; } interface SetupDependencies { @@ -29,14 +30,12 @@ interface SetupDependencies { /** @public */ export type EventAnnotationPluginStart = EventAnnotationService; -export type EventAnnotationPluginSetup = EventAnnotationService; +export type EventAnnotationPluginSetup = void; /** @public */ export class EventAnnotationPlugin implements Plugin { - private readonly eventAnnotationService = new EventAnnotationService(); - public setup( core: CoreSetup, dependencies: SetupDependencies @@ -48,13 +47,12 @@ export class EventAnnotationPlugin dependencies.expressions.registerFunction( getFetchEventAnnotations({ getStartServices: core.getStartServices }) ); - return this.eventAnnotationService; } public start( core: CoreStart, startDependencies: EventAnnotationStartDependencies ): EventAnnotationService { - return this.eventAnnotationService; + return new EventAnnotationService(core, startDependencies.savedObjectsManagement); } } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 6871c1ec0eb95..3151fd22ddd76 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -33,7 +33,7 @@ import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public' import type { UrlForwardingSetup } from '@kbn/url-forwarding-plugin/public'; import type { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/public'; import type { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public'; -import type { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/public'; +import type { EventAnnotationPluginStart } from '@kbn/event-annotation-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; @@ -121,7 +121,6 @@ export interface LensPluginSetupDependencies { embeddable?: EmbeddableSetup; visualizations: VisualizationsSetup; charts: ChartsPluginSetup; - eventAnnotation: EventAnnotationPluginSetup; globalSearch?: GlobalSearchPluginSetup; usageCollection?: UsageCollectionSetup; uiActionsEnhanced: AdvancedUiActionsSetup; @@ -140,7 +139,7 @@ export interface LensPluginStartDependencies { visualizations: VisualizationsStart; embeddable: EmbeddableStart; charts: ChartsPluginStart; - eventAnnotation: EventAnnotationPluginSetup; + eventAnnotation: EventAnnotationPluginStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; dataViewFieldEditor: IndexPatternFieldEditorStart; @@ -264,7 +263,6 @@ export class LensPlugin { embeddable, visualizations, charts, - eventAnnotation, globalSearch, usageCollection, uiActionsEnhanced, @@ -284,12 +282,11 @@ export class LensPlugin { charts, expressions, fieldFormats, - plugins.fieldFormats.deserialize, - eventAnnotation + plugins.fieldFormats.deserialize ); const visualizationMap = await this.editorFrameService!.loadVisualizations(); const datasourceMap = await this.editorFrameService!.loadDatasources(); - const eventAnnotationService = await eventAnnotation.getService(coreStart); + const eventAnnotationService = await plugins.eventAnnotation.getService(); if (plugins.usageCollection) { setUsageCollectionStart(plugins.usageCollection); @@ -382,8 +379,7 @@ export class LensPlugin { charts, expressions, fieldFormats, - deps.fieldFormats.deserialize, - eventAnnotation + deps.fieldFormats.deserialize ); const { @@ -433,8 +429,7 @@ export class LensPlugin { charts, expressions, fieldFormats, - plugins.fieldFormats.deserialize, - eventAnnotation + plugins.fieldFormats.deserialize ); }; @@ -459,8 +454,7 @@ export class LensPlugin { charts: ChartsPluginSetup, expressions: ExpressionsServiceSetup, fieldFormats: FieldFormatsSetup, - formatFactory: FormatFactory, - eventAnnotation: EventAnnotationPluginSetup + formatFactory: FormatFactory ) { const { DatatableVisualization, @@ -498,7 +492,6 @@ export class LensPlugin { charts, editorFrame: editorFrameSetupInterface, formatFactory, - eventAnnotation, }; this.FormBasedDatasource.setup(core, dependencies); this.TextBasedDatasource.setup(core, dependencies); diff --git a/x-pack/plugins/lens/public/visualizations/xy/index.ts b/x-pack/plugins/lens/public/visualizations/xy/index.ts index ce99700d456d3..ec565b295fe0a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/index.ts @@ -7,7 +7,6 @@ import type { CoreSetup } from '@kbn/core/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/public'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { LEGACY_TIME_AXIS } from '@kbn/charts-plugin/common'; @@ -20,7 +19,6 @@ export interface XyVisualizationPluginSetupPlugins { formatFactory: FormatFactory; editorFrame: EditorFrameSetup; charts: ChartsPluginSetup; - eventAnnotation: EventAnnotationPluginSetup; } export class XyVisualization { @@ -36,7 +34,7 @@ export class XyVisualization { ] = await core.getStartServices(); const [palettes, eventAnnotationService] = await Promise.all([ charts.palettes.getPalettes(), - eventAnnotation.getService(coreStart), + eventAnnotation.getService(), ]); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); return getXyVisualization({ From 8c4abb8d2126491838670d514ed0a69f728d48ee Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 20 Mar 2023 12:30:57 -0500 Subject: [PATCH 022/202] add missing type refs --- src/plugins/event_annotation/tsconfig.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index d5ff92a20ffdc..9738e2722dabe 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -19,7 +19,9 @@ "@kbn/core-ui-settings-browser", "@kbn/datemath", "@kbn/ui-theme", - "@kbn/saved-objects-plugin" + "@kbn/saved-objects-plugin", + "@kbn/saved-objects-finder-plugin", + "@kbn/saved-objects-management-plugin" ], "exclude": [ "target/**/*", From d18c833628a4d261b7c29e457d8e1a6df9081237 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 20 Mar 2023 14:07:11 -0500 Subject: [PATCH 023/202] add existing annotation groups from library --- .../buttons/drop_targets_utils.tsx | 4 +- .../config_panel/config_panel.tsx | 9 +- .../editor_frame/config_panel/layer_panel.tsx | 4 +- .../public/state_management/lens_slice.ts | 7 +- x-pack/plugins/lens/public/types.ts | 14 ++- .../public/visualizations/xy/add_layer.tsx | 20 ++--- .../xy/annotations/actions/save_action.tsx | 12 +-- .../xy/load_annotation_library_flyout.tsx | 39 ++------ .../public/visualizations/xy/state_helpers.ts | 6 +- .../lens/public/visualizations/xy/types.ts | 6 +- .../visualizations/xy/visualization.test.ts | 90 +++++++++++++------ .../visualizations/xy/visualization.tsx | 9 +- .../xy/visualization_helpers.tsx | 52 ++++++++--- 13 files changed, 162 insertions(+), 110 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx index 454fe3b191755..25b80b44b1250 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx @@ -159,9 +159,9 @@ export function shouldRemoveSource(source: DragDropIdentifier, dropType: DropTyp ); } -export function onDropForVisualization( +export function onDropForVisualization( props: OnVisDropProps, - activeVisualization: Visualization + activeVisualization: Visualization ) { const { prevState, target, frame, source, group } = props; const { layerId, columnId, groupId } = target; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 3c394910561dd..dddd21237fd9e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -13,9 +13,8 @@ import { UPDATE_FILTER_REFERENCES_ACTION, UPDATE_FILTER_REFERENCES_TRIGGER, } from '@kbn/unified-search-plugin/public'; -import { LayerType } from '../../../../common'; import { changeIndexPattern, removeDimension } from '../../../state_management/lens_slice'; -import { Visualization } from '../../../types'; +import { AddLayerFunction, Visualization } from '../../../types'; import { LayerPanel } from './layer_panel'; import { generateId } from '../../../id_generator'; import { ConfigPanelWrapperProps } from './types'; @@ -230,9 +229,9 @@ export function LayerPanels( [dispatchLens, props.framePublicAPI.dataViews.indexPatterns, props.indexPatternService] ); - const addLayer = (layerType: LayerType) => { + const addLayer: AddLayerFunction = (layerType, extraArg) => { const layerId = generateId(); - dispatchLens(addLayerAction({ layerId, layerType })); + dispatchLens(addLayerAction({ layerId, layerType, extraArg })); setNextFocusedLayerId(layerId); }; @@ -319,7 +318,7 @@ export function LayerPanels( visualization: activeVisualization, visualizationState: visualization.state, layersMeta: props.framePublicAPI, - onAddLayerClick: addLayer, + addLayer, })} ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 82c58960934b3..92ed8edd992ef 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -20,7 +20,6 @@ import { import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -import { LayerType } from '../../../../common'; import { LayerActions } from './layer_actions'; import { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { NativeRenderer } from '../../../native_renderer'; @@ -33,6 +32,7 @@ import { LayerAction, VisualizationDimensionGroupConfig, UserMessagesGetter, + AddLayerFunction, } from '../../../types'; import { DragDropIdentifier, ReorderProvider } from '../../../drag_drop'; import { LayerSettings } from './layer_settings'; @@ -66,7 +66,7 @@ export function LayerPanel( layerId: string; layerIndex: number; isOnlyLayer: boolean; - addLayer: (layerType: LayerType) => void; + addLayer: AddLayerFunction; updateVisualization: StateSetter; updateDatasource: ( datasourceId: string | undefined, diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index 2f19b3eadfb23..f84dccc152808 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -213,6 +213,7 @@ export const cloneLayer = createAction( export const addLayer = createAction<{ layerId: string; layerType: LayerType; + extraArg: unknown; }>('lens/addLayer'); export const setLayerDefaultDimension = createAction<{ @@ -1033,11 +1034,12 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { [addLayer.type]: ( state, { - payload: { layerId, layerType }, + payload: { layerId, layerType, extraArg }, }: { payload: { layerId: string; layerType: LayerType; + extraArg: unknown; }; } ) => { @@ -1055,7 +1057,8 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { state.visualization.state, layerId, layerType, - currentDataViewsId + currentDataViewsId, + extraArg ); const framePublicAPI = selectFramePublicAPI({ lens: current(state) }, datasourceMap); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 322376336e3c0..cf059bc94106a 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1016,7 +1016,9 @@ interface VisualizationStateFromContextChangeProps { context: VisualizeEditorContext; } -export interface Visualization { +export type AddLayerFunction = (layerType: LayerType, extraArg?: T) => void; + +export interface Visualization { /** Plugin ID, such as "lnsXY" */ id: string; @@ -1080,7 +1082,13 @@ export interface Visualization { /** Optional, if the visualization supports multiple layers */ removeLayer?: (state: T, layerId: string) => T; /** Track added layers in internal state */ - appendLayer?: (state: T, layerId: string, type: LayerType, indexPatternId: string) => T; + appendLayer?: ( + state: T, + layerId: string, + type: LayerType, + indexPatternId: string, + extraArg?: ExtraAppendLayerArg + ) => T; /** Retrieve a list of supported layer types with initialization data */ getSupportedLayers: ( @@ -1235,7 +1243,7 @@ export interface Visualization { getAddLayerButtonComponent?: (props: { visualization: Visualization; visualizationState: T; - onAddLayerClick: (layerType: LayerType) => void; + addLayer: (layerType: LayerType) => void; layersMeta: Pick; }) => JSX.Element | null; /** diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index 0727abcb0b8c1..2b730288c6d90 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -19,14 +19,15 @@ import { import { i18n } from '@kbn/i18n'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import { LayerType, Visualization } from '../..'; -import { FramePublicAPI } from '../../types'; +import { Visualization } from '../..'; +import { AddLayerFunction, FramePublicAPI } from '../../types'; import { LoadAnnotationLibraryFlyout } from './load_annotation_library_flyout'; +import type { ExtraAppendLayerArg } from './visualization'; interface AddLayerButtonProps { visualization: Visualization; visualizationState: unknown; - onAddLayerClick: (layerType: LayerType) => void; + addLayer: AddLayerFunction; layersMeta: Pick; eventAnnotationService: EventAnnotationServiceType; } @@ -35,7 +36,7 @@ interface AddLayerButtonProps { export function AddLayerButton({ visualization, visualizationState, - onAddLayerClick, + addLayer, layersMeta, eventAnnotationService, }: AddLayerButtonProps) { @@ -107,7 +108,7 @@ export function AddLayerButton({ })} fill color="text" - onClick={() => onAddLayerClick(supportedLayers[0].type)} + onClick={() => addLayer(supportedLayers[0].type)} iconType="layers" > {i18n.translate('xpack.lens.configPanel.addLayerButton', { @@ -165,7 +166,7 @@ export function AddLayerButton({ icon: icon && , ['data-test-subj']: `lnsLayerAddButton-${type}`, onClick: () => { - onAddLayerClick(type); + addLayer(type); toggleLayersChoice(false); }, }; @@ -184,7 +185,7 @@ export function AddLayerButton({ }), icon: 'plusInCircleFilled', onClick: () => { - onAddLayerClick(LayerTypes.ANNOTATIONS); + addLayer(LayerTypes.ANNOTATIONS); toggleLayersChoice(false); }, 'data-test-subj': 'lnsAnnotationLayer_new', @@ -210,9 +211,8 @@ export function AddLayerButton({ isLoadLibraryVisible={isLoadLibraryVisible} setLoadLibraryFlyoutVisible={setLoadLibraryFlyoutVisible} eventAnnotationService={eventAnnotationService} - addLayer={() => { - // todo: expand to be able to initiate adding a layer from the library with data (not empty) - onAddLayerClick(LayerTypes.ANNOTATIONS); + addLayer={(loadedGroupInfo) => { + addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); }} /> )} diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index d800ee7889066..43b04ca425d42 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -216,16 +216,10 @@ export const getSaveLayerAction = ({ }): LayerAction => { const neverSaved = !isByReferenceAnnotationsLayer(layer); - const displayName = neverSaved - ? i18n.translate('xpack.lens.xyChart.annotations.addAnnotationGroupToLibrary', { - defaultMessage: 'Add to library', - }) - : i18n.translate('xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary', { - defaultMessage: 'Save to library', - }); - return { - displayName, + displayName: i18n.translate('xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary', { + defaultMessage: 'Save to library', + }), description: i18n.translate( 'xpack.lens.xyChart.annotations.addAnnotationGroupToLibraryDescription', { defaultMessage: 'Saves annotation group as separate saved object' } diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx index a14e487e529d6..5652734cdba59 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -6,17 +6,12 @@ */ import React, { useRef } from 'react'; -import { - EuiButton, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiFlyoutFooter, -} from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFlyoutFooter } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { FlyoutContainer } from '../../shared_components/flyout_container'; +import type { ExtraAppendLayerArg } from './visualization'; export function LoadAnnotationLibraryFlyout({ eventAnnotationService, @@ -27,7 +22,7 @@ export function LoadAnnotationLibraryFlyout({ isLoadLibraryVisible: boolean; setLoadLibraryFlyoutVisible: (visible: boolean) => void; eventAnnotationService: EventAnnotationServiceType; - addLayer: () => void; + addLayer: (argument: ExtraAppendLayerArg) => void; }) { const containerPanelRef = useRef(null); const otherElementsHeight = 250; @@ -87,26 +82,6 @@ export function LoadAnnotationLibraryFlyout({ })}
- - { - setLoadLibraryFlyoutVisible(false); - if (selectedItem) { - loadAnnotationGroup(selectedItem?.id).then((loadedGroup) => { - console.log('loadedGroup:', loadedGroup); - addLayer(); - }); - } - }} - iconType="folderOpen" - fill - disabled={!selectedItem} - > - {i18n.translate('xpack.lens.loadAnnotationsLibrary.loadSelected', { - defaultMessage: 'Load selected', - })} - - } @@ -122,9 +97,11 @@ export function LoadAnnotationLibraryFlyout({ >
{ - hasBeenClicked.current = true; - setSelectedItem({ id, type, fullName, savedObject }); + onChoose={({ id }) => { + loadAnnotationGroup(id).then((loadedGroup) => { + addLayer({ ...loadedGroup, annotationGroupId: id }); + setLoadLibraryFlyoutVisible(false); + }); }} fixedPageSize={numberOfElements} /> diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index c43a42a346adb..5e625c92798d9 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -245,8 +245,8 @@ export function injectReferences( if (isPersistedByValueAnnotationsLayer(persistedLayer)) { injectedLayer = { ...persistedLayer, - hide: persistedLayer.hide, - simpleView: persistedLayer.simpleView, + hide: Boolean(persistedLayer.hide), + simpleView: Boolean(persistedLayer.simpleView), indexPatternId: // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? indexPatternIdFromReferences, @@ -276,7 +276,7 @@ export function injectReferences( }; if (isPersistedByReferenceAnnotationsLayer(persistedLayer)) { - // a clean by-reference layer inherits from the library annotation group + // a clean by-reference layer inherits everything from the library annotation group injectedLayer = { ...commonProps, ignoreGlobalFilters: annotationGroup.ignoreGlobalFilters, diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index ace5f88c87fc1..ecc56dc1b70f3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -119,9 +119,9 @@ export interface XYByValueAnnotationLayerConfig { layerId: string; layerType: 'annotations'; annotations: EventAnnotationConfig[]; - hide: boolean | undefined; + hide: boolean; indexPatternId: string; - simpleView: boolean | undefined; + simpleView: boolean; ignoreGlobalFilters: boolean; } @@ -136,7 +136,7 @@ export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig }; export type XYPersistedByReferenceAnnotationLayerConfig = Pick< - XYPersistedByValueAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, 'layerId' | 'layerType' | 'hide' | 'simpleView' > & { persistanceType: 'byReference'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 2371846bae5c7..e58e247ce0c8c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getXyVisualization } from './visualization'; +import { type ExtraAppendLayerArg, getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation, @@ -14,7 +14,7 @@ import { FramePublicAPI, UserMessage, } from '../../types'; -import type { +import { State, XYState, XYLayerConfig, @@ -262,7 +262,7 @@ describe('xy_visualization', () => { }, ] ) - ).toEqual({ + ).toEqual({ ...baseState, layers: [ ...baseState.layers, @@ -272,6 +272,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }); @@ -306,7 +308,7 @@ describe('xy_visualization', () => { }, ] ) - ).toEqual({ + ).toEqual({ ...baseState, layers: [ ...baseState.layers, @@ -316,6 +318,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }); @@ -429,8 +433,8 @@ describe('xy_visualization', () => { annotationGroupRef: refName1, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - hide: undefined, - simpleView: undefined, + hide: false, + simpleView: false, }, { layerId: 'annotation', @@ -439,8 +443,8 @@ describe('xy_visualization', () => { annotationGroupRef: refName2, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - hide: undefined, - simpleView: undefined, + hide: false, + simpleView: false, }, ]; @@ -470,8 +474,8 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId1, ignoreGlobalFilters: persistedAnnotationLayers[0].ignoreGlobalFilters, annotations: persistedAnnotationLayers[0].annotations, - hide: Boolean(persistedAnnotationLayers[0].hide), - simpleView: Boolean(persistedAnnotationLayers[0].simpleView), + hide: persistedAnnotationLayers[0].hide, + simpleView: persistedAnnotationLayers[0].simpleView, indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId1], }, @@ -481,8 +485,8 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId2, ignoreGlobalFilters: persistedAnnotationLayers[1].ignoreGlobalFilters, annotations: persistedAnnotationLayers[1].annotations, - hide: Boolean(persistedAnnotationLayers[1].hide), - simpleView: Boolean(persistedAnnotationLayers[1].simpleView), + hide: persistedAnnotationLayers[1].hide, + simpleView: persistedAnnotationLayers[1].simpleView, indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId2], }, @@ -533,11 +537,13 @@ describe('xy_visualization', () => { exampleState(), 'foo', layerTypes.DATA, - 'indexPattern1' + 'indexPattern1', + undefined ).layers; expect(layers.length).toEqual(exampleState().layers.length + 1); expect(layers[layers.length - 1]).toMatchObject({ layerId: 'foo' }); }); + // TODO test adding annotation layers }); describe('#clearLayer', () => { @@ -774,7 +780,7 @@ describe('xy_visualization', () => { groupId: 'xAnnotation', columnId: 'newCol', }).layers[0] - ).toEqual({ + ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', @@ -792,6 +798,8 @@ describe('xy_visualization', () => { label: 'Event', }, ], + hide: false, + simpleView: false, }); }); @@ -1041,7 +1049,7 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', }, }).layers[0] - ).toEqual({ + ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', @@ -1063,6 +1071,8 @@ describe('xy_visualization', () => { }, ], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }); }); it('dragging field: should replace an existing dimension when dragged to a dimension', () => { @@ -1103,7 +1113,7 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', }, }).layers[0] - ).toEqual({ + ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', @@ -1125,6 +1135,8 @@ describe('xy_visualization', () => { }, ], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }); }); it('dragging operation: should copy previous column if passed and assign a new id', () => { @@ -1162,12 +1174,14 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', }, }).layers[0] - ).toEqual({ + ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }); }); it('dragging operation: should reorder a dimension to a annotation layer', () => { @@ -1206,12 +1220,14 @@ describe('xy_visualization', () => { }, dropType: 'reorder', }).layers[0] - ).toEqual({ + ).toEqual({ layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }); }); @@ -1260,13 +1276,15 @@ describe('xy_visualization', () => { }, dropType: 'replace_duplicate_compatible', }).layers - ).toEqual([ + ).toEqual([ { layerId: 'first', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1274,6 +1292,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [{ ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ]); }); @@ -1322,13 +1342,15 @@ describe('xy_visualization', () => { }, dropType: 'swap_compatible', }).layers - ).toEqual([ + ).toEqual([ { layerId: 'first', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1336,6 +1358,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ]); }); @@ -1383,13 +1407,15 @@ describe('xy_visualization', () => { }, dropType: 'replace_compatible', }).layers - ).toEqual([ + ).toEqual([ { layerId: 'first', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1397,6 +1423,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ]); }); @@ -1446,13 +1474,15 @@ describe('xy_visualization', () => { }, dropType: 'move_compatible', }).layers - ).toEqual([ + ).toEqual([ { layerId: 'first', layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, { layerId: 'second', @@ -1460,6 +1490,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ]); }); @@ -1550,7 +1582,7 @@ describe('xy_visualization', () => { layerId: 'ann', columnId: 'an2', }).layers - ).toEqual([ + ).toEqual([ { layerId: 'first', layerType: layerTypes.DATA, @@ -1564,6 +1596,8 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ]); }); @@ -2501,7 +2535,7 @@ describe('xy_visualization', () => { }); const getErrorMessages = ( - vis: Visualization, + vis: Visualization, state: XYState, frameMock = { datasourceLayers: {} } as Partial ) => @@ -3252,12 +3286,12 @@ describe('xy_visualization', () => { "type": "manual", }, ], - "hide": undefined, + "hide": false, "ignoreGlobalFilters": false, "layerId": "layer-id", "layerType": "annotations", "persistanceType": "byValue", - "simpleView": undefined, + "simpleView": false, }, ] `); @@ -3513,13 +3547,15 @@ describe('xy_visualization', () => { expect(setState).toHaveBeenCalledWith( expect.objectContaining({ - layers: expect.arrayContaining([ + layers: expect.arrayContaining([ { layerId: 'annotation', layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: false, indexPatternId: 'myIndexPattern', + hide: false, + simpleView: false, }, ]), }) diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 9c8ae74f2c7cd..59f6e51f4ee9d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -22,6 +22,7 @@ import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; +import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -107,6 +108,9 @@ import { createAnnotationActions } from './annotations/actions'; import { AddLayerButton } from './add_layer'; const XY_ID = 'lnsXY'; + +export type ExtraAppendLayerArg = EventAnnotationGroupConfig & { annotationGroupId: string }; + export const getXyVisualization = ({ core, storage, @@ -129,7 +133,7 @@ export const getXyVisualization = ({ kibanaTheme: ThemeServiceStart; unifiedSearch: UnifiedSearchPublicPluginStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; -}): Visualization => ({ +}): Visualization => ({ id: XY_ID, visualizationTypes, getVisualizationTypeId(state) { @@ -171,7 +175,7 @@ export const getXyVisualization = ({ return state; }, - appendLayer(state, layerId, layerType, indexPatternId) { + appendLayer(state, layerId, layerType, indexPatternId, extraArg) { if (layerType === 'metricTrendline') { return state; } @@ -186,6 +190,7 @@ export const getXyVisualization = ({ layerId, layerType, indexPatternId, + extraArg, }), ], }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index 6901b6b1cf7d4..1b360750d8665 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { uniq } from 'lodash'; +import { cloneDeep, uniq } from 'lodash'; import { IconChartBarHorizontal, IconChartBarStacked, IconChartMixedXy } from '@kbn/chart-icons'; import type { LayerType as XYLayerType } from '@kbn/expression-xy-plugin/common'; import { DatasourceLayers, OperationMetadata, VisualizationType } from '../../types'; @@ -25,9 +25,11 @@ import { XYPersistedLinkedByValueAnnotationLayerConfig, XYPersistedLayerConfig, XYPersistedByValueAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, } from './types'; import { isHorizontalChart } from './state_helpers'; import { layerTypes } from '../..'; +import type { ExtraAppendLayerArg } from './visualization'; export function getAxisName( axis: 'x' | 'y' | 'yLeft' | 'yRight', @@ -306,18 +308,44 @@ const newLayerFn = { [layerTypes.ANNOTATIONS]: ({ layerId, indexPatternId, + extraArg, }: { layerId: string; indexPatternId: string; - }): XYAnnotationLayerConfig => ({ - layerId, - layerType: layerTypes.ANNOTATIONS, - annotations: [], - indexPatternId, - ignoreGlobalFilters: true, - hide: undefined, - simpleView: undefined, - }), + extraArg: ExtraAppendLayerArg | undefined; + }): XYAnnotationLayerConfig => { + if (extraArg) { + const { annotationGroupId, ...libraryGroupConfig } = extraArg; + + const newLayer: XYByReferenceAnnotationLayerConfig = { + layerId, + layerType: layerTypes.ANNOTATIONS, + annotationGroupId, + // TODO - persist these? + hide: false, + simpleView: false, + + annotations: cloneDeep(libraryGroupConfig.annotations), + indexPatternId: libraryGroupConfig.indexPatternId, + ignoreGlobalFilters: libraryGroupConfig.ignoreGlobalFilters, + __lastSaved: libraryGroupConfig, + }; + + return newLayer; + } + + const newLayer: XYByValueAnnotationLayerConfig = { + layerId, + layerType: layerTypes.ANNOTATIONS, + annotations: [], + indexPatternId, + ignoreGlobalFilters: true, + hide: undefined, + simpleView: undefined, + }; + + return newLayer; + }, }; export function newLayerState({ @@ -325,13 +353,15 @@ export function newLayerState({ layerType = layerTypes.DATA, seriesType, indexPatternId, + extraArg, }: { layerId: string; layerType?: XYLayerType; seriesType: SeriesType; indexPatternId: string; + extraArg?: ExtraAppendLayerArg; }) { - return newLayerFn[layerType]({ layerId, seriesType, indexPatternId }); + return newLayerFn[layerType]({ layerId, seriesType, indexPatternId, extraArg }); } export function getLayersByType(state: State, byType?: string) { From f149af61bd54458f99065ff931c1b50f530c5745 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 21 Mar 2023 08:56:06 -0500 Subject: [PATCH 024/202] add append annotation layer unit tests --- .../__snapshots__/visualization.test.ts.snap | 55 +++++++++++++++++++ .../visualizations/xy/visualization.test.ts | 37 ++++++++++++- .../xy/visualization_helpers.tsx | 4 +- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap index c988b1fda4837..1496ffe97dc30 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap @@ -1,5 +1,60 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`xy_visualization #appendLayer adding an annotation layer adds a by-reference annotation layer 1`] = ` +Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "icon": "circle", + "id": "an1", + "key": Object { + "timestamp": "2022-03-18T08:25:17.140Z", + "type": "point_in_time", + }, + "label": "Event 1", + "type": "manual", + }, + ], + "description": "Some description", + "ignoreGlobalFilters": false, + "indexPatternId": "indexPattern1", + "tags": Array [], + "title": "Title", + }, + "annotationGroupId": "some-annotation-group-id", + "annotations": Array [ + Object { + "icon": "circle", + "id": "an1", + "key": Object { + "timestamp": "2022-03-18T08:25:17.140Z", + "type": "point_in_time", + }, + "label": "Event 1", + "type": "manual", + }, + ], + "hide": false, + "ignoreGlobalFilters": false, + "indexPatternId": "indexPattern1", + "layerId": "", + "layerType": "annotations", + "simpleView": false, +} +`; + +exports[`xy_visualization #appendLayer adding an annotation layer adds a by-value annotation layer 1`] = ` +Object { + "annotations": Array [], + "hide": false, + "ignoreGlobalFilters": true, + "indexPatternId": "indexPattern1", + "layerId": "", + "layerType": "annotations", + "simpleView": false, +} +`; + exports[`xy_visualization #initialize should hydrate by-reference annotation groups 1`] = ` Array [ Object { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index e58e247ce0c8c..f8d36987a382d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -543,7 +543,42 @@ describe('xy_visualization', () => { expect(layers.length).toEqual(exampleState().layers.length + 1); expect(layers[layers.length - 1]).toMatchObject({ layerId: 'foo' }); }); - // TODO test adding annotation layers + + describe('adding an annotation layer', () => { + it('adds a by-value annotation layer', () => { + const state = xyVisualization.appendLayer!( + exampleState(), + '', + layerTypes.ANNOTATIONS, + 'indexPattern1', + undefined + ); + + expect(state.layers[1]).toMatchSnapshot(); + }); + + it('adds a by-reference annotation layer', () => { + const extraArg: ExtraAppendLayerArg = { + annotationGroupId: 'some-annotation-group-id', + annotations: [exampleAnnotation], + description: 'Some description', + indexPatternId: 'indexPattern1', + tags: [], + title: 'Title', + ignoreGlobalFilters: false, + }; + + const state = xyVisualization.appendLayer!( + exampleState(), + '', + layerTypes.ANNOTATIONS, + 'indexPattern1', + extraArg + ); + + expect(state.layers[1]).toMatchSnapshot(); + }); + }); }); describe('#clearLayer', () => { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index 1b360750d8665..ebe262c5f78f9 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -340,8 +340,8 @@ const newLayerFn = { annotations: [], indexPatternId, ignoreGlobalFilters: true, - hide: undefined, - simpleView: undefined, + hide: false, + simpleView: false, }; return newLayer; From 8f0cb7cc1fdcea8983005ca1d85cc80864e914ab Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 21 Mar 2023 12:30:04 -0500 Subject: [PATCH 025/202] functional test for add from library --- .../apps/lens/group3/annotations.ts | 83 ++++++++++++------- .../test/functional/page_objects/lens_page.ts | 15 +++- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index baaa557212b01..45638179d13ae 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -103,52 +103,73 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('xyVisGroupedAnnotationIcon'); }); - it('should save annotation group to library', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickVisType('lens'); + describe('library annotation groups', () => { + const ANNOTATION_GROUP_TITLE = 'library annotation group'; + const FIRST_VIS_TITLE = 'first visualization'; - await PageObjects.lens.goToTimeRange(); - await PageObjects.lens.dragFieldToWorkspace('@timestamp', 'xyVisChart'); + it('should save annotation group to library', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); - await PageObjects.lens.createLayer('annotations'); + await PageObjects.lens.goToTimeRange(); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', 'xyVisChart'); - await PageObjects.lens.performLayerAction('lnsXY_annotationLayer_saveToLibrary', 1); + await PageObjects.lens.createLayer('annotations'); - await PageObjects.visualize.setSaveModalValues('new annotation group', { - description: 'my description', - }); + await PageObjects.lens.performLayerAction('lnsXY_annotationLayer_saveToLibrary', 1); - await testSubjects.click('savedObjectTagSelector'); - await testSubjects.click(`tagSelectorOption-action__create`); + await PageObjects.visualize.setSaveModalValues(ANNOTATION_GROUP_TITLE, { + description: 'my description', + }); - const { tagModal } = PageObjects.tagManagement; + await testSubjects.click('savedObjectTagSelector'); + await testSubjects.click(`tagSelectorOption-action__create`); - expect(await tagModal.isOpened()).to.be(true); + const { tagModal } = PageObjects.tagManagement; - await tagModal.fillForm( - { - name: 'my-new-tag', - color: '#FFCC33', - description: '', - }, - { - submit: true, - } - ); + expect(await tagModal.isOpened()).to.be(true); - expect(await tagModal.isOpened()).to.be(false); + await tagModal.fillForm( + { + name: 'my-new-tag', + color: '#FFCC33', + description: '', + }, + { + submit: true, + } + ); - await testSubjects.click('confirmSaveSavedObjectButton'); + expect(await tagModal.isOpened()).to.be(false); - const toastContents = await toastsService.getToastContent(1); + await testSubjects.click('confirmSaveSavedObjectButton'); - expect(toastContents).to.be( - 'Saved "new annotation group"\nView or manage in the annotation library' - ); + const toastContents = await toastsService.getToastContent(1); + + expect(toastContents).to.be( + `Saved "${ANNOTATION_GROUP_TITLE}"\nView or manage in the annotation library` + ); + + await PageObjects.lens.save(FIRST_VIS_TITLE); + }); + + it('should add annotation group from library', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + + await PageObjects.lens.goToTimeRange(); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', 'xyVisChart'); + + await PageObjects.lens.createLayer('annotations', ANNOTATION_GROUP_TITLE); + + retry.try(async () => { + expect(await PageObjects.lens.getLayerCount()).to.be(2); + }); + }); // TODO check various saving configurations (linked layer, clean by-ref, revert) - // TODO check annotation library + // TODO check annotation library, including delete flow }); }); } diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 374737a1bc354..c85019ff985a0 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -913,7 +913,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont /** * Adds a new layer to the chart, fails if the chart does not support new layers */ - async createLayer(layerType: 'data' | 'referenceLine' | 'annotations' = 'data') { + async createLayer( + layerType: 'data' | 'referenceLine' | 'annotations' = 'data', + annotationFromLibraryTitle?: string + ) { await testSubjects.click('lnsLayerAddButton'); const layerCount = await this.getLayerCount(); @@ -924,10 +927,18 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont ]); return fasterChecks.filter(Boolean).length > 0; }); + if (await testSubjects.exists(`lnsLayerAddButton-${layerType}`)) { await testSubjects.click(`lnsLayerAddButton-${layerType}`); if (layerType === 'annotations') { - await testSubjects.click('lnsAnnotationLayer_new'); + if (!annotationFromLibraryTitle) { + await testSubjects.click('lnsAnnotationLayer_new'); + } else { + await testSubjects.click('lnsAnnotationLayer_addFromLibrary'); + await testSubjects.click( + `savedObjectTitle${annotationFromLibraryTitle.split(' ').join('-')}` + ); + } } } }, From 9c37ce477e571471a71107251fe606981b04ff45 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 21 Mar 2023 14:16:06 -0500 Subject: [PATCH 026/202] unlink action --- .../xy/annotations/actions/index.ts | 18 +- .../annotations/actions/save_action.test.tsx | 2 + .../annotations/actions/unlink_action.test.ts | 76 +++++++ .../xy/annotations/actions/unlink_action.tsx | 198 ++++-------------- .../apps/lens/group3/annotations.ts | 2 + 5 files changed, 124 insertions(+), 172 deletions(-) create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index 5fb596515a7bb..0759d710fbfbf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; @@ -20,6 +19,7 @@ export { KEEP_GLOBAL_FILTERS_ACTION_ID, } from './ignore_filters_action'; +// TODO add unit test to verify that the correct actions are shown export const createAnnotationActions = ({ state, layer, @@ -61,18 +61,10 @@ export const createAnnotationActions = ({ if (isByReferenceAnnotationsLayer(layer)) { actions.push( getUnlinkLayerAction({ - execute: () => { - const title = 'Annotation group name'; // TODO: pass the title from Annotation group state - // save to Lens Saved object state - there's nothing we should do with the Annotation group Saved Object - // update Lens state - core.notifications.toasts.addSuccess( - i18n.translate('xpack.lens.xyChart.annotations.notificationUnlinked', { - defaultMessage: `Unlinked “{title}“ from library`, - values: { title }, - }) - ); - }, - core, + state, + layer, + setState, + toasts: core.notifications.toasts, }) ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index f418320e35d3f..f59234d81c038 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -116,6 +116,8 @@ describe('annotation group save action', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, + hide: false, + simpleView: false, annotations: [ { id: 'some-annotation-id', diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts new file mode 100644 index 0000000000000..2662069a600c5 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts @@ -0,0 +1,76 @@ +/* + * 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 { + XYByValueAnnotationLayerConfig, + XYByReferenceAnnotationLayerConfig, + XYState, +} from '../../types'; +import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; +import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { cloneDeep } from 'lodash'; +import { getUnlinkLayerAction } from './unlink_action'; + +describe('annotation group unlink actions', () => { + const layerId = 'mylayerid'; + + const byValueLayer: XYByValueAnnotationLayerConfig = { + layerId, + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + hide: false, + simpleView: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }; + + const byRefLayer: XYByReferenceAnnotationLayerConfig = { + ...byValueLayer, + annotationGroupId: 'shouldnt show up', + __lastSaved: { + ...cloneDeep(byValueLayer), + title: 'My library group', + description: '', + tags: [], + }, + }; + + const state: XYState = { + layers: [byRefLayer], + legend: { isVisible: false, position: 'bottom' }, + preferredSeriesType: 'area', + }; + + it('should unlink layer from library annotation group', () => { + const toasts = toastsServiceMock.createStartContract(); + + const setState = jest.fn(); + const action = getUnlinkLayerAction({ + state, + layer: byRefLayer, + setState, + toasts, + }); + + action.execute(undefined); + + expect(setState).toHaveBeenCalledWith({ ...state, layers: [byValueLayer] }); + + expect(toasts.addSuccess).toHaveBeenCalledWith( + `Unlinked "${byRefLayer.__lastSaved.title}" from library` + ); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx index d52840b862e27..186e3f26100a9 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx @@ -5,67 +5,52 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; -import type { CoreStart } from '@kbn/core/public'; -import { - EuiButton, - EuiButtonEmpty, - EuiCheckbox, - EuiCheckboxProps, - EuiFlexGroup, - EuiFlexItem, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, -} from '@elastic/eui'; +import { ToastsStart } from '@kbn/core-notifications-browser'; import { i18n } from '@kbn/i18n'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import type { LayerAction } from '../../../../types'; -import { LOCAL_STORAGE_LENS_KEY } from '../../../../settings_storage'; +import type { LayerAction, StateSetter } from '../../../../types'; +import { + XYByReferenceAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, + XYState, +} from '../../types'; -export const getUnlinkLayerAction = (props: { - execute: () => void; - core: Pick; +export const getUnlinkLayerAction = ({ + state, + layer, + setState, + toasts, +}: { + state: XYState; + layer: XYByReferenceAnnotationLayerConfig; + setState: StateSetter; + toasts: ToastsStart; }): LayerAction => { - const { modalTitle, modalDesc } = getButtonCopy('title'); - return { - execute: async () => { - const storage = new Storage(localStorage); - const lensLocalStorage = storage.get(LOCAL_STORAGE_LENS_KEY) ?? {}; - - const updateLensLocalStorage = (partial: Record) => { - storage.set(LOCAL_STORAGE_LENS_KEY, { - ...lensLocalStorage, - ...partial, - }); + execute: () => { + const unlinkedLayer: XYByValueAnnotationLayerConfig = { + layerId: layer.layerId, + layerType: layer.layerType, + indexPatternId: layer.indexPatternId, + ignoreGlobalFilters: layer.ignoreGlobalFilters, + hide: layer.hide, + simpleView: layer.simpleView, + annotations: layer.annotations, }; - if (!lensLocalStorage.skipUnlinkFromLibraryModal) { - const modal = props.core.overlays.openModal( - toMountPoint( - modal.close()} - updateLensLocalStorage={updateLensLocalStorage} - />, - { theme$: props.core.theme.theme$ } - ), - { - 'data-test-subj': 'lnsLayerUnlinkModal', - } - ); - await modal.onClose; - } else { - props.execute(); - } - }, + setState({ + ...state, + layers: state.layers.map((layerToCheck) => + layerToCheck.layerId === layer.layerId ? unlinkedLayer : layerToCheck + ), + }); + toasts.addSuccess( + i18n.translate('xpack.lens.xyChart.annotations.notificationUnlinked', { + defaultMessage: `Unlinked "{title}" from library`, + values: { title: layer.__lastSaved.title }, + }) + ); + }, description: i18n.translate('xpack.lens.xyChart.annotations.unlinksFromLibrary', { defaultMessage: 'Saves the annotation group as a part of the Lens Saved Object', }), @@ -77,108 +62,3 @@ export const getUnlinkLayerAction = (props: { 'data-test-subj': 'lnsXY_annotationLayer_unlinkFromLibrary', }; }; - -const SKIP_UNLINK_FROM_LIBRARY_KEY = 'skipUnlinkFromLibraryModal'; - -const getButtonCopy = (title: string) => { - const modalTitle = i18n.translate('xpack.lens.modalTitle.unlinkAnnotationGroupTitle', { - defaultMessage: 'Unlink "{title}" from library?', - values: { title }, - }); - const modalDesc = i18n.translate( - 'xpack.lens.layer.unlinkModal.unlinkAnnotationGroupDescription', - { - defaultMessage: `Unlinking from the library will cause this annotation group to be saved locally to this visualization. Any changes made will no longer be shared with the originating annotation library group.`, - } - ); - - return { - modalTitle, - modalDesc, - }; -}; - -export const UnlinkConfirmModal = ({ - modalTitle, - modalDesc, - skipUnlinkFromLibraryModal, - execute, - closeModal, - updateLensLocalStorage, -}: { - modalTitle: string; - modalDesc: string; - skipUnlinkFromLibraryModal: boolean; - execute: () => void; - closeModal: () => void; - updateLensLocalStorage: (partial: Record) => void; -}) => { - const [skipDeleteModalLocal, setSkipDeleteModalLocal] = useState(skipUnlinkFromLibraryModal); - const onChangeShouldShowModal: EuiCheckboxProps['onChange'] = useCallback( - ({ target }) => setSkipDeleteModalLocal(target.checked), - [] - ); - - const onUnlink = useCallback(() => { - updateLensLocalStorage({ - [SKIP_UNLINK_FROM_LIBRARY_KEY]: skipDeleteModalLocal, - }); - closeModal(); - execute(); - }, [closeModal, execute, skipDeleteModalLocal, updateLensLocalStorage]); - - return ( - <> - - {modalTitle} - - -

- {modalDesc} - {i18n.translate('xpack.lens.layer.unlinkModal.cannotUndo', { - defaultMessage: `You can't undo this action.`, - })} -

-
- - - - - - - - - - - {i18n.translate('xpack.lens.layer.cancelDelete', { - defaultMessage: `Cancel`, - })} - - - - - {i18n.translate('xpack.lens.layer.unlinkConfirm', { - defaultMessage: `Unlink`, - })} - - - - - - - - ); -}; diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index 45638179d13ae..4e1a490d8c88c 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -151,6 +151,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); await PageObjects.lens.save(FIRST_VIS_TITLE); + + // TODO test that saved object info gets populated on subsequent save }); it('should add annotation group from library', async () => { From 15d8ec7341df1c4310bfc991e979dddd72b2e49a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 22 Mar 2023 13:56:12 -0500 Subject: [PATCH 027/202] revert action --- .../revert_changes_action.test.tsx.snap | 48 +++++ .../actions/ignore_filters_action.ts | 11 +- .../xy/annotations/actions/index.ts | 14 +- .../actions/revert_changes_action.test.tsx | 92 ++++++++++ .../actions/revert_changes_action.ts | 38 ---- .../actions/revert_changes_action.tsx | 169 ++++++++++++++++++ .../xy/annotations/actions/unlink_action.tsx | 2 +- .../public/visualizations/xy/state_helpers.ts | 2 +- 8 files changed, 322 insertions(+), 54 deletions(-) create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts create mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap new file mode 100644 index 0000000000000..b9c63d726fe05 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`revert changes routine reverts changes 1`] = ` +Object { + "layers": Array [ + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "id": "some-different-annotation-id", + "key": Object { + "timestamp": "timestamp2", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "description": "", + "hide": false, + "ignoreGlobalFilters": true, + "indexPatternId": "other index pattern", + "layerId": "some-id", + "layerType": "annotations", + "simpleView": false, + "tags": Array [], + "title": "My library group", + }, + "annotationGroupId": "shouldnt show up", + "annotations": Array [ + Object { + "id": "some-different-annotation-id", + "key": Object { + "timestamp": "timestamp2", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "hide": false, + "ignoreGlobalFilters": true, + "indexPatternId": "other index pattern", + "layerId": "some-id", + "layerType": "annotations", + "simpleView": false, + }, + ], +} +`; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts index 6cbd7f8cbbdd6..52b7c38db16ef 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts @@ -9,18 +9,13 @@ import { i18n } from '@kbn/i18n'; import type { LayerAction, StateSetter } from '../../../../types'; import type { XYState, XYAnnotationLayerConfig } from '../../types'; -export const IGNORE_GLOBAL_FILTERS_ACTION_ID = 'ignoreGlobalFilters'; -export const KEEP_GLOBAL_FILTERS_ACTION_ID = 'keepGlobalFilters'; - export const getIgnoreFilterAction = ({ state, layer, - layerIndex, setState, }: { state: XYState; layer: XYAnnotationLayerConfig; - layerIndex: number; setState: StateSetter; }): LayerAction => { return { @@ -41,8 +36,10 @@ export const getIgnoreFilterAction = ({ 'All the dimensions configured in this layer respect filters defined at kibana level.', }), execute: () => { - const newLayers = [...state.layers]; - newLayers[layerIndex] = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; + const newLayer = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; + const newLayers = state.layers.map((layerToCheck) => + layerToCheck.layerId === layer.layerId ? newLayer : layerToCheck + ); return setState({ ...state, layers: newLayers }); }, icon: !layer.ignoreGlobalFilters ? 'filterIgnore' : 'filter', diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index 0759d710fbfbf..a9ab5e97fee74 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -14,16 +14,13 @@ import { getUnlinkLayerAction } from './unlink_action'; import { getIgnoreFilterAction } from './ignore_filters_action'; import { getSaveLayerAction } from './save_action'; import { isByReferenceAnnotationsLayer } from '../../visualization_helpers'; -export { - IGNORE_GLOBAL_FILTERS_ACTION_ID, - KEEP_GLOBAL_FILTERS_ACTION_ID, -} from './ignore_filters_action'; +import { annotationLayerHasUnsavedChanges } from '../../state_helpers'; +import { getRevertChangesAction } from './revert_changes_action'; // TODO add unit test to verify that the correct actions are shown export const createAnnotationActions = ({ state, layer, - layerIndex, setState, core, isSaveable, @@ -32,7 +29,6 @@ export const createAnnotationActions = ({ }: { state: XYState; layer: XYAnnotationLayerConfig; - layerIndex: number; setState: StateSetter; core: CoreStart; isSaveable?: boolean; @@ -69,7 +65,11 @@ export const createAnnotationActions = ({ ); } - actions.push(getIgnoreFilterAction({ state, layer, layerIndex, setState })); + if (isByReferenceAnnotationsLayer(layer) && annotationLayerHasUnsavedChanges(layer)) { + actions.push(getRevertChangesAction({ state, layer, setState, core })); + } + + actions.push(getIgnoreFilterAction({ state, layer, setState })); return actions; }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx new file mode 100644 index 0000000000000..5758644a8cb1c --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.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 { OverlayRef } from '@kbn/core-mount-utils-browser'; +import { IToasts } from '@kbn/core-notifications-browser'; +import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { cloneDeep } from 'lodash'; +import { + XYByReferenceAnnotationLayerConfig, + XYByValueAnnotationLayerConfig, + XYState, +} from '../../types'; +import { revert } from './revert_changes_action'; + +describe('revert changes routine', () => { + const byValueLayer: XYByValueAnnotationLayerConfig = { + layerId: 'some-id', + layerType: 'annotations', + indexPatternId: 'some-index-pattern', + ignoreGlobalFilters: false, + hide: false, + simpleView: false, + annotations: [ + { + id: 'some-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp', + }, + } as PointInTimeEventAnnotationConfig, + ], + }; + + const byRefLayer: XYByReferenceAnnotationLayerConfig = { + ...byValueLayer, + annotationGroupId: 'shouldnt show up', + __lastSaved: { + ...cloneDeep(byValueLayer), + + // some differences + annotations: [ + { + id: 'some-different-annotation-id', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: 'timestamp2', + }, + } as PointInTimeEventAnnotationConfig, + ], + ignoreGlobalFilters: true, + indexPatternId: 'other index pattern', + + title: 'My library group', + description: '', + tags: [], + }, + }; + + it('reverts changes', async () => { + const setState = jest.fn(); + const modal = { + close: jest.fn(() => Promise.resolve()), + } as Partial as OverlayRef; + + const toasts = { addSuccess: jest.fn() } as Partial as IToasts; + + revert({ + setState, + layer: byRefLayer, + state: { layers: [byRefLayer] } as XYState, + modal, + toasts, + }); + + expect(setState).toHaveBeenCalled(); + expect(toasts.addSuccess).toHaveBeenCalledWith(`Reverted "${byRefLayer.__lastSaved.title}"`); + expect(modal.close).toHaveBeenCalled(); + + const newState = setState.mock.calls[0][0] as XYState; + + expect( + (newState.layers[0] as XYByReferenceAnnotationLayerConfig).ignoreGlobalFilters + ).toBeTruthy(); + expect(newState).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts deleted file mode 100644 index ce171af26fb20..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.ts +++ /dev/null @@ -1,38 +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'; -import type { LayerAction, StateSetter } from '../../../../types'; -import type { XYState, XYAnnotationLayerConfig } from '../../types'; - -export const getRevertAction = ({ - state, - layer, - layerIndex, - setState, -}: { - state: XYState; - layer: XYAnnotationLayerConfig; - layerIndex: number; - setState: StateSetter; -}): LayerAction => { - return { - displayName: i18n.translate('xpack.lens.xyChart.annotations.revertChanges', { - defaultMessage: 'Revert changes', - }), - description: i18n.translate('xpack.lens.xyChart.annotations.revertChangesDescription', { - defaultMessage: 'Restores annotation group to the last saved state.', - }), - execute: () => { - // TODO: open flyout ? - // console.log('TODO: Revert changes action!'); - }, - icon: 'editorUndo', - isCompatible: true, - 'data-test-subj': 'lnsXY_annotationLayer_revertChanges', - }; -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx new file mode 100644 index 0000000000000..a7e52bbb00748 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx @@ -0,0 +1,169 @@ +/* + * 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 { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import { cloneDeep } from 'lodash'; +import { OverlayRef } from '@kbn/core-mount-utils-browser'; +import { IToasts } from '@kbn/core-notifications-browser'; +import type { LayerAction, StateSetter } from '../../../../types'; +import type { XYState, XYByReferenceAnnotationLayerConfig } from '../../types'; + +export const getRevertChangesAction = ({ + state, + layer, + setState, + core, +}: { + state: XYState; + layer: XYByReferenceAnnotationLayerConfig; + setState: StateSetter; + core: Pick; +}): LayerAction => { + return { + displayName: i18n.translate('xpack.lens.xyChart.annotations.revertChanges', { + defaultMessage: 'Revert changes', + }), + description: i18n.translate('xpack.lens.xyChart.annotations.revertChangesDescription', { + defaultMessage: 'Restores annotation group to the last saved state.', + }), + execute: async () => { + const modal = core.overlays.openModal( + toMountPoint( + modal.close()} + onConfirm={() => { + revert({ setState, layer, state, modal, toasts: core.notifications.toasts }); + }} + />, + { + theme$: core.theme.theme$, + } + ), + { + 'data-test-subj': 'lnsAnnotationLayerRevertModal', + } + ); + await modal.onClose; + }, + icon: 'editorUndo', + isCompatible: true, + 'data-test-subj': 'lnsXY_annotationLayer_revertChanges', + }; +}; + +export const revert = ({ + setState, + layer, + state, + modal, + toasts, +}: { + setState: StateSetter; + layer: XYByReferenceAnnotationLayerConfig; + state: XYState; + modal: OverlayRef; + toasts: IToasts; +}) => { + const newLayer: XYByReferenceAnnotationLayerConfig = { + layerId: layer.layerId, + layerType: layer.layerType, + annotationGroupId: layer.annotationGroupId, + hide: layer.hide, + simpleView: layer.simpleView, + + indexPatternId: layer.__lastSaved.indexPatternId, + ignoreGlobalFilters: layer.__lastSaved.ignoreGlobalFilters, + annotations: cloneDeep(layer.__lastSaved.annotations), + __lastSaved: layer.__lastSaved, + }; + setState({ + ...state, + layers: state.layers.map((layerToCheck) => + layerToCheck.layerId === layer.layerId ? newLayer : layerToCheck + ), + }); + + modal.close(); + + toasts.addSuccess( + i18n.translate('xpack.lens.xyChart.annotations.notificationReverted', { + defaultMessage: `Reverted "{title}"`, + values: { title: layer.__lastSaved.title }, + }) + ); +}; + +const RevertChangesConfirmModal = ({ + modalTitle, + onConfirm, + onCancel, +}: { + modalTitle: string; + onConfirm: () => void; + onCancel: () => void; +}) => { + return ( + <> + + {modalTitle} + + +

+ {i18n.translate('xpack.lens.layer.revertModal.revertAnnotationGroupDescription', { + defaultMessage: `This action will remove all unsaved changes that you've made and restore the most recent saved version of this annotation group to you visualization. Any lost unsaved changes cannot be restored.`, + })} +

+
+ + + + + + + + {i18n.translate('xpack.lens.layer.cancelDelete', { + defaultMessage: `Cancel`, + })} + + + + + {i18n.translate('xpack.lens.layer.unlinkConfirm', { + defaultMessage: 'Revert changes', + })} + + + + + + + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx index 186e3f26100a9..54bc4552edf29 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx @@ -46,7 +46,7 @@ export const getUnlinkLayerAction = ({ toasts.addSuccess( i18n.translate('xpack.lens.xyChart.annotations.notificationUnlinked', { - defaultMessage: `Unlinked "{title}" from library`, + defaultMessage: `Unlinked "{title}"`, values: { title: layer.__lastSaved.title }, }) ); diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 5e625c92798d9..ee4c9c2a56f5e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -135,7 +135,7 @@ function getLayerReferenceName(layerId: string) { return `xy-visualization-layer-${layerId}`; } -const annotationLayerHasUnsavedChanges = (layer: XYAnnotationLayerConfig) => { +export const annotationLayerHasUnsavedChanges = (layer: XYAnnotationLayerConfig) => { if (!isByReferenceAnnotationsLayer(layer)) { return false; } From 130777afa6ee545ab53a052eef1acea77b3dd55c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 22 Mar 2023 14:50:09 -0500 Subject: [PATCH 028/202] fix extra annotation group bug --- x-pack/plugins/lens/public/state_management/lens_slice.ts | 8 ++++++-- x-pack/plugins/lens/public/types.ts | 3 ++- .../lens/public/visualizations/xy/annotations/helpers.tsx | 3 ++- .../lens/public/visualizations/xy/visualization.tsx | 5 ++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index f84dccc152808..2fd8c645a9fac 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -1065,7 +1065,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { const { noDatasource } = activeVisualization - .getSupportedLayers(visualizationState, framePublicAPI) + .getSupportedLayers(visualizationState, framePublicAPI, extraArg) .find(({ type }) => type === layerType) || {}; const layersToLinkTo = @@ -1088,6 +1088,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { activeDatasource, layerId, layerType, + extraArg, }); state.visualization.state = activeVisualizationState; @@ -1132,6 +1133,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { layerType, columnId, groupId, + extraArg: undefined, }); state.visualization.state = activeVisualizationState; @@ -1216,6 +1218,7 @@ function addInitialValueIfAvailable({ layerId, columnId, groupId, + extraArg, }: { framePublicAPI: FramePublicAPI; visualizationState: unknown; @@ -1226,10 +1229,11 @@ function addInitialValueIfAvailable({ layerType: string; columnId?: string; groupId?: string; + extraArg: unknown; }) { const { initialDimensions, noDatasource } = activeVisualization - .getSupportedLayers(visualizationState, framePublicAPI) + .getSupportedLayers(visualizationState, framePublicAPI, extraArg) .find(({ type }) => type === layerType) || {}; if (initialDimensions) { diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index cf059bc94106a..1433cae6283a9 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1093,7 +1093,8 @@ export interface Visualization + frame?: Pick, + extraArg?: ExtraAppendLayerArg // included so the visualization can decide whether initial values make sense ) => Array<{ type: LayerType; label: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index c63fc226907c5..97a26e40a3788 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -94,6 +94,7 @@ export function getStaticDate(dataLayers: XYDataLayerConfig[], frame: FramePubli } export const getAnnotationsSupportedLayer = ( + isAddingLibraryAnnotationGroup: boolean, state?: XYState, frame?: Pick ) => { @@ -102,7 +103,7 @@ export const getAnnotationsSupportedLayer = ( const hasDateHistogram = isDateHistogram(dataLayers, frame); const initialDimensions = - state && hasDateHistogram + state && hasDateHistogram && !isAddingLibraryAnnotationGroup ? [ { groupId: 'xAnnotations', diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 59f6e51f4ee9d..56074e70a6661 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -258,10 +258,10 @@ export const getXyVisualization = ({ return state?.layers.find(({ layerId: id }) => id === layerId)?.layerType; }, - getSupportedLayers(state, frame) { + getSupportedLayers(state, frame, extraArg) { return [ supportedDataLayer, - getAnnotationsSupportedLayer(state, frame), + getAnnotationsSupportedLayer(Boolean(extraArg), state, frame), getReferenceSupportedLayer(state, frame), ]; }, @@ -274,7 +274,6 @@ export const getXyVisualization = ({ actions.push( ...createAnnotationActions({ state, - layerIndex, layer, setState, core, From 2d3fda8384479e38a22b56456297b60216ff17ef Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 22 Mar 2023 16:16:27 -0500 Subject: [PATCH 029/202] fix some type errors --- .../expression_xy/public/plugin.ts | 6 +- .../event_annotation_service/service.test.ts | 162 +++++++++--------- src/plugins/event_annotation/public/mocks.ts | 6 +- .../lens/public/app_plugin/mounter.tsx | 2 +- .../editor_frame/data_panel_wrapper.tsx | 4 + .../editor_frame/state_helpers.ts | 2 +- .../workspace_panel/chart_switch.test.tsx | 2 +- .../lens/public/mocks/services_mock.tsx | 2 + .../lens/public/mocks/visualization_mock.ts | 2 +- .../state_management/lens_slice.test.ts | 2 + x-pack/plugins/lens/public/types.ts | 30 ++-- .../visualizations/xy/to_expression.test.ts | 2 + .../visualizations/xy/visualization.test.ts | 49 +++--- .../visualizations/xy/visualization.tsx | 16 +- .../annotations_config_panel/index.test.tsx | 16 ++ .../xy/xy_config_panel/layer_header.test.tsx | 7 + .../visualizations/xy/xy_suggestions.test.ts | 4 + 17 files changed, 188 insertions(+), 126 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 3a6af23cf0b67..c818377896421 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -12,7 +12,7 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { CoreSetup, CoreStart, IUiSettingsClient } from '@kbn/core/public'; -import { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/public'; +import type { EventAnnotationPluginStart } from '@kbn/event-annotation-plugin/public'; import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; import { @@ -37,7 +37,7 @@ export interface XYPluginStartDependencies { data: DataPublicPluginStart; fieldFormats: FieldFormatsStart; charts: ChartsPluginStart; - eventAnnotation: EventAnnotationPluginSetup; + eventAnnotation: EventAnnotationPluginStart; usageCollection?: UsageCollectionStart; } @@ -83,7 +83,7 @@ export class ExpressionXyPlugin { const paletteService = await palettes.getPalettes(); const { theme: kibanaTheme } = coreStart; - const eventAnnotationService = await eventAnnotation.getService(coreStart); + const eventAnnotationService = await eventAnnotation.getService(); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); return { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index b9ea9b28a07db..2654ed0bd4158 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -8,6 +8,7 @@ import { CoreStart } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { EventAnnotationConfig } from '../../common'; import { getEventAnnotationService } from './service'; import { EventAnnotationServiceType } from './types'; @@ -132,7 +133,10 @@ describe('Event Annotation Service', () => { const typedId = id as 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup'; return annotationResolveMocks[typedId]; }); - eventAnnotationService = getEventAnnotationService(core); + eventAnnotationService = getEventAnnotationService( + core, + {} as SavedObjectsManagementPluginStart + ); }); afterEach(() => { jest.clearAllMocks(); @@ -468,16 +472,16 @@ describe('Event Annotation Service', () => { }); }); }); - describe.skip('deleteAnnotationGroup', () => { - it('deletes annotation group along with annotations that reference them', async () => { - await eventAnnotationService.deleteAnnotationGroup('multiAnnotations'); - expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ - { id: 'multiAnnotations', type: 'event-annotation-group' }, - { id: 'annotation1', type: 'event-annotation' }, - { id: 'annotation2', type: 'event-annotation' }, - ]); - }); - }); + // describe.skip('deleteAnnotationGroup', () => { + // it('deletes annotation group along with annotations that reference them', async () => { + // await eventAnnotationService.deleteAnnotationGroup('multiAnnotations'); + // expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ + // { id: 'multiAnnotations', type: 'event-annotation-group' }, + // { id: 'annotation1', type: 'event-annotation' }, + // { id: 'annotation2', type: 'event-annotation' }, + // ]); + // }); + // }); describe('createAnnotationGroup', () => { it('creates annotation group along with annotations', async () => { const annotations = [ @@ -541,72 +545,72 @@ describe('Event Annotation Service', () => { ); }); }); - describe.skip('updateAnnotations', () => { - const upsert = [ - { - id: 'annotation2', - label: 'Query based event', - icon: 'triangle', - color: 'red', - type: 'query', - timeField: 'timestamp', - key: { - type: 'point_in_time', - }, - lineStyle: 'dashed', - lineWidth: 3, - filter: { type: 'kibana_query', query: '', language: 'kuery' }, - }, - { - id: 'annotation4', - label: 'Query based event', - type: 'query', - timeField: 'timestamp', - key: { - type: 'point_in_time', - }, - filter: { type: 'kibana_query', query: '', language: 'kuery' }, - }, - ] as EventAnnotationConfig[]; - it('updates annotations - deletes annotations', async () => { - await eventAnnotationService.updateAnnotations('multiAnnotations', { - delete: ['annotation1', 'annotation2'], - }); - expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ - { id: 'annotation1', type: 'event-annotation' }, - { id: 'annotation2', type: 'event-annotation' }, - ]); - }); - it('updates annotations - inserts new annotations', async () => { - await eventAnnotationService.updateAnnotations('multiAnnotations', { upsert }); - expect(core.savedObjects.client.bulkCreate).toHaveBeenCalledWith([ - { - id: 'annotation2', - type: 'event-annotation', - attributes: upsert[0], - overwrite: true, - references: [ - { - id: 'multiAnnotations', - name: 'event-annotation-group-ref-annotation2', - type: 'event-annotation-group', - }, - ], - }, - { - id: 'annotation4', - type: 'event-annotation', - attributes: upsert[1], - overwrite: true, - references: [ - { - id: 'multiAnnotations', - name: 'event-annotation-group-ref-annotation4', - type: 'event-annotation-group', - }, - ], - }, - ]); - }); - }); + // describe.skip('updateAnnotations', () => { + // const upsert = [ + // { + // id: 'annotation2', + // label: 'Query based event', + // icon: 'triangle', + // color: 'red', + // type: 'query', + // timeField: 'timestamp', + // key: { + // type: 'point_in_time', + // }, + // lineStyle: 'dashed', + // lineWidth: 3, + // filter: { type: 'kibana_query', query: '', language: 'kuery' }, + // }, + // { + // id: 'annotation4', + // label: 'Query based event', + // type: 'query', + // timeField: 'timestamp', + // key: { + // type: 'point_in_time', + // }, + // filter: { type: 'kibana_query', query: '', language: 'kuery' }, + // }, + // ] as EventAnnotationConfig[]; + // it('updates annotations - deletes annotations', async () => { + // await eventAnnotationService.updateAnnotations('multiAnnotations', { + // delete: ['annotation1', 'annotation2'], + // }); + // expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ + // { id: 'annotation1', type: 'event-annotation' }, + // { id: 'annotation2', type: 'event-annotation' }, + // ]); + // }); + // it('updates annotations - inserts new annotations', async () => { + // await eventAnnotationService.updateAnnotations('multiAnnotations', { upsert }); + // expect(core.savedObjects.client.bulkCreate).toHaveBeenCalledWith([ + // { + // id: 'annotation2', + // type: 'event-annotation', + // attributes: upsert[0], + // overwrite: true, + // references: [ + // { + // id: 'multiAnnotations', + // name: 'event-annotation-group-ref-annotation2', + // type: 'event-annotation-group', + // }, + // ], + // }, + // { + // id: 'annotation4', + // type: 'event-annotation', + // attributes: upsert[1], + // overwrite: true, + // references: [ + // { + // id: 'multiAnnotations', + // name: 'event-annotation-group-ref-annotation4', + // type: 'event-annotation-group', + // }, + // ], + // }, + // ]); + // }); + // }); }); diff --git a/src/plugins/event_annotation/public/mocks.ts b/src/plugins/event_annotation/public/mocks.ts index a2c236d9d593e..100b5d3f1c3e2 100644 --- a/src/plugins/event_annotation/public/mocks.ts +++ b/src/plugins/event_annotation/public/mocks.ts @@ -7,7 +7,11 @@ */ import { coreMock } from '@kbn/core/public/mocks'; +import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { getEventAnnotationService } from './event_annotation_service/service'; // not really mocking but avoiding async loading -export const eventAnnotationServiceMock = getEventAnnotationService(coreMock.createStart()); +export const eventAnnotationServiceMock = getEventAnnotationService( + coreMock.createStart(), + {} as SavedObjectsManagementPluginStart +); diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 921a791e81273..57462799ae6d6 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -107,7 +107,7 @@ export async function getLensServices( const storage = new Storage(localStorage); const stateTransfer = embeddable?.getStateTransfer(); const embeddableEditorIncomingState = stateTransfer?.getIncomingEditorState(APP_ID); - const eventAnnotationService = await eventAnnotation.getService(coreStart); + const eventAnnotationService = await eventAnnotation.getService(); return { data, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx index 520f52ad3ea32..a6e36962fb20a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx @@ -11,6 +11,7 @@ import React, { useMemo, memo, useContext, useEffect, useCallback } from 'react' import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { Easteregg } from './easteregg'; import { NativeRenderer } from '../../native_renderer'; import { DragContext, DragDropIdentifier } from '../../drag_drop'; @@ -46,6 +47,7 @@ interface DataPanelWrapperProps { hasSuggestionForField: (field: DragDropIdentifier) => boolean; plugins: { uiActions: UiActionsStart; dataViews: DataViewsPublicPluginStart }; indexPatternService: IndexPatternServiceAPI; + eventAnnotationService: EventAnnotationServiceType; frame: FramePublicAPI; } @@ -80,6 +82,7 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { initializeSources( { datasourceMap: props.datasourceMap, + eventAnnotationService: props.eventAnnotationService, visualizationMap: props.visualizationMap, visualizationState, datasourceStates, @@ -127,6 +130,7 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { dispatchLens, props.plugins.dataViews, props.core.uiSettings, + props.eventAnnotationService, ]); const onChangeIndexPattern = useCallback( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index c0739297f2a1f..84a53dc10be33 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -282,9 +282,9 @@ export function initializeVisualization({ return ( visualizationMap[visualizationState.activeId]?.initialize( () => '', - annotationGroups, visualizationState.state, undefined, + annotationGroups, references, initialContext ) ?? visualizationState.state 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 db7adabff1c39..5b14bb43dfb16 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 @@ -47,7 +47,7 @@ describe('chart_switch', () => { groupLabel: `${id}Group`, }, ], - initialize: jest.fn((_frame, state?: unknown) => { + initialize: jest.fn((_addNewLayer, state) => { return state || `${id} initial state`; }), getSuggestions: jest.fn((options) => { diff --git a/x-pack/plugins/lens/public/mocks/services_mock.tsx b/x-pack/plugins/lens/public/mocks/services_mock.tsx index 019a37001312c..a25c7eb9e0f6c 100644 --- a/x-pack/plugins/lens/public/mocks/services_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/services_mock.tsx @@ -29,6 +29,7 @@ import type { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { presentationUtilPluginMock } from '@kbn/presentation-util-plugin/public/mocks'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import type { LensAttributeService } from '../lens_attribute_service'; import type { LensByValueInput, @@ -177,5 +178,6 @@ export function makeDefaultServices( dataViewEditor: indexPatternEditorPluginMock.createStartContract(), unifiedSearch: unifiedSearchPluginMock.createStartContract(), docLinks: startMock.docLinks, + eventAnnotationService: {} as EventAnnotationServiceType, }; } diff --git a/x-pack/plugins/lens/public/mocks/visualization_mock.ts b/x-pack/plugins/lens/public/mocks/visualization_mock.ts index a03914dfdd396..8c519c59b0072 100644 --- a/x-pack/plugins/lens/public/mocks/visualization_mock.ts +++ b/x-pack/plugins/lens/public/mocks/visualization_mock.ts @@ -30,7 +30,7 @@ export function createMockVisualization(id = 'testVis'): jest.Mocked x), getSuggestions: jest.fn((_options) => []), getRenderEventCounters: jest.fn((_state) => []), - initialize: jest.fn((_frame, _state?) => ({ newState: 'newState' })), + initialize: jest.fn((_addNewLayer, _state) => ({ newState: 'newState' })), getConfiguration: jest.fn((props) => ({ groups: [ { diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts index df9d42015632c..c69931837b3aa 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts @@ -362,6 +362,7 @@ describe('lensSlice', () => { addLayer({ layerId: 'foo', layerType: LayerTypes.DATA, + extraArg: 'some arg', }) ); const state = customStore.getState().lens; @@ -405,6 +406,7 @@ describe('lensSlice', () => { addLayer({ layerId: 'foo', layerType: layerTypes.DATA, + extraArg: undefined, }) ); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 1433cae6283a9..baff908f5b2fd 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -128,8 +128,10 @@ export interface EditorFrameSetup { registerDatasource: ( datasource: Datasource | (() => Promise>) ) => void; - registerVisualization: ( - visualization: Visualization | (() => Promise>) + registerVisualization: ( + visualization: + | Visualization + | (() => Promise>) ) => void; } @@ -1016,8 +1018,9 @@ interface VisualizationStateFromContextChangeProps { context: VisualizeEditorContext; } -export type AddLayerFunction = (layerType: LayerType, extraArg?: T) => void; +export type AddLayerFunction = (layerType: LayerType, extraArg?: T) => void; +export type AnnotationGroups = Record; export interface Visualization { /** Plugin ID, such as "lnsXY" */ id: string; @@ -1028,14 +1031,17 @@ export interface Visualization string, - annotationGroups: Record, - state?: T | P, - mainPalette?: PaletteOutput, - references?: SavedObjectReference[], - initialContext?: VisualizeFieldContext | VisualizeEditorContext - ) => T; + initialize: { + (addNewLayer: () => string, nonPersistedState?: T, mainPalette?: PaletteOutput): T; + ( + addNewLayer: () => string, + persistedState: P, + mainPalette?: PaletteOutput, + annotationGroups?: AnnotationGroups, + references?: SavedObjectReference[], + initialContext?: VisualizeFieldContext | VisualizeEditorContext + ): T; + }; getUsedDataView?: (state: T, layerId: string) => string | undefined; /** @@ -1244,7 +1250,7 @@ export interface Visualization void; + addLayer: AddLayerFunction; layersMeta: Pick; }) => JSX.Element | null; /** diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index 589c0d5995784..bd83ff8fd10fa 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -564,6 +564,8 @@ describe('#toExpression', () => { annotations: [], indexPatternId: 'my-indexPattern', ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index f8d36987a382d..8d9cbe2c49c87 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -13,6 +13,7 @@ import { DatasourcePublicAPI, FramePublicAPI, UserMessage, + AnnotationGroups, } from '../../types'; import { State, @@ -202,7 +203,7 @@ describe('xy_visualization', () => { describe('#initialize', () => { it('loads default state', () => { - const initialState = xyVisualization.initialize(() => 'l1', {}); + const initialState = xyVisualization.initialize(() => 'l1'); expect(initialState.layers).toHaveLength(1); expect((initialState.layers[0] as XYDataLayerConfig).xAccessor).not.toBeDefined(); @@ -232,7 +233,7 @@ describe('xy_visualization', () => { }); it('loads from persisted state', () => { - expect(xyVisualization.initialize(() => 'first', {}, exampleState())).toEqual(exampleState()); + expect(xyVisualization.initialize(() => 'first', exampleState())).toEqual(exampleState()); }); it('should inject data view references on by-value annotation layers', () => { @@ -240,7 +241,6 @@ describe('xy_visualization', () => { expect( xyVisualization.initialize!( () => 'first', - {}, { ...baseState, layers: [ @@ -254,6 +254,7 @@ describe('xy_visualization', () => { ], } as XYPersistedState, undefined, + {}, [ { type: 'index-pattern', @@ -284,7 +285,6 @@ describe('xy_visualization', () => { expect( xyVisualization.initialize!( () => 'first', - {}, { ...baseState, layers: [ @@ -300,6 +300,7 @@ describe('xy_visualization', () => { ], }, undefined, + {}, [ { type: 'index-pattern', @@ -355,24 +356,6 @@ describe('xy_visualization', () => { getAnnotationsLayers( xyVisualization.initialize!( () => 'first', - { - [annotationGroupId1]: { - annotations: [exampleAnnotation], - indexPatternId: 'data-view-123', - ignoreGlobalFilters: true, - title: 'my title!', - description: '', - tags: [], - }, - [annotationGroupId2]: { - annotations: [exampleAnnotation2], - indexPatternId: 'data-view-773203', - ignoreGlobalFilters: true, - title: 'my other title!', - description: '', - tags: [], - }, - }, { ...baseState, layers: [ @@ -392,6 +375,24 @@ describe('xy_visualization', () => { ], } as XYPersistedState, undefined, + { + [annotationGroupId1]: { + annotations: [exampleAnnotation], + indexPatternId: 'data-view-123', + ignoreGlobalFilters: true, + title: 'my title!', + description: '', + tags: [], + }, + [annotationGroupId2]: { + annotations: [exampleAnnotation2], + indexPatternId: 'data-view-773203', + ignoreGlobalFilters: true, + title: 'my other title!', + description: '', + tags: [], + }, + }, references ).layers ) @@ -448,7 +449,7 @@ describe('xy_visualization', () => { }, ]; - const libraryAnnotationGroups = { + const libraryAnnotationGroups: AnnotationGroups = { [annotationGroupId1]: { annotations: [exampleAnnotation], indexPatternId: 'data-view-123', @@ -497,12 +498,12 @@ describe('xy_visualization', () => { getAnnotationsLayers( xyVisualization.initialize!( () => 'first', - libraryAnnotationGroups, { ...baseState, layers: [...baseState.layers, ...persistedAnnotationLayers], } as XYPersistedState, undefined, + libraryAnnotationGroups, references ).layers ) diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 56074e70a6661..06e4266d02ae5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import type { PaletteRegistry } from '@kbn/coloring'; import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { CoreStart, ThemeServiceStart } from '@kbn/core/public'; +import { CoreStart, SavedObjectReference, ThemeServiceStart } from '@kbn/core/public'; import type { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; @@ -23,6 +23,7 @@ import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/ import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; +import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -43,6 +44,8 @@ import type { FramePublicAPI, Suggestion, UserMessage, + VisualizeEditorContext, + AnnotationGroups, } from '../../types'; import type { FormBasedPersistedState } from '../../datasources/form_based/types'; import { @@ -229,10 +232,17 @@ export const getXyVisualization = ({ triggers: [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush], - initialize(addNewLayer, annotationGroups, state, _, references, initialContext) { + initialize( + addNewLayer, + state, + _mainPalette?, + annotationGroups?: AnnotationGroups, + references?: SavedObjectReference[], + initialContext?: VisualizeFieldContext | VisualizeEditorContext + ) { const finalState = state && isPersistedState(state) - ? injectReferences(state, annotationGroups, references, initialContext) + ? injectReferences(state, annotationGroups!, references, initialContext) : state; return ( finalState || { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx index 5601bac2ccbd0..30428d0c5fe48 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx @@ -65,6 +65,8 @@ describe('AnnotationsPanel', () => { indexPatternId: 'indexPattern1', annotations: [customLineStaticAnnotation], ignoreGlobalFilters: true, + hide: false, + simpleView: false, }, ], }; @@ -143,6 +145,8 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, + hide: false, + simpleView: false, }; const component = mount( { layerType: 'annotations', indexPatternId: indexPattern.id, ignoreGlobalFilters: true, + hide: false, + simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -341,6 +347,8 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, + hide: false, + simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -397,6 +405,8 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, + hide: false, + simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -473,6 +483,8 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, + hide: false, + simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -547,6 +559,8 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: indexPattern.id, ignoreGlobalFilters: true, + hide: false, + simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -618,6 +632,8 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, + hide: false, + simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx index 96820dacc4d9e..ce4397dfaf44c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx @@ -29,10 +29,15 @@ describe('layer header', () => { annotations: [], indexPatternId: '', ignoreGlobalFilters: false, + hide: false, + simpleView: false, __lastSaved: { title: byRefGroupTitle, + description: '', + tags: [], annotations: [], indexPatternId: '', + ignoreGlobalFilters: false, }, }; @@ -42,6 +47,8 @@ describe('layer header', () => { annotations: [], indexPatternId: '', ignoreGlobalFilters: false, + hide: false, + simpleView: false, }; const getStateWithLayers = (layers: XYLayerConfig[]): XYState => ({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 7f9bc9f67ab3f..17f481c8928f0 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -554,6 +554,8 @@ describe('xy_suggestions', () => { layerType: LayerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, + hide: false, + simpleView: false, annotations: [ { id: '1', @@ -611,6 +613,8 @@ describe('xy_suggestions', () => { layerType: LayerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, + hide: false, + simpleView: false, annotations: [ { id: '1', From d02132d322b717f2c5a0b04b57f86e2923ea590f Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 23 Mar 2023 12:21:20 -0500 Subject: [PATCH 030/202] remove annotation layers if library group has been deleted --- .../__snapshots__/service.test.ts.snap | 12 ++ .../event_annotation_service/service.test.ts | 137 +++++++++--------- .../event_annotation_service/service.tsx | 4 + .../formula/editor/formula_editor.tsx | 3 +- .../formula/editor/math_completion.ts | 4 +- .../definitions/formula/formula.tsx | 3 +- .../operations/definitions/formula/parse.ts | 3 +- .../operations/definitions/formula/util.ts | 5 +- .../definitions/formula/validation.ts | 3 +- .../editor_frame/state_helpers.ts | 11 +- x-pack/plugins/lens/public/types.ts | 4 + .../public/visualizations/xy/state_helpers.ts | 134 +++++++++-------- .../visualizations/xy/visualization.test.ts | 78 ++++++++++ 13 files changed, 256 insertions(+), 145 deletions(-) create mode 100644 src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap diff --git a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap new file mode 100644 index 0000000000000..b8ddd299db6cc --- /dev/null +++ b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Event Annotation Service loadAnnotationGroup should properly load an annotation group with a multiple annotation 1`] = ` +Object { + "annotations": undefined, + "description": undefined, + "ignoreGlobalFilters": undefined, + "indexPatternId": "ipid", + "tags": undefined, + "title": "groupTitle", +} +`; diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 2654ed0bd4158..e3b651c44dceb 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -6,55 +6,57 @@ * Side Public License, v 1. */ -import { CoreStart } from '@kbn/core/public'; +import { CoreStart, SimpleSavedObject } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; -import { EventAnnotationConfig } from '../../common'; +import { EventAnnotationConfig, EventAnnotationGroupAttributes } from '../../common'; import { getEventAnnotationService } from './service'; import { EventAnnotationServiceType } from './types'; -const annotationGroupResolveMocks = { +type AnnotationGroupSavedObject = SimpleSavedObject; + +const annotationGroupResolveMocks: Record = { nonExistingGroup: { - saved_object: { - attributes: {}, - references: [], - id: 'nonExistingGroup', + attributes: {} as EventAnnotationGroupAttributes, + references: [], + id: 'nonExistingGroup', + error: { + error: 'Saved object not found', + statusCode: 404, + message: 'Not found', }, - outcome: 'exactMatch', - }, + } as Partial as AnnotationGroupSavedObject, noAnnotations: { - saved_object: { - attributes: { - title: 'groupTitle', - }, - type: 'event-annotation-group', - references: [ - { - id: 'ipid', - name: 'ipid', - type: 'index-pattern', - }, - ], + attributes: { + title: 'groupTitle', + description: '', + tags: [], + ignoreGlobalFilters: false, + annotations: [], }, - outcome: 'exactMatch', - }, - multiAnnotations: { - saved_object: { - attributes: { - title: 'groupTitle', + type: 'event-annotation-group', + references: [ + { + id: 'ipid', + name: 'ipid', + type: 'index-pattern', }, - id: 'multiAnnotations', - type: 'event-annotation-group', - references: [ - { - id: 'ipid', - name: 'ipid', - type: 'index-pattern', - }, - ], + ], + } as Partial as AnnotationGroupSavedObject, + multiAnnotations: { + attributes: { + title: 'groupTitle', }, - outcome: 'exactMatch', - }, + id: 'multiAnnotations', + type: 'event-annotation-group', + references: [ + { + id: 'ipid', + name: 'ipid', + type: 'index-pattern', + }, + ], + } as Partial as AnnotationGroupSavedObject, }; const annotationResolveMocks = { @@ -118,21 +120,16 @@ describe('Event Annotation Service', () => { let eventAnnotationService: EventAnnotationServiceType; beforeEach(() => { core = coreMock.createStart(); - (core.savedObjects.client.resolve as jest.Mock).mockImplementation( - (_type, id: 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup') => { - return annotationGroupResolveMocks[id]; - } - ); (core.savedObjects.client.create as jest.Mock).mockImplementation(() => { - return annotationGroupResolveMocks.multiAnnotations.saved_object; + return annotationGroupResolveMocks.multiAnnotations; + }); + (core.savedObjects.client.get as jest.Mock).mockImplementation((_type, id) => { + const typedId = id as 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup'; + return annotationGroupResolveMocks[typedId]; }); (core.savedObjects.client.bulkCreate as jest.Mock).mockImplementation(() => { return annotationResolveMocks.multiAnnotations; }); - (core.savedObjects.client.find as jest.Mock).mockImplementation(({ hasReference: { id } }) => { - const typedId = id as 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup'; - return annotationResolveMocks[typedId]; - }); eventAnnotationService = getEventAnnotationService( core, {} as SavedObjectsManagementPluginStart @@ -446,30 +443,34 @@ describe('Event Annotation Service', () => { } ); }); - describe.skip('loadAnnotationGroup', () => { - it('should not error when loading group doesnt exit', async () => { - expect(await eventAnnotationService.loadAnnotationGroup('nonExistingGroup')).toEqual({ - annotations: [], - indexPatternId: undefined, - title: undefined, - }); + describe('loadAnnotationGroup', () => { + it('should throw error when loading group doesnt exist', async () => { + expect(() => eventAnnotationService.loadAnnotationGroup('nonExistingGroup')).rejects + .toMatchInlineSnapshot(` + Object { + "error": "Saved object not found", + "message": "Not found", + "statusCode": 404, + } + `); }); it('should properly load an annotation group with no annotation', async () => { - expect(await eventAnnotationService.loadAnnotationGroup('noAnnotations')).toEqual({ - annotations: [], - indexPatternId: 'ipid', - title: 'groupTitle', - }); + expect(await eventAnnotationService.loadAnnotationGroup('noAnnotations')) + .toMatchInlineSnapshot(` + Object { + "annotations": Array [], + "description": "", + "ignoreGlobalFilters": false, + "indexPatternId": "ipid", + "tags": Array [], + "title": "groupTitle", + } + `); }); it('should properly load an annotation group with a multiple annotation', async () => { - expect(await eventAnnotationService.loadAnnotationGroup('multiAnnotations')).toEqual({ - annotations: [ - annotationResolveMocks.multiAnnotations.savedObjects[0].attributes, - annotationResolveMocks.multiAnnotations.savedObjects[1].attributes, - ], - indexPatternId: 'ipid', - title: 'groupTitle', - }); + expect( + await eventAnnotationService.loadAnnotationGroup('multiAnnotations') + ).toMatchSnapshot(); }); }); // describe.skip('deleteAnnotationGroup', () => { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 1741ce0ae904d..53eb24133e203 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -47,6 +47,10 @@ export function getEventAnnotationService( savedObjectId ); + if (savedObject.error) { + throw savedObject.error; + } + // const annotations = ( // await client.find({ // type: EVENT_ANNOTATION_TYPE, diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx index dd41a62fd2431..2539fe7b71e73 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/formula_editor.tsx @@ -53,9 +53,10 @@ import { LANGUAGE_ID } from './math_tokenization'; import './formula.scss'; import { FormulaIndexPatternColumn } from '../formula'; import { insertOrReplaceFormulaColumn } from '../parse'; -import { filterByVisibleOperation, nonNullable } from '../util'; +import { filterByVisibleOperation } from '../util'; import { getColumnTimeShiftWarnings, getDateHistogramInterval } from '../../../../time_shift_utils'; import { getDocumentationSections } from './formula_help'; +import { nonNullable } from '../../../../../../types'; function tableHasData( activeData: ParamEditorProps['activeData'], diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts index 11d6797a1c997..889c6ce3274fe 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts @@ -24,9 +24,9 @@ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { parseTimeShift } from '@kbn/data-plugin/common'; import moment from 'moment'; import { DateRange } from '../../../../../../../common/types'; -import type { IndexPattern } from '../../../../../../types'; +import { IndexPattern, nonNullable } from '../../../../../../types'; import { memoizedGetAvailableOperationsByMetadata } from '../../../operations'; -import { tinymathFunctions, groupArgsByType, unquotedStringRegex, nonNullable } from '../util'; +import { tinymathFunctions, groupArgsByType, unquotedStringRegex } from '../util'; import type { GenericOperationDefinition } from '../..'; import { getFunctionSignatureLabel, getHelpTextContent } from './formula_help'; import { hasFunctionFieldArgument } from '../validation'; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx index 20432dcdb6e24..43dd3ee12e0e7 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/formula.tsx @@ -14,11 +14,12 @@ import type { } from '..'; import type { ReferenceBasedIndexPatternColumn } from '../column_types'; import type { IndexPattern } from '../../../../../types'; +import { nonNullable } from '../../../../../types'; import { runASTValidation, tryToParse } from './validation'; import { WrappedFormulaEditor } from './editor'; import { insertOrReplaceFormulaColumn } from './parse'; import { generateFormula } from './generate'; -import { filterByVisibleOperation, nonNullable } from './util'; +import { filterByVisibleOperation } from './util'; import { getManagedColumnsFrom } from '../../layer_helpers'; import { generateMissingFieldMessage, getFilter, isColumnFormatted } from '../helpers'; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts index 57a529df033c4..5c228bef4f781 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { isObject } from 'lodash'; import type { TinymathAST, TinymathVariable, TinymathLocation } from '@kbn/tinymath'; import type { DateRange } from '../../../../../../common/types'; -import type { IndexPattern } from '../../../../../types'; +import { IndexPattern, nonNullable } from '../../../../../types'; import { OperationDefinition, GenericOperationDefinition, @@ -26,7 +26,6 @@ import { getOperationParams, groupArgsByType, mergeWithGlobalFilters, - nonNullable, } from './util'; import { FormulaIndexPatternColumn, isFormulaIndexPatternColumn } from './formula'; import { getColumnOrder } from '../../layer_helpers'; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts index 1d3301bd050e6..3db02b3b1ef35 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/util.ts @@ -14,6 +14,7 @@ import type { TinymathVariable, } from '@kbn/tinymath'; import type { Query } from '@kbn/es-query'; +import { nonNullable } from '../../../../../types'; import type { OperationDefinition, GenericIndexPatternColumn, @@ -736,10 +737,6 @@ Example: Average revenue per customer but in some cases customer id is not provi }, }; -export function nonNullable(v: T): v is NonNullable { - return v != null; -} - export function isMathNode(node: TinymathAST | string) { return isObject(node) && node.type === 'function' && tinymathFunctions[node.name]; } diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts index 520d0f53b143f..147a577294463 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts @@ -27,7 +27,6 @@ import { getValueOrName, groupArgsByType, isMathNode, - nonNullable, tinymathFunctions, } from './util'; @@ -37,7 +36,7 @@ import type { GenericOperationDefinition, } from '..'; import type { FormBasedLayer } from '../../../types'; -import type { IndexPattern } from '../../../../../types'; +import { IndexPattern, nonNullable } from '../../../../../types'; import type { TinymathNodeTypes } from './types'; interface ValidationErrors { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 84a53dc10be33..d914e3e9591ee 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -9,7 +9,7 @@ import { IUiSettingsClient, SavedObjectReference } from '@kbn/core/public'; import { Ast } from '@kbn/interpreter'; import memoizeOne from 'memoize-one'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; -import { difference } from 'lodash'; +import { difference, noop } from 'lodash'; import type { DataViewsContract, DataViewSpec } from '@kbn/data-views-plugin/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; @@ -180,14 +180,13 @@ const initializeEventAnnotationGroups = async ( const annotationGroups: Record = {}; await Promise.all( - // TODO error handling - references - ?.filter((ref) => ref.type === EVENT_ANNOTATION_GROUP_TYPE) + (references || []) + .filter((ref) => ref.type === EVENT_ANNOTATION_GROUP_TYPE) .map(({ id }) => eventAnnotationService .loadAnnotationGroup(id) - .then((group) => (annotationGroups[id] = group)) - ) || [] + .then((group) => (annotationGroups[id] = group), noop) + ) ); return annotationGroups; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index baff908f5b2fd..74ab13c041d58 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1371,6 +1371,10 @@ export function isLensTableRowContextMenuClickEvent( return event.name === 'tableRowContextMenuClick'; } +export function nonNullable(v: T): v is NonNullable { + return v != null; +} + /** * Expression renderer handlers specifically for lens renderers. This is a narrowed down * version of the general render handlers, specifying supported event types. If this type is diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index ee4c9c2a56f5e..f2a2296eddc96 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -19,7 +19,13 @@ import fastIsEqual from 'fast-deep-equal'; import { cloneDeep } from 'lodash'; import { validateQuery } from '../../shared_components'; import { DataViewsState } from '../../state_management'; -import type { FramePublicAPI, DatasourcePublicAPI, VisualizeEditorContext } from '../../types'; +import { + FramePublicAPI, + DatasourcePublicAPI, + VisualizeEditorContext, + nonNullable, + AnnotationGroups, +} from '../../types'; import { visualizationTypes, XYLayerConfig, @@ -220,7 +226,7 @@ export function isPersistedState(state: XYPersistedState | XYState): state is XY export function injectReferences( state: XYPersistedState, - annotationGroups: Record, + annotationGroups?: AnnotationGroups, references?: SavedObjectReference[], initialContext?: VisualizeFieldContext | VisualizeEditorContext ): XYState { @@ -228,75 +234,85 @@ export function injectReferences( return state as XYState; } + if (!annotationGroups) { + throw new Error( + 'xy visualization: injecting references relies on annotation groups but they were not provided' + ); + } + const fallbackIndexPatternId = references.find(({ type }) => type === 'index-pattern')!.id; return { ...state, - layers: state.layers.map((persistedLayer) => { - if (!isPersistedAnnotationsLayer(persistedLayer)) { - return persistedLayer as XYLayerConfig; - } - - const indexPatternIdFromReferences = - references.find(({ name }) => name === getLayerReferenceName(persistedLayer.layerId))?.id || - fallbackIndexPatternId; - - let injectedLayer: XYAnnotationLayerConfig; - - if (isPersistedByValueAnnotationsLayer(persistedLayer)) { - injectedLayer = { - ...persistedLayer, - hide: Boolean(persistedLayer.hide), - simpleView: Boolean(persistedLayer.simpleView), - indexPatternId: - // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? - indexPatternIdFromReferences, - }; - } else { - const annotationGroupId = references?.find( - ({ name }) => name === persistedLayer.annotationGroupRef - )?.id; + layers: state.layers + .map((persistedLayer) => { + if (!isPersistedAnnotationsLayer(persistedLayer)) { + return persistedLayer as XYLayerConfig; + } - const annotationGroup = annotationGroupId ? annotationGroups[annotationGroupId] : undefined; + const indexPatternIdFromReferences = + references.find(({ name }) => name === getLayerReferenceName(persistedLayer.layerId)) + ?.id || fallbackIndexPatternId; - if (!annotationGroupId || !annotationGroup) { - throw new Error("can't find annotation group!"); // TODO - better handling - } + let injectedLayer: XYAnnotationLayerConfig; - // declared as a separate variable for type checking - const commonProps: Pick< - XYByReferenceAnnotationLayerConfig, - 'layerId' | 'layerType' | 'annotationGroupId' | 'hide' | 'simpleView' | '__lastSaved' - > = { - layerId: persistedLayer.layerId, - layerType: persistedLayer.layerType, - annotationGroupId, - hide: persistedLayer.hide, - simpleView: persistedLayer.simpleView, - __lastSaved: annotationGroup, - }; - - if (isPersistedByReferenceAnnotationsLayer(persistedLayer)) { - // a clean by-reference layer inherits everything from the library annotation group + if (isPersistedByValueAnnotationsLayer(persistedLayer)) { injectedLayer = { - ...commonProps, - ignoreGlobalFilters: annotationGroup.ignoreGlobalFilters, - indexPatternId: annotationGroup.indexPatternId, - annotations: cloneDeep(annotationGroup.annotations), + ...persistedLayer, + hide: Boolean(persistedLayer.hide), + simpleView: Boolean(persistedLayer.simpleView), + indexPatternId: + // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? + indexPatternIdFromReferences, }; } else { - // a linked by-value layer gets settings from visualization state while - // still maintaining the reference to the library annotation group - injectedLayer = { - ...commonProps, - ignoreGlobalFilters: persistedLayer.ignoreGlobalFilters, - indexPatternId: indexPatternIdFromReferences, - annotations: cloneDeep(persistedLayer.annotations), + const annotationGroupId = references?.find( + ({ name }) => name === persistedLayer.annotationGroupRef + )?.id; + + const annotationGroup = annotationGroupId + ? annotationGroups[annotationGroupId] + : undefined; + + if (!annotationGroupId || !annotationGroup) { + return undefined; // the annotation group this layer was referencing is gone, so remove the layer + } + + // declared as a separate variable for type checking + const commonProps: Pick< + XYByReferenceAnnotationLayerConfig, + 'layerId' | 'layerType' | 'annotationGroupId' | 'hide' | 'simpleView' | '__lastSaved' + > = { + layerId: persistedLayer.layerId, + layerType: persistedLayer.layerType, + annotationGroupId, + hide: persistedLayer.hide, + simpleView: persistedLayer.simpleView, + __lastSaved: annotationGroup, }; + + if (isPersistedByReferenceAnnotationsLayer(persistedLayer)) { + // a clean by-reference layer inherits everything from the library annotation group + injectedLayer = { + ...commonProps, + ignoreGlobalFilters: annotationGroup.ignoreGlobalFilters, + indexPatternId: annotationGroup.indexPatternId, + annotations: cloneDeep(annotationGroup.annotations), + }; + } else { + // a linked by-value layer gets settings from visualization state while + // still maintaining the reference to the library annotation group + injectedLayer = { + ...commonProps, + ignoreGlobalFilters: persistedLayer.ignoreGlobalFilters, + indexPatternId: indexPatternIdFromReferences, + annotations: cloneDeep(persistedLayer.annotations), + }; + } } - } - return injectedLayer; - }), + return injectedLayer; + }) + .filter(nonNullable), }; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 8d9cbe2c49c87..e4733455e74f6 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -509,6 +509,84 @@ describe('xy_visualization', () => { ) ).toEqual(expectedAnnotationLayers); }); + + it('should remove annotation layers linked to deleted library annotation groups', () => { + const annotationGroupId1 = 'my-annotation-group-id1'; + const annotationGroupId2 = 'my-annotation-group-id2'; + + const refName1 = 'my-reference'; + const refName2 = 'my-other-reference'; + + const dataViewId = 'some-index-pattern-*'; + + const references: SavedObjectReference[] = [ + { + name: refName1, + id: annotationGroupId1, + type: 'event-annotation-group', + }, + { + name: refName2, + id: annotationGroupId2, + type: 'event-annotation-group', + }, + { + name: 'some-name', + id: dataViewId, + type: 'index-pattern', + }, + ]; + + const persistedAnnotationLayers: XYPersistedLinkedByValueAnnotationLayerConfig[] = [ + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + persistanceType: 'linked', + annotationGroupRef: refName1, + ignoreGlobalFilters: false, // different from the persisted group + annotations: [], // different from the persisted group + hide: false, + simpleView: false, + }, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + persistanceType: 'linked', + annotationGroupRef: refName2, + ignoreGlobalFilters: false, // different from the persisted group + annotations: [], // different from the persisted group + hide: false, + simpleView: false, + }, + ]; + + const libraryAnnotationGroups: AnnotationGroups = { + [annotationGroupId1]: { + annotations: [exampleAnnotation], + indexPatternId: 'data-view-123', + ignoreGlobalFilters: true, + title: 'my title!', + description: '', + tags: [], + }, + }; + + const baseState = exampleState(); + expect( + getAnnotationsLayers( + xyVisualization.initialize!( + () => 'first', + { + ...baseState, + layers: [...baseState.layers, ...persistedAnnotationLayers], + } as XYPersistedState, + undefined, + libraryAnnotationGroups, + references + ).layers + ) + ).toHaveLength(1); + }); }); describe('#removeLayer', () => { From ae5486cd1af24d0052ec1905b0581ac574b3c062 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 23 Mar 2023 15:38:28 -0500 Subject: [PATCH 031/202] Add missing group functional test --- .../apps/lens/group3/annotations.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index 4e1a490d8c88c..4bec636f52625 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -9,7 +9,15 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header', 'tagManagement']); + const PageObjects = getPageObjects([ + 'visualize', + 'lens', + 'common', + 'header', + 'tagManagement', + 'settings', + 'savedObjects', + ]); const find = getService('find'); const retry = getService('retry'); const toastsService = getService('toasts'); @@ -106,6 +114,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('library annotation groups', () => { const ANNOTATION_GROUP_TITLE = 'library annotation group'; const FIRST_VIS_TITLE = 'first visualization'; + const SECOND_VIS_TITLE = 'second visualization'; it('should save annotation group to library', async () => { await PageObjects.visualize.navigateToNewVisualization(); @@ -167,6 +176,27 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { retry.try(async () => { expect(await PageObjects.lens.getLayerCount()).to.be(2); }); + + await PageObjects.lens.save(SECOND_VIS_TITLE); + }); + + it('should remove layer for deleted annotation group', async () => { + // TODO - delete from listing page instead + + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + await PageObjects.savedObjects.searchForObject(ANNOTATION_GROUP_TITLE); + await PageObjects.savedObjects.clickCheckboxByTitle(ANNOTATION_GROUP_TITLE); + await PageObjects.savedObjects.clickDelete({ confirmDelete: true }); + + await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.visualize.loadSavedVisualization(FIRST_VIS_TITLE, { + navigateToVisualize: false, + }); + + retry.try(async () => { + expect(await PageObjects.lens.getLayerCount()).to.be(1); + }); }); // TODO check various saving configurations (linked layer, clean by-ref, revert) From 0abb99155173a26e2ab0b9e333ec4671b0c9c3a3 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 24 Mar 2023 16:12:01 -0500 Subject: [PATCH 032/202] warn users only when they're discarding annotation changes --- x-pack/plugins/lens/public/app_plugin/app.tsx | 22 ++- .../app_plugin/lens_document_equality.test.ts | 177 ++++++++++++++---- .../app_plugin/lens_document_equality.ts | 18 +- .../editor_frame/state_helpers.ts | 1 + .../init_middleware/load_initial.ts | 156 ++++++++------- .../lens/public/state_management/types.ts | 2 + x-pack/plugins/lens/public/types.ts | 8 + .../visualizations/xy/visualization.test.ts | 106 +++++++++++ .../visualizations/xy/visualization.tsx | 8 + 9 files changed, 388 insertions(+), 110 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index a9b37caf488bb..1d2fb127dc39e 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -110,6 +110,7 @@ export function App({ isLoading, isSaveable, visualization, + annotationGroups, } = useLensSelector((state) => state.lens); const selectorDependencies = useMemo( @@ -177,7 +178,9 @@ export function App({ persistedDoc, lastKnownDoc, data.query.filterManager.inject.bind(data.query.filterManager), - datasourceMap + datasourceMap, + visualizationMap, + annotationGroups ) && (isSaveable || persistedDoc) ) { @@ -206,6 +209,8 @@ export function App({ application.capabilities.visualize.save, data.query.filterManager, datasourceMap, + visualizationMap, + annotationGroups, ]); const getLegacyUrlConflictCallout = useCallback(() => { @@ -371,7 +376,14 @@ export function App({ initialDocFromContext, persistedDoc, ].map((refDoc) => - isLensEqual(refDoc, lastKnownDoc, data.query.filterManager.inject, datasourceMap) + isLensEqual( + refDoc, + lastKnownDoc, + data.query.filterManager.inject, + datasourceMap, + visualizationMap, + annotationGroups + ) ); if (initialDocFromContextUnchanged || currentDocHasBeenSavedInLens) { onAppLeave((actions) => { @@ -383,6 +395,7 @@ export function App({ } } }, [ + annotationGroups, application, data.query.filterManager.inject, datasourceMap, @@ -391,6 +404,7 @@ export function App({ lastKnownDoc, onAppLeave, persistedDoc, + visualizationMap, ]); const navigateToVizEditor = useCallback(() => { @@ -597,7 +611,9 @@ export function App({ persistedDoc, lastKnownDoc, data.query.filterManager.inject.bind(data.query.filterManager), - datasourceMap + datasourceMap, + visualizationMap, + annotationGroups ) } goBackToOriginatingApp={goBackToOriginatingApp} diff --git a/x-pack/plugins/lens/public/app_plugin/lens_document_equality.test.ts b/x-pack/plugins/lens/public/app_plugin/lens_document_equality.test.ts index 8bd1e6e980bbb..babde51e39f27 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_document_equality.test.ts +++ b/x-pack/plugins/lens/public/app_plugin/lens_document_equality.test.ts @@ -8,11 +8,19 @@ import { Filter, FilterStateStore } from '@kbn/es-query'; import { isLensEqual } from './lens_document_equality'; import { Document } from '../persistence/saved_object_store'; -import { Datasource, DatasourceMap } from '../types'; +import { + AnnotationGroups, + Datasource, + DatasourceMap, + Visualization, + VisualizationMap, +} from '../types'; + +const visualizationType = 'lnsSomeVis'; const defaultDoc: Document = { title: 'some-title', - visualizationType: 'lnsXY', + visualizationType, state: { query: { query: '', @@ -53,23 +61,67 @@ describe('lens document equality', () => { ); let mockDatasourceMap: DatasourceMap; + let mockVisualizationMap: VisualizationMap; + let mockAnnotationGroups: AnnotationGroups; beforeEach(() => { mockDatasourceMap = { - indexpattern: { isEqual: jest.fn(() => true) }, - } as unknown as DatasourceMap; + indexpattern: { isEqual: jest.fn(() => true) } as Partial as Datasource, + }; + + mockVisualizationMap = { + [visualizationType]: { + isEqual: jest.fn(() => true), + } as Partial as Visualization, + }; + + mockAnnotationGroups = {}; }); it('returns true when documents are equal', () => { expect( - isLensEqual(defaultDoc, defaultDoc, mockInjectFilterReferences, mockDatasourceMap) + isLensEqual( + defaultDoc, + defaultDoc, + mockInjectFilterReferences, + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups + ) ).toBeTruthy(); }); it('handles undefined documents', () => { - expect(isLensEqual(undefined, undefined, mockInjectFilterReferences, {})).toBeTruthy(); - expect(isLensEqual(undefined, {} as Document, mockInjectFilterReferences, {})).toBeFalsy(); - expect(isLensEqual({} as Document, undefined, mockInjectFilterReferences, {})).toBeFalsy(); + expect( + isLensEqual( + undefined, + undefined, + mockInjectFilterReferences, + {}, + mockVisualizationMap, + mockAnnotationGroups + ) + ).toBeTruthy(); + expect( + isLensEqual( + undefined, + {} as Document, + mockInjectFilterReferences, + {}, + mockVisualizationMap, + mockAnnotationGroups + ) + ).toBeFalsy(); + expect( + isLensEqual( + {} as Document, + undefined, + mockInjectFilterReferences, + {}, + mockVisualizationMap, + mockAnnotationGroups + ) + ).toBeFalsy(); }); it('should compare visualization type', () => { @@ -78,7 +130,9 @@ describe('lens document equality', () => { defaultDoc, { ...defaultDoc, visualizationType: 'other-type' }, mockInjectFilterReferences, - mockDatasourceMap + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeFalsy(); }); @@ -98,26 +152,9 @@ describe('lens document equality', () => { }, }, mockInjectFilterReferences, - mockDatasourceMap - ) - ).toBeFalsy(); - }); - - it('should compare the visualization state', () => { - expect( - isLensEqual( - defaultDoc, - { - ...defaultDoc, - state: { - ...defaultDoc.state, - visualization: { - some: 'other-props', - }, - }, - }, - mockInjectFilterReferences, - mockDatasourceMap + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeFalsy(); }); @@ -139,7 +176,9 @@ describe('lens document equality', () => { }, }, mockInjectFilterReferences, - { ...mockDatasourceMap, foodatasource: { isEqual: () => true } as unknown as Datasource } + { ...mockDatasourceMap, foodatasource: { isEqual: () => true } as unknown as Datasource }, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeFalsy(); @@ -167,7 +206,9 @@ describe('lens document equality', () => { }, }, mockInjectFilterReferences, - { ...mockDatasourceMap, foodatasource: { isEqual: () => true } as unknown as Datasource } + { ...mockDatasourceMap, foodatasource: { isEqual: () => true } as unknown as Datasource }, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeTruthy(); }); @@ -176,7 +217,67 @@ describe('lens document equality', () => { // datasource's isEqual returns false (mockDatasourceMap.indexpattern.isEqual as jest.Mock).mockReturnValue(false); expect( - isLensEqual(defaultDoc, defaultDoc, mockInjectFilterReferences, mockDatasourceMap) + isLensEqual( + defaultDoc, + defaultDoc, + mockInjectFilterReferences, + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups + ) + ).toBeFalsy(); + }); + }); + + describe('comparing the visualizations', () => { + it('delegates to visualization class if visualization.isEqual available', () => { + expect( + isLensEqual( + defaultDoc, + defaultDoc, + mockInjectFilterReferences, + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups + ) + ).toBeTruthy(); + + expect(mockVisualizationMap[visualizationType].isEqual).toHaveBeenCalled(); + + (mockVisualizationMap[visualizationType].isEqual as jest.Mock).mockReturnValue(false); + + expect( + isLensEqual( + defaultDoc, + defaultDoc, + mockInjectFilterReferences, + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups + ) + ).toBeFalsy(); + }); + + it('direct comparison if no isEqual implementation', () => { + delete mockVisualizationMap[visualizationType].isEqual; + + expect( + isLensEqual( + defaultDoc, + { + ...defaultDoc, + state: { + ...defaultDoc.state, + visualization: { + some: 'other-props', + }, + }, + }, + mockInjectFilterReferences, + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups + ) ).toBeFalsy(); }); }); @@ -197,7 +298,9 @@ describe('lens document equality', () => { defaultDoc, { ...defaultDoc, state: { ...defaultDoc.state, filters: filtersWithPinned } }, mockInjectFilterReferences, - mockDatasourceMap + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeTruthy(); }); @@ -221,7 +324,9 @@ describe('lens document equality', () => { }, }, mockInjectFilterReferences, - mockDatasourceMap + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeTruthy(); }); @@ -241,7 +346,9 @@ describe('lens document equality', () => { }, }, mockInjectFilterReferences, - mockDatasourceMap + mockDatasourceMap, + mockVisualizationMap, + mockAnnotationGroups ) ).toBeTruthy(); }); diff --git a/x-pack/plugins/lens/public/app_plugin/lens_document_equality.ts b/x-pack/plugins/lens/public/app_plugin/lens_document_equality.ts index dae571acf10ed..1313ae15a0ddc 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_document_equality.ts +++ b/x-pack/plugins/lens/public/app_plugin/lens_document_equality.ts @@ -8,7 +8,7 @@ import { isEqual, intersection, union } from 'lodash'; import { FilterManager } from '@kbn/data-plugin/public'; import { Document } from '../persistence/saved_object_store'; -import { DatasourceMap } from '../types'; +import { AnnotationGroups, DatasourceMap, VisualizationMap } from '../types'; import { removePinnedFilters } from './save_modal_container'; const removeNonSerializable = (obj: Parameters[0]) => @@ -18,7 +18,9 @@ export const isLensEqual = ( doc1In: Document | undefined, doc2In: Document | undefined, injectFilterReferences: FilterManager['inject'], - datasourceMap: DatasourceMap + datasourceMap: DatasourceMap, + visualizationMap: VisualizationMap, + annotationGroups: AnnotationGroups ) => { if (doc1In === undefined || doc2In === undefined) { return doc1In === doc2In; @@ -36,7 +38,17 @@ export const isLensEqual = ( return false; } - if (!isEqual(doc1.state.visualization, doc2.state.visualization)) { + const isEqualFromVis = visualizationMap[doc1.visualizationType]?.isEqual; + const visualizationStateIsEqual = isEqualFromVis + ? isEqualFromVis( + doc1.state.visualization, + doc1.references, + doc2.state.visualization, + doc2.references, + annotationGroups + ) + : isEqual(doc1.state.visualization, doc2.state.visualization); + if (!visualizationStateIsEqual) { return false; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index d914e3e9591ee..2b694d8ff3c0e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -246,6 +246,7 @@ export async function initializeSources( return { indexPatterns, indexPatternRefs, + annotationGroups, datasourceStates: initializeDatasources({ datasourceMap, datasourceStates, diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index a00ad9581769e..d16fd6e20b48c 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -159,39 +159,48 @@ export function loadInitial( isFullEditor: true, } ) - .then(({ datasourceStates, visualizationState, indexPatterns, indexPatternRefs }) => { - const currentSessionId = - initialStateFromLocator?.searchSessionId || data.search.session.getSessionId(); - store.dispatch( - setState({ - isSaveable: true, - filters: initialStateFromLocator.filters || data.query.filterManager.getFilters(), - query: initialStateFromLocator.query || emptyState.query, - searchSessionId: currentSessionId, - activeDatasourceId: emptyState.activeDatasourceId, - visualization: { - activeId: emptyState.visualization.activeId, - state: visualizationState, - }, - dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), - datasourceStates: Object.entries(datasourceStates).reduce( - (state, [datasourceId, datasourceState]) => ({ - ...state, - [datasourceId]: { - ...datasourceState, - isLoading: false, - }, - }), - {} - ), - isLoading: false, - }) - ); + .then( + ({ + datasourceStates, + visualizationState, + indexPatterns, + indexPatternRefs, + annotationGroups, + }) => { + const currentSessionId = + initialStateFromLocator?.searchSessionId || data.search.session.getSessionId(); + store.dispatch( + setState({ + isSaveable: true, + filters: initialStateFromLocator.filters || data.query.filterManager.getFilters(), + query: initialStateFromLocator.query || emptyState.query, + searchSessionId: currentSessionId, + activeDatasourceId: emptyState.activeDatasourceId, + visualization: { + activeId: emptyState.visualization.activeId, + state: visualizationState, + }, + dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), + datasourceStates: Object.entries(datasourceStates).reduce( + (state, [datasourceId, datasourceState]) => ({ + ...state, + [datasourceId]: { + ...datasourceState, + isLoading: false, + }, + }), + {} + ), + isLoading: false, + annotationGroups, + }) + ); - if (autoApplyDisabled) { - store.dispatch(disableAutoApply()); + if (autoApplyDisabled) { + store.dispatch(disableAutoApply()); + } } - }) + ) .catch((e: { message: string }) => { notifications.toasts.addDanger({ title: e.message, @@ -310,46 +319,55 @@ export function loadInitial( }, { isFullEditor: true } ) - .then(({ datasourceStates, visualizationState, indexPatterns, indexPatternRefs }) => { - const currentSessionId = data.search.session.getSessionId(); - store.dispatch( - setState({ - isSaveable: true, - sharingSavedObjectProps, - filters: data.query.filterManager.getFilters(), - query: doc.state.query, - searchSessionId: - dashboardFeatureFlag.allowByValueEmbeddables && - Boolean(embeddableEditorIncomingState?.originatingApp) && - !(initialInput as LensByReferenceInput)?.savedObjectId && - currentSessionId - ? currentSessionId - : data.search.session.start(), - persistedDoc: doc, - activeDatasourceId: getInitialDatasourceId(datasourceMap, doc), - visualization: { - activeId: doc.visualizationType, - state: visualizationState, - }, - dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), - datasourceStates: Object.entries(datasourceStates).reduce( - (state, [datasourceId, datasourceState]) => ({ - ...state, - [datasourceId]: { - ...datasourceState, - isLoading: false, - }, - }), - {} - ), - isLoading: false, - }) - ); + .then( + ({ + datasourceStates, + visualizationState, + indexPatterns, + indexPatternRefs, + annotationGroups, + }) => { + const currentSessionId = data.search.session.getSessionId(); + store.dispatch( + setState({ + isSaveable: true, + sharingSavedObjectProps, + filters: data.query.filterManager.getFilters(), + query: doc.state.query, + searchSessionId: + dashboardFeatureFlag.allowByValueEmbeddables && + Boolean(embeddableEditorIncomingState?.originatingApp) && + !(initialInput as LensByReferenceInput)?.savedObjectId && + currentSessionId + ? currentSessionId + : data.search.session.start(), + persistedDoc: doc, + activeDatasourceId: getInitialDatasourceId(datasourceMap, doc), + visualization: { + activeId: doc.visualizationType, + state: visualizationState, + }, + dataViews: getInitialDataViewsObject(indexPatterns, indexPatternRefs), + datasourceStates: Object.entries(datasourceStates).reduce( + (state, [datasourceId, datasourceState]) => ({ + ...state, + [datasourceId]: { + ...datasourceState, + isLoading: false, + }, + }), + {} + ), + isLoading: false, + annotationGroups, + }) + ); - if (autoApplyDisabled) { - store.dispatch(disableAutoApply()); + if (autoApplyDisabled) { + store.dispatch(disableAutoApply()); + } } - }) + ) .catch((e: { message: string }) => notifications.toasts.addDanger({ title: e.message, diff --git a/x-pack/plugins/lens/public/state_management/types.ts b/x-pack/plugins/lens/public/state_management/types.ts index 4377060a35197..b18638568485b 100644 --- a/x-pack/plugins/lens/public/state_management/types.ts +++ b/x-pack/plugins/lens/public/state_management/types.ts @@ -22,6 +22,7 @@ import type { VisualizeEditorContext, IndexPattern, IndexPatternRef, + AnnotationGroups, } from '../types'; export interface VisualizationState { activeId: string | null; @@ -63,6 +64,7 @@ export interface LensAppState extends EditorFrameState { sharingSavedObjectProps?: Omit; // Dataview/Indexpattern management has moved in here from datasource dataViews: DataViewsState; + annotationGroups: AnnotationGroups; } export type DispatchSetState = (state: Partial) => { diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 32a47c3d605d3..e8c303c6f21ea 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1304,6 +1304,14 @@ export interface Visualization Suggestion | undefined; getVisualizationInfo?: (state: T) => VisualizationInfo; + + isEqual?: ( + state1: P, + references1: SavedObjectReference[], + state2: P, + references2: SavedObjectReference[], + annotationGroups: AnnotationGroups + ) => boolean; } // Use same technique as TriggerContext diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index e4733455e74f6..e86325ab59403 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -3676,4 +3676,110 @@ describe('xy_visualization', () => { ); }); }); + + describe('#isEqual', () => { + const annotationGroupId1 = 'my-annotation-group-id1'; + const annotationGroupId2 = 'my-annotation-group-id2'; + + const refName1 = 'my-reference'; + const refName2 = 'my-other-reference'; + + const references1: SavedObjectReference[] = [ + { + name: refName1, + id: annotationGroupId1, + type: 'event-annotation-group', + }, + { + name: 'some-name', + id: 'some-index-pattern-*', + type: 'index-pattern', + }, + ]; + + const references2: SavedObjectReference[] = [ + { + name: refName2, + id: annotationGroupId1, + type: 'event-annotation-group', + }, + { + name: 'some-name', + id: 'some-index-pattern-*', + type: 'index-pattern', + }, + ]; + + const baseState = exampleState(); + + const state1 = { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + persistanceType: 'byReference', + annotationGroupRef: refName1, + } as XYPersistedByReferenceAnnotationLayerConfig, + ], + } as XYPersistedState; + + const state2 = { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + persistanceType: 'byReference', + annotationGroupRef: refName2, + } as XYPersistedByReferenceAnnotationLayerConfig, + ], + } as XYPersistedState; + + const annotationGroups = { + [annotationGroupId1]: { + annotations: [exampleAnnotation], + indexPatternId: 'data-view-123', + ignoreGlobalFilters: true, + title: 'my title!', + description: '', + tags: [], + }, + [annotationGroupId2]: { + annotations: [exampleAnnotation2], + indexPatternId: 'data-view-123', + ignoreGlobalFilters: true, + title: 'my title!', + description: '', + tags: [], + }, + }; + + it('compares after injecting annotation groups', () => { + expect(xyVisualization.isEqual!(state1, references1, state2, references2, annotationGroups)); + + expect( + xyVisualization.isEqual!( + state1, + references1, + state2, + [ + { + name: refName2, + id: annotationGroupId2, + type: 'event-annotation-group', + }, + { + name: 'some-name', + id: 'some-index-pattern-*', + type: 'index-pattern', + }, + ], + annotationGroups + ) + ).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 06e4266d02ae5..780d704fe2a6b 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -24,6 +24,7 @@ import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import { isEqual } from 'lodash'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -1009,6 +1010,13 @@ export const getXyVisualization = ({ layers: visualizationLayersInfo, }; }, + + isEqual(state1, references1, state2, references2, annotationGroups) { + return isEqual( + injectReferences(state1, annotationGroups, references1), + injectReferences(state2, annotationGroups, references2) + ); + }, }); const getMappedAccessors = ({ From 1e80420030e75db7c4181ffd50255d85aefcba6b Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 29 Mar 2023 10:29:00 -0500 Subject: [PATCH 033/202] support saving and loading groups with ad-hoc data views --- src/plugins/event_annotation/common/types.ts | 4 +- src/plugins/event_annotation/kibana.jsonc | 3 +- .../__snapshots__/service.test.ts.snap | 1 + .../event_annotation_service/service.test.ts | 30 +++++- .../event_annotation_service/service.tsx | 70 ++++++++++---- .../editor_frame/state_helpers.ts | 7 ++ .../__snapshots__/load_initial.test.tsx.snap | 1 + .../public/state_management/lens_slice.ts | 1 + .../__snapshots__/save_action.test.tsx.snap | 66 +++++++++++++ .../xy/annotations/actions/index.ts | 4 + .../annotations/actions/save_action.test.tsx | 93 +++++++++++++------ .../xy/annotations/actions/save_action.tsx | 15 ++- .../visualizations/xy/visualization.tsx | 1 + 13 files changed, 243 insertions(+), 53 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 0f50b3687ca12..b3e704ef647e5 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { KibanaQueryOutput } from '@kbn/data-plugin/common'; +import { DataViewSpec, KibanaQueryOutput } from '@kbn/data-plugin/common'; import { DatatableColumn } from '@kbn/expressions-plugin/common'; import { $Values } from '@kbn/utility-types'; import { AvailableAnnotationIcons } from './constants'; @@ -88,6 +88,7 @@ export interface EventAnnotationGroupAttributes { tags: string[]; ignoreGlobalFilters: boolean; annotations: EventAnnotationConfig[]; + dataViewSpec?: DataViewSpec; } export interface EventAnnotationGroupConfig { @@ -97,6 +98,7 @@ export interface EventAnnotationGroupConfig { title: string; description: string; tags: string[]; + dataViewSpec?: DataViewSpec; } export type EventAnnotationArgs = diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index c72cc150d869d..b4df5edf135af 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -13,7 +13,8 @@ "data", ], "requiredBundles": [ - "savedObjectsFinder" + "savedObjectsFinder", + "dataViews" ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap index b8ddd299db6cc..73073c0e7ea16 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap +++ b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap @@ -3,6 +3,7 @@ exports[`Event Annotation Service loadAnnotationGroup should properly load an annotation group with a multiple annotation 1`] = ` Object { "annotations": undefined, + "dataViewSpec": undefined, "description": undefined, "ignoreGlobalFilters": undefined, "indexPatternId": "ipid", diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index e3b651c44dceb..c131ca288a88d 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -57,6 +57,17 @@ const annotationGroupResolveMocks: Record = }, ], } as Partial as AnnotationGroupSavedObject, + withAdHocDataView: { + attributes: { + title: 'groupTitle', + dataViewSpec: { + id: 'my-id', + }, + } as Partial, + id: 'multiAnnotations', + type: 'event-annotation-group', + references: [], + } as Partial as AnnotationGroupSavedObject, }; const annotationResolveMocks = { @@ -124,7 +135,7 @@ describe('Event Annotation Service', () => { return annotationGroupResolveMocks.multiAnnotations; }); (core.savedObjects.client.get as jest.Mock).mockImplementation((_type, id) => { - const typedId = id as 'multiAnnotations' | 'noAnnotations' | 'nonExistingGroup'; + const typedId = id as keyof typeof annotationGroupResolveMocks; return annotationGroupResolveMocks[typedId]; }); (core.savedObjects.client.bulkCreate as jest.Mock).mockImplementation(() => { @@ -459,6 +470,7 @@ describe('Event Annotation Service', () => { .toMatchInlineSnapshot(` Object { "annotations": Array [], + "dataViewSpec": undefined, "description": "", "ignoreGlobalFilters": false, "indexPatternId": "ipid", @@ -472,6 +484,11 @@ describe('Event Annotation Service', () => { await eventAnnotationService.loadAnnotationGroup('multiAnnotations') ).toMatchSnapshot(); }); + it('populates id if group has ad-hoc data view', async () => { + const group = await eventAnnotationService.loadAnnotationGroup('withAdHocDataView'); + + expect(group.indexPatternId).toBe(group.dataViewSpec?.id); + }); }); // describe.skip('deleteAnnotationGroup', () => { // it('deletes annotation group along with annotations that reference them', async () => { @@ -503,6 +520,8 @@ describe('Event Annotation Service', () => { title: 'newGroupTitle', description: 'my description', tags: ['my', 'many', 'tags'], + ignoreGlobalFilters: false, + dataViewSpec: null, annotations, }, { @@ -533,7 +552,14 @@ describe('Event Annotation Service', () => { expect(core.savedObjects.client.update).toHaveBeenCalledWith( 'event-annotation-group', 'multiAnnotations', - { title: 'newTitle', description: '', tags: [], annotations: [] }, + { + title: 'newTitle', + description: '', + tags: [], + annotations: [], + dataViewSpec: null, + ignoreGlobalFilters: false, + }, { references: [ { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 53eb24133e203..e97fbd605c8d5 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -10,9 +10,10 @@ import React from 'react'; import { partition } from 'lodash'; import { queryToAst } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; -import { CoreStart, SavedObjectsClientContract } from '@kbn/core/public'; +import { CoreStart, SavedObjectReference, SavedObjectsClientContract } from '@kbn/core/public'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import { EventAnnotationConfig, EventAnnotationGroupAttributes, @@ -63,13 +64,23 @@ export function getEventAnnotationService( // .filter(({ error }) => !error) // .map((annotation) => annotation.attributes); + const adHocDataViewSpec = savedObject.attributes.dataViewSpec + ? DataViewPersistableStateService.inject( + savedObject.attributes.dataViewSpec, + savedObject.references + ) + : undefined; + return { title: savedObject.attributes.title, description: savedObject.attributes.description, tags: savedObject.attributes.tags, ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters, - indexPatternId: savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!, + indexPatternId: adHocDataViewSpec + ? adHocDataViewSpec.id! + : savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!, annotations: savedObject.attributes.annotations, + dataViewSpec: adHocDataViewSpec, }; }; @@ -89,23 +100,46 @@ export function getEventAnnotationService( // ]); // }; + const extractDataViewInformation = (group: EventAnnotationGroupConfig) => { + let { dataViewSpec = null } = group; + + let references: SavedObjectReference[]; + + if (dataViewSpec) { + if (!dataViewSpec.id) + throw new Error( + 'tried to create annotation group with a data view spec that did not include an ID!' + ); + + const { state, references: refsFromDataView } = + DataViewPersistableStateService.extract(dataViewSpec); + dataViewSpec = state; + references = refsFromDataView; + } else { + references = [ + { + type: 'index-pattern', + id: group.indexPatternId, + name: `event-annotation-group_dataView-ref-${group.indexPatternId}`, + }, + ]; + } + + return { references, dataViewSpec }; + }; + const createAnnotationGroup = async ( group: EventAnnotationGroupConfig ): Promise<{ id: string }> => { - const { title, description, tags, ignoreGlobalFilters, indexPatternId, annotations } = group; + const { references, dataViewSpec } = extractDataViewInformation(group); + const { title, description, tags, ignoreGlobalFilters, annotations } = group; const groupSavedObjectId = ( await client.create( EVENT_ANNOTATION_GROUP_TYPE, - { title, description, tags, ignoreGlobalFilters, annotations }, + { title, description, tags, ignoreGlobalFilters, annotations, dataViewSpec }, { - references: [ - { - type: 'index-pattern', - id: indexPatternId, - name: `event-annotation-group_dataView-ref-${indexPatternId}`, - }, - ], + references, } ) ).id; @@ -117,19 +151,15 @@ export function getEventAnnotationService( group: EventAnnotationGroupConfig, annotationGroupId: string ): Promise => { - const { title, description, tags, ignoreGlobalFilters, indexPatternId, annotations } = group; + const { references, dataViewSpec } = extractDataViewInformation(group); + const { title, description, tags, ignoreGlobalFilters, annotations } = group; + await client.update( EVENT_ANNOTATION_GROUP_TYPE, annotationGroupId, - { title, description, tags, ignoreGlobalFilters, annotations }, + { title, description, tags, ignoreGlobalFilters, annotations, dataViewSpec }, { - references: [ - { - type: 'index-pattern', - id: indexPatternId, - name: `event-annotation-group_dataView-ref-${indexPatternId}`, - }, - ], + references, } ); }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 2b694d8ff3c0e..17bfbaac4a706 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -123,6 +123,13 @@ export async function initializeDataViews( return [id, spec]; }) ); + + for (const group of Object.values(annotationGroups)) { + if (group.dataViewSpec?.id) { + adHocDataViews[group.dataViewSpec.id] = group.dataViewSpec; + } + } + const { isFullEditor } = options ?? {}; // make it explicit or TS will infer never[] and break few lines down diff --git a/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap b/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap index 8b7050304fb80..b93e1dafa225c 100644 --- a/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap +++ b/x-pack/plugins/lens/public/state_management/__snapshots__/load_initial.test.tsx.snap @@ -4,6 +4,7 @@ exports[`Initializing the store should initialize all datasources with state fro Object { "lens": Object { "activeDatasourceId": "testDatasource", + "annotationGroups": Object {}, "dataViews": Object { "indexPatternRefs": Array [], "indexPatterns": Object {}, diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index 2fd8c645a9fac..9ede1a6b226f7 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -50,6 +50,7 @@ export const initialState: LensAppState = { indexPatternRefs: [], indexPatterns: {}, }, + annotationGroups: {}, }; export const getPreloadedState = ({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap index d0e4e1c7dbc70..a947c50182fde 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -17,6 +17,7 @@ Array [ "type": "manual", }, ], + "dataViewSpec": undefined, "description": "my description", "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", @@ -36,10 +37,12 @@ Array [ "type": "manual", }, ], + "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", + "simpleView": false, }, ], "legend": Object { @@ -69,6 +72,7 @@ Array [ "type": "manual", }, ], + "dataViewSpec": undefined, "description": "my description", "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", @@ -88,10 +92,69 @@ Array [ "type": "manual", }, ], + "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", + "simpleView": false, + }, + ], + "legend": Object { + "isVisible": true, + "position": "bottom", + }, + "preferredSeriesType": "area", + }, + ], +] +`; + +exports[`annotation group save action save routine successful initial save with ad-hoc data view 1`] = ` +Array [ + Array [ + Object { + "layers": Array [ + Object { + "__lastSaved": Object { + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "dataViewSpec": Object { + "id": "some-adhoc-data-view-id", + }, + "description": "my description", + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "tags": Array [ + "my-tag", + ], + "title": "my title", + }, + "annotationGroupId": "saved-id-123", + "annotations": Array [ + Object { + "id": "some-annotation-id", + "key": Object { + "timestamp": "timestamp", + "type": "point_in_time", + }, + "type": "manual", + }, + ], + "hide": false, + "ignoreGlobalFilters": false, + "indexPatternId": "some-index-pattern", + "layerId": "mylayerid", + "layerType": "annotations", + "simpleView": false, }, ], "legend": Object { @@ -121,6 +184,7 @@ Array [ "type": "manual", }, ], + "dataViewSpec": undefined, "description": "my description", "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", @@ -140,10 +204,12 @@ Array [ "type": "manual", }, ], + "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", + "simpleView": false, }, ], "legend": Object { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index a9ab5e97fee74..9ed2729a51a06 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -8,6 +8,7 @@ import type { CoreStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; +import { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; @@ -26,6 +27,7 @@ export const createAnnotationActions = ({ isSaveable, eventAnnotationService, savedObjectsTagging, + dataViews, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -34,6 +36,7 @@ export const createAnnotationActions = ({ isSaveable?: boolean; eventAnnotationService: EventAnnotationServiceType; savedObjectsTagging?: SavedObjectTaggingPluginStart; + dataViews: DataViewsContract; }): LayerAction[] => { const actions = []; @@ -50,6 +53,7 @@ export const createAnnotationActions = ({ eventAnnotationService, toasts: core.notifications.toasts, savedObjectsTagging, + dataViews, }) ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index f59234d81c038..7704d68227572 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -19,6 +19,8 @@ import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { SavedObjectSaveModal } from '@kbn/saved-objects-plugin/public'; import { taggingApiMock } from '@kbn/saved-objects-tagging-plugin/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; describe('annotation group save action', () => { describe('save modal', () => { @@ -132,33 +134,41 @@ describe('annotation group save action', () => { const savedId = 'saved-id-123'; - const getProps: () => Parameters[0] = () => ({ - state: { - preferredSeriesType: 'area', - legend: { isVisible: true, position: 'bottom' }, - layers: [{ layerId } as XYAnnotationLayerConfig], - } as XYState, - layer, - setState: jest.fn(), - eventAnnotationService: { - createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), - updateAnnotationGroup: jest.fn(), - loadAnnotationGroup: jest.fn(), - toExpression: jest.fn(), - toFetchExpression: jest.fn(), - renderEventAnnotationGroupSavedObjectFinder: jest.fn(), - } as EventAnnotationServiceType, - toasts: toastsServiceMock.createStartContract(), - modalOnSaveProps: { - newTitle: 'my title', - newDescription: 'my description', - closeModal: jest.fn(), - newTags: ['my-tag'], - newCopyOnSave: false, - isTitleDuplicateConfirmed: false, - onTitleDuplicate: () => {}, - }, - }); + const getProps: () => Parameters[0] = () => { + const dataViews = dataViewPluginMocks.createStartContract(); + dataViews.get.mockResolvedValue({ + isPersisted: () => true, + } as Partial as DataView); + + return { + state: { + preferredSeriesType: 'area', + legend: { isVisible: true, position: 'bottom' }, + layers: [{ layerId } as XYAnnotationLayerConfig], + } as XYState, + layer, + setState: jest.fn(), + eventAnnotationService: { + createAnnotationGroup: jest.fn(() => Promise.resolve({ id: savedId })), + updateAnnotationGroup: jest.fn(), + loadAnnotationGroup: jest.fn(), + toExpression: jest.fn(), + toFetchExpression: jest.fn(), + renderEventAnnotationGroupSavedObjectFinder: jest.fn(), + } as EventAnnotationServiceType, + toasts: toastsServiceMock.createStartContract(), + modalOnSaveProps: { + newTitle: 'my title', + newDescription: 'my description', + closeModal: jest.fn(), + newTags: ['my-tag'], + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + onTitleDuplicate: () => {}, + }, + dataViews, + }; + }; let props: ReturnType; beforeEach(() => { @@ -184,6 +194,35 @@ describe('annotation group save action', () => { expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); }); + test('successful initial save with ad-hoc data view', async () => { + const dataViewSpec = { + id: 'some-adhoc-data-view-id', + } as DataViewSpec; + + (props.dataViews.get as jest.Mock).mockResolvedValueOnce({ + isPersisted: () => false, // ad-hoc + toSpec: () => dataViewSpec, + } as Partial); + + await onSave(props); + + expect(props.eventAnnotationService.createAnnotationGroup).toHaveBeenCalledWith({ + annotations: props.layer.annotations, + indexPatternId: props.layer.indexPatternId, + ignoreGlobalFilters: props.layer.ignoreGlobalFilters, + title: props.modalOnSaveProps.newTitle, + description: props.modalOnSaveProps.newDescription, + tags: props.modalOnSaveProps.newTags, + dataViewSpec, + }); + + expect(props.modalOnSaveProps.closeModal).toHaveBeenCalled(); + + expect((props.setState as jest.Mock).mock.calls).toMatchSnapshot(); + + expect(props.toasts.addSuccess).toHaveBeenCalledTimes(1); + }); + test('failed initial save', async () => { (props.eventAnnotationService.createAnnotationGroup as jest.Mock).mockRejectedValue( new Error('oh noooooo') diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 43b04ca425d42..388bf543b7d79 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -19,6 +19,7 @@ import { import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; import { EuiIcon } from '@elastic/eui'; import { type SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; +import { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYByReferenceAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types'; import { isByReferenceAnnotationsLayer } from '../../visualization_helpers'; @@ -92,10 +93,13 @@ const saveAnnotationGroupToLibrary = async ( newTags, newCopyOnSave, }: Pick, - eventAnnotationService: EventAnnotationServiceType + eventAnnotationService: EventAnnotationServiceType, + dataViews: DataViewsContract ): Promise<{ id: string; config: EventAnnotationGroupConfig }> => { let savedId: string; + const dataView = await dataViews.get(layer.indexPatternId); + const saveAsNew = !isByReferenceAnnotationsLayer(layer) || newCopyOnSave; const groupConfig: EventAnnotationGroupConfig = { @@ -105,6 +109,7 @@ const saveAnnotationGroupToLibrary = async ( title: newTitle, description: newDescription, tags: newTags, + dataViewSpec: dataView.isPersisted() ? undefined : dataView.toSpec(), }; if (saveAsNew) { @@ -127,6 +132,7 @@ export const onSave = async ({ eventAnnotationService, toasts, modalOnSaveProps: { newTitle, newDescription, newTags, closeModal, newCopyOnSave }, + dataViews, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -134,13 +140,15 @@ export const onSave = async ({ eventAnnotationService: EventAnnotationServiceType; toasts: ToastsStart; modalOnSaveProps: ModalOnSaveProps; + dataViews: DataViewsContract; }) => { let savedInfo: Awaited>; try { savedInfo = await saveAnnotationGroupToLibrary( layer, { newTitle, newDescription, newTags, newCopyOnSave }, - eventAnnotationService + eventAnnotationService, + dataViews ); } catch (err) { toasts.addError(err, { @@ -206,6 +214,7 @@ export const getSaveLayerAction = ({ eventAnnotationService, toasts, savedObjectsTagging, + dataViews, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -213,6 +222,7 @@ export const getSaveLayerAction = ({ eventAnnotationService: EventAnnotationServiceType; toasts: ToastsStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; + dataViews: DataViewsContract; }): LayerAction => { const neverSaved = !isByReferenceAnnotationsLayer(layer); @@ -238,6 +248,7 @@ export const getSaveLayerAction = ({ eventAnnotationService, toasts, modalOnSaveProps: props, + dataViews, }); }} title={neverSaved ? '' : layer.__lastSaved.title} diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 780d704fe2a6b..ff3cb3225c1f8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -291,6 +291,7 @@ export const getXyVisualization = ({ isSaveable, eventAnnotationService, savedObjectsTagging, + dataViews: data.dataViews, }) ); } From 7778006ee669afe8e7e52793dfdd11c8736e92f4 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 29 Mar 2023 15:31:35 -0500 Subject: [PATCH 034/202] add ad-hoc data view to lens when annotation group added --- x-pack/plugins/lens/public/app_plugin/app.tsx | 3 +- .../lens/public/data_views_service/service.ts | 67 +++++++++++++++---- .../config_panel/config_panel.tsx | 7 ++ x-pack/plugins/lens/public/types.ts | 1 + .../public/visualizations/xy/add_layer.tsx | 8 +-- .../visualizations/xy/visualization.tsx | 13 +++- 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 1d2fb127dc39e..8911af087f1ae 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -433,7 +433,6 @@ export function App({ dataViews, uiActions, core: { http, notifications, uiSettings }, - data, contextDataViewSpec: (initialContext as VisualizeFieldContext | undefined)?.dataViewSpec, updateIndexPatterns: (newIndexPatternsState, options) => { dispatch(updateIndexPatterns(newIndexPatternsState)); @@ -448,7 +447,7 @@ export function App({ } }, }), - [dataViews, uiActions, http, notifications, uiSettings, data, initialContext, dispatch] + [dataViews, uiActions, http, notifications, uiSettings, initialContext, dispatch] ); const onTextBasedSavedAndExit = useCallback(async ({ onSave, onCancel }) => { diff --git a/x-pack/plugins/lens/public/data_views_service/service.ts b/x-pack/plugins/lens/public/data_views_service/service.ts index 5192de1d2385e..5181e5741d510 100644 --- a/x-pack/plugins/lens/public/data_views_service/service.ts +++ b/x-pack/plugins/lens/public/data_views_service/service.ts @@ -8,7 +8,6 @@ import type { DataViewsContract, DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { ActionExecutionContext, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { UPDATE_FILTER_REFERENCES_ACTION, @@ -21,7 +20,6 @@ import { generateId } from '../id_generator'; export interface IndexPatternServiceProps { core: Pick; - data: DataPublicPluginStart; dataViews: DataViewsContract; uiActions: UiActionsStart; contextDataViewSpec?: DataViewSpec; @@ -54,10 +52,16 @@ export interface IndexPatternServiceAPI { cache: IndexPatternMap; onIndexPatternRefresh?: () => void; }) => Promise; + /** - * Load indexPatternRefs with title and ids + * Takes a data view spec and creates the both the data view and the Lens index pattern + * and adds everything to the caches. */ - loadIndexPatternRefs: (options: { isFullEditor: boolean }) => Promise; + addIndexPatternFromDataViewSpec: ( + spec: DataViewSpec, + cache: IndexPatternMap | undefined + ) => Promise; + /** * Ensure an indexPattern is loaded in the cache, usually used in conjuction with a indexPattern change action. */ @@ -81,16 +85,15 @@ export interface IndexPatternServiceAPI { ) => void; } -export function createIndexPatternService({ +export const createIndexPatternService = ({ core, dataViews, - data, updateIndexPatterns, replaceIndexPattern, uiActions, contextDataViewSpec, -}: IndexPatternServiceProps): IndexPatternServiceAPI { - const onChangeError = (err: Error) => +}: IndexPatternServiceProps): IndexPatternServiceAPI => { + const showLoadingDataViewError = (err: Error) => core.notifications.toasts.addError(err, { title: i18n.translate('xpack.lens.indexPattern.dataViewLoadError', { defaultMessage: 'Error loading data view', @@ -104,6 +107,48 @@ export function createIndexPatternService({ ...args, }); }, + addIndexPatternFromDataViewSpec: async ( + spec: DataViewSpec, + cache: IndexPatternMap | undefined + ) => { + // TODO - extract this routine into a function and unit test? + if (!spec.id) { + showLoadingDataViewError( + new Error( + i18n.translate('xpack.lens.indexPattern.noIdError', { + defaultMessage: 'Tried to use data view spec without an ID', + }) + ) + ); + return; + } + const dataView = await dataViews.create(spec); + const indexPatterns = await ensureIndexPattern({ + id: dataView.id!, + onError: showLoadingDataViewError, + dataViews, + cache, + }); + + if (!indexPatterns) { + return; + } + + const refs = await loadIndexPatternRefs(dataViews); + + const newIndexPattern = indexPatterns[dataView.id!]; + + const newRef: IndexPatternRef = { + id: newIndexPattern.id!, + name: newIndexPattern.name, + title: newIndexPattern.title, + }; + + updateIndexPatterns({ + indexPatterns, + indexPatternRefs: [...refs, newRef], + }); + }, replaceDataViewId: async (dataView: DataView) => { const newDataView = await dataViews.create({ ...dataView.toSpec(), id: generateId() }); @@ -131,9 +176,7 @@ export function createIndexPatternService({ } as ActionExecutionContext); }, ensureIndexPattern: (args) => - ensureIndexPattern({ onError: onChangeError, dataViews, ...args }), - loadIndexPatternRefs: async ({ isFullEditor }) => - isFullEditor ? loadIndexPatternRefs(dataViews) : [], + ensureIndexPattern({ onError: showLoadingDataViewError, dataViews, ...args }), getDefaultIndex: () => core.uiSettings.get('defaultIndex'), }; -} +}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index dddd21237fd9e..d956424ea6951 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -13,6 +13,7 @@ import { UPDATE_FILTER_REFERENCES_ACTION, UPDATE_FILTER_REFERENCES_TRIGGER, } from '@kbn/unified-search-plugin/public'; +import { DataViewSpec } from '@kbn/data-views-plugin/common'; import { changeIndexPattern, removeDimension } from '../../../state_management/lens_slice'; import { AddLayerFunction, Visualization } from '../../../types'; import { LayerPanel } from './layer_panel'; @@ -216,6 +217,7 @@ export function LayerPanels( id: indexPatternId, cache: props.framePublicAPI.dataViews.indexPatterns, }); + dispatchLens( changeIndexPattern({ indexPatternId, @@ -319,6 +321,11 @@ export function LayerPanels( visualizationState: visualization.state, layersMeta: props.framePublicAPI, addLayer, + addIndexPatternFromDataViewSpec: (spec: DataViewSpec) => + props.indexPatternService.addIndexPatternFromDataViewSpec( + spec, + props.framePublicAPI.dataViews.indexPatterns + ), })} ); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index e8c303c6f21ea..4c6c07efd9f30 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1239,6 +1239,7 @@ export interface Visualization; + addIndexPatternFromDataViewSpec: (spec: DataViewSpec) => Promise; }) => JSX.Element | null; /** * Creates map of columns ids and unique lables. Used only for noDatasource layers diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index 2b730288c6d90..4ae7d20945732 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -27,7 +27,8 @@ import type { ExtraAppendLayerArg } from './visualization'; interface AddLayerButtonProps { visualization: Visualization; visualizationState: unknown; - addLayer: AddLayerFunction; + addLayer: AddLayerFunction; + onAddLayerFromAnnotationGroup: (arg: ExtraAppendLayerArg) => void; layersMeta: Pick; eventAnnotationService: EventAnnotationServiceType; } @@ -37,6 +38,7 @@ export function AddLayerButton({ visualization, visualizationState, addLayer, + onAddLayerFromAnnotationGroup, layersMeta, eventAnnotationService, }: AddLayerButtonProps) { @@ -211,9 +213,7 @@ export function AddLayerButton({ isLoadLibraryVisible={isLoadLibraryVisible} setLoadLibraryFlyoutVisible={setLoadLibraryFlyoutVisible} eventAnnotationService={eventAnnotationService} - addLayer={(loadedGroupInfo) => { - addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); - }} + addLayer={onAddLayerFromAnnotationGroup} /> )} diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index ff3cb3225c1f8..e18851262deac 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -689,7 +689,18 @@ export const getXyVisualization = ({ ); }, getAddLayerButtonComponent: (props) => { - return ; + return ( + { + if (loadedGroupInfo.dataViewSpec) { + await props.addIndexPatternFromDataViewSpec(loadedGroupInfo.dataViewSpec); + } + props.addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); + }} + /> + ); }, toExpression: (state, layers, attributes, datasourceExpressionsByLayers = {}) => toExpression( From ffd9a1da7b66e9e0ecdccc56b7f42f54cc7286fb Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 29 Mar 2023 15:34:33 -0500 Subject: [PATCH 035/202] fix some types --- .../editor_frame/data_panel_wrapper.test.tsx | 7 ++++++- .../editor_frame/data_panel_wrapper.tsx | 11 +++++++---- .../editor_frame/editor_frame.test.tsx | 2 ++ .../lens/public/editor_frame_service/service.tsx | 1 + .../lens/public/embeddable/embeddable_factory.ts | 10 +++------- .../lens/public/mocks/data_views_service_mock.ts | 3 --- x-pack/plugins/lens/public/plugin.ts | 6 +++++- 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx index 500895887a239..b2ac0786e3302 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.test.tsx @@ -15,6 +15,7 @@ import { disableAutoApply } from '../../state_management/lens_slice'; import { selectTriggerApplyChanges } from '../../state_management'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { createIndexPatternServiceMock } from '../../mocks/data_views_service_mock'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; describe('Data Panel Wrapper', () => { describe('Datasource data panel properties', () => { @@ -39,7 +40,11 @@ describe('Data Panel Wrapper', () => { core={{} as DatasourceDataPanelProps['core']} dropOntoWorkspace={(field: DragDropIdentifier) => {}} hasSuggestionForField={(field: DragDropIdentifier) => true} - plugins={{ uiActions: {} as UiActionsStart, dataViews: {} as DataViewsPublicPluginStart }} + plugins={{ + uiActions: {} as UiActionsStart, + dataViews: {} as DataViewsPublicPluginStart, + eventAnnotationService: {} as EventAnnotationServiceType, + }} indexPatternService={createIndexPatternServiceMock()} frame={createMockFramePublicAPI()} />, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx index 5dd3c44c091b9..61df1c6f86d56 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx @@ -45,9 +45,12 @@ interface DataPanelWrapperProps { core: DatasourceDataPanelProps['core']; dropOntoWorkspace: (field: DragDropIdentifier) => void; hasSuggestionForField: (field: DragDropIdentifier) => boolean; - plugins: { uiActions: UiActionsStart; dataViews: DataViewsPublicPluginStart }; + plugins: { + uiActions: UiActionsStart; + dataViews: DataViewsPublicPluginStart; + eventAnnotationService: EventAnnotationServiceType; + }; indexPatternService: IndexPatternServiceAPI; - eventAnnotationService: EventAnnotationServiceType; frame: FramePublicAPI; } @@ -82,7 +85,7 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { initializeSources( { datasourceMap: props.datasourceMap, - eventAnnotationService: props.eventAnnotationService, + eventAnnotationService: props.plugins.eventAnnotationService, visualizationMap: props.visualizationMap, visualizationState, datasourceStates, @@ -130,7 +133,7 @@ export const DataPanelWrapper = memo((props: DataPanelWrapperProps) => { dispatchLens, props.plugins.dataViews, props.core.uiSettings, - props.eventAnnotationService, + props.plugins.eventAnnotationService, ]); const onChangeIndexPattern = useCallback( 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 853b1c880f335..01ac257dbcb2d 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 @@ -51,6 +51,7 @@ import { getLensInspectorService } from '../../lens_inspector_service'; import { toExpression } from '@kbn/interpreter'; import { createIndexPatternServiceMock } from '../../mocks/data_views_service_mock'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; function generateSuggestion(state = {}): DatasourceSuggestion { return { @@ -94,6 +95,7 @@ function getDefaultProps() { expressions: expressionsPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), dataViews: wrapDataViewsContract(), + eventAnnotationService: {} as EventAnnotationServiceType, }, palettes: chartPluginMock.createPaletteRegistry(), lensInspector: getLensInspectorService(inspectorPluginMock.createStartContract()), 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 1d183fbe196c2..939cd9da38c4b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -51,6 +51,7 @@ export interface EditorFrameStartPlugins { expressions: ExpressionsStart; charts: ChartsPluginSetup; dataViews: DataViewsPublicPluginStart; + eventAnnotationService: EventAnnotationServiceType; } export interface EditorFramePlugins { diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts index f57e532bc1380..c40ec33212ae9 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts @@ -14,7 +14,6 @@ import type { } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; -import { Ast } from '@kbn/interpreter'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { DataPublicPluginStart, FilterManager, TimefilterContract } from '@kbn/data-plugin/public'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; @@ -32,7 +31,8 @@ import type { Document } from '../persistence/saved_object_store'; import type { LensAttributeService } from '../lens_attribute_service'; import { DOC_TYPE } from '../../common/constants'; import { extract, inject } from '../../common/embeddable_factory'; -import type { DatasourceMap, IndexPatternMap, IndexPatternRef, VisualizationMap } from '../types'; +import type { DatasourceMap, VisualizationMap } from '../types'; +import type { DocumentToExpressionReturnType } from '../editor_frame_service/editor_frame'; export interface LensEmbeddableStartServices { data: DataPublicPluginStart; @@ -46,11 +46,7 @@ export interface LensEmbeddableStartServices { dataViews: DataViewsContract; uiActions?: UiActionsStart; usageCollection?: UsageCollectionSetup; - documentToExpression: (doc: Document) => Promise<{ - ast: Ast | null; - indexPatterns: IndexPatternMap; - indexPatternRefs: IndexPatternRef[]; - }>; + documentToExpression: (doc: Document) => Promise; injectFilterReferences: FilterManager['inject']; visualizationMap: VisualizationMap; datasourceMap: DatasourceMap; diff --git a/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts b/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts index 2846e46a27d6c..bc906436f393c 100644 --- a/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts +++ b/x-pack/plugins/lens/public/mocks/data_views_service_mock.ts @@ -6,7 +6,6 @@ */ import { coreMock } from '@kbn/core/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { @@ -19,14 +18,12 @@ export function createIndexPatternServiceMock({ core = coreMock.createStart(), dataViews = dataViewPluginMocks.createStartContract(), uiActions = uiActionsPluginMock.createStartContract(), - data = dataPluginMock.createStartContract(), updateIndexPatterns = jest.fn(), replaceIndexPattern = jest.fn(), }: Partial = {}): IndexPatternServiceAPI { return createIndexPatternService({ core, dataViews, - data, updateIndexPatterns, replaceIndexPattern, uiActions, diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 3151fd22ddd76..256ca738eedf2 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -33,7 +33,10 @@ import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public' import type { UrlForwardingSetup } from '@kbn/url-forwarding-plugin/public'; import type { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/public'; import type { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public'; -import type { EventAnnotationPluginStart } from '@kbn/event-annotation-plugin/public'; +import type { + EventAnnotationPluginStart, + EventAnnotationServiceType, +} from '@kbn/event-annotation-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; @@ -149,6 +152,7 @@ export interface LensPluginStartDependencies { usageCollection?: UsageCollectionStart; docLinks: DocLinksStart; share?: SharePluginStart; + eventAnnotationService: EventAnnotationServiceType; } export interface LensPublicSetup { From dda256c72bcc06a250f5e44879df14e1446820fe Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 30 Mar 2023 14:49:13 -0500 Subject: [PATCH 036/202] add annotation listing page v1 --- src/plugins/event_annotation/common/types.ts | 3 + src/plugins/event_annotation/kibana.jsonc | 7 +- .../public/components/list_view.test.tsx | 69 ++++++++++++++ .../public/components/list_view.tsx | 86 ++++++++++++++++++ .../__snapshots__/service.test.ts.snap | 60 +++++++++++++ .../event_annotation_service/service.test.ts | 43 +++++++++ .../event_annotation_service/service.tsx | 89 +++++++++++++++---- .../public/event_annotation_service/types.ts | 8 ++ src/plugins/event_annotation/public/plugin.ts | 46 +++++++++- .../event_annotation/public/render_app.tsx | 67 ++++++++++++++ 10 files changed, 460 insertions(+), 18 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/list_view.test.tsx create mode 100644 src/plugins/event_annotation/public/components/list_view.tsx create mode 100644 src/plugins/event_annotation/public/render_app.tsx diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index b3e704ef647e5..ed94031a164c0 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; import { DataViewSpec, KibanaQueryOutput } from '@kbn/data-plugin/common'; import { DatatableColumn } from '@kbn/expressions-plugin/common'; import { $Values } from '@kbn/utility-types'; @@ -101,6 +102,8 @@ export interface EventAnnotationGroupConfig { dataViewSpec?: DataViewSpec; } +export type EventAnnotationGroupContent = UserContentCommonSchema; + export type EventAnnotationArgs = | ManualPointEventAnnotationArgs | ManualRangeEventAnnotationArgs diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index b4df5edf135af..4fb91af6e0c19 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -11,10 +11,15 @@ "expressions", "savedObjectsManagement", "data", + "presentationUtil", + ], + "optionalPlugins": [ + "savedObjectsTagging", ], "requiredBundles": [ "savedObjectsFinder", - "dataViews" + "dataViews", + "kibanaReact" ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/components/list_view.test.tsx b/src/plugins/event_annotation/public/components/list_view.test.tsx new file mode 100644 index 0000000000000..2457e78175a9c --- /dev/null +++ b/src/plugins/event_annotation/public/components/list_view.test.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 React from 'react'; +import { + EventAnnotationGroupListView, + SAVED_OBJECTS_LIMIT_SETTING, + SAVED_OBJECTS_PER_PAGE_SETTING, +} from './list_view'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; +import { TableListView } from '@kbn/content-management-table-list'; +import { EventAnnotationServiceType } from '../event_annotation_service/types'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import type { ShallowWrapper } from 'enzyme'; + +describe('annotation list view', () => { + let wrapper: ShallowWrapper; + let mockEventAnnotationService: EventAnnotationServiceType; + beforeEach(() => { + mockEventAnnotationService = { + findAnnotationGroupContent: jest.fn(), + } as Partial as EventAnnotationServiceType; + + const mockUiSettings = { + get: jest.fn( + (key) => + ({ + [SAVED_OBJECTS_LIMIT_SETTING]: 30, + [SAVED_OBJECTS_PER_PAGE_SETTING]: 10, + }[key]) + ), + } as Partial as IUiSettingsClient; + + wrapper = shallowWithIntl( + + ); + }); + + it('renders a table list view', () => { + expect(wrapper.debug()).toMatchInlineSnapshot( + `""` + ); + }); + + it('searches for items', () => { + const searchQuery = 'My Search Query'; + const references = [{ id: 'first_id', type: 'sometype' }]; + const referencesToExclude = [{ id: 'second_id', type: 'sometype' }]; + wrapper.find(TableListView).prop('findItems')(searchQuery, { + references, + referencesToExclude, + }); + + expect(mockEventAnnotationService.findAnnotationGroupContent).toHaveBeenCalledWith( + 'My Search Query', + 30, + [{ id: 'first_id', type: 'sometype' }], + [{ id: 'second_id', type: 'sometype' }] + ); + }); +}); diff --git a/src/plugins/event_annotation/public/components/list_view.tsx b/src/plugins/event_annotation/public/components/list_view.tsx new file mode 100644 index 0000000000000..d306c2cca026d --- /dev/null +++ b/src/plugins/event_annotation/public/components/list_view.tsx @@ -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 React, { useCallback } from 'react'; +import { TableListView } from '@kbn/content-management-table-list'; +import { i18n } from '@kbn/i18n'; +import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; +import type { EventAnnotationServiceType } from '../event_annotation_service/types'; + +export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; +export const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; + +export const EventAnnotationGroupListView = ({ + uiSettings, + eventAnnotationService, +}: { + uiSettings: IUiSettingsClient; + eventAnnotationService: EventAnnotationServiceType; +}) => { + const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); + const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + + const fetchItems = useCallback( + ( + searchTerm: string, + { + references, + referencesToExclude, + }: { + references?: SavedObjectsFindOptionsReference[]; + referencesToExclude?: SavedObjectsFindOptionsReference[]; + } = {} + ) => { + // todo - allow page size changes + return eventAnnotationService.findAnnotationGroupContent( + searchTerm, + listingLimit, // TODO is this right? + references, + referencesToExclude + ); + }, + [eventAnnotationService, listingLimit] + ); + + return ( + {}} + // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => + // getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) + // } + /> + ); +}; diff --git a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap index 73073c0e7ea16..cc1585cd3668d 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap +++ b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap @@ -1,5 +1,65 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Event Annotation Service findAnnotationGroupContent should retrieve saved objects and format them 1`] = ` +Object { + "hits": Array [ + Object { + "attributes": Object { + "description": undefined, + "title": undefined, + }, + "id": "nonExistingGroup", + "references": Array [], + "type": undefined, + "updatedAt": "", + }, + Object { + "attributes": Object { + "description": "", + "title": "groupTitle", + }, + "id": undefined, + "references": Array [ + Object { + "id": "ipid", + "name": "ipid", + "type": "index-pattern", + }, + ], + "type": "event-annotation-group", + "updatedAt": "", + }, + Object { + "attributes": Object { + "description": undefined, + "title": "groupTitle", + }, + "id": "multiAnnotations", + "references": Array [ + Object { + "id": "ipid", + "name": "ipid", + "type": "index-pattern", + }, + ], + "type": "event-annotation-group", + "updatedAt": "", + }, + Object { + "attributes": Object { + "description": undefined, + "title": "groupTitle", + }, + "id": "multiAnnotations", + "references": Array [], + "type": "event-annotation-group", + "updatedAt": "", + }, + ], + "total": 10, +} +`; + exports[`Event Annotation Service loadAnnotationGroup should properly load an annotation group with a multiple annotation 1`] = ` Object { "annotations": undefined, diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index c131ca288a88d..761cf30dfd296 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { SavedObjectsFindResponse } from '@kbn/core-saved-objects-api-browser'; import { CoreStart, SimpleSavedObject } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; @@ -138,6 +139,10 @@ describe('Event Annotation Service', () => { const typedId = id as keyof typeof annotationGroupResolveMocks; return annotationGroupResolveMocks[typedId]; }); + (core.savedObjects.client.find as jest.Mock).mockResolvedValue({ + total: 10, + savedObjects: Object.values(annotationGroupResolveMocks), + } as Pick, 'total' | 'savedObjects'>); (core.savedObjects.client.bulkCreate as jest.Mock).mockImplementation(() => { return annotationResolveMocks.multiAnnotations; }); @@ -490,6 +495,44 @@ describe('Event Annotation Service', () => { expect(group.indexPatternId).toBe(group.dataViewSpec?.id); }); }); + describe('findAnnotationGroupContent', () => { + it('should retrieve saved objects and format them', async () => { + const searchTerm = 'my search'; + + const content = await eventAnnotationService.findAnnotationGroupContent(searchTerm, 20, [ + { type: 'mytype', id: '1234' }, + ]); + + expect(content).toMatchSnapshot(); + + expect((core.savedObjects.client.find as jest.Mock).mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "defaultSearchOperator": "AND", + "hasNoReference": undefined, + "hasReference": Array [ + Object { + "id": "1234", + "type": "mytype", + }, + ], + "page": 1, + "perPage": 20, + "search": "my search*", + "searchFields": Array [ + "title^3", + "description", + ], + "type": Array [ + "event-annotation-group", + ], + }, + ], + ] + `); + }); + }); // describe.skip('deleteAnnotationGroup', () => { // it('deletes annotation group along with annotations that reference them', async () => { // await eventAnnotationService.deleteAnnotationGroup('multiAnnotations'); diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index e97fbd605c8d5..593fc718f0bb3 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -10,10 +10,18 @@ import React from 'react'; import { partition } from 'lodash'; import { queryToAst } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; -import { CoreStart, SavedObjectReference, SavedObjectsClientContract } from '@kbn/core/public'; +import { + CoreStart, + SavedObjectReference, + SavedObjectsClientContract, + SavedObjectsFindOptions, + SavedObjectsFindOptionsReference, + SimpleSavedObject, +} from '@kbn/core/public'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; +import { EventAnnotationGroupContent } from '../../common/types'; import { EventAnnotationConfig, EventAnnotationGroupAttributes, @@ -40,6 +48,44 @@ export function getEventAnnotationService( ): EventAnnotationServiceType { const client: SavedObjectsClientContract = core.savedObjects.client; + const mapSavedObjectToGroupConfig = ( + savedObject: SimpleSavedObject + ): EventAnnotationGroupConfig => { + const adHocDataViewSpec = savedObject.attributes.dataViewSpec + ? DataViewPersistableStateService.inject( + savedObject.attributes.dataViewSpec, + savedObject.references + ) + : undefined; + + return { + title: savedObject.attributes.title, + description: savedObject.attributes.description, + tags: savedObject.attributes.tags, + ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters, + indexPatternId: adHocDataViewSpec + ? adHocDataViewSpec.id! + : savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!, + annotations: savedObject.attributes.annotations, + dataViewSpec: adHocDataViewSpec, + }; + }; + + const mapSavedObjectToGroupContent = ( + savedObject: SimpleSavedObject + ): EventAnnotationGroupContent => { + return { + id: savedObject.id, + references: savedObject.references, + type: savedObject.type, + updatedAt: savedObject.updatedAt ? savedObject.updatedAt : '', + attributes: { + title: savedObject.attributes.title, + description: savedObject.attributes.description, + }, + }; + }; + const loadAnnotationGroup = async ( savedObjectId: string ): Promise => { @@ -64,23 +110,33 @@ export function getEventAnnotationService( // .filter(({ error }) => !error) // .map((annotation) => annotation.attributes); - const adHocDataViewSpec = savedObject.attributes.dataViewSpec - ? DataViewPersistableStateService.inject( - savedObject.attributes.dataViewSpec, - savedObject.references - ) - : undefined; + return mapSavedObjectToGroupConfig(savedObject); + }; + + const findAnnotationGroupContent = async ( + searchTerm: string, + pageSize: number, + references?: SavedObjectsFindOptionsReference[], + referencesToExclude?: SavedObjectsFindOptionsReference[] + ): Promise<{ total: number; hits: EventAnnotationGroupContent[] }> => { + const searchOptions: SavedObjectsFindOptions = { + type: [EVENT_ANNOTATION_GROUP_TYPE], + searchFields: ['title^3', 'description'], + search: searchTerm ? `${searchTerm}*` : undefined, + perPage: pageSize, + page: 1, + defaultSearchOperator: 'AND' as const, + hasReference: references, + hasNoReference: referencesToExclude, + }; + + const { total, savedObjects } = await client.find( + searchOptions + ); return { - title: savedObject.attributes.title, - description: savedObject.attributes.description, - tags: savedObject.attributes.tags, - ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters, - indexPatternId: adHocDataViewSpec - ? adHocDataViewSpec.id! - : savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!, - annotations: savedObject.attributes.annotations, - dataViewSpec: adHocDataViewSpec, + total, + hits: savedObjects.map(mapSavedObjectToGroupContent), }; }; @@ -194,6 +250,7 @@ export function getEventAnnotationService( return { loadAnnotationGroup, + findAnnotationGroupContent, // updateAnnotations, updateAnnotationGroup, createAnnotationGroup, diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index aaeba1d3dbaa0..635a056859237 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -8,10 +8,18 @@ import { ExpressionAstExpression } from '@kbn/expressions-plugin/common/ast'; import { SavedObjectCommon } from '@kbn/saved-objects-plugin/common'; +import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; +import { EventAnnotationGroupContent } from '../../common/types'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; export interface EventAnnotationServiceType { loadAnnotationGroup: (savedObjectId: string) => Promise; + findAnnotationGroupContent: ( + searchTerm: string, + pageSize: number, + references?: SavedObjectsFindOptionsReference[], + referencesToExclude?: SavedObjectsFindOptionsReference[] + ) => Promise<{ total: number; hits: EventAnnotationGroupContent[] }>; // deleteAnnotationGroup: (savedObjectId: string) => Promise; createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; // updateAnnotations: ( diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 4d390f308a474..d8acca503d1d9 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -6,7 +6,15 @@ * Side Public License, v 1. */ -import { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; +import { + type Plugin, + type CoreSetup, + type CoreStart, + DEFAULT_APP_CATEGORIES, + type AppMountParameters, +} from '@kbn/core/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -18,10 +26,13 @@ import { eventAnnotationGroup, } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; +import type { EventAnnotationAppServices } from './render_app'; export interface EventAnnotationStartDependencies { savedObjectsManagement: SavedObjectsManagementPluginStart; data: DataPublicPluginStart; + savedObjectsTagging: SavedObjectTaggingPluginStart; + presentationUtil: PresentationUtilPluginStart; } interface SetupDependencies { @@ -47,6 +58,39 @@ export class EventAnnotationPlugin dependencies.expressions.registerFunction( getFetchEventAnnotations({ getStartServices: core.getStartServices }) ); + + core.application.register({ + id: 'annotations', + title: 'Event Annotation Library', + order: 8000, + euiIconType: 'logoKibana', + defaultPath: '#/', + category: DEFAULT_APP_CATEGORIES.kibana, + mount: async (params: AppMountParameters) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + + const eventAnnotationService = await new EventAnnotationService( + coreStart, + pluginsStart.savedObjectsManagement + ).getService(); + + const services: EventAnnotationAppServices = { + core: coreStart, + history: params.history, + savedObjectsTagging: pluginsStart.savedObjectsTagging, + eventAnnotationService, + PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, + }; + + const { renderApp } = await import('./render_app'); + + const unmount = renderApp(params, services); + + return () => { + unmount(); + }; + }, + }); } public start( diff --git a/src/plugins/event_annotation/public/render_app.tsx b/src/plugins/event_annotation/public/render_app.tsx new file mode 100644 index 0000000000000..14714696a46dc --- /dev/null +++ b/src/plugins/event_annotation/public/render_app.tsx @@ -0,0 +1,67 @@ +/* + * 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, { FC } from 'react'; +import { AppMountParameters, ScopedHistory } from '@kbn/core-application-browser'; +import { KibanaThemeProvider, toMountPoint } from '@kbn/kibana-react-plugin/public'; +import ReactDOM from 'react-dom'; +import { FormattedRelative } from '@kbn/i18n-react'; +import { Router } from 'react-router-dom'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { EventAnnotationGroupListView } from './components/list_view'; +import { EventAnnotationServiceType } from './event_annotation_service/types'; + +export interface EventAnnotationAppServices { + history: ScopedHistory; + core: CoreStart; + savedObjectsTagging: SavedObjectsTaggingApi; + eventAnnotationService: EventAnnotationServiceType; + PresentationUtilContextProvider: FC; + // setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; + // savedObjectsTagging?: SavedObjectsTaggingApi; +} + +export const renderApp = ( + { element }: AppMountParameters, + services: EventAnnotationAppServices +) => { + // // add readonly badge if saving restricted + // if (!services.visualizeCapabilities.save) { + // addBadgeToAppChrome(services.chrome); + // } + + const app = ( + + + + + + + + + + + + ); + + ReactDOM.render(app, element); + + return () => ReactDOM.unmountComponentAtNode(element); +}; From 8f8b118f13f36c554251b66e07d6f8dace3a0631 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 31 Mar 2023 10:42:58 -0500 Subject: [PATCH 037/202] add delete capabilities --- .../public/components/list_view.test.tsx | 67 +++++++++++++++++-- .../public/components/list_view.tsx | 12 +++- .../event_annotation_service/service.test.ts | 19 +++--- .../event_annotation_service/service.tsx | 29 ++++---- .../public/event_annotation_service/types.ts | 2 +- .../event_annotation/public/render_app.tsx | 1 + 6 files changed, 94 insertions(+), 36 deletions(-) diff --git a/src/plugins/event_annotation/public/components/list_view.test.tsx b/src/plugins/event_annotation/public/components/list_view.test.tsx index 2457e78175a9c..dda307a85258d 100644 --- a/src/plugins/event_annotation/public/components/list_view.test.tsx +++ b/src/plugins/event_annotation/public/components/list_view.test.tsx @@ -12,18 +12,19 @@ import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING, } from './list_view'; -import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { TableListView } from '@kbn/content-management-table-list'; import { EventAnnotationServiceType } from '../event_annotation_service/types'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; -import type { ShallowWrapper } from 'enzyme'; +import { shallow, ShallowWrapper } from 'enzyme'; +import { EVENT_ANNOTATION_GROUP_TYPE } from '../../common'; describe('annotation list view', () => { - let wrapper: ShallowWrapper; + let wrapper: ShallowWrapper; let mockEventAnnotationService: EventAnnotationServiceType; beforeEach(() => { mockEventAnnotationService = { findAnnotationGroupContent: jest.fn(), + deleteAnnotationGroups: jest.fn(), } as Partial as EventAnnotationServiceType; const mockUiSettings = { @@ -36,21 +37,24 @@ describe('annotation list view', () => { ), } as Partial as IUiSettingsClient; - wrapper = shallowWithIntl( + wrapper = shallow( ); }); it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot( - `""` + `""` ); }); - it('searches for items', () => { + it('searches for groups', () => { const searchQuery = 'My Search Query'; const references = [{ id: 'first_id', type: 'sometype' }]; const referencesToExclude = [{ id: 'second_id', type: 'sometype' }]; @@ -66,4 +70,55 @@ describe('annotation list view', () => { [{ id: 'second_id', type: 'sometype' }] ); }); + + describe('deleting groups', () => { + it('prevent deleting when user is missing perms', () => { + wrapper.setProps({ visualizeCapabilities: { delete: false } }); + + expect(wrapper.find(TableListView).prop('deleteItems')).toBeUndefined(); + }); + + it('deletes groups using the service', () => { + expect(wrapper.find(TableListView).prop('deleteItems')).toBeDefined(); + + wrapper.find(TableListView).prop('deleteItems')!([ + { + id: 'some-id-1', + references: [ + { + type: 'index-pattern', + name: 'metrics-*', + id: 'metrics-*', + }, + ], + type: EVENT_ANNOTATION_GROUP_TYPE, + updatedAt: '', + attributes: { + title: 'group1', + }, + }, + { + id: 'some-id-2', + references: [], + type: EVENT_ANNOTATION_GROUP_TYPE, + updatedAt: '', + attributes: { + title: 'group2', + }, + }, + ]); + + expect((mockEventAnnotationService.deleteAnnotationGroups as jest.Mock).mock.calls) + .toMatchInlineSnapshot(` + Array [ + Array [ + Array [ + "some-id-1", + "some-id-2", + ], + ], + ] + `); + }); + }); }); diff --git a/src/plugins/event_annotation/public/components/list_view.tsx b/src/plugins/event_annotation/public/components/list_view.tsx index d306c2cca026d..0775692d90ca3 100644 --- a/src/plugins/event_annotation/public/components/list_view.tsx +++ b/src/plugins/event_annotation/public/components/list_view.tsx @@ -19,9 +19,11 @@ export const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; export const EventAnnotationGroupListView = ({ uiSettings, eventAnnotationService, + visualizeCapabilities, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; + visualizeCapabilities: Record>; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -56,7 +58,11 @@ export const EventAnnotationGroupListView = ({ // for data exploration purposes // createItem={createNewGroup} findItems={fetchItems} - // deleteItems={visualizeCapabilities.delete ? deleteItems : undefined} + deleteItems={ + visualizeCapabilities.delete + ? (items) => eventAnnotationService.deleteAnnotationGroups(items.map(({ id }) => id)) + : undefined + } // editItem={visualizeCapabilities.save ? editItem : undefined} // customTableColumn={getCustomColumn()} listingLimit={listingLimit} @@ -69,10 +75,10 @@ export const EventAnnotationGroupListView = ({ // }} // emptyPrompt={noItemsFragment} entityName={i18n.translate('eventAnnotation.tableList.entityName', { - defaultMessage: 'visualization', + defaultMessage: 'annotation group', })} entityNamePlural={i18n.translate('eventAnnotation.tableList.entityNamePlural', { - defaultMessage: 'visualizations', + defaultMessage: 'annotation groups', })} tableListTitle={i18n.translate('eventAnnotation.tableList.listTitle', { defaultMessage: 'Annotation Library', diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index 761cf30dfd296..b7569e7e11248 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -533,16 +533,15 @@ describe('Event Annotation Service', () => { `); }); }); - // describe.skip('deleteAnnotationGroup', () => { - // it('deletes annotation group along with annotations that reference them', async () => { - // await eventAnnotationService.deleteAnnotationGroup('multiAnnotations'); - // expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ - // { id: 'multiAnnotations', type: 'event-annotation-group' }, - // { id: 'annotation1', type: 'event-annotation' }, - // { id: 'annotation2', type: 'event-annotation' }, - // ]); - // }); - // }); + describe('deleteAnnotationGroups', () => { + it('deletes annotation group along with annotations that reference them', async () => { + await eventAnnotationService.deleteAnnotationGroups(['id1', 'id2']); + expect(core.savedObjects.client.bulkDelete).toHaveBeenCalledWith([ + { id: 'id1', type: 'event-annotation-group' }, + { id: 'id2', type: 'event-annotation-group' }, + ]); + }); + }); describe('createAnnotationGroup', () => { it('creates annotation group along with annotations', async () => { const annotations = [ diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 593fc718f0bb3..d826a45fd8a22 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -140,21 +140,18 @@ export function getEventAnnotationService( }; }; - // const deleteAnnotationGroup = async (savedObjectId: string): Promise => { - // const annotationsSOs = ( - // await client.find({ - // type: EVENT_ANNOTATION_TYPE, - // hasReference: { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // }, - // }) - // ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); - // await client.bulkDelete([ - // { type: EVENT_ANNOTATION_GROUP_TYPE, id: savedObjectId }, - // ...annotationsSOs, - // ]); - // }; + const deleteAnnotationGroups = async (ids: string[]): Promise => { + // const annotationsSOs = ( + // await client.find({ + // type: EVENT_ANNOTATION_TYPE, + // hasReference: { + // type: EVENT_ANNOTATION_GROUP_TYPE, + // id: savedObjectId, + // }, + // }) + // ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); + await client.bulkDelete([...ids.map((id) => ({ type: EVENT_ANNOTATION_GROUP_TYPE, id }))]); + }; const extractDataViewInformation = (group: EventAnnotationGroupConfig) => { let { dataViewSpec = null } = group; @@ -254,7 +251,7 @@ export function getEventAnnotationService( // updateAnnotations, updateAnnotationGroup, createAnnotationGroup, - // deleteAnnotationGroup, + deleteAnnotationGroups, renderEventAnnotationGroupSavedObjectFinder: (props: { fixedPageSize: number; onChoose: (value: { diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index 635a056859237..80323b7020c83 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -20,7 +20,7 @@ export interface EventAnnotationServiceType { references?: SavedObjectsFindOptionsReference[], referencesToExclude?: SavedObjectsFindOptionsReference[] ) => Promise<{ total: number; hits: EventAnnotationGroupContent[] }>; - // deleteAnnotationGroup: (savedObjectId: string) => Promise; + deleteAnnotationGroups: (ids: string[]) => Promise; createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; // updateAnnotations: ( // savedObjectId: string, diff --git a/src/plugins/event_annotation/public/render_app.tsx b/src/plugins/event_annotation/public/render_app.tsx index 14714696a46dc..811f842288faf 100644 --- a/src/plugins/event_annotation/public/render_app.tsx +++ b/src/plugins/event_annotation/public/render_app.tsx @@ -53,6 +53,7 @@ export const renderApp = ( From b00cf551f64a55fe1e00565ccf4da7d0f7471479 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 31 Mar 2023 12:07:48 -0500 Subject: [PATCH 038/202] store tags as references --- src/plugins/event_annotation/common/types.ts | 3 +- .../__snapshots__/service.test.ts.snap | 7 ++- .../event_annotation_service/service.test.ts | 28 +++++++++-- .../event_annotation_service/service.tsx | 49 +++++++++++-------- 4 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index ed94031a164c0..502b99c5c21cc 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -86,10 +86,9 @@ export type EventAnnotationConfig = export interface EventAnnotationGroupAttributes { title: string; description: string; - tags: string[]; ignoreGlobalFilters: boolean; annotations: EventAnnotationConfig[]; - dataViewSpec?: DataViewSpec; + dataViewSpec?: DataViewSpec | null; } export interface EventAnnotationGroupConfig { diff --git a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap index cc1585cd3668d..d59cc3acc0d6d 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap +++ b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap @@ -25,6 +25,11 @@ Object { "name": "ipid", "type": "index-pattern", }, + Object { + "id": "some-tag", + "name": "some-tag", + "type": "tag", + }, ], "type": "event-annotation-group", "updatedAt": "", @@ -67,7 +72,7 @@ Object { "description": undefined, "ignoreGlobalFilters": undefined, "indexPatternId": "ipid", - "tags": undefined, + "tags": Array [], "title": "groupTitle", } `; diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index b7569e7e11248..2f882944f5db1 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -42,6 +42,11 @@ const annotationGroupResolveMocks: Record = name: 'ipid', type: 'index-pattern', }, + { + id: 'some-tag', + name: 'some-tag', + type: 'tag', + }, ], } as Partial as AnnotationGroupSavedObject, multiAnnotations: { @@ -479,7 +484,9 @@ describe('Event Annotation Service', () => { "description": "", "ignoreGlobalFilters": false, "indexPatternId": "ipid", - "tags": Array [], + "tags": Array [ + "some-tag", + ], "title": "groupTitle", } `); @@ -551,7 +558,7 @@ describe('Event Annotation Service', () => { await eventAnnotationService.createAnnotationGroup({ title: 'newGroupTitle', description: 'my description', - tags: ['my', 'many', 'tags'], + tags: ['tag1', 'tag2', 'tag3'], indexPatternId: 'ipid', ignoreGlobalFilters: false, annotations, @@ -561,7 +568,6 @@ describe('Event Annotation Service', () => { { title: 'newGroupTitle', description: 'my description', - tags: ['my', 'many', 'tags'], ignoreGlobalFilters: false, dataViewSpec: null, annotations, @@ -573,6 +579,21 @@ describe('Event Annotation Service', () => { name: 'event-annotation-group_dataView-ref-ipid', type: 'index-pattern', }, + { + id: 'tag1', + name: 'tag1', + type: 'tag', + }, + { + id: 'tag2', + name: 'tag2', + type: 'tag', + }, + { + id: 'tag3', + name: 'tag3', + type: 'tag', + }, ], } ); @@ -597,7 +618,6 @@ describe('Event Annotation Service', () => { { title: 'newTitle', description: '', - tags: [], annotations: [], dataViewSpec: null, ignoreGlobalFilters: false, diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index d826a45fd8a22..25615de9229d3 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -61,7 +61,7 @@ export function getEventAnnotationService( return { title: savedObject.attributes.title, description: savedObject.attributes.description, - tags: savedObject.attributes.tags, + tags: savedObject.references.filter((ref) => ref.type === 'tag').map(({ id }) => id), ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters, indexPatternId: adHocDataViewSpec ? adHocDataViewSpec.id! @@ -181,20 +181,35 @@ export function getEventAnnotationService( return { references, dataViewSpec }; }; - const createAnnotationGroup = async ( + const getAnnotationGroupAttributesAndReferences = ( group: EventAnnotationGroupConfig - ): Promise<{ id: string }> => { + ): { attributes: EventAnnotationGroupAttributes; references: SavedObjectReference[] } => { const { references, dataViewSpec } = extractDataViewInformation(group); const { title, description, tags, ignoreGlobalFilters, annotations } = group; + references.push( + ...tags.map((tag) => ({ + id: tag, + name: tag, + type: 'tag', + })) + ); + + return { + attributes: { title, description, ignoreGlobalFilters, annotations, dataViewSpec }, + references, + }; + }; + + const createAnnotationGroup = async ( + group: EventAnnotationGroupConfig + ): Promise<{ id: string }> => { + const { attributes, references } = getAnnotationGroupAttributesAndReferences(group); + const groupSavedObjectId = ( - await client.create( - EVENT_ANNOTATION_GROUP_TYPE, - { title, description, tags, ignoreGlobalFilters, annotations, dataViewSpec }, - { - references, - } - ) + await client.create(EVENT_ANNOTATION_GROUP_TYPE, attributes, { + references, + }) ).id; return { id: groupSavedObjectId }; @@ -204,17 +219,11 @@ export function getEventAnnotationService( group: EventAnnotationGroupConfig, annotationGroupId: string ): Promise => { - const { references, dataViewSpec } = extractDataViewInformation(group); - const { title, description, tags, ignoreGlobalFilters, annotations } = group; + const { attributes, references } = getAnnotationGroupAttributesAndReferences(group); - await client.update( - EVENT_ANNOTATION_GROUP_TYPE, - annotationGroupId, - { title, description, tags, ignoreGlobalFilters, annotations, dataViewSpec }, - { - references, - } - ); + await client.update(EVENT_ANNOTATION_GROUP_TYPE, annotationGroupId, attributes, { + references, + }); }; // const updateAnnotations = async ( From 32d17c4ab9e805156152e968565f4bb4bf9ad3fd Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 31 Mar 2023 15:00:22 -0500 Subject: [PATCH 039/202] finish link from save toast --- .../event_annotation/common/constants.ts | 2 + src/plugins/event_annotation/common/index.ts | 2 +- .../public/components/list_view.tsx | 74 +++++++++---------- src/plugins/event_annotation/public/plugin.ts | 3 +- .../xy/annotations/actions/index.ts | 2 + .../xy/annotations/actions/save_action.tsx | 23 +++++- .../apps/lens/group3/annotations.ts | 3 +- 7 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts index 04255cee00c22..08c73c946c355 100644 --- a/src/plugins/event_annotation/common/constants.ts +++ b/src/plugins/event_annotation/common/constants.ts @@ -25,3 +25,5 @@ export const AvailableAnnotationIcons = { } as const; export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; + +export const ANNOTATION_LIBRARY_APP_ID = 'annotations'; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index f7a62d4f3918a..106632d2049ca 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -36,4 +36,4 @@ export type { EventAnnotationGroupAttributes, } from './types'; -export { EVENT_ANNOTATION_GROUP_TYPE } from './constants'; +export { EVENT_ANNOTATION_GROUP_TYPE, ANNOTATION_LIBRARY_APP_ID } from './constants'; diff --git a/src/plugins/event_annotation/public/components/list_view.tsx b/src/plugins/event_annotation/public/components/list_view.tsx index 0775692d90ca3..5c214c323e598 100644 --- a/src/plugins/event_annotation/public/components/list_view.tsx +++ b/src/plugins/event_annotation/public/components/list_view.tsx @@ -51,42 +51,42 @@ export const EventAnnotationGroupListView = ({ ); return ( - eventAnnotationService.deleteAnnotationGroups(items.map(({ id }) => id)) - : undefined - } - // editItem={visualizeCapabilities.save ? editItem : undefined} - // customTableColumn={getCustomColumn()} - listingLimit={listingLimit} - initialPageSize={initialPageSize} - initialFilter={''} - // contentEditor={{ - // isReadonly: !visualizeCapabilities.save, - // onSave: onContentEditorSave, - // customValidators: contentEditorValidators, - // }} - // emptyPrompt={noItemsFragment} - entityName={i18n.translate('eventAnnotation.tableList.entityName', { - defaultMessage: 'annotation group', - })} - entityNamePlural={i18n.translate('eventAnnotation.tableList.entityNamePlural', { - defaultMessage: 'annotation groups', - })} - tableListTitle={i18n.translate('eventAnnotation.tableList.listTitle', { - defaultMessage: 'Annotation Library', - })} - onClickTitle={() => {}} - // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => - // getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) - // } - /> +
+ eventAnnotationService.deleteAnnotationGroups(items.map(({ id }) => id)) + : undefined + } + // editItem={visualizeCapabilities.save ? editItem : undefined} + // customTableColumn={getCustomColumn()} + listingLimit={listingLimit} + initialPageSize={initialPageSize} + initialFilter={''} + // contentEditor={{ + // isReadonly: !visualizeCapabilities.save, + // onSave: onContentEditorSave, + // customValidators: contentEditorValidators, + // }} + // emptyPrompt={noItemsFragment} + entityName={i18n.translate('eventAnnotation.tableList.entityName', { + defaultMessage: 'annotation group', + })} + entityNamePlural={i18n.translate('eventAnnotation.tableList.entityNamePlural', { + defaultMessage: 'annotation groups', + })} + tableListTitle={i18n.translate('eventAnnotation.tableList.listTitle', { + defaultMessage: 'Annotation Library', + })} + onClickTitle={() => {}} + // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => + // getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) + // } + /> +
); }; diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index d8acca503d1d9..a5a3af6c512aa 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -27,6 +27,7 @@ import { } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; import type { EventAnnotationAppServices } from './render_app'; +import { ANNOTATION_LIBRARY_APP_ID } from '../common/constants'; export interface EventAnnotationStartDependencies { savedObjectsManagement: SavedObjectsManagementPluginStart; @@ -60,7 +61,7 @@ export class EventAnnotationPlugin ); core.application.register({ - id: 'annotations', + id: ANNOTATION_LIBRARY_APP_ID, title: 'Event Annotation Library', order: 8000, euiIconType: 'logoKibana', diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index 9ed2729a51a06..b0958a9fde92a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -9,6 +9,7 @@ import type { CoreStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { ANNOTATION_LIBRARY_APP_ID } from '@kbn/event-annotation-plugin/common'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; @@ -54,6 +55,7 @@ export const createAnnotationActions = ({ toasts: core.notifications.toasts, savedObjectsTagging, dataViews, + goToAnnotationLibrary: () => core.application.navigateToApp(ANNOTATION_LIBRARY_APP_ID), }) ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 388bf543b7d79..50f2669d06bdc 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -17,7 +17,7 @@ import { SavedObjectSaveModal, } from '@kbn/saved-objects-plugin/public'; import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; -import { EuiIcon } from '@elastic/eui'; +import { EuiIcon, EuiLink } from '@elastic/eui'; import { type SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; @@ -133,6 +133,7 @@ export const onSave = async ({ toasts, modalOnSaveProps: { newTitle, newDescription, newTags, closeModal, newCopyOnSave }, dataViews, + goToAnnotationLibrary, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -141,6 +142,7 @@ export const onSave = async ({ toasts: ToastsStart; modalOnSaveProps: ModalOnSaveProps; dataViews: DataViewsContract; + goToAnnotationLibrary: () => Promise; }) => { let savedInfo: Awaited>; try { @@ -196,9 +198,21 @@ export const onSave = async ({
annotation library, + link: ( + goToAnnotationLibrary()} + > + {i18n.translate( + 'xpack.lens.xyChart.annotations.saveAnnotationGroupToLibrary.annotationLibrary', + { + defaultMessage: 'annotation library', + } + )} + + ), }} />
, @@ -215,6 +229,7 @@ export const getSaveLayerAction = ({ toasts, savedObjectsTagging, dataViews, + goToAnnotationLibrary, }: { state: XYState; layer: XYAnnotationLayerConfig; @@ -223,6 +238,7 @@ export const getSaveLayerAction = ({ toasts: ToastsStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; dataViews: DataViewsContract; + goToAnnotationLibrary: () => Promise; }): LayerAction => { const neverSaved = !isByReferenceAnnotationsLayer(layer); @@ -249,6 +265,7 @@ export const getSaveLayerAction = ({ toasts, modalOnSaveProps: props, dataViews, + goToAnnotationLibrary, }); }} title={neverSaved ? '' : layer.__lastSaved.title} diff --git a/x-pack/test/functional/apps/lens/group3/annotations.ts b/x-pack/test/functional/apps/lens/group3/annotations.ts index 4bec636f52625..c3e7e660a3754 100644 --- a/x-pack/test/functional/apps/lens/group3/annotations.ts +++ b/x-pack/test/functional/apps/lens/group3/annotations.ts @@ -20,6 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]); const find = getService('find'); const retry = getService('retry'); + const browser = getService('browser'); const toastsService = getService('toasts'); const testSubjects = getService('testSubjects'); const from = 'Sep 19, 2015 @ 06:31:44.000'; @@ -156,7 +157,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const toastContents = await toastsService.getToastContent(1); expect(toastContents).to.be( - `Saved "${ANNOTATION_GROUP_TITLE}"\nView or manage in the annotation library` + `Saved "${ANNOTATION_GROUP_TITLE}"\nView or manage in the annotation library.` ); await PageObjects.lens.save(FIRST_VIS_TITLE); From f17afb5d7ae1b5225248d21f81de19c580676d00 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 31 Mar 2023 15:09:07 -0500 Subject: [PATCH 040/202] don't include fields when saving ad-hoc data view --- .../visualizations/xy/annotations/actions/save_action.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx index 50f2669d06bdc..9d9c42494e506 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.tsx @@ -109,7 +109,7 @@ const saveAnnotationGroupToLibrary = async ( title: newTitle, description: newDescription, tags: newTags, - dataViewSpec: dataView.isPersisted() ? undefined : dataView.toSpec(), + dataViewSpec: dataView.isPersisted() ? undefined : dataView.toSpec(false), }; if (saveAsNew) { From f6b95a3ad5f400fcced73f7bef1ec29e4a80dad2 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 3 Apr 2023 12:20:32 -0500 Subject: [PATCH 041/202] add some missing type references --- src/plugins/event_annotation/tsconfig.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 9738e2722dabe..e360699ed5938 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -21,7 +21,10 @@ "@kbn/ui-theme", "@kbn/saved-objects-plugin", "@kbn/saved-objects-finder-plugin", - "@kbn/saved-objects-management-plugin" + "@kbn/saved-objects-management-plugin", + "@kbn/saved-objects-tagging-plugin", + "@kbn/presentation-util-plugin", + "@kbn/content-management-table-list", ], "exclude": [ "target/**/*", From 09db78747400c6867037f80017df12809a7705d5 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 3 Apr 2023 12:21:19 -0500 Subject: [PATCH 042/202] fix a type problem --- .../visualizations/xy/annotations/actions/save_action.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index 7704d68227572..ffba67d77336d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -155,7 +155,7 @@ describe('annotation group save action', () => { toExpression: jest.fn(), toFetchExpression: jest.fn(), renderEventAnnotationGroupSavedObjectFinder: jest.fn(), - } as EventAnnotationServiceType, + } as Partial as EventAnnotationServiceType, toasts: toastsServiceMock.createStartContract(), modalOnSaveProps: { newTitle: 'my title', From 5c558d2ead1741174c0c1d90e9923d9246eace8b Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 4 Apr 2023 10:41:12 -0500 Subject: [PATCH 043/202] editing basic group metadata --- .../event_annotation_group_editor.test.tsx | 96 +++++++++++++++++ .../event_annotation_group_editor.tsx | 75 +++++++++++++ .../public/components/list_view.test.tsx | 73 ++++++++++++- .../public/components/list_view.tsx | 100 ++++++++++++++++-- .../event_annotation/public/render_app.tsx | 1 + 5 files changed, 331 insertions(+), 14 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx create mode 100644 src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx new file mode 100644 index 0000000000000..6fa8592919b26 --- /dev/null +++ b/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx @@ -0,0 +1,96 @@ +/* + * 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, { ChangeEvent, FormEvent } from 'react'; +import { EventAnnotationGroupConfig } from '../../common'; +import { shallow, ShallowWrapper } from 'enzyme'; +import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; +import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; +import { EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; + +describe('event annotation group editor', () => { + const mockTaggingApi = taggingApiMock.create(); + + const group: EventAnnotationGroupConfig = { + annotations: [], + description: '', + tags: [], + indexPatternId: 'my-index-pattern', + title: 'My group', + ignoreGlobalFilters: false, + }; + it('reports group updates', () => { + const updateMock = jest.fn(); + const wrapper = shallow( + + ); + + (wrapper.find("[data-test-subj='annotationGroupTitle']") as ShallowWrapper).prop( + 'onChange' + )!({ + target: { + value: 'im a new title!', + } as Partial as EventTarget, + } as FormEvent); + + ( + wrapper.find( + "[data-test-subj='annotationGroupDescription']" + ) as ShallowWrapper + ).prop('onChange')!({ + target: { + value: 'im a new description!', + }, + } as ChangeEvent); + + wrapper + .find(mockTaggingApi.ui.components.SavedObjectSaveModalTagSelector) + .prop('onTagsSelected')(['im a new tag!']); + + expect(updateMock.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "annotations": Array [], + "description": "", + "ignoreGlobalFilters": false, + "indexPatternId": "my-index-pattern", + "tags": Array [], + "title": "im a new title!", + }, + ], + Array [ + Object { + "annotations": Array [], + "description": "im a new description!", + "ignoreGlobalFilters": false, + "indexPatternId": "my-index-pattern", + "tags": Array [], + "title": "My group", + }, + ], + Array [ + Object { + "annotations": Array [], + "description": "", + "ignoreGlobalFilters": false, + "indexPatternId": "my-index-pattern", + "tags": Array [ + "im a new tag!", + ], + "title": "My group", + }, + ], + ] + `); + }); +}); diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx new file mode 100644 index 0000000000000..fd87a15880c77 --- /dev/null +++ b/src/plugins/event_annotation/public/components/event_annotation_group_editor.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 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 { EuiFieldText, EuiForm, EuiFormRow, EuiText, EuiTextArea, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import React from 'react'; +import { EventAnnotationGroupConfig } from '../../common'; + +export const EventAnnotationGroupEditor = ({ + group, + update, + savedObjectsTagging, +}: { + group: EventAnnotationGroupConfig; + update: (group: EventAnnotationGroupConfig) => void; + savedObjectsTagging: SavedObjectsTaggingApi; +}) => { + const { euiTheme } = useEuiTheme(); + + return ( + <> + +

Details

+
+ + + + update({ + ...group, + title: value, + }) + } + /> + + + + update({ + ...group, + description: value, + }) + } + /> + + + + update({ + ...group, + tags, + }) + } + /> + + + + ); +}; diff --git a/src/plugins/event_annotation/public/components/list_view.test.tsx b/src/plugins/event_annotation/public/components/list_view.test.tsx index dda307a85258d..8655e2f64f07e 100644 --- a/src/plugins/event_annotation/public/components/list_view.test.tsx +++ b/src/plugins/event_annotation/public/components/list_view.test.tsx @@ -12,19 +12,36 @@ import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING, } from './list_view'; -import { TableListView } from '@kbn/content-management-table-list'; +import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; import { EventAnnotationServiceType } from '../event_annotation_service/types'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { shallow, ShallowWrapper } from 'enzyme'; -import { EVENT_ANNOTATION_GROUP_TYPE } from '../../common'; +import { EventAnnotationGroupConfig, EVENT_ANNOTATION_GROUP_TYPE } from '../../common'; +import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; + +import { EuiButton, EuiFlyout } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; +import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; describe('annotation list view', () => { + const group: EventAnnotationGroupConfig = { + annotations: [], + description: '', + tags: [], + indexPatternId: 'my-index-pattern', + title: 'My group', + ignoreGlobalFilters: false, + }; + let wrapper: ShallowWrapper; let mockEventAnnotationService: EventAnnotationServiceType; + beforeEach(() => { mockEventAnnotationService = { findAnnotationGroupContent: jest.fn(), deleteAnnotationGroups: jest.fn(), + loadAnnotationGroup: jest.fn().mockResolvedValue(group), + updateAnnotationGroup: jest.fn(() => Promise.resolve()), } as Partial as EventAnnotationServiceType; const mockUiSettings = { @@ -40,18 +57,22 @@ describe('annotation list view', () => { wrapper = shallow( ); }); it('renders a table list view', () => { - expect(wrapper.debug()).toMatchInlineSnapshot( - `""` - ); + expect(wrapper.debug()).toMatchInlineSnapshot(` + "
+ +
" + `); }); it('searches for groups', () => { @@ -121,4 +142,46 @@ describe('annotation list view', () => { `); }); }); + + describe('editing groups', () => { + it('prevents editing when user is missing perms', () => { + wrapper.setProps({ visualizeCapabilities: { save: false } }); + + expect(wrapper.find(TableListView).prop('deleteItems')).toBeUndefined(); + }); + + it('edits existing group', async () => { + expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); + + act(() => { + wrapper.find(TableListView).prop('editItem')!({ id: '1234' } as UserContentCommonSchema); + }); + + // wait one tick to give promise time to settle + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(mockEventAnnotationService.loadAnnotationGroup).toHaveBeenCalledWith('1234'); + + expect(wrapper.find(EuiFlyout).exists()).toBeTruthy(); + + const updatedGroup = { ...group, tags: ['my-new-tag'] }; + + wrapper.find(EventAnnotationGroupEditor).prop('update')(updatedGroup); + + ( + wrapper.find('[data-test-subj="saveAnnotationGroup"]') as ShallowWrapper< + Parameters[0] + > + ).prop('onClick')!({} as any); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(mockEventAnnotationService.updateAnnotationGroup).toHaveBeenCalledWith( + updatedGroup, + '1234' + ); + + expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); + }); + }); }); diff --git a/src/plugins/event_annotation/public/components/list_view.tsx b/src/plugins/event_annotation/public/components/list_view.tsx index 5c214c323e598..7f4e15bcbb411 100644 --- a/src/plugins/event_annotation/public/components/list_view.tsx +++ b/src/plugins/event_annotation/public/components/list_view.tsx @@ -6,12 +6,29 @@ * Side Public License, v 1. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { TableListView } from '@kbn/content-management-table-list'; import { i18n } from '@kbn/i18n'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, + htmlIdGenerator, +} from '@elastic/eui'; +import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; +import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; +import { EventAnnotationGroupContent } from '../../common/types'; export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; export const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; @@ -20,10 +37,12 @@ export const EventAnnotationGroupListView = ({ uiSettings, eventAnnotationService, visualizeCapabilities, + savedObjectsTagging, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; visualizeCapabilities: Record>; + savedObjectsTagging: SavedObjectsTaggingApi; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -50,9 +69,67 @@ export const EventAnnotationGroupListView = ({ [eventAnnotationService, listingLimit] ); + const [groupToEditInfo, setGroupToEditInfo] = useState<{ + group: EventAnnotationGroupConfig; + id?: string; + }>(); + + const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); + + const flyout = groupToEditInfo ? ( + setGroupToEditInfo(undefined)} size={'s'}> + + +

Edit annotation group

+
+
+ + + setGroupToEditInfo({ ...groupToEditInfo, group })} + savedObjectsTagging={savedObjectsTagging} + /> + + + + + + setGroupToEditInfo(undefined)}> + + + + + + (groupToEditInfo.id + ? eventAnnotationService.updateAnnotationGroup( + groupToEditInfo.group, + groupToEditInfo.id + ) + : eventAnnotationService.createAnnotationGroup(groupToEditInfo.group) + ).then(() => { + setGroupToEditInfo(undefined); + }) + } + > + + + + + +
+ ) : undefined; + return (
- // we allow users to create visualizations even if they can't save them // for data exploration purposes // createItem={createNewGroup} @@ -62,16 +139,18 @@ export const EventAnnotationGroupListView = ({ ? (items) => eventAnnotationService.deleteAnnotationGroups(items.map(({ id }) => id)) : undefined } - // editItem={visualizeCapabilities.save ? editItem : undefined} + editItem={ + visualizeCapabilities.save + ? ({ id }) => + eventAnnotationService + .loadAnnotationGroup(id) + .then((group) => setGroupToEditInfo({ group, id })) + : undefined + } // customTableColumn={getCustomColumn()} listingLimit={listingLimit} initialPageSize={initialPageSize} initialFilter={''} - // contentEditor={{ - // isReadonly: !visualizeCapabilities.save, - // onSave: onContentEditorSave, - // customValidators: contentEditorValidators, - // }} // emptyPrompt={noItemsFragment} entityName={i18n.translate('eventAnnotation.tableList.entityName', { defaultMessage: 'annotation group', @@ -82,11 +161,14 @@ export const EventAnnotationGroupListView = ({ tableListTitle={i18n.translate('eventAnnotation.tableList.listTitle', { defaultMessage: 'Annotation Library', })} - onClickTitle={() => {}} + onClickTitle={(item) => { + // TODO - what happens if I click here? + }} // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => // getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) // } /> + {flyout}
); }; diff --git a/src/plugins/event_annotation/public/render_app.tsx b/src/plugins/event_annotation/public/render_app.tsx index 811f842288faf..6a7e3e548e382 100644 --- a/src/plugins/event_annotation/public/render_app.tsx +++ b/src/plugins/event_annotation/public/render_app.tsx @@ -51,6 +51,7 @@ export const renderApp = ( }} > Date: Mon, 17 Apr 2023 11:10:49 -0500 Subject: [PATCH 044/202] remove null return --- .../content-management/table_list/src/table_list_view.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 82d0d0f48aecf..7f78f79e846ca 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -986,10 +986,6 @@ export const TableListView = ({ const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); - if (!hasInitialFetchReturned) { - return null; - } - return ( Date: Mon, 17 Apr 2023 11:15:06 -0500 Subject: [PATCH 045/202] update table list view unit test --- .../table_list/src/table_list_view.test.tsx | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index e6428e1612b46..9bd6801e4df3e 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -17,11 +17,7 @@ import type { LocationDescriptor, History } from 'history'; import { WithServices } from './__jest__'; import { getTagList } from './mocks'; -import { - TableList, - TableListProps as TableListProps, - UserContentCommonSchema, -} from './table_list_view'; +import TableListView, { UserContentCommonSchema, type TableListViewProps } from './table_list_view'; const mockUseEffect = useEffect; @@ -49,7 +45,7 @@ interface Router { }; } -const requiredProps: TableListProps = { +const requiredProps: TableListViewProps = { entityName: 'test', entityNamePlural: 'tests', listingLimit: 500, @@ -75,10 +71,13 @@ describe('TableListView', () => { jest.useRealTimers(); }); - const setup = registerTestBed(WithServices(TableList), { - defaultProps: { ...requiredProps }, - memoryRouter: { wrapComponent: true }, - }); + const setup = registerTestBed( + WithServices(TableListView), + { + defaultProps: { ...requiredProps }, + memoryRouter: { wrapComponent: true }, + } + ); test('render default empty prompt', async () => { let testBed: TestBed; @@ -337,8 +336,8 @@ describe('TableListView', () => { }); describe('column sorting', () => { - const setupColumnSorting = registerTestBed( - WithServices(TableList, { + const setupColumnSorting = registerTestBed( + WithServices(TableListView, { TagList: getTagList({ references: [] }), }), { @@ -546,8 +545,8 @@ describe('TableListView', () => { }); describe('content editor', () => { - const setupInspector = registerTestBed( - WithServices(TableList), + const setupInspector = registerTestBed( + WithServices(TableListView), { defaultProps: { ...requiredProps }, memoryRouter: { wrapComponent: true }, @@ -597,8 +596,8 @@ describe('TableListView', () => { }); describe('tag filtering', () => { - const setupTagFiltering = registerTestBed( - WithServices(TableList, { + const setupTagFiltering = registerTestBed( + WithServices(TableListView, { getTagList: () => [ { id: 'id-tag-1', name: 'tag-1', type: 'tag', description: '', color: '' }, { id: 'id-tag-2', name: 'tag-2', type: 'tag', description: '', color: '' }, @@ -749,8 +748,8 @@ describe('TableListView', () => { describe('url state', () => { let router: Router | undefined; - const setupTagFiltering = registerTestBed( - WithServices(TableList, { + const setupTagFiltering = registerTestBed( + WithServices(TableListView, { getTagList: () => [ { id: 'id-tag-1', name: 'tag-1', type: 'tag', description: '', color: '' }, { id: 'id-tag-2', name: 'tag-2', type: 'tag', description: '', color: '' }, From 9bf9feb9b6ab3f30e93f77322d139933242edeb2 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 17 Apr 2023 12:15:38 -0500 Subject: [PATCH 046/202] TabbedTableListView on visualize page --- .../content-management/table_list/index.ts | 3 +- .../table_list/src/index.ts | 5 +- .../table_list/src/table_list_view.tsx | 73 +++++++++++++++++- .../components/visualize_listing.tsx | 74 +++++++++++-------- 4 files changed, 122 insertions(+), 33 deletions(-) diff --git a/packages/content-management/table_list/index.ts b/packages/content-management/table_list/index.ts index b007cb38acd8b..52d3038d19a9c 100644 --- a/packages/content-management/table_list/index.ts +++ b/packages/content-management/table_list/index.ts @@ -8,10 +8,11 @@ export { TableListView, + TabbedTableListView, TableList, TableListViewProvider, TableListViewKibanaProvider, } from './src'; -export type { UserContentCommonSchema } from './src'; +export type { UserContentCommonSchema, TableListTab } from './src'; export type { TableListViewKibanaDependencies } from './src/services'; diff --git a/packages/content-management/table_list/src/index.ts b/packages/content-management/table_list/src/index.ts index 8e56517fb2d63..5436fbd9b699e 100644 --- a/packages/content-management/table_list/src/index.ts +++ b/packages/content-management/table_list/src/index.ts @@ -6,12 +6,13 @@ * Side Public License, v 1. */ -export { TableList, TableListView } from './table_list_view'; +export { TableList, TableListView, TabbedTableListView } from './table_list_view'; export type { - TableListProps as TableListProps, + TableListViewProps, State as TableListViewState, UserContentCommonSchema, + TableListTab, } from './table_list_view'; export { TableListViewProvider, TableListViewKibanaProvider } from './services'; diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 7f78f79e846ca..92fcf818efc60 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -28,6 +28,8 @@ import { CriteriaWithPagination, Query, Ast, + EuiTabs, + EuiTab, } from '@elastic/eui'; import { keyBy, uniq, get } from 'lodash'; import { i18n } from '@kbn/i18n'; @@ -132,6 +134,23 @@ type TableListProps = Pick< | 'withoutPageTemplateWrapper' > & { onFetchSuccess: () => void }; +export interface TableListTab { + title: string; + getTableList: ( + propsFromParent: Pick, 'tableListTitle' | 'onFetchSuccess'> + ) => React.ReactNode; +} + +type TabbedTableListViewProps = Pick< + TableListViewProps, + | 'tableListTitle' + | 'tableListDescription' + | 'additionalRightSideActions' + | 'headingId' + | 'children' + | 'withoutPageTemplateWrapper' +> & { tabs: TableListTab[] }; + export interface State { items: T[]; hasInitialFetchReturned: boolean; @@ -951,7 +970,7 @@ function TableListComp({ ); } -const TableList = React.memo(TableListComp) as typeof TableListComp; +export const TableList = React.memo(TableListComp) as typeof TableListComp; export const TableListView = ({ tableListTitle, @@ -1030,5 +1049,57 @@ export const TableListView = ({ ); }; +export const TabbedTableListView = ({ + tableListTitle, + tableListDescription, + additionalRightSideActions, + headingId, + children, + withoutPageTemplateWrapper, + tabs, +}: TabbedTableListViewProps) => { + const PageTemplate = withoutPageTemplateWrapper + ? (React.Fragment as unknown as typeof KibanaPageTemplate) + : KibanaPageTemplate; + + const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); + + const [selectedTab, setSelectedTab] = useState(0); + + const tableList = useMemo( + () => + tabs[selectedTab].getTableList({ + tableListTitle, + onFetchSuccess: () => {}, + }), + [selectedTab, tableListTitle, tabs] + ); + + return ( + + {tableListTitle}} + description={tableListDescription} + rightSideItems={additionalRightSideActions?.slice(0, 2)} + data-test-subj="top-nav" + > + + {tabs.map((tab, index) => ( + setSelectedTab(index)} isSelected={index === selectedTab}> + {tab.title} + + ))} + + + + {/* Any children passed to the component */} + {children} + + {tableList} + + + ); +}; + // eslint-disable-next-line import/no-default-export export default TableListView; diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index 40606238fee8e..bb21a45b5c22e 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -19,7 +19,11 @@ import { useLocation } from 'react-router-dom'; import type { SavedObjectsFindOptionsReference } from '@kbn/core/public'; import { useKibana, useExecutionContext } from '@kbn/kibana-react-plugin/public'; -import { TableListView } from '@kbn/content-management-table-list'; +import { + TableList, + TabbedTableListView, + type TableListTab, +} from '@kbn/content-management-table-list'; import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; import { findListItems } from '../../utils/saved_visualize_utils'; @@ -289,38 +293,50 @@ export const VisualizeListing = () => { /> ); + const tabs: TableListTab[] = [ + { + title: 'Visualizations', + getTableList: (propsFromParent) => ( + + id="vis" + // we allow users to create visualizations even if they can't save them + // for data exploration purposes + createItem={createNewVis} + findItems={fetchItems} + deleteItems={visualizeCapabilities.delete ? deleteItems : undefined} + editItem={visualizeCapabilities.save ? editItem : undefined} + customTableColumn={getCustomColumn()} + listingLimit={listingLimit} + initialPageSize={initialPageSize} + initialFilter={''} + contentEditor={{ + isReadonly: !visualizeCapabilities.save, + onSave: onContentEditorSave, + customValidators: contentEditorValidators, + }} + emptyPrompt={noItemsFragment} + entityName={i18n.translate('visualizations.listing.table.entityName', { + defaultMessage: 'visualization', + })} + entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { + defaultMessage: 'visualizations', + })} + getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => + getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) + } + {...propsFromParent} + /> + ), + }, + ]; + return ( - - id="vis" + - getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) - } + tabs={tabs} > {dashboardCapabilities.createNew && ( <> @@ -328,6 +344,6 @@ export const VisualizeListing = () => { )} -
+ ); }; From 613c00df0e5b374626b6abf0bba3a514ce160699 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 17 Apr 2023 17:44:48 -0500 Subject: [PATCH 047/202] tabbed table list view --- .../content-management/table_list/index.ts | 2 +- .../table_list/src/components/table.tsx | 2 +- .../table_list/src/index.ts | 9 +- .../src/tabbed_table_list_view.test.tsx | 106 ++++++++++ .../table_list/src/tabbed_table_list_view.tsx | 88 ++++++++ .../table_list/src/table_list_view.test.tsx | 2 +- .../table_list/src/table_list_view.tsx | 94 +-------- src/plugins/event_annotation/kibana.jsonc | 1 + ...list_view.test.tsx => table_list.test.tsx} | 33 +-- .../{list_view.tsx => table_list.tsx} | 27 ++- .../public/get_table_list.tsx | 50 +++++ src/plugins/event_annotation/public/plugin.ts | 69 ++++--- .../event_annotation/public/render_app.tsx | 69 ------- src/plugins/visualizations/public/plugin.ts | 10 +- src/plugins/visualizations/public/types.ts | 3 + .../components/visualize_listing.tsx | 193 +++++++++++------- .../public/visualize_app/types.ts | 3 +- 17 files changed, 474 insertions(+), 287 deletions(-) create mode 100644 packages/content-management/table_list/src/tabbed_table_list_view.test.tsx create mode 100644 packages/content-management/table_list/src/tabbed_table_list_view.tsx rename src/plugins/event_annotation/public/components/{list_view.test.tsx => table_list.test.tsx} (80%) rename src/plugins/event_annotation/public/components/{list_view.tsx => table_list.tsx} (88%) create mode 100644 src/plugins/event_annotation/public/get_table_list.tsx delete mode 100644 src/plugins/event_annotation/public/render_app.tsx diff --git a/packages/content-management/table_list/index.ts b/packages/content-management/table_list/index.ts index 52d3038d19a9c..f65a63e496054 100644 --- a/packages/content-management/table_list/index.ts +++ b/packages/content-management/table_list/index.ts @@ -14,5 +14,5 @@ export { TableListViewKibanaProvider, } from './src'; -export type { UserContentCommonSchema, TableListTab } from './src'; +export type { UserContentCommonSchema, TableListTab, TableListTabParentProps } from './src'; export type { TableListViewKibanaDependencies } from './src/services'; diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 6e4b4e71c2669..8eeca2c04efd2 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -23,7 +23,7 @@ import { useServices } from '../services'; import type { Action } from '../actions'; import type { State as TableListViewState, - TableListProps as TableListProps, + TableListProps, UserContentCommonSchema, } from '../table_list_view'; import { TableSortSelect } from './table_sort_select'; diff --git a/packages/content-management/table_list/src/index.ts b/packages/content-management/table_list/src/index.ts index 5436fbd9b699e..31d887e732a3e 100644 --- a/packages/content-management/table_list/src/index.ts +++ b/packages/content-management/table_list/src/index.ts @@ -6,13 +6,18 @@ * Side Public License, v 1. */ -export { TableList, TableListView, TabbedTableListView } from './table_list_view'; +export { TableList, TableListView } from './table_list_view'; export type { TableListViewProps, State as TableListViewState, UserContentCommonSchema, - TableListTab, } from './table_list_view'; +export { + TabbedTableListView, + type TableListTab, + type TableListTabParentProps, +} from './tabbed_table_list_view'; + export { TableListViewProvider, TableListViewKibanaProvider } from './services'; diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx b/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx new file mode 100644 index 0000000000000..8424d0a72b82d --- /dev/null +++ b/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx @@ -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 React from 'react'; +import { shallow } from 'enzyme'; +import { + TabbedTableListView, + TableListTabParentProps, + TableListTab, +} from './tabbed_table_list_view'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import { EuiTab } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; + +// Mock the necessary props for the component +const title = 'Test Title'; +const description = 'Test Description'; +const headingId = 'test-heading-id'; +const children =
Test Children
; +const tabs: TableListTab[] = [ + { + title: 'Tab 1', + getTableList: async (props: TableListTabParentProps) =>
Test Table List 1
, + }, + { + title: 'Tab 2', + getTableList: async (props: TableListTabParentProps) =>
Test Table List 2
, + }, +]; + +describe('TabbedTableListView', () => { + it('should render without errors', () => { + const wrapper = shallow( + + ); + expect(wrapper.exists()).toBe(true); + }); + + it('should render the correct title and description', () => { + const wrapper = shallow( + + ); + expect(wrapper.find(KibanaPageTemplate.Header).prop('pageTitle')).toMatchInlineSnapshot(` + + Test Title + + `); + expect(wrapper.find(KibanaPageTemplate.Header).prop('description')).toContain(description); + }); + + it('should render the correct number of tabs', () => { + const wrapper = shallow( + + ); + expect(wrapper.find(EuiTab).length).toEqual(tabs.length); + }); + + it('should switch tabs when a tab is clicked', () => { + const wrapper = shallow( + + ); + + const getTab = (index: number) => wrapper.find(EuiTab).at(index); + + expect(getTab(0).prop('isSelected')).toBeTruthy(); + expect(getTab(1).prop('isSelected')).toBeFalsy(); + + act(() => { + getTab(1).simulate('click'); + }); + + expect(getTab(0).prop('isSelected')).toBeFalsy(); + expect(getTab(1).prop('isSelected')).toBeTruthy(); + }); +}); diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.tsx b/packages/content-management/table_list/src/tabbed_table_list_view.tsx new file mode 100644 index 0000000000000..62f067697543f --- /dev/null +++ b/packages/content-management/table_list/src/tabbed_table_list_view.tsx @@ -0,0 +1,88 @@ +/* + * 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 { EuiTab, EuiTabs } from '@elastic/eui'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import React, { useEffect, useState } from 'react'; +import type { + TableListProps, + TableListViewProps, + UserContentCommonSchema, +} from './table_list_view'; + +export type TableListTabParentProps = + Pick, 'onFetchSuccess'>; + +export interface TableListTab { + title: string; + getTableList: ( + propsFromParent: TableListTabParentProps + ) => Promise | React.ReactNode; +} + +type TabbedTableListViewProps = Pick< + TableListViewProps, + 'title' | 'description' | 'headingId' | 'children' +> & { tabs: TableListTab[] }; + +export const TabbedTableListView = ({ + title, + description, + headingId, + children, + tabs, +}: TabbedTableListViewProps) => { + const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); + + const [selectedTab, setSelectedTab] = useState(0); + + const [tableList, setTableList] = useState(null); + + useEffect(() => { + async function loadTableList() { + const newTableList = await tabs[selectedTab].getTableList({ + onFetchSuccess: () => { + if (!hasInitialFetchReturned) { + setHasInitialFetchReturned(true); + } + }, + }); + setTableList(newTableList); + } + + loadTableList(); + }, [hasInitialFetchReturned, selectedTab, tabs]); + + return ( + + {title}} + description={description} + data-test-subj="top-nav" + > + + {tabs.map((tab, index) => ( + setSelectedTab(index)} + isSelected={index === selectedTab} + > + {tab.title} + + ))} + + + + {/* Any children passed to the component */} + {children} + + {tableList} + + + ); +}; diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index 9bd6801e4df3e..93dde8fde3c1c 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -51,7 +51,7 @@ const requiredProps: TableListViewProps = { listingLimit: 500, initialFilter: '', initialPageSize: 20, - tableListTitle: 'test title', + title: 'test title', findItems: jest.fn().mockResolvedValue({ total: 0, hits: [] }), getDetailViewLink: () => 'http://elastic.co', urlStateEnabled: false, diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 92fcf818efc60..3176787b82a27 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -28,8 +28,6 @@ import { CriteriaWithPagination, Query, Ast, - EuiTabs, - EuiTab, } from '@elastic/eui'; import { keyBy, uniq, get } from 'lodash'; import { i18n } from '@kbn/i18n'; @@ -61,8 +59,8 @@ interface ContentEditorConfig export interface TableListViewProps { entityName: string; entityNamePlural: string; - tableListTitle: string; - tableListDescription?: string; + title: string; + description?: string; listingLimit: number; initialFilter?: string; initialPageSize: number; @@ -110,9 +108,8 @@ export interface TableListViewProps = Pick< +export type TableListProps = Pick< TableListViewProps, - | 'tableListTitle' | 'entityName' | 'entityNamePlural' | 'initialFilter' @@ -132,24 +129,7 @@ type TableListProps = Pick< | 'contentEditor' | 'titleColumnName' | 'withoutPageTemplateWrapper' -> & { onFetchSuccess: () => void }; - -export interface TableListTab { - title: string; - getTableList: ( - propsFromParent: Pick, 'tableListTitle' | 'onFetchSuccess'> - ) => React.ReactNode; -} - -type TabbedTableListViewProps = Pick< - TableListViewProps, - | 'tableListTitle' - | 'tableListDescription' - | 'additionalRightSideActions' - | 'headingId' - | 'children' - | 'withoutPageTemplateWrapper' -> & { tabs: TableListTab[] }; +> & { onFetchSuccess: () => void; tableCaption: string }; export interface State { items: T[]; @@ -270,7 +250,7 @@ const urlStateSerializer = (updated: { }; function TableListComp({ - tableListTitle, + tableCaption, entityName, entityNamePlural, initialFilter: initialQuery, @@ -945,7 +925,7 @@ function TableListComp({ entityNamePlural={entityNamePlural} tagsToTableItemMap={tagsToTableItemMap} deleteItems={deleteItems} - tableCaption={tableListTitle} + tableCaption={tableCaption} onTableChange={onTableChange} onTableSearchChange={onTableSearchChange} onSortChange={onSortChange} @@ -973,8 +953,8 @@ function TableListComp({ export const TableList = React.memo(TableListComp) as typeof TableListComp; export const TableListView = ({ - tableListTitle, - tableListDescription, + title, + description, entityName, entityNamePlural, initialFilter, @@ -1008,8 +988,8 @@ export const TableListView = ({ return ( {tableListTitle}} - description={tableListDescription} + pageTitle={{title}} + description={description} rightSideItems={additionalRightSideActions?.slice(0, 2)} data-test-subj="top-nav" /> @@ -1018,7 +998,7 @@ export const TableListView = ({ {children} ({ ); }; -export const TabbedTableListView = ({ - tableListTitle, - tableListDescription, - additionalRightSideActions, - headingId, - children, - withoutPageTemplateWrapper, - tabs, -}: TabbedTableListViewProps) => { - const PageTemplate = withoutPageTemplateWrapper - ? (React.Fragment as unknown as typeof KibanaPageTemplate) - : KibanaPageTemplate; - - const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); - - const [selectedTab, setSelectedTab] = useState(0); - - const tableList = useMemo( - () => - tabs[selectedTab].getTableList({ - tableListTitle, - onFetchSuccess: () => {}, - }), - [selectedTab, tableListTitle, tabs] - ); - - return ( - - {tableListTitle}} - description={tableListDescription} - rightSideItems={additionalRightSideActions?.slice(0, 2)} - data-test-subj="top-nav" - > - - {tabs.map((tab, index) => ( - setSelectedTab(index)} isSelected={index === selectedTab}> - {tab.title} - - ))} - - - - {/* Any children passed to the component */} - {children} - - {tableList} - - - ); -}; - // eslint-disable-next-line import/no-default-export export default TableListView; diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index 4fb91af6e0c19..97774e6fac54f 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -12,6 +12,7 @@ "savedObjectsManagement", "data", "presentationUtil", + "visualizations" ], "optionalPlugins": [ "savedObjectsTagging", diff --git a/src/plugins/event_annotation/public/components/list_view.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx similarity index 80% rename from src/plugins/event_annotation/public/components/list_view.test.tsx rename to src/plugins/event_annotation/public/components/table_list.test.tsx index 8655e2f64f07e..da8d687f32c7a 100644 --- a/src/plugins/event_annotation/public/components/list_view.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { - EventAnnotationGroupListView, + EventAnnotationGroupTableList, SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING, -} from './list_view'; -import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; +} from './table_list'; +import { TableList, UserContentCommonSchema } from '@kbn/content-management-table-list'; import { EventAnnotationServiceType } from '../event_annotation_service/types'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { shallow, ShallowWrapper } from 'enzyme'; @@ -33,7 +33,7 @@ describe('annotation list view', () => { ignoreGlobalFilters: false, }; - let wrapper: ShallowWrapper; + let wrapper: ShallowWrapper; let mockEventAnnotationService: EventAnnotationServiceType; beforeEach(() => { @@ -54,8 +54,8 @@ describe('annotation list view', () => { ), } as Partial as IUiSettingsClient; - wrapper = shallow( - ( + { delete: true, save: true, }} + parentProps={{ + onFetchSuccess: () => {}, + }} /> ); }); it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot(` - "
- -
" + " + + " `); }); @@ -79,7 +82,7 @@ describe('annotation list view', () => { const searchQuery = 'My Search Query'; const references = [{ id: 'first_id', type: 'sometype' }]; const referencesToExclude = [{ id: 'second_id', type: 'sometype' }]; - wrapper.find(TableListView).prop('findItems')(searchQuery, { + wrapper.find(TableList).prop('findItems')(searchQuery, { references, referencesToExclude, }); @@ -96,13 +99,13 @@ describe('annotation list view', () => { it('prevent deleting when user is missing perms', () => { wrapper.setProps({ visualizeCapabilities: { delete: false } }); - expect(wrapper.find(TableListView).prop('deleteItems')).toBeUndefined(); + expect(wrapper.find(TableList).prop('deleteItems')).toBeUndefined(); }); it('deletes groups using the service', () => { - expect(wrapper.find(TableListView).prop('deleteItems')).toBeDefined(); + expect(wrapper.find(TableList).prop('deleteItems')).toBeDefined(); - wrapper.find(TableListView).prop('deleteItems')!([ + wrapper.find(TableList).prop('deleteItems')!([ { id: 'some-id-1', references: [ @@ -147,14 +150,14 @@ describe('annotation list view', () => { it('prevents editing when user is missing perms', () => { wrapper.setProps({ visualizeCapabilities: { save: false } }); - expect(wrapper.find(TableListView).prop('deleteItems')).toBeUndefined(); + expect(wrapper.find(TableList).prop('deleteItems')).toBeUndefined(); }); it('edits existing group', async () => { expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); act(() => { - wrapper.find(TableListView).prop('editItem')!({ id: '1234' } as UserContentCommonSchema); + wrapper.find(TableList).prop('editItem')!({ id: '1234' } as UserContentCommonSchema); }); // wait one tick to give promise time to settle diff --git a/src/plugins/event_annotation/public/components/list_view.tsx b/src/plugins/event_annotation/public/components/table_list.tsx similarity index 88% rename from src/plugins/event_annotation/public/components/list_view.tsx rename to src/plugins/event_annotation/public/components/table_list.tsx index 7f4e15bcbb411..7e82698c04adf 100644 --- a/src/plugins/event_annotation/public/components/list_view.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -7,7 +7,7 @@ */ import React, { useCallback, useMemo, useState } from 'react'; -import { TableListView } from '@kbn/content-management-table-list'; +import { TableList, TableListTabParentProps } from '@kbn/content-management-table-list'; import { i18n } from '@kbn/i18n'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; @@ -33,16 +33,18 @@ import { EventAnnotationGroupContent } from '../../common/types'; export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; export const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; -export const EventAnnotationGroupListView = ({ +export const EventAnnotationGroupTableList = ({ uiSettings, eventAnnotationService, visualizeCapabilities, savedObjectsTagging, + parentProps, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; visualizeCapabilities: Record>; savedObjectsTagging: SavedObjectsTaggingApi; + parentProps: TableListTabParentProps; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -128,11 +130,12 @@ export const EventAnnotationGroupListView = ({ ) : undefined; return ( -
- - // we allow users to create visualizations even if they can't save them - // for data exploration purposes + <> + // createItem={createNewGroup} + tableCaption={i18n.translate('eventAnnotation.tableList.listTitle', { + defaultMessage: 'Annotation Library', + })} findItems={fetchItems} deleteItems={ visualizeCapabilities.delete @@ -147,28 +150,22 @@ export const EventAnnotationGroupListView = ({ .then((group) => setGroupToEditInfo({ group, id })) : undefined } - // customTableColumn={getCustomColumn()} listingLimit={listingLimit} initialPageSize={initialPageSize} initialFilter={''} - // emptyPrompt={noItemsFragment} + // emptyPrompt={noItemsFragment} TODO entityName={i18n.translate('eventAnnotation.tableList.entityName', { defaultMessage: 'annotation group', })} entityNamePlural={i18n.translate('eventAnnotation.tableList.entityNamePlural', { defaultMessage: 'annotation groups', })} - tableListTitle={i18n.translate('eventAnnotation.tableList.listTitle', { - defaultMessage: 'Annotation Library', - })} onClickTitle={(item) => { // TODO - what happens if I click here? }} - // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => - // getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) - // } + {...parentProps} /> {flyout} -
+ ); }; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx new file mode 100644 index 0000000000000..23aca27102652 --- /dev/null +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -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 React, { FC } from 'react'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { FormattedRelative } from '@kbn/i18n-react'; +import { + TableListTabParentProps, + TableListViewKibanaProvider, +} from '@kbn/content-management-table-list'; +import { CoreStart } from '@kbn/core-lifecycle-browser'; +import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { EventAnnotationGroupTableList } from './components/table_list'; +import { EventAnnotationServiceType } from './event_annotation_service/types'; + +export interface EventAnnotationListingPageServices { + core: CoreStart; + savedObjectsTagging: SavedObjectsTaggingApi; + eventAnnotationService: EventAnnotationServiceType; + PresentationUtilContextProvider: FC; +} + +export const getTableList = ( + parentProps: TableListTabParentProps, + services: EventAnnotationListingPageServices +) => { + return ( + + + + ); +}; diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index a5a3af6c512aa..7ea31040b2dd0 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -6,18 +6,13 @@ * Side Public License, v 1. */ -import { - type Plugin, - type CoreSetup, - type CoreStart, - DEFAULT_APP_CATEGORIES, - type AppMountParameters, -} from '@kbn/core/public'; +import { type Plugin, type CoreSetup, type CoreStart } from '@kbn/core/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; import { EventAnnotationService } from './event_annotation_service'; import { manualPointEventAnnotation, @@ -26,8 +21,7 @@ import { eventAnnotationGroup, } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; -import type { EventAnnotationAppServices } from './render_app'; -import { ANNOTATION_LIBRARY_APP_ID } from '../common/constants'; +import { EventAnnotationListingPageServices, getTableList } from './get_table_list'; export interface EventAnnotationStartDependencies { savedObjectsManagement: SavedObjectsManagementPluginStart; @@ -38,6 +32,7 @@ export interface EventAnnotationStartDependencies { interface SetupDependencies { expressions: ExpressionsSetup; + visualizations: VisualizationsSetup; } /** @public */ @@ -60,14 +55,9 @@ export class EventAnnotationPlugin getFetchEventAnnotations({ getStartServices: core.getStartServices }) ); - core.application.register({ - id: ANNOTATION_LIBRARY_APP_ID, - title: 'Event Annotation Library', - order: 8000, - euiIconType: 'logoKibana', - defaultPath: '#/', - category: DEFAULT_APP_CATEGORIES.kibana, - mount: async (params: AppMountParameters) => { + dependencies.visualizations.listingViewRegistry.add({ + title: 'Annotation Groups', + getTableList: async (props) => { const [coreStart, pluginsStart] = await core.getStartServices(); const eventAnnotationService = await new EventAnnotationService( @@ -75,23 +65,50 @@ export class EventAnnotationPlugin pluginsStart.savedObjectsManagement ).getService(); - const services: EventAnnotationAppServices = { + const services: EventAnnotationListingPageServices = { core: coreStart, - history: params.history, savedObjectsTagging: pluginsStart.savedObjectsTagging, eventAnnotationService, PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, }; - const { renderApp } = await import('./render_app'); - - const unmount = renderApp(params, services); - - return () => { - unmount(); - }; + // TODO wire parent props + return getTableList(props, services); }, }); + + // core.application.register({ + // id: ANNOTATION_LIBRARY_APP_ID, + // title: 'Event Annotation Library', + // order: 8000, + // euiIconType: 'logoKibana', + // defaultPath: '#/', + // category: DEFAULT_APP_CATEGORIES.kibana, + // mount: async (params: AppMountParameters) => { + // const [coreStart, pluginsStart] = await core.getStartServices(); + + // const eventAnnotationService = await new EventAnnotationService( + // coreStart, + // pluginsStart.savedObjectsManagement + // ).getService(); + + // const services: EventAnnotationAppServices = { + // core: coreStart, + // history: params.history, + // savedObjectsTagging: pluginsStart.savedObjectsTagging, + // eventAnnotationService, + // PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, + // }; + + // const { renderApp } = await import('./render_app'); + + // const unmount = renderApp(params, services); + + // return () => { + // unmount(); + // }; + // }, + // }); } public start( diff --git a/src/plugins/event_annotation/public/render_app.tsx b/src/plugins/event_annotation/public/render_app.tsx deleted file mode 100644 index 6a7e3e548e382..0000000000000 --- a/src/plugins/event_annotation/public/render_app.tsx +++ /dev/null @@ -1,69 +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 React, { FC } from 'react'; -import { AppMountParameters, ScopedHistory } from '@kbn/core-application-browser'; -import { KibanaThemeProvider, toMountPoint } from '@kbn/kibana-react-plugin/public'; -import ReactDOM from 'react-dom'; -import { FormattedRelative } from '@kbn/i18n-react'; -import { Router } from 'react-router-dom'; -import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; -import { CoreStart } from '@kbn/core-lifecycle-browser'; -import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { EventAnnotationGroupListView } from './components/list_view'; -import { EventAnnotationServiceType } from './event_annotation_service/types'; - -export interface EventAnnotationAppServices { - history: ScopedHistory; - core: CoreStart; - savedObjectsTagging: SavedObjectsTaggingApi; - eventAnnotationService: EventAnnotationServiceType; - PresentationUtilContextProvider: FC; - // setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - // savedObjectsTagging?: SavedObjectsTaggingApi; -} - -export const renderApp = ( - { element }: AppMountParameters, - services: EventAnnotationAppServices -) => { - // // add readonly badge if saving restricted - // if (!services.visualizeCapabilities.save) { - // addBadgeToAppChrome(services.chrome); - // } - - const app = ( - - - - - - - - - - - - ); - - ReactDOM.render(app, element); - - return () => ReactDOM.unmountComponentAtNode(element); -}; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index a5b0b6baa24e7..ccd26fc826cdb 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -103,6 +103,7 @@ import { } from './services'; import { VisualizeConstants } from '../common/constants'; import { EditInLensAction } from './actions/edit_in_lens_action'; +import { ListingViewRegistry } from './types'; /** * Interface for this plugin's returned setup/start contracts. @@ -110,8 +111,10 @@ import { EditInLensAction } from './actions/edit_in_lens_action'; * @public */ -export type VisualizationsSetup = TypesSetup & { visEditorsRegistry: VisEditorsRegistry }; - +export type VisualizationsSetup = TypesSetup & { + visEditorsRegistry: VisEditorsRegistry; + listingViewRegistry: ListingViewRegistry; +}; export interface VisualizationsStart extends TypesStart { showNewVisModal: typeof showNewVisModal; } @@ -234,6 +237,7 @@ export class VisualizationsPlugin }; const start = createStartServicesGetter(core.getStartServices); + const listingViewRegistry: ListingViewRegistry = new Set(); const visEditorsRegistry = createVisEditorsRegistry(); core.application.register({ @@ -308,6 +312,7 @@ export class VisualizationsPlugin getKibanaVersion: () => this.initializerContext.env.packageInfo.version, spaces: pluginsStart.spaces, visEditorsRegistry, + listingViewRegistry, unifiedSearch: pluginsStart.unifiedSearch, }; @@ -367,6 +372,7 @@ export class VisualizationsPlugin return { ...this.types.setup(), visEditorsRegistry, + listingViewRegistry, }; } diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index c69f7894fa326..b78dcbaf71921 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -16,6 +16,7 @@ import { import type { ISearchSource } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/public'; +import { TableListTab } from '@kbn/content-management-table-list'; import type { Vis } from './vis'; import type { PersistedState } from './persisted_state'; import type { VisParams, SerializedVis } from '../common'; @@ -94,3 +95,5 @@ export interface VisEditorOptionsProps { setValidity(isValid: boolean): void; setTouched(isTouched: boolean): void; } + +export type ListingViewRegistry = Pick, 'add'>; diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index bb21a45b5c22e..89f4fad427e7a 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -8,7 +8,14 @@ import './visualize_listing.scss'; -import React, { useCallback, useRef, useMemo, useEffect, MouseEvent } from 'react'; +import React, { + useCallback, + useRef, + useMemo, + useEffect, + MouseEvent, + MutableRefObject, +} from 'react'; import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -26,6 +33,7 @@ import { } from '@kbn/content-management-table-list'; import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { TableListViewProps } from '@kbn/content-management-table-list/src'; import { findListItems } from '../../utils/saved_visualize_utils'; import { updateBasicSoAttributes } from '../../utils/saved_objects_utils/update_basic_attributes'; import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title'; @@ -77,71 +85,33 @@ const toTableListViewSavedObject = (savedObject: Record): Visua }, }; }; +type CustomTableViewProps = Pick< + TableListViewProps, + 'createItem' | 'findItems' | 'deleteItems' | 'editItem' | 'contentEditor' | 'emptyPrompt' +>; -export const VisualizeListing = () => { +const useTableListViewProps = ( + closeNewVisModal: MutableRefObject<() => void>, + listingLimit: number +): CustomTableViewProps => { const { services: { - core, application, - executionContext, - chrome, history, - toastNotifications, - stateTransferService, - savedObjects, uiSettings, - visualizeCapabilities, - dashboardCapabilities, - kbnUrlStateStorage, - overlays, + savedObjects, savedObjectsTagging, + overlays, + toastNotifications, + visualizeCapabilities, }, } = useKibana(); - const { pathname } = useLocation(); - const closeNewVisModal = useRef(() => {}); - const visualizedUserContent = useRef(); - const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); - const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); - - useExecutionContext(executionContext, { - type: 'application', - page: 'list', - }); - useEffect(() => { - if (pathname === '/new') { - // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately - closeNewVisModal.current = showNewVisModal({ - onClose: () => { - // In case the user came via a URL to this page, change the URL to the regular landing page URL after closing the modal - history.push(VisualizeConstants.LANDING_PAGE_PATH); - }, - }); - } else { - // close modal window if exists - closeNewVisModal.current(); - } - }, [history, pathname]); - - useMount(() => { - // Reset editor state for all apps if the visualize listing page is loaded. - stateTransferService.clearEditorState(); - chrome.setBreadcrumbs([ - { - text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', { - defaultMessage: 'Visualize Library', - }), - }, - ]); - chrome.docTitle.change( - i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' }) - ); - }); - useUnmount(() => closeNewVisModal.current()); + const visualizedUserContent = useRef(); const createNewVis = useCallback(() => { closeNewVisModal.current = showNewVisModal(); - }, []); + }, [closeNewVisModal]); const editItem = useCallback( ({ attributes: { editUrl, editApp } }: VisualizeUserContent) => { @@ -269,6 +239,75 @@ export const VisualizeListing = () => { [savedObjects.client, toastNotifications] ); + const props: CustomTableViewProps = { + findItems: fetchItems, + deleteItems, + contentEditor: { + isReadonly: !visualizeCapabilities.save, + onSave: onContentEditorSave, + customValidators: contentEditorValidators, + }, + editItem, + emptyPrompt: noItemsFragment, + createItem: createNewVis, + }; + + return props; +}; + +export const VisualizeListing = () => { + const { + services: { + application, + executionContext, + chrome, + history, + stateTransferService, + dashboardCapabilities, + uiSettings, + kbnUrlStateStorage, + listingViewRegistry, + }, + } = useKibana(); + const { pathname } = useLocation(); + const closeNewVisModal = useRef(() => {}); + + useExecutionContext(executionContext, { + type: 'application', + page: 'list', + }); + + useEffect(() => { + if (pathname === '/new') { + // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately + closeNewVisModal.current = showNewVisModal({ + onClose: () => { + // In case the user came via a URL to this page, change the URL to the regular landing page URL after closing the modal + history.push(VisualizeConstants.LANDING_PAGE_PATH); + }, + }); + } else { + // close modal window if exists + closeNewVisModal.current(); + } + }, [history, pathname]); + + useMount(() => { + // Reset editor state for all apps if the visualize listing page is loaded. + stateTransferService.clearEditorState(); + chrome.setBreadcrumbs([ + { + text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', { + defaultMessage: 'Visualize Library', + }), + }, + ]); + chrome.docTitle.change( + i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' }) + ); + }); + useUnmount(() => closeNewVisModal.current()); + const calloutMessage = ( { /> ); - const tabs: TableListTab[] = [ - { + const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); + const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + + const tableViewProps = useTableListViewProps(closeNewVisModal, listingLimit); + + const visualizeLibraryTitle = i18n.translate('visualizations.listing.table.listTitle', { + defaultMessage: 'Visualize Library', + }); + + const visualizeTab: TableListTab = useMemo( + () => ({ title: 'Visualizations', getTableList: (propsFromParent) => ( id="vis" // we allow users to create visualizations even if they can't save them // for data exploration purposes - createItem={createNewVis} - findItems={fetchItems} - deleteItems={visualizeCapabilities.delete ? deleteItems : undefined} - editItem={visualizeCapabilities.save ? editItem : undefined} customTableColumn={getCustomColumn()} listingLimit={listingLimit} initialPageSize={initialPageSize} initialFilter={''} - contentEditor={{ - isReadonly: !visualizeCapabilities.save, - onSave: onContentEditorSave, - customValidators: contentEditorValidators, - }} - emptyPrompt={noItemsFragment} entityName={i18n.translate('visualizations.listing.table.entityName', { defaultMessage: 'visualization', })} @@ -322,20 +360,33 @@ export const VisualizeListing = () => { defaultMessage: 'visualizations', })} getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => - getVisualizeListItemLink(core.application, kbnUrlStateStorage, editApp, editUrl, error) + getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) } + tableCaption={visualizeLibraryTitle} + {...tableViewProps} {...propsFromParent} /> ), - }, - ]; + }), + [ + application, + initialPageSize, + kbnUrlStateStorage, + listingLimit, + tableViewProps, + visualizeLibraryTitle, + ] + ); + + const tabs = useMemo( + () => [visualizeTab, ...Array.from(listingViewRegistry as Set)], + [listingViewRegistry, visualizeTab] + ); return ( {dashboardCapabilities.createNew && ( diff --git a/src/plugins/visualizations/public/visualize_app/types.ts b/src/plugins/visualizations/public/visualize_app/types.ts index aa16be35277f3..53f3f11602364 100644 --- a/src/plugins/visualizations/public/visualize_app/types.ts +++ b/src/plugins/visualizations/public/visualize_app/types.ts @@ -48,7 +48,7 @@ import type { VisParams, } from '..'; -import type { SavedVisState } from '../types'; +import type { ListingViewRegistry, SavedVisState } from '../types'; import type { createVisEmbeddableFromObject } from '../embeddable'; import type { VisEditorsRegistry } from '../vis_editors_registry'; @@ -112,6 +112,7 @@ export interface VisualizeServices extends CoreStart { spaces?: SpacesPluginStart; theme: ThemeServiceStart; visEditorsRegistry: VisEditorsRegistry; + listingViewRegistry: ListingViewRegistry; unifiedSearch: UnifiedSearchPublicPluginStart; } From b67b401342a94177de2540e20ebaa2b51642a1e7 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 18 Apr 2023 17:17:37 -0500 Subject: [PATCH 048/202] URL navigation between tabs --- .../src/tabbed_table_list_view.test.tsx | 20 +++++++++++ .../table_list/src/tabbed_table_list_view.tsx | 20 +++++++---- src/plugins/event_annotation/public/plugin.ts | 34 +------------------ .../visualizations/common/constants.ts | 1 + .../public/visualize_app/app.tsx | 6 +++- .../components/visualize_listing.tsx | 9 ++++- 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx b/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx index 8424d0a72b82d..ef17abbf82f10 100644 --- a/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx +++ b/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx @@ -25,10 +25,12 @@ const children =
Test Children
; const tabs: TableListTab[] = [ { title: 'Tab 1', + id: 'tab-1', getTableList: async (props: TableListTabParentProps) =>
Test Table List 1
, }, { title: 'Tab 2', + id: 'tab-2', getTableList: async (props: TableListTabParentProps) =>
Test Table List 2
, }, ]; @@ -42,6 +44,8 @@ describe('TabbedTableListView', () => { headingId={headingId} children={children} tabs={tabs} + activeTabId={'tab-1'} + changeActiveTab={() => {}} /> ); expect(wrapper.exists()).toBe(true); @@ -55,6 +59,8 @@ describe('TabbedTableListView', () => { headingId={headingId} children={children} tabs={tabs} + activeTabId={'tab-1'} + changeActiveTab={() => {}} /> ); expect(wrapper.find(KibanaPageTemplate.Header).prop('pageTitle')).toMatchInlineSnapshot(` @@ -75,12 +81,16 @@ describe('TabbedTableListView', () => { headingId={headingId} children={children} tabs={tabs} + activeTabId={'tab-1'} + changeActiveTab={() => {}} /> ); expect(wrapper.find(EuiTab).length).toEqual(tabs.length); }); it('should switch tabs when a tab is clicked', () => { + const changeActiveTab = jest.fn(); + const wrapper = shallow( { headingId={headingId} children={children} tabs={tabs} + activeTabId={'tab-1'} + changeActiveTab={changeActiveTab} /> ); @@ -100,6 +112,14 @@ describe('TabbedTableListView', () => { getTab(1).simulate('click'); }); + expect(changeActiveTab).toHaveBeenCalledWith('tab-2'); + + act(() => { + wrapper.setProps({ + activeTabId: 'tab-2', + }); + }); + expect(getTab(0).prop('isSelected')).toBeFalsy(); expect(getTab(1).prop('isSelected')).toBeTruthy(); }); diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.tsx b/packages/content-management/table_list/src/tabbed_table_list_view.tsx index 62f067697543f..f0337d7bf53a5 100644 --- a/packages/content-management/table_list/src/tabbed_table_list_view.tsx +++ b/packages/content-management/table_list/src/tabbed_table_list_view.tsx @@ -8,7 +8,7 @@ import { EuiTab, EuiTabs } from '@elastic/eui'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import type { TableListProps, TableListViewProps, @@ -20,6 +20,7 @@ export type TableListTabParentProps { title: string; + id: string; getTableList: ( propsFromParent: TableListTabParentProps ) => Promise | React.ReactNode; @@ -28,7 +29,7 @@ export interface TableListTab, 'title' | 'description' | 'headingId' | 'children' -> & { tabs: TableListTab[] }; +> & { tabs: TableListTab[]; activeTabId: string; changeActiveTab: (id: string) => void }; export const TabbedTableListView = ({ title, @@ -36,16 +37,21 @@ export const TabbedTableListView = ({ headingId, children, tabs, + activeTabId, + changeActiveTab, }: TabbedTableListViewProps) => { const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); - const [selectedTab, setSelectedTab] = useState(0); + const getActiveTab = useCallback( + () => tabs.find((tab) => tab.id === activeTabId) ?? tabs[0], + [activeTabId, tabs] + ); const [tableList, setTableList] = useState(null); useEffect(() => { async function loadTableList() { - const newTableList = await tabs[selectedTab].getTableList({ + const newTableList = await getActiveTab().getTableList({ onFetchSuccess: () => { if (!hasInitialFetchReturned) { setHasInitialFetchReturned(true); @@ -56,7 +62,7 @@ export const TabbedTableListView = ({ } loadTableList(); - }, [hasInitialFetchReturned, selectedTab, tabs]); + }, [hasInitialFetchReturned, activeTabId, tabs, getActiveTab]); return ( @@ -69,8 +75,8 @@ export const TabbedTableListView = ({ {tabs.map((tab, index) => ( setSelectedTab(index)} - isSelected={index === selectedTab} + onClick={() => changeActiveTab(tab.id)} + isSelected={tab.id === getActiveTab().id} > {tab.title} diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 7ea31040b2dd0..b665fd4a767fb 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -57,6 +57,7 @@ export class EventAnnotationPlugin dependencies.visualizations.listingViewRegistry.add({ title: 'Annotation Groups', + id: 'annotations', getTableList: async (props) => { const [coreStart, pluginsStart] = await core.getStartServices(); @@ -76,39 +77,6 @@ export class EventAnnotationPlugin return getTableList(props, services); }, }); - - // core.application.register({ - // id: ANNOTATION_LIBRARY_APP_ID, - // title: 'Event Annotation Library', - // order: 8000, - // euiIconType: 'logoKibana', - // defaultPath: '#/', - // category: DEFAULT_APP_CATEGORIES.kibana, - // mount: async (params: AppMountParameters) => { - // const [coreStart, pluginsStart] = await core.getStartServices(); - - // const eventAnnotationService = await new EventAnnotationService( - // coreStart, - // pluginsStart.savedObjectsManagement - // ).getService(); - - // const services: EventAnnotationAppServices = { - // core: coreStart, - // history: params.history, - // savedObjectsTagging: pluginsStart.savedObjectsTagging, - // eventAnnotationService, - // PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, - // }; - - // const { renderApp } = await import('./render_app'); - - // const unmount = renderApp(params, services); - - // return () => { - // unmount(); - // }; - // }, - // }); } public start( diff --git a/src/plugins/visualizations/common/constants.ts b/src/plugins/visualizations/common/constants.ts index 5fd21e426da8d..a2ac0cdde2a23 100644 --- a/src/plugins/visualizations/common/constants.ts +++ b/src/plugins/visualizations/common/constants.ts @@ -21,6 +21,7 @@ export const VISUALIZE_APP_NAME = 'visualize'; export const VisualizeConstants = { VISUALIZE_BASE_PATH: '/app/visualize', LANDING_PAGE_PATH: '/', + LANDING_PAGE_PATH_WITH_TAB: '/:activeTab', WIZARD_STEP_1_PAGE_PATH: '/new', WIZARD_STEP_2_PAGE_PATH: '/new/configure', CREATE_PATH: '/create', diff --git a/src/plugins/visualizations/public/visualize_app/app.tsx b/src/plugins/visualizations/public/visualize_app/app.tsx index 0a4e46288616f..625c0846221a5 100644 --- a/src/plugins/visualizations/public/visualize_app/app.tsx +++ b/src/plugins/visualizations/public/visualize_app/app.tsx @@ -139,7 +139,11 @@ export const VisualizeApp = ({ onAppLeave }: VisualizeAppProps) => { diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index 89f4fad427e7a..4fcb493c87c56 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -22,7 +22,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import useUnmount from 'react-use/lib/useUnmount'; import useMount from 'react-use/lib/useMount'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useParams } from 'react-router-dom'; import type { SavedObjectsFindOptionsReference } from '@kbn/core/public'; import { useKibana, useExecutionContext } from '@kbn/kibana-react-plugin/public'; @@ -344,6 +344,7 @@ export const VisualizeListing = () => { const visualizeTab: TableListTab = useMemo( () => ({ title: 'Visualizations', + id: 'visualizations', getTableList: (propsFromParent) => ( id="vis" @@ -383,11 +384,17 @@ export const VisualizeListing = () => { [listingViewRegistry, visualizeTab] ); + const { activeTab } = useParams<{ activeTab: string }>(); + return ( { + application.navigateToUrl(`#/${id}`); + }} > {dashboardCapabilities.createNew && ( <> From b3187442710128fe52cd9655d8c33617f2f5d1d1 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 18 Apr 2023 17:43:22 -0500 Subject: [PATCH 049/202] restore create button --- .../table_list/src/components/table.tsx | 8 ++++++-- .../content-management/table_list/src/table_list_view.tsx | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 8eeca2c04efd2..d44baaa9250da 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -17,6 +17,7 @@ import { SearchFilterConfig, Direction, Query, + Search, } from '@elastic/eui'; import { useServices } from '../services'; @@ -51,6 +52,7 @@ interface Props extends State, TagManageme tableColumns: Array>; hasUpdatedAtMetadata: boolean; deleteItems: TableListProps['deleteItems']; + renderCreateButton: () => React.ReactElement | undefined; onSortChange: (column: SortColumnField, direction: Direction) => void; onTableChange: (criteria: CriteriaWithPagination) => void; onTableSearchChange: (arg: { query: Query | null; queryText: string }) => void; @@ -71,6 +73,7 @@ export function Table({ entityNamePlural, tagsToTableItemMap, deleteItems, + renderCreateButton, tableCaption, onTableChange, onTableSearchChange, @@ -177,10 +180,11 @@ export function Table({ return [tableSortSelectFilter, tagFilterPanel]; }, [tableSortSelectFilter, tagFilterPanel]); - const search = useMemo(() => { + const search = useMemo((): Search => { return { onChange: onTableSearchChange, toolsLeft: renderToolsLeft(), + toolsRight: renderCreateButton(), query: searchQuery.query ?? undefined, box: { incremental: true, @@ -188,7 +192,7 @@ export function Table({ }, filters: searchFilters, }; - }, [onTableSearchChange, renderToolsLeft, searchFilters, searchQuery.query]); + }, [onTableSearchChange, renderCreateButton, renderToolsLeft, searchFilters, searchQuery.query]); const noItemsMessage = ( ({ dispatch={dispatch} items={items} + renderCreateButton={renderCreateButton} isFetchingItems={isFetchingItems} searchQuery={searchQuery} tableColumns={tableColumns} From c4019940bd89207f8b9229d45edf5366552ebe8b Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 18 Apr 2023 18:27:04 -0500 Subject: [PATCH 050/202] some minor cosmetic fixes to visualization listing page --- .../table_list/src/tabbed_table_list_view.tsx | 6 + .../components/visualize_listing.tsx | 131 +++++++++--------- 2 files changed, 72 insertions(+), 65 deletions(-) diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.tsx b/packages/content-management/table_list/src/tabbed_table_list_view.tsx index f0337d7bf53a5..c6b3524347660 100644 --- a/packages/content-management/table_list/src/tabbed_table_list_view.tsx +++ b/packages/content-management/table_list/src/tabbed_table_list_view.tsx @@ -7,6 +7,7 @@ */ import { EuiTab, EuiTabs } from '@elastic/eui'; +import { css } from '@emotion/react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import React, { useCallback, useEffect, useState } from 'react'; import type { @@ -70,6 +71,11 @@ export const TabbedTableListView = ({ pageTitle={{title}} description={description} data-test-subj="top-nav" + css={css` + .euiPageHeaderContent { + padding-bottom: 0; + } + `} > {tabs.map((tab, index) => ( diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index 4fcb493c87c56..c4755a7ad3dfb 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -308,30 +308,6 @@ export const VisualizeListing = () => { }); useUnmount(() => closeNewVisModal.current()); - const calloutMessage = ( - { - event.preventDefault(); - application.navigateToUrl(application.getUrlForApp('dashboards')); - }} - > - - - ), - }} - /> - ); - const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -341,43 +317,75 @@ export const VisualizeListing = () => { defaultMessage: 'Visualize Library', }); - const visualizeTab: TableListTab = useMemo( - () => ({ + const visualizeTab: TableListTab = useMemo(() => { + const calloutMessage = ( + { + event.preventDefault(); + application.navigateToUrl(application.getUrlForApp('dashboards')); + }} + > + + + ), + }} + /> + ); + + return { title: 'Visualizations', id: 'visualizations', getTableList: (propsFromParent) => ( - - id="vis" - // we allow users to create visualizations even if they can't save them - // for data exploration purposes - customTableColumn={getCustomColumn()} - listingLimit={listingLimit} - initialPageSize={initialPageSize} - initialFilter={''} - entityName={i18n.translate('visualizations.listing.table.entityName', { - defaultMessage: 'visualization', - })} - entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { - defaultMessage: 'visualizations', - })} - getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => - getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) - } - tableCaption={visualizeLibraryTitle} - {...tableViewProps} - {...propsFromParent} - /> + <> + {dashboardCapabilities.createNew && ( + <> + + + + )} + + id="vis" + // we allow users to create visualizations even if they can't save them + // for data exploration purposes + customTableColumn={getCustomColumn()} + listingLimit={listingLimit} + initialPageSize={initialPageSize} + initialFilter={''} + entityName={i18n.translate('visualizations.listing.table.entityName', { + defaultMessage: 'visualization', + })} + entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { + defaultMessage: 'visualizations', + })} + getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => + getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) + } + tableCaption={visualizeLibraryTitle} + {...tableViewProps} + {...propsFromParent} + /> + ), - }), - [ - application, - initialPageSize, - kbnUrlStateStorage, - listingLimit, - tableViewProps, - visualizeLibraryTitle, - ] - ); + }; + }, [ + application, + dashboardCapabilities.createNew, + initialPageSize, + kbnUrlStateStorage, + listingLimit, + tableViewProps, + visualizeLibraryTitle, + ]); const tabs = useMemo( () => [visualizeTab, ...Array.from(listingViewRegistry as Set)], @@ -395,13 +403,6 @@ export const VisualizeListing = () => { changeActiveTab={(id) => { application.navigateToUrl(`#/${id}`); }} - > - {dashboardCapabilities.createNew && ( - <> - - - - )} - + /> ); }; From f2ce71a6903573ca1fe4949782300d9b832053e0 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 19 Apr 2023 08:04:44 -0500 Subject: [PATCH 051/202] restore table list to main state --- .../content-management/table_list/index.ts | 7 +- .../src/components/item_details.tsx | 4 +- .../table_list/src/components/table.tsx | 4 +- .../table_list/src/index.ts | 4 +- .../table_list/src/table_list_view.test.tsx | 75 +++-- .../table_list/src/table_list_view.tsx | 258 ++++++------------ 6 files changed, 152 insertions(+), 200 deletions(-) diff --git a/packages/content-management/table_list/index.ts b/packages/content-management/table_list/index.ts index b007cb38acd8b..532b35450d541 100644 --- a/packages/content-management/table_list/index.ts +++ b/packages/content-management/table_list/index.ts @@ -6,12 +6,7 @@ * Side Public License, v 1. */ -export { - TableListView, - TableList, - TableListViewProvider, - TableListViewKibanaProvider, -} from './src'; +export { TableListView, TableListViewProvider, TableListViewKibanaProvider } from './src'; export type { UserContentCommonSchema } from './src'; export type { TableListViewKibanaDependencies } from './src/services'; diff --git a/packages/content-management/table_list/src/components/item_details.tsx b/packages/content-management/table_list/src/components/item_details.tsx index 92945a238478a..ccfbb5e3ea55a 100644 --- a/packages/content-management/table_list/src/components/item_details.tsx +++ b/packages/content-management/table_list/src/components/item_details.tsx @@ -12,11 +12,11 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import type { Tag } from '../types'; import { useServices } from '../services'; -import type { UserContentCommonSchema, TableListProps as TableListProps } from '../table_list_view'; +import type { UserContentCommonSchema, Props as TableListViewProps } from '../table_list_view'; import { TagBadge } from './tag_badge'; type InheritedProps = Pick< - TableListProps, + TableListViewProps, 'onClickTitle' | 'getDetailViewLink' | 'id' >; interface Props extends InheritedProps { diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 6e4b4e71c2669..330eb67be4278 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -23,7 +23,7 @@ import { useServices } from '../services'; import type { Action } from '../actions'; import type { State as TableListViewState, - TableListProps as TableListProps, + Props as TableListViewProps, UserContentCommonSchema, } from '../table_list_view'; import { TableSortSelect } from './table_sort_select'; @@ -50,7 +50,7 @@ interface Props extends State, TagManageme tableCaption: string; tableColumns: Array>; hasUpdatedAtMetadata: boolean; - deleteItems: TableListProps['deleteItems']; + deleteItems: TableListViewProps['deleteItems']; onSortChange: (column: SortColumnField, direction: Direction) => void; onTableChange: (criteria: CriteriaWithPagination) => void; onTableSearchChange: (arg: { query: Query | null; queryText: string }) => void; diff --git a/packages/content-management/table_list/src/index.ts b/packages/content-management/table_list/src/index.ts index 8e56517fb2d63..df0d1e22bc106 100644 --- a/packages/content-management/table_list/src/index.ts +++ b/packages/content-management/table_list/src/index.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -export { TableList, TableListView } from './table_list_view'; +export { TableListView } from './table_list_view'; export type { - TableListProps as TableListProps, + Props as TableListViewProps, State as TableListViewState, UserContentCommonSchema, } from './table_list_view'; diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index e6428e1612b46..62c83fb5b9454 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -18,8 +18,8 @@ import type { LocationDescriptor, History } from 'history'; import { WithServices } from './__jest__'; import { getTagList } from './mocks'; import { - TableList, - TableListProps as TableListProps, + TableListView, + Props as TableListViewProps, UserContentCommonSchema, } from './table_list_view'; @@ -49,7 +49,7 @@ interface Router { }; } -const requiredProps: TableListProps = { +const requiredProps: TableListViewProps = { entityName: 'test', entityNamePlural: 'tests', listingLimit: 500, @@ -66,6 +66,12 @@ const twoDaysAgoToString = new Date(twoDaysAgo.getTime()).toDateString(); const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); const yesterdayToString = new Date(yesterday.getTime()).toDateString(); +const getActions = (testBed: TestBed) => ({ + openSortSelect() { + testBed.find('tableSortSelectBtn').at(0).simulate('click'); + }, +}); + describe('TableListView', () => { beforeAll(() => { jest.useFakeTimers({ legacyFakeTimers: true }); @@ -75,10 +81,13 @@ describe('TableListView', () => { jest.useRealTimers(); }); - const setup = registerTestBed(WithServices(TableList), { - defaultProps: { ...requiredProps }, - memoryRouter: { wrapComponent: true }, - }); + const setup = registerTestBed( + WithServices(TableListView), + { + defaultProps: { ...requiredProps }, + memoryRouter: { wrapComponent: true }, + } + ); test('render default empty prompt', async () => { let testBed: TestBed; @@ -303,6 +312,36 @@ describe('TableListView', () => { expect(lastRowTitle).toBe('Item 19'); }); + test('should allow changing the number of rows in the table', async () => { + let testBed: TestBed; + + await act(async () => { + testBed = await setup({ + initialPageSize, + findItems: jest.fn().mockResolvedValue({ total: hits.length, hits: [...hits] }), + }); + }); + + const { component, table, find } = testBed!; + component.update(); + + let { tableCellsValues } = table.getMetaData('itemsInMemTable'); + expect(tableCellsValues.length).toBe(requiredProps.initialPageSize); + + // Changing the "Rows per page" also sends the "sort" column information and thus updates the sorting. + // We test that the "sort by" column has not changed before and after changing the number of rows + expect(find('tableSortSelectBtn').at(0).text()).toBe('Recently updated'); + + // Open the "Rows per page" drop down + find('tablePaginationPopoverButton').simulate('click'); + find('tablePagination-10-rows').simulate('click'); + + ({ tableCellsValues } = table.getMetaData('itemsInMemTable')); + expect(tableCellsValues.length).toBe(10); + + expect(find('tableSortSelectBtn').at(0).text()).toBe('Recently updated'); // Still the same + }); + test('should navigate to page 2', async () => { let testBed: TestBed; @@ -337,8 +376,8 @@ describe('TableListView', () => { }); describe('column sorting', () => { - const setupColumnSorting = registerTestBed( - WithServices(TableList, { + const setupColumnSorting = registerTestBed( + WithServices(TableListView, { TagList: getTagList({ references: [] }), }), { @@ -347,12 +386,6 @@ describe('TableListView', () => { } ); - const getActions = (testBed: TestBed) => ({ - openSortSelect() { - testBed.find('tableSortSelectBtn').at(0).simulate('click'); - }, - }); - const hits: UserContentCommonSchema[] = [ { id: '123', @@ -546,8 +579,8 @@ describe('TableListView', () => { }); describe('content editor', () => { - const setupInspector = registerTestBed( - WithServices(TableList), + const setupInspector = registerTestBed( + WithServices(TableListView), { defaultProps: { ...requiredProps }, memoryRouter: { wrapComponent: true }, @@ -597,8 +630,8 @@ describe('TableListView', () => { }); describe('tag filtering', () => { - const setupTagFiltering = registerTestBed( - WithServices(TableList, { + const setupTagFiltering = registerTestBed( + WithServices(TableListView, { getTagList: () => [ { id: 'id-tag-1', name: 'tag-1', type: 'tag', description: '', color: '' }, { id: 'id-tag-2', name: 'tag-2', type: 'tag', description: '', color: '' }, @@ -749,8 +782,8 @@ describe('TableListView', () => { describe('url state', () => { let router: Router | undefined; - const setupTagFiltering = registerTestBed( - WithServices(TableList, { + const setupTagFiltering = registerTestBed( + WithServices(TableListView, { getTagList: () => [ { id: 'id-tag-1', name: 'tag-1', type: 'tag', description: '', color: '' }, { id: 'id-tag-2', name: 'tag-2', type: 'tag', description: '', color: '' }, diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 82d0d0f48aecf..1612649f80bda 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -6,15 +6,7 @@ * Side Public License, v 1. */ -import React, { - useReducer, - useCallback, - useEffect, - useRef, - useMemo, - ReactNode, - useState, -} from 'react'; +import React, { useReducer, useCallback, useEffect, useRef, useMemo, ReactNode } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { EuiBasicTableColumn, @@ -56,7 +48,7 @@ interface ContentEditorConfig enabled?: boolean; } -export interface TableListViewProps { +export interface Props { entityName: string; entityNamePlural: string; tableListTitle: string; @@ -108,30 +100,6 @@ export interface TableListViewProps = Pick< - TableListViewProps, - | 'tableListTitle' - | 'entityName' - | 'entityNamePlural' - | 'initialFilter' - | 'headingId' - | 'initialPageSize' - | 'listingLimit' - | 'urlStateEnabled' - | 'customTableColumn' - | 'emptyPrompt' - | 'findItems' - | 'createItem' - | 'editItem' - | 'deleteItems' - | 'getDetailViewLink' - | 'onClickTitle' - | 'id' - | 'contentEditor' - | 'titleColumnName' - | 'withoutPageTemplateWrapper' -> & { onFetchSuccess: () => void }; - export interface State { items: T[]; hasInitialFetchReturned: boolean; @@ -250,8 +218,20 @@ const urlStateSerializer = (updated: { return updatedQueryParams; }; -function TableListComp({ +const tableColumnMetadata = { + title: { + field: 'attributes.title', + name: 'Name, description, tags', + }, + updatedAt: { + field: 'updatedAt', + name: 'Last updated', + }, +} as const; + +function TableListViewComp({ tableListTitle, + tableListDescription, entityName, entityNamePlural, initialFilter: initialQuery, @@ -269,10 +249,11 @@ function TableListComp({ onClickTitle, id: listingId = 'userContent', contentEditor = { enabled: false }, + children, titleColumnName, + additionalRightSideActions = [], withoutPageTemplateWrapper, - onFetchSuccess: onInitialFetchReturned, -}: TableListProps) { +}: Props) { if (!getDetailViewLink && !onClickTitle) { throw new Error( `[TableListView] One o["getDetailViewLink" or "onClickTitle"] prop must be provided.` @@ -401,7 +382,6 @@ function TableListComp({ response, }, }); - onInitialFetchReturned(); } } catch (err) { dispatch({ @@ -409,7 +389,7 @@ function TableListComp({ data: err, }); } - }, [searchQueryParser, searchQuery.text, findItems, onInitialFetchReturned]); + }, [searchQueryParser, findItems, searchQuery.text]); const updateQuery = useCallback( (query: Query) => { @@ -468,7 +448,7 @@ function TableListComp({ const tableColumns = useMemo(() => { const columns: Array> = [ { - field: 'attributes.title', + field: tableColumnMetadata.title.field, name: titleColumnName ?? i18n.translate('contentManagement.tableList.mainColumnName', { @@ -502,7 +482,7 @@ function TableListComp({ if (hasUpdatedAtMetadata) { columns.push({ - field: 'updatedAt', + field: tableColumnMetadata.updatedAt.field, name: i18n.translate('contentManagement.tableList.lastUpdatedColumnTitle', { defaultMessage: 'Last updated', }), @@ -661,8 +641,17 @@ function TableListComp({ } = {}; if (criteria.sort) { + // We need to serialise the field as the return either (1) the field _name_ (e.g. "Last updated") + // when changing the "Rows per page" select value or (2) the field _value_ (e.g. "updatedAt") when clicking the column title + let fieldSerialized: unknown = criteria.sort.field; + if (fieldSerialized === tableColumnMetadata.title.name) { + fieldSerialized = tableColumnMetadata.title.field; + } else if (fieldSerialized === tableColumnMetadata.updatedAt.name) { + fieldSerialized = tableColumnMetadata.updatedAt.field; + } + data.sort = { - field: criteria.sort.field as SortColumnField, + field: fieldSerialized as SortColumnField, direction: criteria.sort.direction, }; } @@ -894,145 +883,80 @@ function TableListComp({ ? 'table-is-ready' : 'table-is-loading'; - return ( - <> - {/* Too many items error */} - {showLimitError && ( - - )} - - {/* Error while fetching items */} - {showFetchError && renderFetchError()} - - {/* Table of items */} -
- - dispatch={dispatch} - items={items} - isFetchingItems={isFetchingItems} - searchQuery={searchQuery} - tableColumns={tableColumns} - hasUpdatedAtMetadata={hasUpdatedAtMetadata} - tableSort={tableSort} - pagination={pagination} - selectedIds={selectedIds} - entityName={entityName} - entityNamePlural={entityNamePlural} - tagsToTableItemMap={tagsToTableItemMap} - deleteItems={deleteItems} - tableCaption={tableListTitle} - onTableChange={onTableChange} - onTableSearchChange={onTableSearchChange} - onSortChange={onSortChange} - addOrRemoveIncludeTagFilter={addOrRemoveIncludeTagFilter} - addOrRemoveExcludeTagFilter={addOrRemoveExcludeTagFilter} - clearTagSelection={clearTagSelection} - /> - - {/* Delete modal */} - {showDeleteModal && ( - - isDeletingItems={isDeletingItems} - entityName={entityName} - entityNamePlural={entityNamePlural} - items={selectedItems} - onConfirm={deleteSelectedItems} - onCancel={() => dispatch({ type: 'onCancelDeleteItems' })} - /> - )} -
- - ); -} - -const TableList = React.memo(TableListComp) as typeof TableListComp; - -export const TableListView = ({ - tableListTitle, - tableListDescription, - entityName, - entityNamePlural, - initialFilter, - headingId, - initialPageSize, - listingLimit, - urlStateEnabled = true, - customTableColumn, - emptyPrompt, - findItems, - createItem, - editItem, - deleteItems, - getDetailViewLink, - onClickTitle, - id: listingId, - contentEditor, - children, - titleColumnName, - additionalRightSideActions, - withoutPageTemplateWrapper, -}: TableListViewProps) => { - const PageTemplate = withoutPageTemplateWrapper - ? (React.Fragment as unknown as typeof KibanaPageTemplate) - : KibanaPageTemplate; - - const pageDataTestSubject = `${entityName}LandingPage`; - - const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); - - if (!hasInitialFetchReturned) { - return null; - } - return ( {tableListTitle}} description={tableListDescription} - rightSideItems={additionalRightSideActions?.slice(0, 2)} + rightSideItems={[ + renderCreateButton() ?? , + ...additionalRightSideActions?.slice(0, 2), + ]} data-test-subj="top-nav" /> {/* Any children passed to the component */} {children} - { - if (!hasInitialFetchReturned) { - setHasInitialFetchReturned(true); - } - }} - /> + {/* Too many items error */} + {showLimitError && ( + + )} + + {/* Error while fetching items */} + {showFetchError && renderFetchError()} + + {/* Table of items */} +
+ + dispatch={dispatch} + items={items} + isFetchingItems={isFetchingItems} + searchQuery={searchQuery} + tableColumns={tableColumns} + hasUpdatedAtMetadata={hasUpdatedAtMetadata} + tableSort={tableSort} + pagination={pagination} + selectedIds={selectedIds} + entityName={entityName} + entityNamePlural={entityNamePlural} + tagsToTableItemMap={tagsToTableItemMap} + deleteItems={deleteItems} + tableCaption={tableListTitle} + onTableChange={onTableChange} + onTableSearchChange={onTableSearchChange} + onSortChange={onSortChange} + addOrRemoveIncludeTagFilter={addOrRemoveIncludeTagFilter} + addOrRemoveExcludeTagFilter={addOrRemoveExcludeTagFilter} + clearTagSelection={clearTagSelection} + /> + + {/* Delete modal */} + {showDeleteModal && ( + + isDeletingItems={isDeletingItems} + entityName={entityName} + entityNamePlural={entityNamePlural} + items={selectedItems} + onConfirm={deleteSelectedItems} + onCancel={() => dispatch({ type: 'onCancelDeleteItems' })} + /> + )} +
); -}; +} + +const TableListView = React.memo(TableListViewComp) as typeof TableListViewComp; + +export { TableListView }; // eslint-disable-next-line import/no-default-export export default TableListView; From 4711f68b909a2bc688018d941c8cc3a469856c9c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 19 Apr 2023 08:21:04 -0500 Subject: [PATCH 052/202] merge branch --- .github/CODEOWNERS | 14 +- .i18nrc.json | 1 + NOTICE.txt | 5 +- api_docs/actions.devdocs.json | 8 +- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.devdocs.json | 576 ++++- api_docs/aiops.mdx | 7 +- api_docs/alerting.devdocs.json | 618 +++++- api_docs/alerting.mdx | 4 +- api_docs/apm.mdx | 2 +- api_docs/asset_manager.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.devdocs.json | 38 +- api_docs/cases.mdx | 4 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_chat.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.devdocs.json | 26 +- api_docs/content_management.mdx | 4 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.devdocs.json | 35 + api_docs/dashboard.mdx | 4 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.devdocs.json | 114 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.devdocs.json | 6 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.devdocs.json | 130 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 32 +- api_docs/deprecations_by_plugin.mdx | 27 +- api_docs/deprecations_by_team.mdx | 10 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.devdocs.json | 8 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/embeddable.devdocs.json | 21 + api_docs/embeddable.mdx | 4 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/exploratory_view.devdocs.json | 124 +- api_docs/exploratory_view.mdx | 4 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.devdocs.json | 30 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.devdocs.json | 24 + api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.devdocs.json | 45 +- api_docs/fleet.mdx | 4 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_alerts_as_data_utils.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_analytics_shippers_gainsight.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cell_actions.mdx | 2 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mocks.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_content_editor.mdx | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- ..._core_custom_branding_browser_internal.mdx | 2 +- ...kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- ...n_core_custom_branding_server_internal.mdx | 2 +- .../kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.devdocs.json | 8 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.devdocs.json | 5 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...bn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.devdocs.json | 24 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- .../kbn_core_lifecycle_browser.devdocs.json | 4 - api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- ...ore_saved_objects_api_browser.devdocs.json | 54 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- ...core_saved_objects_api_server.devdocs.json | 222 +- .../kbn_core_saved_objects_api_server.mdx | 4 +- ...d_objects_api_server_internal.devdocs.json | 32 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- ...bn_core_saved_objects_browser.devdocs.json | 16 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- ...kbn_core_saved_objects_common.devdocs.json | 24 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- ...kbn_core_saved_objects_server.devdocs.json | 22 + api_docs/kbn_core_saved_objects_server.mdx | 4 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- ...n_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- .../kbn_core_ui_settings_common.devdocs.json | 15 + api_docs/kbn_core_ui_settings_common.mdx | 4 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_dom_drag_drop.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_expandable_flyout.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_csv.devdocs.json | 214 ++ api_docs/kbn_generate_csv.mdx | 30 + api_docs/kbn_generate_csv_types.devdocs.json | 244 +++ api_docs/kbn_generate_csv_types.mdx | 30 + api_docs/kbn_guided_onboarding.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- .../kbn_language_documentation_popover.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.devdocs.json | 41 +- api_docs/kbn_ml_agg_utils.mdx | 4 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_number_utils.devdocs.json | 84 + api_docs/kbn_ml_number_utils.mdx | 30 + api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_random_sampler_utils.mdx | 2 +- api_docs/kbn_ml_route_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.devdocs.json | 50 +- api_docs/kbn_object_versioning.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_reporting_common.devdocs.json | 1306 ++++++++++++ api_docs/kbn_reporting_common.mdx | 39 + api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_saved_objects_settings.devdocs.json | 58 + api_docs/kbn_saved_objects_settings.mdx | 30 + api_docs/kbn_security_solution_side_nav.mdx | 2 +- ...kbn_security_solution_storybook_config.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- ...n_securitysolution_data_table.devdocs.json | 1862 +++++++++++++++++ api_docs/kbn_securitysolution_data_table.mdx | 42 + api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_grouping.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- ...n_securitysolution_list_utils.devdocs.json | 88 +- api_docs/kbn_securitysolution_list_utils.mdx | 4 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_types.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_std.devdocs.json | 56 + api_docs/kbn_std.mdx | 4 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.devdocs.json | 24 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.devdocs.json | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 4 +- api_docs/lens.devdocs.json | 44 +- api_docs/lens.mdx | 4 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.devdocs.json | 4 + api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.devdocs.json | 4 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.devdocs.json | 147 +- api_docs/observability.mdx | 4 +- api_docs/observability_shared.devdocs.json | 36 +- api_docs/observability_shared.mdx | 7 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 56 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.devdocs.json | 48 +- api_docs/rule_registry.mdx | 4 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.devdocs.json | 4 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 4 +- api_docs/security.mdx | 2 +- api_docs/security_solution.devdocs.json | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 4 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.devdocs.json | 98 +- api_docs/triggers_actions_ui.mdx | 4 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.devdocs.json | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_histogram.devdocs.json | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- dev_docs/tutorials/versioning_http_apis.mdx | 117 +- docs/developer/plugin-list.asciidoc | 2 +- docs/index-custom-title-page.html | 150 +- .../connectors/action-types/email.asciidoc | 6 +- docs/settings/alert-action-settings.asciidoc | 3 + .../public/app.tsx | 112 +- .../portable_dashboards_example/tsconfig.json | 3 +- fleet_packages.json | 2 +- package.json | 6 +- .../shippers/fullstory/src/load_snippet.ts | 2 +- packages/core/http/core-http-common/index.ts | 2 +- .../http/core-http-common/src/constants.ts | 3 +- .../http/core-http-common/src/versioning.ts | 15 +- .../src/router.ts | 11 +- .../core_versioned_route.test.ts | 97 +- .../versioned_router/core_versioned_route.ts | 74 +- .../versioned_router/core_versioned_router.ts | 28 +- .../handler_resolvers.test.ts | 35 + .../src/versioned_router/handler_resolvers.ts | 22 + .../inject_response_headers.ts | 26 + .../is_valid_route_version.test.ts | 21 +- .../is_valid_route_version.ts | 20 +- .../src/versioned_router/types.ts | 14 + .../tsconfig.json | 2 +- .../src/http_service.ts | 7 +- .../core-http-server/src/versioning/types.ts | 2 +- .../src/lib/aggregations/validation.test.ts | 17 + .../src/lib/aggregations/validation.ts | 8 + .../kibana_migrator.test.ts.snap | 5 - .../build_active_mappings.test.ts.snap | 10 - .../src/core/build_active_mappings.ts | 6 - .../core-ui-settings-common/index.ts | 2 + .../core-ui-settings-common/src/timezones.ts | 17 + .../src/settings/date_formats.ts | 11 +- .../src/field_maps/ecs_field_map.ts | 8 + packages/kbn-optimizer/limits.yml | 2 +- packages/kbn-saved-objects-settings/README.md | 3 + packages/kbn-saved-objects-settings/index.ts | 10 + .../kbn-saved-objects-settings/jest.config.js | 13 + .../kbn-saved-objects-settings/kibana.jsonc | 5 + .../kbn-saved-objects-settings/package.json | 6 + .../kbn-saved-objects-settings/tsconfig.json | 17 + .../__snapshots__/index.test.tsx.snap | 24 +- .../src/field_value_match/index.test.tsx | 6 +- .../src/field_value_match_any/index.test.tsx | 6 +- .../src/field_value_wildcard/index.test.tsx | 6 +- .../__snapshots__/list_header.test.tsx.snap | 24 +- .../__snapshots__/edit_modal.test.tsx.snap | 24 +- .../src/hooks/use_get_group_selector.tsx | 4 +- .../src/helpers/index.test.ts | 40 +- .../src/helpers/index.ts | 10 +- .../src/types/index.ts | 10 +- .../tsconfig.json | 3 +- .../file/file_upload/impl/src/file_upload.tsx | 2 +- renovate.json | 2 +- src/cli/serve/serve.js | 41 +- .../http/versioned_router.test.ts | 202 +- .../group2/check_registered_types.test.ts | 16 +- .../resources/base/bin/kibana-docker | 1 + src/dev/license_checker/config.ts | 3 +- src/dev/storybook/aliases.ts | 1 + .../partition_vis_component.test.tsx.snap | 10 +- .../components/partition_vis_component.tsx | 29 +- .../server/rpc/routes/routes.ts | 2 +- src/plugins/content_management/tsconfig.json | 2 +- .../options_list_cluster_settings_route.ts | 12 + .../dashboard_app/_dashboard_app_strings.ts | 130 -- .../public/dashboard_app/dashboard_router.tsx | 68 +- .../dashboard_listing.test.tsx.snap | 543 ----- .../listing/dashboard_listing.test.tsx | 212 -- .../listing/dashboard_listing.tsx | 383 ---- .../dashboard_listing_page.test.tsx | 127 ++ .../listing_page/dashboard_listing_page.tsx | 107 + .../dashboard_no_match.tsx | 0 .../get_dashboard_list_item_link.test.ts | 0 .../get_dashboard_list_item_link.ts | 0 .../top_nav/use_dashboard_menu_items.tsx | 2 +- .../_dashboard_listing_strings.ts | 144 ++ .../confirm_overlays.tsx | 4 +- .../dashboard_listing.test.tsx | 105 + .../dashboard_listing/dashboard_listing.tsx | 264 +++ .../dashboard_listing_empty_prompt.test.tsx | 111 + .../dashboard_listing_empty_prompt.tsx | 157 ++ .../dashboard_unsaved_listing.test.tsx | 23 +- .../dashboard_unsaved_listing.tsx | 21 +- .../public/dashboard_listing/index.tsx | 36 + src/plugins/dashboard/public/index.ts | 2 + src/plugins/dashboard/public/plugin.tsx | 6 +- .../dashboard_saved_object.stub.ts | 1 + .../__snapshots__/data_view.test.tsx.snap | 8 +- .../components/data_table.tsx | 17 +- .../data/server/saved_objects/query.ts | 16 +- .../server/saved_objects/schemas/query.ts | 32 + .../field_editor/form_fields/script_field.tsx | 7 +- .../preview/field_preview_context.tsx | 15 +- .../components/preview/preview_controller.ts | 2 + .../public/components/preview/types.ts | 5 + .../get_saved_object_counts.test.ts | 116 +- .../get_saved_object_counts.ts | 44 +- .../kibana_usage_collector.test.ts | 12 +- .../kibana_usage_collector.ts | 11 +- .../saved_objects_count_collector.test.ts | 7 +- .../saved_objects_count_collector.ts | 5 +- .../kibana_usage_collection/server/plugin.ts | 2 +- .../kibana_usage_collection/tsconfig.json | 1 + .../saved_objects_finder/common/index.ts | 3 +- .../saved_objects_finder/tsconfig.json | 1 + .../register_telemetry_saved_object.ts | 28 +- test/api_integration/apis/telemetry/index.ts | 2 +- test/api_integration/apis/telemetry/opt_in.ts | 65 +- .../apis/telemetry/telemetry_config.ts | 2 +- .../apis/telemetry/telemetry_last_reported.ts | 33 +- .../telemetry/telemetry_optin_notice_seen.ts | 22 +- test/functional/apps/context/_filters.ts | 10 +- test/functional/services/data_grid.ts | 2 + tsconfig.base.json | 4 + .../exploratory_view_example/public/app.tsx | 2 +- .../.storybook/main.js | 8 + .../kbn-securitysolution-data-table/README.md | 35 + .../common/constants.ts | 501 +++++ .../common/types/data_table/index.ts | 15 +- .../common/types/detail_panel.ts | 73 + .../common/types/header_actions/index.ts | 107 + .../common/types/index.ts | 19 + .../common/types/risk_scores.ts | 18 + .../common/types/session_view/index.ts | 13 + .../column_headers/default_headers.ts | 3 +- .../column_headers/helpers.test.tsx | 5 +- .../data_table/column_headers/helpers.tsx | 51 +- .../data_table/column_headers/translations.ts | 2 +- .../components/data_table/constants.ts | 9 + .../data_table/data_table.stories.tsx | 107 + .../components/data_table/helpers.test.tsx | 4 +- .../components/data_table/helpers.tsx | 11 +- .../components/data_table/index.test.tsx | 53 +- .../components/data_table/index.tsx | 73 +- .../components/data_table/pagination.ts | 0 .../components/data_table/types.ts | 4 +- .../components/data_table/utils.ts | 22 + .../components/toolbar/bulk_actions/types.ts | 39 + .../components/toolbar/unit/index.ts | 11 + .../components/toolbar/unit/styles.tsx | 17 + .../components/toolbar/unit/translations.ts | 14 + .../hooks/use_selector.tsx | 16 + .../kbn-securitysolution-data-table/index.ts | 43 + .../jest.config.js} | 10 +- .../kibana.jsonc | 5 + .../demo_data/endpoint/library_load_event.ts | 63 + ...cess_execution_malware_prevention_alert.ts | 80 + .../endpoint/registry_modification_event.ts | 64 + .../mock/demo_data/timeline.ts | 1117 ++++++++++ .../mock/global_state.ts | 57 + .../mock/header.ts | 144 ++ .../mock/mock_local_storage.ts | 27 + .../mock/mock_source.ts | 515 +++++ .../mock/mock_timeline_data.ts | 74 + .../mock/test_providers.tsx | 86 + .../package.json | 6 + .../store/data_table/actions.ts | 12 +- .../store/data_table/defaults.ts | 4 +- .../store/data_table/helpers.test.tsx | 7 +- .../store/data_table/helpers.ts | 5 +- .../store/data_table/index.ts | 0 .../store/data_table/inputs.ts | 0 .../store/data_table/model.ts | 8 +- .../store/data_table/reducer.ts | 1 + .../store/data_table/selectors.ts | 1 + .../store/data_table/translations.ts | 4 +- .../store/data_table/types.ts | 2 +- .../tsconfig.json | 24 + .../utils/use_mount_appended.ts | 31 + x-pack/packages/ml/agg_utils/tsconfig.json | 2 +- .../src/random_sampler_wrapper.test.ts | 18 +- .../src/random_sampler_wrapper.ts | 2 +- .../ml/url_state/src/url_state.test.tsx | 118 +- .../packages/ml/url_state/src/url_state.tsx | 15 +- .../server/action_type_registry.test.ts | 93 +- .../actions/server/actions_client.test.ts | 94 +- .../actions/server/actions_config.mock.ts | 1 + .../actions/server/actions_config.test.ts | 1 + .../plugins/actions/server/actions_config.ts | 2 + x-pack/plugins/actions/server/config.test.ts | 3 + x-pack/plugins/actions/server/config.ts | 1 + .../axios_utils_connection.test.ts | 1 + .../axios_utils_proxy.test.ts | 1 + .../server/lib/action_executor.test.ts | 59 +- .../server/lib/custom_host_settings.test.ts | 1 + .../lib/ensure_sufficient_license.test.ts | 6 + .../actions/server/lib/license_state.test.ts | 11 + .../server/lib/validate_with_schema.test.ts | 31 +- x-pack/plugins/actions/server/plugin.test.ts | 31 +- .../actions/server/saved_objects/index.ts | 9 +- .../server/saved_objects/mappings.json | 74 - .../actions/server/saved_objects/mappings.ts | 93 + .../transform_connectors_for_export.test.ts | 5 + x-pack/plugins/actions/server/types.ts | 8 +- x-pack/plugins/aiops/common/index.ts | 4 + x-pack/plugins/aiops/kibana.jsonc | 3 +- .../explain_log_rate_spikes_analysis.tsx | 15 +- .../field_stats_content.tsx | 191 ++ .../field_stats_popover.tsx | 89 + .../components/field_stats_popover/index.ts | 8 + .../spike_analysis_table.tsx | 57 +- .../spike_analysis_table_groups.tsx | 16 +- x-pack/plugins/aiops/public/hooks/use_data.ts | 52 +- x-pack/plugins/aiops/public/index.ts | 5 + .../aiops/public/shared_lazy_components.tsx | 7 +- x-pack/plugins/aiops/tsconfig.json | 1 + .../alerting/server/rules_client/lib/index.ts | 2 + .../format_legacy_actions.test.ts} | 240 ++- .../format_legacy_actions.ts | 153 ++ .../migrate_legacy_actions.test.ts | 357 ++++ .../migrate_legacy_actions.ts | 113 + .../retrieve_migrated_legacy_actions.mock.ts | 368 ++++ .../retrieve_migrated_legacy_actions.test.ts | 267 +++ .../retrieve_migrated_legacy_actions.ts | 117 ++ .../transform_legacy_actions.test.ts | 89 + .../transform_legacy_actions.ts | 59 + .../transform_to_notify_when.test.ts | 23 + .../transform_to_notify_when.ts | 25 + .../lib/siem_legacy_actions/types.ts | 50 + .../rules_client/methods/bulk_delete.ts | 24 +- .../rules_client/methods/bulk_disable.ts | 19 +- .../server/rules_client/methods/bulk_edit.ts | 19 +- .../rules_client/methods/bulk_enable.ts | 19 +- .../server/rules_client/methods/create.ts | 2 +- .../server/rules_client/methods/delete.ts | 7 + .../server/rules_client/methods/disable.ts | 23 +- .../server/rules_client/methods/enable.ts | 38 +- .../server/rules_client/methods/find.ts | 37 +- .../server/rules_client/methods/get.ts | 16 +- .../server/rules_client/methods/resolve.ts | 16 + .../server/rules_client/methods/update.ts | 19 +- .../rules_client/tests/bulk_delete.test.ts | 53 + .../rules_client/tests/bulk_disable.test.ts | 61 +- .../rules_client/tests/bulk_edit.test.ts | 66 + .../rules_client/tests/bulk_enable.test.ts | 55 +- .../server/rules_client/tests/delete.test.ts | 35 + .../server/rules_client/tests/disable.test.ts | 45 + .../server/rules_client/tests/enable.test.ts | 68 + .../server/rules_client/tests/find.test.ts | 43 + .../server/rules_client/tests/get.test.ts | 76 + .../server/rules_client/tests/resolve.test.ts | 86 + .../server/rules_client/tests/test_helpers.ts | 15 + .../server/rules_client/tests/update.test.ts | 71 + .../alerting/server/saved_objects/mappings.ts | 128 +- .../saved_objects/rules_settings_mappings.ts | 58 +- x-pack/plugins/apm/kibana.jsonc | 6 +- .../plugins/apm/public/application/index.tsx | 1 + .../components/routing/apm_error_boundary.tsx | 4 +- .../routing/templates/apm_main_template.tsx | 6 +- .../templates/settings_template.stories.tsx | 2 +- .../apm_plugin/mock_apm_plugin_context.tsx | 1 + x-pack/plugins/apm/public/plugin.ts | 8 +- x-pack/plugins/apm/readme.md | 14 +- x-pack/plugins/apm/tsconfig.json | 1 + .../cases/common/api/cases/comment/files.ts | 16 +- .../cases/common/constants/mime_types.ts | 8 +- x-pack/plugins/cases/common/types.ts | 1 + x-pack/plugins/cases/public/application.tsx | 21 +- .../client/attachment_framework/types.ts | 28 +- .../ui/get_all_cases_selector_modal.tsx | 6 +- .../cases/public/client/ui/get_cases.tsx | 6 +- .../public/client/ui/get_cases_context.tsx | 12 +- .../client/ui/get_create_case_flyout.tsx | 6 +- .../public/client/ui/get_recent_cases.tsx | 6 +- .../public/common/mock/test_providers.tsx | 54 +- .../public/common/use_cases_toast.test.tsx | 20 + .../cases/public/common/use_cases_toast.tsx | 3 + .../cases/public/components/app/index.tsx | 10 +- .../components/case_action_bar/actions.tsx | 4 + .../case_view/case_view_page.test.tsx | 3 +- .../components/case_view/case_view_page.tsx | 2 + .../case_view/case_view_tabs.test.tsx | 64 +- .../components/case_view/case_view_tabs.tsx | 42 +- .../components/case_view_files.test.tsx | 114 + .../case_view/components/case_view_files.tsx | 104 + .../components/case_view/translations.ts | 4 + .../public/components/cases_context/index.tsx | 54 +- .../public/components/files/add_file.test.tsx | 241 +++ .../public/components/files/add_file.tsx | 141 ++ .../files/file_delete_button.test.tsx | 155 ++ .../components/files/file_delete_button.tsx | 62 + .../files/file_download_button.test.tsx | 57 + .../components/files/file_download_button.tsx | 46 + .../components/files/file_name_link.test.tsx | 58 + .../components/files/file_name_link.tsx | 44 + .../components/files/file_preview.test.tsx | 38 + .../public/components/files/file_preview.tsx | 56 + .../components/files/file_type.test.tsx | 187 ++ .../public/components/files/file_type.tsx | 106 + .../components/files/files_table.test.tsx | 233 +++ .../public/components/files/files_table.tsx | 83 + .../files/files_utility_bar.test.tsx | 42 + .../components/files/files_utility_bar.tsx | 36 + .../public/components/files/translations.tsx | 115 + .../cases/public/components/files/types.ts | 12 + .../files/use_file_preview.test.tsx | 54 + .../components/files/use_file_preview.tsx | 17 + .../files/use_files_table_columns.test.tsx | 73 + .../files/use_files_table_columns.tsx | 71 + .../public/components/files/utils.test.tsx | 97 + .../cases/public/components/files/utils.tsx | 61 + .../lens/use_lens_open_visualization.tsx | 3 + .../components/property_actions/index.tsx | 24 +- .../user_actions/comment/comment.test.tsx | 195 +- .../comment/registered_attachments.tsx | 52 +- .../alert_property_actions.tsx | 6 +- .../property_actions.test.tsx | 2 + .../property_actions/property_actions.tsx | 4 +- ...ered_attachments_property_actions.test.tsx | 19 +- ...egistered_attachments_property_actions.tsx | 11 +- .../user_comment_property_actions.tsx | 5 + .../cases/public/containers/__mocks__/api.ts | 10 + .../cases/public/containers/api.test.tsx | 27 + x-pack/plugins/cases/public/containers/api.ts | 17 + .../cases/public/containers/constants.ts | 4 + .../plugins/cases/public/containers/mock.ts | 15 + .../cases/public/containers/translations.ts | 8 + .../use_delete_file_attachment.test.tsx | 120 ++ .../containers/use_delete_file_attachment.tsx | 43 + .../use_get_case_file_stats.test.tsx | 64 + .../containers/use_get_case_file_stats.tsx | 57 + .../containers/use_get_case_files.test.tsx | 70 + .../public/containers/use_get_case_files.tsx | 61 + x-pack/plugins/cases/public/files/index.ts | 3 + .../public/internal_attachments/index.ts | 15 + x-pack/plugins/cases/public/plugin.ts | 7 + x-pack/plugins/cases/public/types.ts | 4 +- .../cases/server/client/alerts/types.ts | 2 +- .../server/client/attachments/delete.test.ts | 13 - .../cases/server/client/attachments/delete.ts | 1 - .../cases/server/client/cases/delete.test.ts | 32 +- .../cases/server/client/cases/delete.ts | 3 +- .../common/limiter_checker/test_utils.ts | 2 +- .../server/services/alerts/index.test.ts | 42 + .../cases/server/services/alerts/index.ts | 32 +- x-pack/plugins/cases/server/services/mocks.ts | 4 +- x-pack/plugins/cases/tsconfig.json | 4 + .../cloud_defend/public/common/utils.test.ts | 3 +- .../components/control_general_view/index.tsx | 5 + .../control_general_view/translations.ts | 8 + .../index.test.tsx | 7 + .../control_general_view_selector/index.tsx | 28 +- .../hooks/policy_schema.json | 20 - .../control_yaml_view/index.test.tsx | 2 +- .../plugins/cloud_defend/public/test/mocks.ts | 4 +- x-pack/plugins/cloud_defend/public/types.ts | 9 +- .../server/assets/fullstory_library.js | 7 +- ...on_product_no_results_magnifying_glass.svg | 30 + .../public/common/constants.ts | 1 + .../use_cloud_posture_table.ts | 12 + .../public/components/empty_state.tsx | 80 + .../public/components/no_findings_states.tsx | 2 +- .../components/no_vulnerabilities_states.tsx | 2 +- .../public/components/test_subjects.ts | 1 + .../components/vulnerability_badges.tsx | 33 +- .../latest_findings_container.tsx | 2 + .../latest_findings_table.test.tsx | 6 +- .../latest_findings/latest_findings_table.tsx | 25 +- .../findings_by_resource_container.tsx | 3 +- .../findings_by_resource_table.test.tsx | 7 +- .../findings_by_resource_table.tsx | 22 +- .../resource_findings_container.tsx | 21 +- .../resource_findings_table.test.tsx | 7 +- .../resource_findings_table.tsx | 23 +- .../pages/configurations/test_subjects.ts | 5 - .../public/pages/vulnerabilities/types.ts | 98 +- .../public/pages/vulnerabilities/utils.ts | 27 + .../pages/vulnerabilities/vulnerabilities.tsx | 159 +- .../vulnerability_finding_flyout.tsx | 149 ++ .../vulnerability_json_tab.tsx | 32 + .../vulnerability_overview_tab.tsx | 260 +++ .../latest_vulnerabilities_transforms.ts | 2 +- .../common/connectors/native_connectors.ts | 142 +- .../ml_inference_pipeline/index.test.ts | 70 + .../common/ml_inference_pipeline/index.ts | 13 +- .../common/types/analytics.ts | 4 +- .../common/types/connectors.ts | 41 +- .../common/types/pipelines.ts | 3 + ..._analytics_events_exist_api_logic.test.ts} | 8 +- .../check_analytics_events_exist_api_logic.ts | 31 + .../check_analytics_events_index_api_logic.ts | 31 - ...tics_collection_no_events_callout.test.tsx | 61 + ...analytics_collection_no_events_callout.tsx | 78 + ...ollection_no_events_callout_logic.test.ts} | 24 +- ...ics_collection_no_events_callout_logic.tsx | 47 + .../analytics_collection_overview.test.tsx | 17 + .../analytics_collection_overview.tsx | 6 +- .../analytics_events_index_exists_logic.tsx | 52 - .../__mocks__/search_indices.mock.ts | 33 +- .../__mocks__/view_index.mock.ts | 33 +- .../api/engines/fetch_indices_api_logic.ts | 2 +- .../pipelines/create_ml_inference_pipeline.ts | 10 +- .../components/engine/engine_overview.tsx | 130 -- .../engine/engine_overview_logic.test.ts | 487 ----- .../engine/engine_overview_logic.ts | 93 - .../components/engine/engine_router.tsx | 2 +- .../components/engine/engine_view.tsx | 12 +- .../connector_configuration_config.tsx | 4 +- .../connector_configuration_field.tsx | 145 ++ .../connector_configuration_form.tsx | 91 +- .../connector_configuration_logic.test.ts | 322 ++- .../connector_configuration_logic.ts | 93 +- .../connector/connector_scheduling.tsx | 2 +- .../automatic_crawl_scheduler.tsx | 3 +- .../ml_inference/ml_inference_logic.test.ts | 87 +- .../ml_inference/ml_inference_logic.ts | 15 +- .../pipelines/ml_inference/test_pipeline.tsx | 25 +- .../enterprise_search_content/routes.ts | 3 +- .../enterprise_search_cron_editor.tsx | 4 +- .../applications/shared/layout/nav.test.tsx | 190 +- .../public/applications/shared/layout/nav.tsx | 112 +- ...test.ts => analytics_events_exist.test.ts} | 16 +- ...ex_exists.ts => analytics_events_exist.ts} | 8 +- .../server/lib/connectors/start_sync.ts | 4 +- .../lib/engines/field_capabilities.test.ts | 1 + .../server/lib/engines/field_capabilities.ts | 1 + .../routes/enterprise_search/analytics.ts | 6 +- .../e2e/journeys/step_duration.journey.ts | 23 +- x-pack/plugins/exploratory_view/kibana.jsonc | 3 +- .../embeddable/embeddable.tsx | 2 +- .../exploratory_view/embeddable/index.tsx | 2 +- .../embeddable/use_app_data_view.ts | 2 +- .../embeddable/use_local_data_view.ts | 2 +- .../header/add_to_case_action.tsx | 5 +- .../exploratory_view/hooks/use_add_to_case.ts | 2 +- .../hooks/use_lens_formula_helper.ts | 2 +- .../shared/exploratory_view/index.tsx | 6 +- .../columns/chart_type_select.tsx | 2 +- .../series_editor/columns/chart_types.tsx | 2 +- .../public/components/shared/index.tsx | 2 +- .../public/hooks/use_kibana_space.tsx | 2 +- .../plugins/exploratory_view/public/plugin.ts | 9 +- x-pack/plugins/exploratory_view/tsconfig.json | 2 +- x-pack/plugins/fleet/common/constants/epm.ts | 3 - .../plugins/fleet/common/constants/index.ts | 1 + .../fleet/common/constants/locators.ts | 19 + x-pack/plugins/fleet/common/index.ts | 2 + .../services/validate_package_policy.test.ts | 29 +- .../services/validate_package_policy.ts | 6 +- .../plugins/fleet/common/types/models/epm.ts | 2 + .../common/types/models/package_policy.ts | 6 + .../steps/add_fleet_server_host.tsx | 21 +- .../steps/get_started.tsx | 21 +- .../utils/install_command_utils.test.ts | 48 +- .../utils/install_command_utils.ts | 2 + .../steps/step_configure_package.test.tsx | 27 +- .../steps/step_configure_package.tsx | 6 +- .../steps/step_define_package_policy.tsx | 6 +- .../components/page_steps/add_integration.tsx | 4 +- .../single_page_layout/index.tsx | 4 +- .../edit_package_policy_page/index.tsx | 4 +- .../components/agent_dashboard_link.test.tsx | 11 + .../components/agent_dashboard_link.tsx | 22 +- .../components/dashboards_buttons.tsx | 53 + .../components/search_and_filter_bar.test.tsx | 11 + .../components/search_and_filter_bar.tsx | 21 +- .../components/data_stream_row_actions.tsx | 10 +- .../output_form_validators.test.tsx | 4 +- .../output_form_validators.tsx | 3 +- .../plugins/fleet/public/constants/index.ts | 5 +- x-pack/plugins/fleet/public/hooks/index.ts | 1 + .../plugins/fleet/public/hooks/use_locator.ts | 6 +- .../security_solution/to_v8_8_0.test.ts | 4 +- .../migrations/security_solution/to_v8_8_0.ts | 2 +- ...kage_policies_to_agent_permissions.test.ts | 54 +- .../package_policies_to_agent_permissions.ts | 26 +- .../services/api_keys/logstash_api_keys.ts | 6 +- .../fleet/server/services/data_streams.ts | 2 +- .../server/services/epm/archive/parse.test.ts | 51 + .../server/services/epm/archive/parse.ts | 10 +- .../elasticsearch/template/template.test.ts | 22 + .../epm/elasticsearch/template/template.ts | 8 + .../fleet/server/services/epm/fields/field.ts | 4 + .../server/services/package_policy.test.ts | 36 + .../fleet/server/services/package_policy.ts | 24 +- .../server/types/models/package_policy.ts | 2 + .../http_api/log_alerts/chart_preview_data.ts | 2 +- x-pack/plugins/infra/kibana.jsonc | 16 +- .../components/logs_ratio_chart.tsx | 236 +++ .../alert_details_app_section/index.tsx | 257 ++- .../hooks/use_chart_preview_data.tsx | 53 + .../plugins/infra/public/apps/metrics_app.tsx | 4 +- .../logging/inline_log_view_splash_page.tsx | 2 +- .../infra/public/components/page_template.tsx | 4 +- .../subscription_splash_content.tsx | 2 +- .../containers/logs/log_stream/index.ts | 28 +- .../pages/link_to/link_to_logs.test.tsx | 2 +- .../log_entry_categories/page_content.tsx | 2 +- .../logs/log_entry_rate/page_content.tsx | 2 +- .../pages/logs/shared/page_template.tsx | 4 +- .../metadata/add_metadata_filter_button.tsx | 120 ++ .../metadata/build_metadata_filter.ts | 46 + .../host_details_flyout/metadata/table.tsx | 13 +- .../hosts/components/unified_search_bar.tsx | 14 +- .../hosts/hooks/use_hosts_table.test.ts | 4 +- .../metrics/hosts/hooks/use_hosts_table.tsx | 4 +- .../public/pages/metrics/page_template.tsx | 4 +- x-pack/plugins/infra/public/plugin.ts | 2 +- x-pack/plugins/infra/public/types.ts | 6 + x-pack/plugins/infra/tsconfig.json | 1 + .../plugins/kubernetes_security/kibana.jsonc | 2 +- .../lens/public/app_plugin/lens_top_nav.tsx | 12 +- .../lens/public/app_plugin/share_action.ts | 8 + .../datasources/form_based/form_based.tsx | 6 +- .../datasources/form_based/layer_settings.tsx | 161 +- .../datasources/form_based/layerpanel.tsx | 25 +- .../form_based}/sampling_icon.tsx | 0 .../public/datasources/form_based/utils.tsx | 2 +- .../editor_frame/config_panel/layer_panel.tsx | 62 +- .../dataview_picker/dataview_picker.tsx | 103 +- .../dataview_picker/trigger.tsx | 101 + .../legend/legend_settings_popover.tsx | 18 +- x-pack/plugins/lens/public/types.ts | 8 +- .../partition/layer_settings.test.tsx | 9 +- .../partition/layer_settings.tsx | 59 +- .../visualizations/partition/to_expression.ts | 5 +- .../visualizations/partition/toolbar.tsx | 5 +- .../partition/visualization.test.ts | 21 +- .../partition/visualization.tsx | 2 +- .../visualizations/xy/layer_settings.tsx | 53 + .../visualizations/xy/visualization.test.ts | 131 +- .../visualizations/xy/visualization.tsx | 21 + .../xy/xy_config_panel/layer_header.tsx | 31 +- .../components/builder/entry_renderer.tsx | 10 +- x-pack/plugins/maps/common/constants.ts | 6 + x-pack/plugins/maps/common/i18n_getters.ts | 12 - .../public/api/create_layer_descriptors.ts | 19 +- x-pack/plugins/maps/public/api/ems.ts | 7 +- .../maps/public/classes/layers/index.ts | 2 +- .../assign_feature_ids.test.ts | 3 +- .../assign_feature_ids.ts | 5 +- .../geojson_vector_layer.tsx | 2 +- .../public/classes/layers/wizards/index.ts | 2 +- .../map_container/map_container.tsx | 1 + .../maps/public/embeddable/map_component.tsx | 85 +- .../maps/public/embeddable/map_embeddable.tsx | 21 +- .../embeddable/map_embeddable_factory.ts | 11 +- .../maps/public/feature_catalogue_entry.ts | 5 +- x-pack/plugins/maps/public/index.ts | 6 +- x-pack/plugins/maps/public/kibana_services.ts | 29 +- .../maps/public/lazy_load_bundle/index.ts | 106 - .../public/lazy_load_bundle/lazy/index.ts | 17 - .../public/legacy_visualizations/index.ts | 1 - .../region_map/region_map_visualization.tsx | 8 +- .../tile_map/tile_map_visualization.tsx | 10 +- x-pack/plugins/maps/public/locators.ts | 257 --- .../locators/map_locator/get_location.ts | 50 + .../map_locator/locator_definition.test.ts} | 8 +- .../map_locator/locator_definition.ts | 22 + .../maps/public/locators/map_locator/types.ts | 64 + .../region_map_locator/get_location.ts | 55 + .../region_map_locator/locator_definition.ts | 24 + .../locators/region_map_locator/types.ts | 33 + .../locators/tile_map_locator/get_location.ts | 52 + .../tile_map_locator/locator_definition.ts | 24 + .../public/locators/tile_map_locator/types.ts | 31 + .../maps/public/map_attribute_service.ts | 5 +- x-pack/plugins/maps/public/plugin.ts | 27 +- .../routes/list_page/maps_list_view.tsx | 9 +- .../routes/map_page/map_app/map_app.tsx | 12 +- .../map_page/saved_map/get_breadcrumbs.tsx | 4 +- .../public/routes/map_page/top_nav_config.tsx | 4 +- .../action.ts} | 34 +- .../filter_by_map_extent/is_compatible.ts | 17 + .../modal.tsx} | 13 +- .../filter_by_map_extent/types.ts | 16 + .../public/trigger_actions/get_maps_link.ts | 50 + .../synchronize_movement/action.ts | 40 + .../synchronize_movement/is_compatible.ts | 32 + .../modal.tsx} | 13 +- .../synchronize_movement/types.ts | 12 + .../synchronize_movement_action.tsx | 69 - .../visualize_geo_field_action.ts | 50 +- x-pack/plugins/maps/public/util.ts | 5 +- .../plugins/ml/server/lib/ml_client/types.ts | 11 +- .../providers/trained_models.ts | 41 + .../common/field_names/infra_metrics.ts | 3 + x-pack/plugins/observability/kibana.jsonc | 28 +- .../hideable_react_query_dev_tools.tsx | 2 +- .../public/application/index.tsx | 2 +- .../observability/public/data/slo/common.ts | 16 +- .../hooks/slo/use_fetch_rules_for_slo.ts | 90 + .../public/pages/alerts/alerts.tsx | 15 +- .../slo_edit/components/slo_edit_form.tsx | 17 +- .../public/pages/slo_edit/slo_edit.test.tsx | 137 ++ .../components/badges/slo_badges.stories.tsx | 3 +- .../slos/components/badges/slo_badges.tsx | 12 +- .../slo_indicator_type_badge.stories.tsx | 2 +- .../badges/slo_rules_badge.stories.tsx | 35 + .../components/badges/slo_rules_badge.tsx | 41 + .../badges/slo_time_window_badge.stories.tsx | 2 +- .../pages/slos/components/slo_list_item.tsx | 29 +- .../pages/slos/components/slo_list_items.tsx | 18 +- .../pages/slos/components/slo_sparkline.tsx | 20 +- .../pages/slos/components/slo_summary.tsx | 35 +- x-pack/plugins/observability/public/plugin.ts | 42 +- .../kibana_react.storybook_decorator.tsx | 1 + .../slo_mappings_template.ts | 2 + .../slo_settings_template.ts | 6 +- .../observability/server/assets/constants.ts | 6 +- .../index_templates/slo_index_templates.ts | 2 + .../ingest_templates/slo_pipeline_template.ts | 2 + .../slo_transform_template.ts | 2 + .../lib/rules/slo_burn_rate/executor.test.ts | 3 + .../lib/rules/slo_burn_rate/executor.ts | 3 + .../lib/rules/slo_burn_rate/field_map.ts | 23 + .../lib/rules/slo_burn_rate/register.ts | 3 +- x-pack/plugins/observability/server/plugin.ts | 9 +- .../apm_transaction_duration.test.ts.snap | 12 +- .../apm_transaction_error_rate.test.ts.snap | 12 +- .../__snapshots__/kql_custom.test.ts.snap | 12 +- x-pack/plugins/observability/tsconfig.json | 4 +- .../public/hooks/use_track_metric.tsx | 91 + .../observability_shared/public/index.ts | 9 +- .../observability_shared/tsconfig.json | 2 + x-pack/plugins/profiling/kibana.jsonc | 6 +- .../profiling_app_page_template/index.tsx | 4 +- x-pack/plugins/profiling/public/plugin.tsx | 2 +- .../public/routing/router_error_boundary.tsx | 4 +- x-pack/plugins/profiling/public/types.ts | 6 + x-pack/plugins/profiling/tsconfig.json | 1 + .../reporting_panel_content.tsx | 13 +- .../alert_data_client/alerts_client.mock.ts | 1 + .../server/alert_data_client/alerts_client.ts | 80 +- .../tests/remove_alerts_from_case.test.ts | 109 - .../tests/remove_cases_from_alerts.test.ts | 179 ++ .../security_solution/common/constants.ts | 1 + .../response_schema.ts | 3 - .../review_rule_upgrade/response_schema.ts | 9 - .../diff/three_way_diff/three_way_diff.ts | 14 +- .../three_way_diff/three_way_diff_outcome.ts | 14 +- .../common/endpoint/models/policy_config.ts | 3 +- .../models/policy_config_helpers.test.ts | 2 +- .../common/endpoint/types/index.ts | 1 + .../common/experimental_features.ts | 6 + .../security_solution/index.ts | 21 +- .../security_solution/users/common/index.ts | 14 +- .../security_solution/users/index.ts | 7 +- .../users/managed_details/index.ts | 33 + .../{details => observed_details}/index.ts | 4 +- .../common/types/header_actions/index.ts | 2 +- .../security_solution/common/types/index.ts | 1 - .../e2e/dashboards/entity_analytics.cy.ts | 8 +- .../detection_page_filters.cy.ts | 22 +- .../cypress/screens/common/filter_group.ts | 3 + .../cypress/tasks/common/filter_group.ts | 6 +- .../filter/cell_action/filter_in.test.ts | 3 +- .../filter/cell_action/filter_out.test.ts | 3 +- .../cell_action/toggle_column.test.ts | 3 +- .../cell_action/toggle_column.ts | 7 +- .../security_solution/public/app/types.ts | 2 +- .../control_columns/row_action/index.test.tsx | 2 +- .../control_columns/row_action/index.tsx | 2 +- .../transform_control_columns.tsx | 5 +- .../drag_and_drop/draggable_wrapper.test.tsx | 3 +- .../drag_and_drop/draggable_wrapper.tsx | 2 +- .../components/drag_and_drop/helpers.ts | 2 +- .../event_details/event_fields_browser.tsx | 3 +- .../events_tab/events_query_tab_body.test.tsx | 3 +- .../events_tab/events_query_tab_body.tsx | 7 +- .../events_viewer/default_model.tsx | 4 +- .../components/events_viewer/helpers.tsx | 4 +- .../components/events_viewer/index.test.tsx | 2 +- .../common/components/events_viewer/index.tsx | 53 +- .../events_viewer/right_top_menu.tsx | 4 +- .../events_viewer/selectors/index.ts | 2 +- .../summary_view_select/index.tsx | 2 +- .../events_viewer/use_alert_bulk_actions.tsx | 2 +- .../events_viewer/use_timelines_events.tsx | 3 +- .../components/filter_group/buttons.tsx | 28 +- .../common/components/filter_group/config.ts | 4 +- .../components/filter_group/context_menu.tsx | 15 +- .../filter_group/filter_group_context.tsx | 2 + .../filter_group/filters_changed_banner.tsx | 30 +- .../common/components/filter_group/index.tsx | 136 +- .../components/filter_group/translations.ts | 28 + .../common/components/filter_group/types.ts | 2 +- .../header_actions/actions.test.tsx | 2 +- .../components/header_actions/actions.tsx | 3 +- .../ml/anomaly/anomaly_table_provider.tsx | 4 +- .../__snapshots__/anomaly_score.test.tsx.snap | 1 + .../anomaly_scores.test.tsx.snap | 3 +- .../ml/score/anomaly_score.test.tsx | 3 - .../components/ml/score/anomaly_score.tsx | 4 +- .../components/ml/score/anomaly_scores.tsx | 3 +- .../__snapshots__/index.test.tsx.snap | 2 +- .../sessions_viewer/default_headers.ts | 10 +- .../components/sessions_viewer/index.test.tsx | 2 +- .../components/sessions_viewer/index.tsx | 5 +- .../sessions_viewer/translations.ts | 6 +- .../components/sessions_viewer/types.ts | 4 +- .../bulk_actions/alert_bulk_actions.tsx | 11 +- .../toolbar/bulk_actions/translations.ts | 3 +- .../common/components/top_n/helpers.test.tsx | 2 +- .../public/common/components/top_n/helpers.ts | 2 +- .../common/components/top_n/index.test.tsx | 2 +- .../use_first_last_seen.test.ts | 19 + .../use_first_last_seen.tsx | 6 +- .../hooks/flyout/use_init_flyout_url_param.ts | 9 +- .../hooks/flyout/use_sync_flyout_url_param.ts | 9 +- .../common/hooks/use_data_table_filters.ts | 16 +- .../lib/cell_actions/add_to_timeline.tsx | 2 +- .../public/common/lib/cell_actions/copy.tsx | 2 +- .../default_cell_actions.test.tsx | 2 +- .../common/lib/cell_actions/field_value.tsx | 2 +- .../common/lib/cell_actions/filter_for.tsx | 2 +- .../common/lib/cell_actions/filter_out.tsx | 2 +- .../register_alerts_table_configuration.tsx | 2 +- .../public/common/mock/global_state.ts | 2 +- .../common/mock/storybook_providers.tsx | 68 + .../public/common/mock/timeline_results.ts | 2 +- .../data_table/epic_local_storage.test.tsx | 23 +- .../store/data_table/epic_local_storage.ts | 22 +- .../public/common/store/reducer.ts | 4 +- .../public/common/store/store.ts | 2 +- .../public/common/store/types.ts | 2 +- .../pages/rule_details/index.tsx | 10 +- .../components/alerts_table/actions.tsx | 2 +- .../alerts_table/alerts_grouping.tsx | 4 +- .../alerts_table/default_config.tsx | 4 +- .../alerts_table/grouping_settings/index.tsx | 2 +- .../components/alerts_table/index.test.tsx | 2 +- .../components/alerts_table/index.tsx | 14 +- .../alert_context_menu.test.tsx | 3 +- .../timeline_actions/alert_context_menu.tsx | 2 +- .../use_add_bulk_to_timeline.tsx | 6 +- .../eql_query_bar/eql_query_bar.test.tsx | 2 +- .../rule_preview/preview_histogram.test.tsx | 2 +- .../rules/rule_preview/preview_histogram.tsx | 3 +- .../components/take_action_dropdown/index.tsx | 2 +- .../render_cell_value.tsx | 5 +- .../use_actions_column.tsx | 2 +- .../use_alert_actions.tsx | 2 +- .../use_bulk_actions.tsx | 2 +- .../use_cell_actions.tsx | 5 +- .../use_persistent_controls.tsx | 12 +- .../public/detections/index.ts | 4 +- .../detection_engine/detection_engine.tsx | 10 +- .../stat_items/use_kpi_matrix_status.ts | 4 +- .../explore/components/stat_items/utils.tsx | 8 +- .../components/kpi_hosts/common/index.tsx | 4 +- .../hosts/pages/details/details_tabs.test.tsx | 2 +- .../hosts/pages/details/details_tabs.tsx | 2 +- .../explore/hosts/pages/details/index.tsx | 4 +- .../public/explore/hosts/pages/hosts.tsx | 4 +- .../public/explore/hosts/pages/hosts_tabs.tsx | 2 +- .../pages/navigation/sessions_tab_body.tsx | 2 +- .../security_solution/public/explore/index.ts | 2 +- .../network/pages/details/details_tabs.tsx | 2 +- .../pages/navigation/network_routes.tsx | 2 +- .../public/explore/network/pages/network.tsx | 4 +- .../index.test.tsx | 8 +- .../{details => observed_details}/index.tsx | 19 +- .../translations.ts | 0 .../users/pages/details/details_tabs.tsx | 2 +- .../explore/users/pages/details/index.tsx | 7 +- .../public/explore/users/pages/users_tabs.tsx | 2 +- .../components/user_entity_overview.test.tsx | 7 +- .../right/components/user_entity_overview.tsx | 4 +- .../security_solution/public/helpers.tsx | 4 +- .../public/kubernetes/pages/index.tsx | 2 +- .../policy/models/advanced_policy_schema.ts | 11 + .../policy/store/policy_details/index.test.ts | 2 +- .../components/graph_overlay/index.test.tsx | 2 +- .../components/graph_overlay/index.tsx | 3 +- .../side_panel/hooks/use_detail_panel.tsx | 2 +- .../timelines/components/side_panel/index.tsx | 8 +- .../new_user_detail/__mocks__/index.ts | 105 + .../side_panel/new_user_detail/columns.tsx | 133 ++ .../side_panel/new_user_detail/constants.ts | 12 + .../side_panel/new_user_detail/hooks.test.ts | 61 + .../side_panel/new_user_detail/hooks.ts | 236 +++ .../new_user_detail/managed_user.test.tsx | 81 + .../new_user_detail/managed_user.tsx | 157 ++ .../new_user_detail/observed_user.test.tsx | 63 + .../new_user_detail/observed_user.tsx | 119 ++ .../new_user_detail/risk_score_field.test.tsx | 46 + .../new_user_detail/risk_score_field.tsx | 68 + .../new_user_detail/translations.ts | 222 ++ .../side_panel/new_user_detail/types.ts | 53 + .../user_details_content.stories.tsx | 157 ++ .../user_details_content.test.tsx | 130 ++ .../new_user_detail/user_details_content.tsx | 182 ++ .../user_details/expandable_user.tsx | 4 +- .../side_panel/user_details/index.tsx | 27 + .../side_panel/user_details/translations.ts | 15 + .../side_panel/user_details/types.ts | 1 + .../timeline/body/events/stateful_event.tsx | 3 +- .../alert_field_badge/index.test.tsx | 2 +- .../alert_renderer/helpers/index.test.tsx | 3 +- .../renderers/reason_column_renderer.test.tsx | 3 +- .../body/renderers/reason_column_renderer.tsx | 2 +- .../use_session_view.test.tsx | 2 +- .../session_tab_content/use_session_view.tsx | 5 +- .../timelines/containers/helpers.test.ts | 2 +- .../public/timelines/containers/helpers.ts | 2 +- .../containers/local_storage/index.test.ts | 5 +- .../containers/local_storage/index.tsx | 11 +- .../containers/local_storage/types.ts | 3 +- .../public/timelines/store/timeline/types.ts | 2 +- .../plugins/security_solution/public/types.ts | 3 + .../endpoint/endpoint_app_context_services.ts | 6 +- .../endpoint/lib/policy/license_watch.test.ts | 26 +- .../endpoint/lib/policy/license_watch.ts | 6 + .../server/endpoint/mocks.ts | 2 + .../fleet_integration.test.ts | 13 +- .../fleet_integration/fleet_integration.ts | 10 +- .../handlers/create_default_policy.test.ts | 7 +- .../handlers/create_default_policy.ts | 7 +- .../api/get_prebuilt_rules_status/route.ts | 4 +- .../route.test.ts | 13 - .../api/review_rule_installation/route.ts | 2 +- .../api/review_rule_upgrade/route.ts | 28 +- .../logic/diff/calculate_rule_diff.ts | 11 +- .../algorithms/simple_diff_algorithm.ts | 24 +- .../calculation/calculate_rule_fields_diff.ts | 74 +- .../calculation/diff_calculation_helpers.ts | 8 +- .../update_prebuilt_rules.test.ts | 16 +- .../rule_objects/update_prebuilt_rules.ts | 19 +- .../routes/__mocks__/request_responses.ts | 356 +--- .../rule_actions_legacy/index.ts | 2 - ...gacy_get_bulk_rule_actions_saved_object.ts | 84 - .../api/rules/bulk_actions/route.test.ts | 13 - .../api/rules/bulk_actions/route.ts | 111 +- .../api/rules/bulk_delete_rules/route.ts | 14 +- .../api/rules/bulk_patch_rules/route.test.ts | 16 - .../api/rules/bulk_patch_rules/route.ts | 10 +- .../api/rules/bulk_update_rules/route.test.ts | 15 - .../api/rules/bulk_update_rules/route.ts | 10 +- .../api/rules/delete_rule/route.test.ts | 15 +- .../api/rules/delete_rule/route.ts | 14 +- .../api/rules/find_rules/route.ts | 14 +- .../api/rules/patch_rule/route.test.ts | 15 - .../api/rules/patch_rule/route.ts | 10 +- .../api/rules/read_rule/route.ts | 11 +- .../api/rules/update_rule/route.test.ts | 15 +- .../api/rules/update_rule/route.ts | 10 +- .../detection_engine/rule_management/index.ts | 6 +- .../logic/export/get_export_all.ts | 12 +- .../logic/export/get_export_by_object_ids.ts | 12 +- .../logic/import/import_rules_utils.ts | 9 +- .../legacy_action_migration.test.ts | 486 ----- .../rule_actions/legacy_action_migration.ts | 178 -- .../normalization/rule_actions.test.ts | 332 +-- .../normalization/rule_actions.ts | 56 +- .../normalization/rule_converters.ts | 11 +- .../rule_management/utils/utils.test.ts | 86 +- .../rule_management/utils/utils.ts | 21 +- .../rule_management/utils/validate.test.ts | 4 +- .../rule_management/utils/validate.ts | 7 +- .../telemetry/filterlists/endpoint_alerts.ts | 1 + .../security_solution/server/plugin.ts | 2 + .../server/plugin_contract.ts | 1 + .../query.first_or_last_seen.dsl.ts | 4 +- .../factory/users/all/index.ts | 1 - .../security_solution/factory/users/index.ts | 6 +- .../__snapshots__/index.test.tsx.snap | 173 ++ ...uery.managed_user_details.dsl.test.ts.snap | 36 + .../users/managed_details/index.test.tsx | 112 + .../factory/users/managed_details/index.ts | 40 + .../query.managed_user_details.dsl.test.ts | 20 + .../query.managed_user_details.dsl.ts | 31 + .../__mocks__/index.ts | 16 +- .../__snapshots__/index.test.tsx.snap | 29 +- ...ry.observed_user_details.dsl.test.ts.snap} | 19 + .../helper.test.ts | 4 - .../{details => observed_details}/helpers.ts | 7 +- .../index.test.tsx | 14 +- .../{details => observed_details}/index.ts | 18 +- .../query.observed_user_details.dsl.test.ts} | 4 +- .../query.observed_user_details.dsl.ts} | 12 +- .../plugins/security_solution/tsconfig.json | 6 +- .../common/types/process_tree/index.ts | 2 +- x-pack/plugins/session_view/kibana.jsonc | 2 +- .../detail_panel_alert_list_item/index.tsx | 4 +- .../public/components/process_tree/helpers.ts | 14 + .../public/components/process_tree/index.tsx | 12 + .../public/components/process_tree/styles.ts | 2 - .../__snapshots__/index.test.tsx.snap | 2 + .../process_tree_node/index.test.tsx | 2 +- .../components/process_tree_node/index.tsx | 43 +- .../components/process_tree_node/styles.ts | 26 +- .../process_tree_node/text_highlight.tsx | 1 - .../public/components/session_view/index.tsx | 4 + .../utils/alert_category_display_test.test.ts | 10 +- .../utils/alert_category_display_text.ts | 2 +- .../nav_control_popover.test.tsx.snap | 210 +- .../nav_control/nav_control_popover.tsx | 16 +- .../email/email_params.test.tsx | 2 +- .../server_log/server_log_params.test.tsx | 2 +- .../connector_types/email/index.test.ts | 36 +- .../server/connector_types/email/index.ts | 16 +- .../server/connector_types/es_index/index.ts | 1 + .../connector_types/server_log/index.ts | 2 + .../server/connector_types/slack/index.ts | 1 + .../server/connector_types/teams/index.ts | 1 + .../monitor_types_project.ts | 2 +- x-pack/plugins/synthetics/kibana.jsonc | 16 +- .../common/components/permissions.tsx | 2 +- .../common/components/stderr_logs.tsx | 4 +- .../browser_steps_list.tsx | 1 + .../use_retrieve_step_image.ts | 76 +- .../synthetics_page_template.tsx | 6 +- .../common/screenshot/empty_thumbnail.tsx | 19 +- .../screenshot/journey_screenshot_dialog.tsx | 4 +- ...journey_step_screenshot_container.test.tsx | 18 +- .../journey_step_screenshot_container.tsx | 5 +- .../getting_started/getting_started_page.tsx | 4 + .../monitor_add_edit/form/field_config.tsx | 4 +- .../monitor_add_edit/form/formatter.ts | 9 +- .../management/disabled_callout.tsx | 3 +- .../management/invalid_api_key_callout.tsx | 4 +- .../management/monitor_list_table/columns.tsx | 48 +- .../monitor_list_table/monitor_list.tsx | 3 - .../monitors_page/overview/overview_page.tsx | 2 +- .../network_data/data_formatting.test.ts | 38 +- .../common/network_data/data_formatting.ts | 2 +- .../public/apps/synthetics/routes.tsx | 2 +- .../state/browser_journey/api.test.ts | 8 +- .../synthetics/state/browser_journey/api.ts | 24 +- .../public/apps/synthetics/synthetics_app.tsx | 1 + .../synthetics/utils/testing/rtl_helpers.tsx | 9 + .../public/legacy_uptime/app/uptime_app.tsx | 1 + .../app/uptime_page_template.tsx | 4 +- .../legacy_uptime/lib/helper/rtl_helpers.tsx | 8 + .../public/legacy_uptime/routes.tsx | 2 +- x-pack/plugins/synthetics/public/plugin.ts | 8 +- .../monitor_cruds/monitor_validation.test.ts | 51 +- .../monitor_cruds/monitor_validation.ts | 108 +- .../routes/overview_status/overview_status.ts | 10 +- .../project_monitor_formatter.ts | 24 +- .../project_monitor_formatter_legacy.ts | 12 +- .../service_api_client.test.ts | 114 +- .../synthetics_service/service_api_client.ts | 60 +- .../synthetics_service.test.ts | 44 + .../synthetics_service/synthetics_service.ts | 6 +- .../server/synthetics_service/utils/mocks.ts | 2 +- x-pack/plugins/synthetics/tsconfig.json | 1 + .../server/lib/calculate_health_status.ts | 24 +- .../server/lib/log_health_metrics.test.ts | 40 +- .../server/lib/log_health_metrics.ts | 9 +- .../server/monitoring/capacity_estimation.ts | 24 +- .../monitoring/monitoring_stats_stream.ts | 1 + .../monitoring/task_run_statistics.test.ts | 12 +- .../server/monitoring/task_run_statistics.ts | 4 +- .../task_manager/server/routes/health.test.ts | 59 +- .../task_manager/server/routes/health.ts | 24 +- .../server/saved_objects/index.ts | 6 +- .../server/saved_objects/mappings.json | 55 - .../server/saved_objects/mappings.ts | 66 + .../__snapshots__/wrapper.test.tsx.snap | 3 +- .../field_selector.test.tsx.snap | 12 +- .../plugins/translations/translations/en.json | 213 ++ .../translations/translations/fr-FR.json | 25 - .../translations/translations/ja-JP.json | 25 - .../translations/translations/zh-CN.json | 25 - .../components/rule_snooze/scheduler.tsx | 3 +- .../plugins/triggers_actions_ui/tsconfig.json | 7 +- .../upgrade_assistant/server/plugin.ts | 8 +- .../server/routes/__mocks__/routes.mock.ts | 3 +- .../server/routes/ml_snapshots.test.ts | 15 +- .../server/routes/ml_snapshots.ts | 10 +- .../reindex_indices/batch_reindex_indices.ts | 13 +- .../routes/reindex_indices/reindex_indices.ts | 20 +- .../server/saved_object_types/index.ts | 4 + .../ml_upgrade_operation_saved_object_type.ts | 2 +- .../reindex_operation_saved_object_type.ts | 2 +- x-pack/plugins/ux/kibana.jsonc | 1 + .../plugins/ux/public/application/ux_app.tsx | 2 + .../components/app/rum_dashboard/rum_home.tsx | 4 +- x-pack/plugins/ux/public/plugin.ts | 8 +- x-pack/plugins/ux/tsconfig.json | 1 + x-pack/test/accessibility/apps/maps.ts | 4 +- .../alerting_api_integration/common/config.ts | 3 + .../server/ms_exchage_server_simulation.ts | 2 +- .../actions_simulators/server/plugin.ts | 6 + .../plugins/alerts/server/action_types.ts | 28 + .../tests/actions/connector_types/email.ts | 16 +- .../spaces_only/tests/actions/config.ts | 1 + .../actions/connector_types/stack/email.ts | 23 + .../apis/lists/create_exception_list_item.ts | 3 +- .../apis/synthetics/add_monitor_project.ts | 187 +- .../synthetics/add_monitor_project_legacy.ts | 2 +- .../api_integration/apis/telemetry/index.ts | 2 - .../api_integration/apis/telemetry/opt_in.ts | 125 -- .../telemetry/telemetry_optin_notice_seen.ts | 35 - .../rest/fixtures/project_tcp_monitor.json | 6 +- .../common/lib/alerts.ts | 134 +- .../cases_api_integration/common/lib/mock.ts | 2 +- .../tests/common/cases/delete_cases.ts | 209 ++ .../tests/common/comments/delete_comment.ts | 12 +- .../tests/common/comments/delete_comments.ts | 9 +- .../tests/no_public_base_url/push.ts | 3 +- .../pages/findings.ts | 18 +- .../group1/delete_rules.ts | 13 + .../group1/delete_rules_bulk.ts | 13 + .../group10/import_rules.ts | 43 + .../group10/legacy_actions_migrations.ts | 22 +- .../group10/patch_rules.ts | 13 +- .../group10/patch_rules_bulk.ts | 16 +- .../group10/perform_bulk_action.ts | 86 +- .../group10/update_rules.ts | 15 +- .../group10/update_rules_bulk.ts | 17 +- .../utils/wait_for_rule_status.ts | 3 + .../agent_policy_datastream_permissions.ts | 127 ++ .../apis/agent_policy/index.js | 3 +- .../apis/agents/request_diagnostics.ts | 6 +- .../all_assets/0.2.0/manifest.yml | 4 +- .../test_logs/agent/stream/log.yml.hbs | 4 + .../data_stream/test_logs/fields/ecs.yml | 6 + .../data_stream/test_logs/fields/fields.yml | 16 + .../0.2.0/data_stream/test_logs/manifest.yml | 20 + .../test_metrics/agent/stream/cpu.yml.hbs | 6 + .../data_stream/test_metrics/fields/ecs.yml | 6 + .../test_metrics/fields/fields.yml | 16 + .../data_stream/test_metrics/manifest.yml | 31 + .../dynamic_datastream/0.2.0/docs/README.md | 3 + .../dynamic_datastream/0.2.0/manifest.yml | 21 + .../0.1.0/docs/README.md | 3 + .../0.1.0/img/logo_overrides_64_color.svg | 7 + .../0.1.0/manifest.yml | 24 + .../test/fleet_api_integration/apis/index.js | 16 +- .../apis/integrations/elastic_agent.ts | 11 +- .../apis/package_policy/create.ts | 25 + .../apps/aiops/explain_log_rate_spikes.ts | 50 +- .../test/functional/apps/aiops/test_data.ts | 21 +- x-pack/test/functional/apps/aiops/types.ts | 43 +- .../test/functional/apps/infra/hosts_view.ts | 17 +- .../apps/lens/group1/layer_actions.ts | 14 +- .../apps/lens/group3/lens_reporting.ts | 9 + .../data_visualizer/index_data_visualizer.ts | 4 +- .../notifications/notification_list.ts | 3 +- ...undamental_config_errors_on_dashboard.json | 10 +- .../page_objects/infra_hosts_view.ts | 19 + .../aiops/explain_log_rate_spikes_page.ts | 32 +- .../public/attachments/external_reference.tsx | 13 +- .../public/attachments/persistable_state.tsx | 11 +- .../observability/pages/alerts/add_to_case.ts | 3 +- .../apps/observability/pages/alerts/index.ts | 6 - .../observability/pages/alerts/rule_stats.ts | 6 - .../task_manager/task_management.ts | 6 +- .../apps/endpoint/endpoint_list.ts | 8 +- yarn.lock | 18 +- 1595 files changed, 31803 insertions(+), 9946 deletions(-) create mode 100644 api_docs/kbn_generate_csv.devdocs.json create mode 100644 api_docs/kbn_generate_csv.mdx create mode 100644 api_docs/kbn_generate_csv_types.devdocs.json create mode 100644 api_docs/kbn_generate_csv_types.mdx create mode 100644 api_docs/kbn_ml_number_utils.devdocs.json create mode 100644 api_docs/kbn_ml_number_utils.mdx create mode 100644 api_docs/kbn_reporting_common.devdocs.json create mode 100644 api_docs/kbn_reporting_common.mdx create mode 100644 api_docs/kbn_saved_objects_settings.devdocs.json create mode 100644 api_docs/kbn_saved_objects_settings.mdx create mode 100644 api_docs/kbn_securitysolution_data_table.devdocs.json create mode 100644 api_docs/kbn_securitysolution_data_table.mdx rename src/plugins/dashboard/public/dashboard_app/listing/index.ts => packages/core/http/core-http-common/src/constants.ts (79%) create mode 100644 packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.test.ts create mode 100644 packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts create mode 100644 packages/core/http/core-http-router-server-internal/src/versioned_router/inject_response_headers.ts create mode 100644 packages/core/ui-settings/core-ui-settings-common/src/timezones.ts create mode 100644 packages/kbn-saved-objects-settings/README.md create mode 100644 packages/kbn-saved-objects-settings/index.ts create mode 100644 packages/kbn-saved-objects-settings/jest.config.js create mode 100644 packages/kbn-saved-objects-settings/kibana.jsonc create mode 100644 packages/kbn-saved-objects-settings/package.json create mode 100644 packages/kbn-saved-objects-settings/tsconfig.json delete mode 100644 src/plugins/dashboard/public/dashboard_app/listing/__snapshots__/dashboard_listing.test.tsx.snap delete mode 100644 src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.test.tsx delete mode 100644 src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx create mode 100644 src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx create mode 100644 src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx rename src/plugins/dashboard/public/dashboard_app/{listing => listing_page}/dashboard_no_match.tsx (100%) rename src/plugins/dashboard/public/dashboard_app/{listing => listing_page}/get_dashboard_list_item_link.test.ts (100%) rename src/plugins/dashboard/public/dashboard_app/{listing => listing_page}/get_dashboard_list_item_link.ts (100%) create mode 100644 src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts rename src/plugins/dashboard/public/{dashboard_app/listing => dashboard_listing}/confirm_overlays.tsx (96%) create mode 100644 src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx create mode 100644 src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx create mode 100644 src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx create mode 100644 src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx rename src/plugins/dashboard/public/{dashboard_app/listing => dashboard_listing}/dashboard_unsaved_listing.test.tsx (88%) rename src/plugins/dashboard/public/{dashboard_app/listing => dashboard_listing}/dashboard_unsaved_listing.tsx (92%) create mode 100644 src/plugins/dashboard/public/dashboard_listing/index.tsx create mode 100644 src/plugins/data/server/saved_objects/schemas/query.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/.storybook/main.js create mode 100644 x-pack/packages/kbn-securitysolution-data-table/README.md create mode 100644 x-pack/packages/kbn-securitysolution-data-table/common/constants.ts rename x-pack/{plugins/security_solution => packages/kbn-securitysolution-data-table}/common/types/data_table/index.ts (91%) create mode 100644 x-pack/packages/kbn-securitysolution-data-table/common/types/detail_panel.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/common/types/header_actions/index.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/common/types/index.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/common/types/risk_scores.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/column_headers/default_headers.ts (91%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/column_headers/helpers.test.tsx (99%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/column_headers/helpers.tsx (83%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/column_headers/translations.ts (82%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/constants.ts (59%) create mode 100644 x-pack/packages/kbn-securitysolution-data-table/components/data_table/data_table.stories.tsx rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/helpers.test.tsx (98%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/helpers.tsx (91%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/index.test.tsx (88%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/index.tsx (88%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/pagination.ts (100%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/components/data_table/types.ts (93%) create mode 100644 x-pack/packages/kbn-securitysolution-data-table/components/data_table/utils.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/components/toolbar/bulk_actions/types.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/index.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/styles.tsx create mode 100644 x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/translations.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/hooks/use_selector.tsx create mode 100644 x-pack/packages/kbn-securitysolution-data-table/index.ts rename x-pack/{plugins/task_manager/server/lib/calculate_health_status.mock.ts => packages/kbn-securitysolution-data-table/jest.config.js} (61%) create mode 100644 x-pack/packages/kbn-securitysolution-data-table/kibana.jsonc create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/library_load_event.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/process_execution_malware_prevention_alert.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/registry_modification_event.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/timeline.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/global_state.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/header.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/mock_local_storage.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/mock_source.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/mock_timeline_data.ts create mode 100644 x-pack/packages/kbn-securitysolution-data-table/mock/test_providers.tsx create mode 100644 x-pack/packages/kbn-securitysolution-data-table/package.json rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/actions.ts (92%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/defaults.ts (94%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/helpers.test.tsx (94%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/helpers.ts (97%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/index.ts (100%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/inputs.ts (100%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/model.ts (90%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/reducer.ts (99%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/selectors.ts (99%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/translations.ts (80%) rename x-pack/{plugins/security_solution/public/common => packages/kbn-securitysolution-data-table}/store/data_table/types.ts (98%) create mode 100644 x-pack/packages/kbn-securitysolution-data-table/tsconfig.json create mode 100644 x-pack/packages/kbn-securitysolution-data-table/utils/use_mount_appended.ts delete mode 100644 x-pack/plugins/actions/server/saved_objects/mappings.json create mode 100644 x-pack/plugins/actions/server/saved_objects/mappings.ts create mode 100644 x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_content.tsx create mode 100644 x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_popover.tsx create mode 100644 x-pack/plugins/aiops/public/components/field_stats_popover/index.ts rename x-pack/plugins/{security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_get_bulk_rule_actions_saved_object.test.ts => alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts} (66%) create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.test.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/types.ts create mode 100644 x-pack/plugins/cases/public/components/case_view/components/case_view_files.test.tsx create mode 100644 x-pack/plugins/cases/public/components/case_view/components/case_view_files.tsx create mode 100644 x-pack/plugins/cases/public/components/files/add_file.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/add_file.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_delete_button.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_delete_button.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_download_button.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_download_button.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_name_link.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_name_link.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_preview.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_preview.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_type.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/file_type.tsx create mode 100644 x-pack/plugins/cases/public/components/files/files_table.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/files_table.tsx create mode 100644 x-pack/plugins/cases/public/components/files/files_utility_bar.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/files_utility_bar.tsx create mode 100644 x-pack/plugins/cases/public/components/files/translations.tsx create mode 100644 x-pack/plugins/cases/public/components/files/types.ts create mode 100644 x-pack/plugins/cases/public/components/files/use_file_preview.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/use_file_preview.tsx create mode 100644 x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/use_files_table_columns.tsx create mode 100644 x-pack/plugins/cases/public/components/files/utils.test.tsx create mode 100644 x-pack/plugins/cases/public/components/files/utils.tsx create mode 100644 x-pack/plugins/cases/public/containers/use_delete_file_attachment.test.tsx create mode 100644 x-pack/plugins/cases/public/containers/use_delete_file_attachment.tsx create mode 100644 x-pack/plugins/cases/public/containers/use_get_case_file_stats.test.tsx create mode 100644 x-pack/plugins/cases/public/containers/use_get_case_file_stats.tsx create mode 100644 x-pack/plugins/cases/public/containers/use_get_case_files.test.tsx create mode 100644 x-pack/plugins/cases/public/containers/use_get_case_files.tsx create mode 100644 x-pack/plugins/cases/public/internal_attachments/index.ts create mode 100644 x-pack/plugins/cloud_security_posture/public/assets/illustrations/illustration_product_no_results_magnifying_glass.svg create mode 100644 x-pack/plugins/cloud_security_posture/public/components/empty_state.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_json_tab.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx rename x-pack/plugins/enterprise_search/public/applications/analytics/api/{check_analytics_events_index/check_analytics_events_index_api_logic.test.ts => check_analytics_events/check_analytics_events_exist_api_logic.test.ts} (75%) create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/api/check_analytics_events/check_analytics_events_exist_api_logic.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/api/check_analytics_events_index/check_analytics_events_index_api_logic.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout.tsx rename x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/{analytics_events_index_exists_logic.test.ts => analytics_collection_no_events_callout/analytics_collection_no_events_callout_logic.test.ts} (50%) create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout_logic.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_events_index_exists_logic.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview_logic.test.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_overview_logic.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_field.tsx rename x-pack/plugins/enterprise_search/server/lib/analytics/{analytics_events_index_exists.test.ts => analytics_events_exist.test.ts} (60%) rename x-pack/plugins/enterprise_search/server/lib/analytics/{analytics_events_index_exists.ts => analytics_events_exist.ts} (75%) create mode 100644 x-pack/plugins/fleet/common/constants/locators.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/dashboards_buttons.tsx create mode 100644 x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/logs_ratio_chart.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/add_metadata_filter_button.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/build_metadata_filter.ts rename x-pack/plugins/lens/public/{shared_components/dataview_picker => datasources/form_based}/sampling_icon.tsx (100%) create mode 100644 x-pack/plugins/lens/public/shared_components/dataview_picker/trigger.tsx create mode 100644 x-pack/plugins/lens/public/visualizations/xy/layer_settings.tsx delete mode 100644 x-pack/plugins/maps/public/lazy_load_bundle/index.ts delete mode 100644 x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts delete mode 100644 x-pack/plugins/maps/public/locators.ts create mode 100644 x-pack/plugins/maps/public/locators/map_locator/get_location.ts rename x-pack/plugins/maps/public/{locators.test.ts => locators/map_locator/locator_definition.test.ts} (92%) create mode 100644 x-pack/plugins/maps/public/locators/map_locator/locator_definition.ts create mode 100644 x-pack/plugins/maps/public/locators/map_locator/types.ts create mode 100644 x-pack/plugins/maps/public/locators/region_map_locator/get_location.ts create mode 100644 x-pack/plugins/maps/public/locators/region_map_locator/locator_definition.ts create mode 100644 x-pack/plugins/maps/public/locators/region_map_locator/types.ts create mode 100644 x-pack/plugins/maps/public/locators/tile_map_locator/get_location.ts create mode 100644 x-pack/plugins/maps/public/locators/tile_map_locator/locator_definition.ts create mode 100644 x-pack/plugins/maps/public/locators/tile_map_locator/types.ts rename x-pack/plugins/maps/public/trigger_actions/{filter_by_map_extent_action.tsx => filter_by_map_extent/action.ts} (63%) create mode 100644 x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/is_compatible.ts rename x-pack/plugins/maps/public/trigger_actions/{filter_by_map_extent_modal.tsx => filter_by_map_extent/modal.tsx} (78%) create mode 100644 x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/types.ts create mode 100644 x-pack/plugins/maps/public/trigger_actions/get_maps_link.ts create mode 100644 x-pack/plugins/maps/public/trigger_actions/synchronize_movement/action.ts create mode 100644 x-pack/plugins/maps/public/trigger_actions/synchronize_movement/is_compatible.ts rename x-pack/plugins/maps/public/trigger_actions/{synchronize_movement_modal.tsx => synchronize_movement/modal.tsx} (84%) create mode 100644 x-pack/plugins/maps/public/trigger_actions/synchronize_movement/types.ts delete mode 100644 x-pack/plugins/maps/public/trigger_actions/synchronize_movement_action.tsx create mode 100644 x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts create mode 100644 x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx create mode 100644 x-pack/plugins/observability/server/lib/rules/slo_burn_rate/field_map.ts create mode 100644 x-pack/plugins/observability_shared/public/hooks/use_track_metric.tsx delete mode 100644 x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_alerts_from_case.test.ts create mode 100644 x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/users/managed_details/index.ts rename x-pack/plugins/security_solution/common/search_strategy/security_solution/users/{details => observed_details}/index.ts (78%) create mode 100644 x-pack/plugins/security_solution/public/common/mock/storybook_providers.tsx rename x-pack/plugins/security_solution/public/explore/users/containers/users/{details => observed_details}/index.test.tsx (88%) rename x-pack/plugins/security_solution/public/explore/users/containers/users/{details => observed_details}/index.tsx (71%) rename x-pack/plugins/security_solution/public/explore/users/containers/users/{details => observed_details}/translations.ts (100%) create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/columns.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/constants.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/hooks.test.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/hooks.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.test.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/observed_user.test.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/observed_user.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.test.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/translations.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/types.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/user_details_content.stories.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/user_details_content.test.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/user_details_content.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/side_panel/user_details/translations.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_get_bulk_rule_actions_saved_object.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_actions/legacy_action_migration.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_actions/legacy_action_migration.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/__snapshots__/query.managed_user_details.dsl.test.ts.snap create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/index.test.tsx create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/index.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/query.managed_user_details.dsl.test.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/query.managed_user_details.dsl.ts rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details => observed_details}/__mocks__/index.ts (91%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details => observed_details}/__snapshots__/index.test.tsx.snap (94%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details/__snapshots__/query.user_details.dsl.test.ts.snap => observed_details/__snapshots__/query.observed_user_details.dsl.test.ts.snap} (85%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details => observed_details}/helper.test.ts (85%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details => observed_details}/helpers.ts (86%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details => observed_details}/index.test.tsx (59%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details => observed_details}/index.ts (64%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details/query.user_details.dsl.test.ts => observed_details/query.observed_user_details.dsl.test.ts} (71%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/{details/query.user_details.dsl.ts => observed_details/query.observed_user_details.dsl.ts} (65%) delete mode 100644 x-pack/plugins/task_manager/server/saved_objects/mappings.json create mode 100644 x-pack/plugins/task_manager/server/saved_objects/mappings.ts create mode 100644 x-pack/plugins/translations/translations/en.json delete mode 100644 x-pack/test/api_integration/apis/telemetry/opt_in.ts delete mode 100644 x-pack/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts create mode 100644 x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy_datastream_permissions.ts create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_logs/agent/stream/log.yml.hbs create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_logs/fields/ecs.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_logs/fields/fields.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_logs/manifest.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_metrics/agent/stream/cpu.yml.hbs create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_metrics/fields/ecs.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_metrics/fields/fields.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/data_stream/test_metrics/manifest.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/docs/README.md create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/dynamic_datastream/0.2.0/manifest.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/single_input_no_streams/0.1.0/docs/README.md create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/single_input_no_streams/0.1.0/img/logo_overrides_64_color.svg create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/single_input_no_streams/0.1.0/manifest.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 26be09f9bb2f0..270c2ce5d2b48 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -431,7 +431,7 @@ src/plugins/kibana_overview @elastic/appex-sharedux src/plugins/kibana_react @elastic/appex-sharedux src/plugins/kibana_usage_collection @elastic/kibana-core src/plugins/kibana_utils @elastic/kibana-app-services -x-pack/plugins/kubernetes_security @elastic/awp-viz +x-pack/plugins/kubernetes_security @elastic/sec-cloudnative-integrations packages/kbn-language-documentation-popover @elastic/kibana-visualizations x-pack/plugins/lens @elastic/kibana-visualizations x-pack/plugins/license_api_guard @elastic/platform-deployment-management @@ -529,6 +529,7 @@ test/plugin_functional/plugins/saved_objects_hidden_from_http_apis_type @elastic test/plugin_functional/plugins/saved_objects_hidden_type @elastic/kibana-core src/plugins/saved_objects_management @elastic/kibana-core src/plugins/saved_objects @elastic/kibana-core +packages/kbn-saved-objects-settings @elastic/appex-sharedux src/plugins/saved_objects_tagging_oss @elastic/appex-sharedux x-pack/plugins/saved_objects_tagging @elastic/appex-sharedux src/plugins/saved_search @elastic/kibana-data-discovery @@ -546,6 +547,7 @@ packages/security-solution/side_nav @elastic/security-threat-hunting-explore packages/security-solution/storybook/config @elastic/security-threat-hunting-explore x-pack/test/security_functional/plugins/test_endpoints @elastic/kibana-security packages/kbn-securitysolution-autocomplete @elastic/security-solution-platform +x-pack/packages/kbn-securitysolution-data-table @elastic/security-threat-hunting-investigations packages/kbn-securitysolution-ecs @elastic/security-threat-hunting-explore packages/kbn-securitysolution-es-utils @elastic/security-solution-platform packages/kbn-securitysolution-exception-list-components @elastic/security-solution-platform @@ -565,7 +567,7 @@ packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui test/plugin_functional/plugins/session_notifications @elastic/kibana-core -x-pack/plugins/session_view @elastic/awp-viz +x-pack/plugins/session_view @elastic/sec-cloudnative-integrations packages/kbn-set-map @elastic/kibana-operations examples/share_examples @elastic/kibana-app-services src/plugins/share @elastic/appex-sharedux @@ -1174,8 +1176,8 @@ x-pack/plugins/security_solution/cypress/README.md @elastic/security-engineering x-pack/test/security_solution_cypress @elastic/security-engineering-productivity ## Security Solution sub teams - adaptive-workload-protection -x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/awp-viz -x-pack/plugins/security_solution/public/kubernetes @elastic/awp-viz +x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/sec-cloudnative-integrations +x-pack/plugins/security_solution/public/kubernetes @elastic/sec-cloudnative-integrations ## Security Solution sub teams - Protections Experience x-pack/plugins/security_solution/public/threat_intelligence @elastic/protections-experience @@ -1219,8 +1221,8 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience # Logstash #CC# /x-pack/plugins/logstash/ @elastic/logstash -# EUI design -/src/plugins/kibana_react/public/page_template/ @elastic/eui-design @elastic/appex-sharedux +# EUI team +/src/plugins/kibana_react/public/page_template/ @elastic/eui-team @elastic/appex-sharedux # Landing page for guided onboarding in Home plugin /src/plugins/home/public/application/components/guided_onboarding @elastic/platform-onboarding diff --git a/.i18nrc.json b/.i18nrc.json index a9a4985f68e5d..d64d2989dc44c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -80,6 +80,7 @@ "savedObjects": "src/plugins/saved_objects", "savedObjectsFinder": "src/plugins/saved_objects_finder", "savedObjectsManagement": "src/plugins/saved_objects_management", + "securitySolutionDataTable": "packages/kbn-securitysolution-data-table", "server": "src/legacy/server", "share": "src/plugins/share", "sharedUXPackages": "packages/shared-ux", diff --git a/NOTICE.txt b/NOTICE.txt index cd0ba2a9c1bd9..45af6e5231783 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -257,8 +257,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- -This code is part of the Services provided by FullStory, Inc. For license information, please refer to https://www.fullstory.com/legal/terms-and-conditions/. -Portions of this code are licensed separately and can be found in https://edge.fullstory.com/s/fs.js.LICENSE.txt +This code is part of the Services provided by FullStory, Inc. For license information, please refer to https://www.fullstory.com/legal/terms-and-conditions/ +Portions of this code are licensed under the following license: + For license information please see fs.js.LICENSE.txt --- This product bundles bootstrap@3.3.6 which is available under a diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index 0cdbf07af71d5..01b2670ee2eac 100644 --- a/api_docs/actions.devdocs.json +++ b/api_docs/actions.devdocs.json @@ -1391,13 +1391,13 @@ "label": "validate", "description": [], "signature": [ - "{ params?: ", + "{ params: ", "ValidatorType", - " | undefined; config?: ", + "; config: ", "ValidatorType", - " | undefined; secrets?: ", + "; secrets: ", "ValidatorType", - " | undefined; connector?: ((config: Config, secrets: Secrets) => string | null) | undefined; } | undefined" + "; connector?: ((config: Config, secrets: Secrets) => string | null) | undefined; }" ], "path": "x-pack/plugins/actions/server/types.ts", "deprecated": false, diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 5f8a96ce6cf3d..139f36cb6eeff 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 52970515764f5..ef559a0a5d7d7 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.devdocs.json b/api_docs/aiops.devdocs.json index ce17371ced270..1d4576e8b91bb 100644 --- a/api_docs/aiops.devdocs.json +++ b/api_docs/aiops.devdocs.json @@ -10,11 +10,17 @@ "tags": [], "label": "ChangePointDetection", "description": [ - "\nLazy-wrapped LogCategorizationAppStateProps React component" + "\nLazy-wrapped ChangePointDetectionAppStateProps React component" ], "signature": [ "(props: React.PropsWithChildren<", - "LogCategorizationAppStateProps", + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.ChangePointDetectionAppStateProps", + "text": "ChangePointDetectionAppStateProps" + }, ">) => JSX.Element" ], "path": "x-pack/plugins/aiops/public/shared_lazy_components.tsx", @@ -32,7 +38,13 @@ ], "signature": [ "React.PropsWithChildren<", - "LogCategorizationAppStateProps", + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.ChangePointDetectionAppStateProps", + "text": "ChangePointDetectionAppStateProps" + }, ">" ], "path": "x-pack/plugins/aiops/public/shared_lazy_components.tsx", @@ -55,7 +67,13 @@ ], "signature": [ "(props: React.PropsWithChildren<", - "ExplainLogRateSpikesAppStateProps", + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.ExplainLogRateSpikesAppStateProps", + "text": "ExplainLogRateSpikesAppStateProps" + }, ">) => JSX.Element" ], "path": "x-pack/plugins/aiops/public/shared_lazy_components.tsx", @@ -73,7 +91,13 @@ ], "signature": [ "React.PropsWithChildren<", - "ExplainLogRateSpikesAppStateProps", + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.ExplainLogRateSpikesAppStateProps", + "text": "ExplainLogRateSpikesAppStateProps" + }, ">" ], "path": "x-pack/plugins/aiops/public/shared_lazy_components.tsx", @@ -96,7 +120,13 @@ ], "signature": [ "(props: React.PropsWithChildren<", - "LogCategorizationAppStateProps", + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.LogCategorizationAppStateProps", + "text": "LogCategorizationAppStateProps" + }, ">) => JSX.Element" ], "path": "x-pack/plugins/aiops/public/shared_lazy_components.tsx", @@ -114,7 +144,13 @@ ], "signature": [ "React.PropsWithChildren<", - "LogCategorizationAppStateProps", + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.LogCategorizationAppStateProps", + "text": "LogCategorizationAppStateProps" + }, ">" ], "path": "x-pack/plugins/aiops/public/shared_lazy_components.tsx", @@ -127,7 +163,527 @@ "initialIsOpen": false } ], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies", + "type": "Interface", + "tags": [], + "label": "AiopsAppDependencies", + "description": [], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.application", + "type": "Object", + "tags": [], + "label": "application", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.data", + "type": "Object", + "tags": [], + "label": "data", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataPluginApi", + "section": "def-public.DataPublicPluginStart", + "text": "DataPublicPluginStart" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.charts", + "type": "CompoundType", + "tags": [], + "label": "charts", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "public", + "docId": "kibChartsPluginApi", + "section": "def-public.ChartsPluginSetup", + "text": "ChartsPluginSetup" + }, + " & { activeCursor: ", + "ActiveCursor", + "; }" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.fieldFormats", + "type": "CompoundType", + "tags": [], + "label": "fieldFormats", + "description": [], + "signature": [ + "Omit<", + { + "pluginId": "fieldFormats", + "scope": "common", + "docId": "kibFieldFormatsPluginApi", + "section": "def-common.FieldFormatsRegistry", + "text": "FieldFormatsRegistry" + }, + ", \"init\" | \"register\"> & { deserialize: ", + { + "pluginId": "fieldFormats", + "scope": "common", + "docId": "kibFieldFormatsPluginApi", + "section": "def-common.FormatFactory", + "text": "FormatFactory" + }, + "; }" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.notifications", + "type": "Object", + "tags": [], + "label": "notifications", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsSetup", + "text": "NotificationsSetup" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.storage", + "type": "Object", + "tags": [], + "label": "storage", + "description": [], + "signature": [ + { + "pluginId": "kibanaUtils", + "scope": "public", + "docId": "kibKibanaUtilsPluginApi", + "section": "def-public.IStorageWrapper", + "text": "IStorageWrapper" + }, + "" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.theme", + "type": "Object", + "tags": [], + "label": "theme", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-theme-browser", + "scope": "common", + "docId": "kibKbnCoreThemeBrowserPluginApi", + "section": "def-common.ThemeServiceStart", + "text": "ThemeServiceStart" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.uiSettings", + "type": "Object", + "tags": [], + "label": "uiSettings", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-ui-settings-browser", + "scope": "common", + "docId": "kibKbnCoreUiSettingsBrowserPluginApi", + "section": "def-common.IUiSettingsClient", + "text": "IUiSettingsClient" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.unifiedSearch", + "type": "Object", + "tags": [], + "label": "unifiedSearch", + "description": [], + "signature": [ + { + "pluginId": "unifiedSearch", + "scope": "public", + "docId": "kibUnifiedSearchPluginApi", + "section": "def-public.UnifiedSearchPublicPluginStart", + "text": "UnifiedSearchPublicPluginStart" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.share", + "type": "CompoundType", + "tags": [], + "label": "share", + "description": [], + "signature": [ + "{ toggleShareContextMenu: (options: ", + { + "pluginId": "share", + "scope": "public", + "docId": "kibSharePluginApi", + "section": "def-public.ShowShareMenuOptions", + "text": "ShowShareMenuOptions" + }, + ") => void; } & { url: ", + { + "pluginId": "share", + "scope": "public", + "docId": "kibSharePluginApi", + "section": "def-public.BrowserUrlService", + "text": "BrowserUrlService" + }, + "; navigate(options: ", + "RedirectOptions", + "<", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.SerializableRecord", + "text": "SerializableRecord" + }, + ">): void; }" + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.AiopsAppDependencies.lens", + "type": "Object", + "tags": [], + "label": "lens", + "description": [], + "signature": [ + { + "pluginId": "lens", + "scope": "public", + "docId": "kibLensPluginApi", + "section": "def-public.LensPublicStart", + "text": "LensPublicStart" + } + ], + "path": "x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.ChangePointDetectionAppStateProps", + "type": "Interface", + "tags": [], + "label": "ChangePointDetectionAppStateProps", + "description": [], + "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "aiops", + "id": "def-public.ChangePointDetectionAppStateProps.dataView", + "type": "Object", + "tags": [], + "label": "dataView", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + } + ], + "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.ChangePointDetectionAppStateProps.savedSearch", + "type": "CompoundType", + "tags": [], + "label": "savedSearch", + "description": [], + "signature": [ + { + "pluginId": "savedSearch", + "scope": "public", + "docId": "kibSavedSearchPluginApi", + "section": "def-public.SavedSearch", + "text": "SavedSearch" + }, + " | null" + ], + "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.ChangePointDetectionAppStateProps.appDependencies", + "type": "Object", + "tags": [], + "label": "appDependencies", + "description": [], + "signature": [ + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.AiopsAppDependencies", + "text": "AiopsAppDependencies" + } + ], + "path": "x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_root.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.ExplainLogRateSpikesAppStateProps", + "type": "Interface", + "tags": [], + "label": "ExplainLogRateSpikesAppStateProps", + "description": [], + "path": "x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "aiops", + "id": "def-public.ExplainLogRateSpikesAppStateProps.dataView", + "type": "Object", + "tags": [], + "label": "dataView", + "description": [ + "The data view to analyze." + ], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + } + ], + "path": "x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.ExplainLogRateSpikesAppStateProps.savedSearch", + "type": "CompoundType", + "tags": [], + "label": "savedSearch", + "description": [ + "The saved search to analyze." + ], + "signature": [ + { + "pluginId": "savedSearch", + "scope": "public", + "docId": "kibSavedSearchPluginApi", + "section": "def-public.SavedSearch", + "text": "SavedSearch" + }, + " | null" + ], + "path": "x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.ExplainLogRateSpikesAppStateProps.appDependencies", + "type": "Object", + "tags": [], + "label": "appDependencies", + "description": [ + "App dependencies" + ], + "signature": [ + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.AiopsAppDependencies", + "text": "AiopsAppDependencies" + } + ], + "path": "x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.LogCategorizationAppStateProps", + "type": "Interface", + "tags": [], + "label": "LogCategorizationAppStateProps", + "description": [], + "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "aiops", + "id": "def-public.LogCategorizationAppStateProps.dataView", + "type": "Object", + "tags": [], + "label": "dataView", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + } + ], + "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.LogCategorizationAppStateProps.savedSearch", + "type": "CompoundType", + "tags": [], + "label": "savedSearch", + "description": [], + "signature": [ + { + "pluginId": "savedSearch", + "scope": "public", + "docId": "kibSavedSearchPluginApi", + "section": "def-public.SavedSearch", + "text": "SavedSearch" + }, + " | null" + ], + "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "aiops", + "id": "def-public.LogCategorizationAppStateProps.appDependencies", + "type": "Object", + "tags": [], + "label": "appDependencies", + "description": [], + "signature": [ + { + "pluginId": "aiops", + "scope": "public", + "docId": "kibAiopsPluginApi", + "section": "def-public.AiopsAppDependencies", + "text": "AiopsAppDependencies" + } + ], + "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_app_state.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [], "objects": [] @@ -201,7 +757,9 @@ "type": "boolean", "tags": [], "label": "CHANGE_POINT_DETECTION_ENABLED", - "description": [], + "description": [ + "\nThis is an internal hard coded feature flag so we can easily turn on/off the\n\"Change Point Detection UI\" during development until the first release." + ], "signature": [ "true" ], diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 2112e92ef1832..06179f373550e 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; @@ -21,13 +21,16 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 1 | 2 | +| 37 | 0 | 22 | 0 | ## Client ### Functions +### Interfaces + + ## Server ### Setup diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index adf1582e2734c..ff1ed064224ef 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -1269,6 +1269,23 @@ "children": [], "returnComment": [] }, + { + "parentPluginId": "alerting", + "id": "def-server.AlertingApiRequestHandlerContext.getMaintenanceWindowClient", + "type": "Function", + "tags": [], + "label": "getMaintenanceWindowClient", + "description": [], + "signature": [ + "() => ", + "MaintenanceWindowClient" + ], + "path": "x-pack/plugins/alerting/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "alerting", "id": "def-server.AlertingApiRequestHandlerContext.listTypes", @@ -5781,6 +5798,42 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.DateRange", + "type": "Interface", + "tags": [], + "label": "DateRange", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.DateRange.gte", + "type": "string", + "tags": [], + "label": "gte", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.DateRange.lte", + "type": "string", + "tags": [], + "label": "lte", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.DefaultRuleAggregationResult", @@ -6347,6 +6400,257 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowClientContext", + "type": "Interface", + "tags": [], + "label": "MaintenanceWindowClientContext", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowClientContext.getModificationMetadata", + "type": "Function", + "tags": [], + "label": "getModificationMetadata", + "description": [], + "signature": [ + "() => Promise<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.MaintenanceWindowModificationMetadata", + "text": "MaintenanceWindowModificationMetadata" + }, + ">" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowClientContext.savedObjectsClient", + "type": "Object", + "tags": [], + "label": "savedObjectsClient", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + } + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowClientContext.logger", + "type": "Object", + "tags": [], + "label": "logger", + "description": [], + "signature": [ + { + "pluginId": "@kbn/logging", + "scope": "common", + "docId": "kibKbnLoggingPluginApi", + "section": "def-common.Logger", + "text": "Logger" + } + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowModificationMetadata", + "type": "Interface", + "tags": [], + "label": "MaintenanceWindowModificationMetadata", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowModificationMetadata.createdBy", + "type": "CompoundType", + "tags": [], + "label": "createdBy", + "description": [], + "signature": [ + "string | null" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowModificationMetadata.updatedBy", + "type": "CompoundType", + "tags": [], + "label": "updatedBy", + "description": [], + "signature": [ + "string | null" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowModificationMetadata.createdAt", + "type": "string", + "tags": [], + "label": "createdAt", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowModificationMetadata.updatedAt", + "type": "string", + "tags": [], + "label": "updatedAt", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties", + "type": "Interface", + "tags": [], + "label": "MaintenanceWindowSOProperties", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.duration", + "type": "number", + "tags": [], + "label": "duration", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.expirationDate", + "type": "string", + "tags": [], + "label": "expirationDate", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.events", + "type": "Array", + "tags": [], + "label": "events", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.DateRange", + "text": "DateRange" + }, + "[]" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.rRule", + "type": "CompoundType", + "tags": [], + "label": "rRule", + "description": [], + "signature": [ + "Partial<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RRuleRecord", + "text": "RRuleRecord" + }, + "> & Pick<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RRuleRecord", + "text": "RRuleRecord" + }, + ", \"dtstart\" | \"tzid\">" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.MappedParamsProperties", @@ -6396,7 +6700,7 @@ "tags": [], "label": "RRuleRecord", "description": [], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -6407,7 +6711,7 @@ "tags": [], "label": "dtstart", "description": [], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6418,7 +6722,7 @@ "tags": [], "label": "tzid", "description": [], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6432,7 +6736,7 @@ "signature": [ "0 | 2 | 6 | 5 | 4 | 3 | 1 | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6446,7 +6750,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6460,7 +6764,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6474,7 +6778,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6489,7 +6793,7 @@ "WeekdayStr", " | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6503,7 +6807,7 @@ "signature": [ "(string | number)[] | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6517,7 +6821,7 @@ "signature": [ "number[] | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6531,7 +6835,7 @@ "signature": [ "number[] | undefined" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6545,7 +6849,7 @@ "signature": [ "number[]" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6559,7 +6863,7 @@ "signature": [ "number[]" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6573,7 +6877,7 @@ "signature": [ "number[]" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6587,7 +6891,7 @@ "signature": [ "number[]" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6601,7 +6905,7 @@ "signature": [ "number[]" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false }, @@ -6615,7 +6919,7 @@ "signature": [ "number[]" ], - "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", + "path": "x-pack/plugins/alerting/common/rrule_type.ts", "deprecated": false, "trackAdoption": false } @@ -7206,7 +7510,14 @@ "label": "frequency", "description": [], "signature": [ - "{ summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined" + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleActionFrequency", + "text": "RuleActionFrequency" + }, + " | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -7236,6 +7547,76 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.RuleActionFrequency", + "type": "Interface", + "tags": [], + "label": "RuleActionFrequency", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleActionFrequency", + "text": "RuleActionFrequency" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectAttributes", + "text": "SavedObjectAttributes" + } + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.RuleActionFrequency.summary", + "type": "boolean", + "tags": [], + "label": "summary", + "description": [], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.RuleActionFrequency.notifyWhen", + "type": "CompoundType", + "tags": [], + "label": "notifyWhen", + "description": [], + "signature": [ + "\"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.RuleActionFrequency.throttle", + "type": "CompoundType", + "tags": [], + "label": "throttle", + "description": [], + "signature": [ + "string | null" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.RuleAggregationFormattedResult", @@ -8530,6 +8911,18 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowStatus", + "type": "Enum", + "tags": [], + "label": "MaintenanceWindowStatus", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.RuleExecutionStatusErrorReasons", @@ -8794,6 +9187,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.ENABLE_MAINTENANCE_WINDOWS", + "type": "boolean", + "tags": [], + "label": "ENABLE_MAINTENANCE_WINDOWS", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/plugins/alerting/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.ExecutionLogSortFields", @@ -8884,6 +9292,103 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.MAINTENANCE_WINDOW_FEATURE_ID", + "type": "string", + "tags": [], + "label": "MAINTENANCE_WINDOW_FEATURE_ID", + "description": [], + "signature": [ + "\"maintenanceWindow\"" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE", + "type": "string", + "tags": [], + "label": "MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE", + "description": [], + "signature": [ + "\"maintenance-window\"" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindow", + "type": "Type", + "tags": [], + "label": "MaintenanceWindow", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.MaintenanceWindowSOProperties", + "text": "MaintenanceWindowSOProperties" + }, + " & ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.MaintenanceWindowModificationMetadata", + "text": "MaintenanceWindowModificationMetadata" + }, + " & { status: ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.MaintenanceWindowStatus", + "text": "MaintenanceWindowStatus" + }, + "; eventStartTime: string | null; eventEndTime: string | null; id: string; }" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOAttributes", + "type": "Type", + "tags": [], + "label": "MaintenanceWindowSOAttributes", + "description": [], + "signature": [ + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.MaintenanceWindowSOProperties", + "text": "MaintenanceWindowSOProperties" + }, + " & ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.MaintenanceWindowModificationMetadata", + "text": "MaintenanceWindowModificationMetadata" + } + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.MappedParams", @@ -9087,6 +9592,37 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.RRuleParams", + "type": "Type", + "tags": [], + "label": "RRuleParams", + "description": [], + "signature": [ + "Partial<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RRuleRecord", + "text": "RRuleRecord" + }, + "> & Pick<", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RRuleRecord", + "text": "RRuleRecord" + }, + ", \"dtstart\" | \"tzid\">" + ], + "path": "x-pack/plugins/alerting/common/rrule_type.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.RuleActionAlertsFilterProperty", @@ -9280,7 +9816,15 @@ "label": "RuleSnooze", "description": [], "signature": [ - "{ duration: number; rRule: SnoozeRRule; id?: string | undefined; skipRecurrences?: string[] | undefined; }[]" + "{ duration: number; rRule: ", + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RRuleParams", + "text": "RRuleParams" + }, + "; id?: string | undefined; skipRecurrences?: string[] | undefined; }[]" ], "path": "x-pack/plugins/alerting/common/rule_snooze_type.ts", "deprecated": false, @@ -9858,6 +10402,42 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.MAINTENANCE_WINDOW_API_PRIVILEGES", + "type": "Object", + "tags": [], + "label": "MAINTENANCE_WINDOW_API_PRIVILEGES", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-common.MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW", + "type": "string", + "tags": [], + "label": "READ_MAINTENANCE_WINDOW", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MAINTENANCE_WINDOW_API_PRIVILEGES.WRITE_MAINTENANCE_WINDOW", + "type": "string", + "tags": [], + "label": "WRITE_MAINTENANCE_WINDOW", + "description": [], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.rawAlertInstance", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 66e4d6f3a5da1..45d873622cc86 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 567 | 1 | 546 | 41 | +| 601 | 1 | 580 | 42 | ## Client diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 765b41fd43653..e5bbbe4962403 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index c6bb484d70423..3ac0fced2b457 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index e578d9c320779..f6f092e5107d3 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 4aab5b7dafd22..c414bab498ebb 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 9b4cf5436604a..92e14941438e6 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.devdocs.json b/api_docs/cases.devdocs.json index 3e59b79ad242b..890144e0b538d 100644 --- a/api_docs/cases.devdocs.json +++ b/api_docs/cases.devdocs.json @@ -794,6 +794,30 @@ "section": "def-common.CommentType", "text": "CommentType" }, + ".externalReference; owner: string; } | { externalReferenceId: string; externalReferenceStorage: { type: ", + { + "pluginId": "cases", + "scope": "common", + "docId": "kibCasesPluginApi", + "section": "def-common.ExternalReferenceStorageType", + "text": "ExternalReferenceStorageType" + }, + ".savedObject; soType: string; }; externalReferenceAttachmentTypeId: string; externalReferenceMetadata: { [x: string]: ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.JsonValue", + "text": "JsonValue" + }, + "; } | null; type: ", + { + "pluginId": "cases", + "scope": "common", + "docId": "kibCasesPluginApi", + "section": "def-common.CommentType", + "text": "CommentType" + }, ".externalReference; owner: string; } | { type: ", { "pluginId": "cases", @@ -992,7 +1016,17 @@ "section": "def-public.CaseAttachmentsWithoutOwner", "text": "CaseAttachmentsWithoutOwner" }, - " | undefined; headerContent?: React.ReactNode; }) => void; close: () => void; }; useCasesAddToExistingCaseModal: (props?: AddToExistingFlyoutProps) => { open: ({ attachments }?: { attachments?: ", + " | undefined; headerContent?: React.ReactNode; }) => void; close: () => void; }; useCasesAddToExistingCaseModal: (props?: ", + "AddToExistingCaseModalProps", + ") => { open: ({ getAttachments, }?: { getAttachments?: (({ theCase }: { theCase?: ", + { + "pluginId": "cases", + "scope": "common", + "docId": "kibCasesPluginApi", + "section": "def-common.Case", + "text": "Case" + }, + " | undefined; }) => ", { "pluginId": "cases", "scope": "public", @@ -1000,7 +1034,7 @@ "section": "def-public.CaseAttachmentsWithoutOwner", "text": "CaseAttachmentsWithoutOwner" }, - " | undefined; }) => void; close: () => void; }; }" + ") | undefined; }) => void; close: () => void; }; }" ], "path": "x-pack/plugins/cases/public/types.ts", "deprecated": false, diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 6d9b0e66636b8..0d6a40ab12ce6 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 96 | 0 | 79 | 31 | +| 96 | 0 | 79 | 32 | ## Client diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 81fd944cf18b5..3eeb9afd99e19 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 2b1a32e14fdb5..c06de42b2b643 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 32f3e747da885..58149b8a1328b 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index c0211c6f4d584..cfb98e4160b23 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index 33ecfa74c9da8..3ea5f9b64d1cd 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index f3d8ada1ca19a..19c8ac364cca9 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 40d105fe433b0..bb4a888ea42b7 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 27938c3bf1974..57dff11f3c70c 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.devdocs.json b/api_docs/content_management.devdocs.json index 69a49d0e79b17..b1fc3a2404541 100644 --- a/api_docs/content_management.devdocs.json +++ b/api_docs/content_management.devdocs.json @@ -1845,30 +1845,8 @@ "label": "utils", "description": [], "signature": [ - "{ getTransforms: (definitions: ", - { - "pluginId": "@kbn/object-versioning", - "scope": "common", - "docId": "kibKbnObjectVersioningPluginApi", - "section": "def-common.ServiceDefinitionVersioned", - "text": "ServiceDefinitionVersioned" - }, - ", requestVersion: number, _compiled?: { [path: string]: ", - { - "pluginId": "@kbn/object-versioning", - "scope": "common", - "docId": "kibKbnObjectVersioningPluginApi", - "section": "def-common.ObjectMigrationDefinition", - "text": "ObjectMigrationDefinition" - }, - "; } | undefined) => ", - { - "pluginId": "@kbn/object-versioning", - "scope": "common", - "docId": "kibKbnObjectVersioningPluginApi", - "section": "def-common.ServiceTransforms", - "text": "ServiceTransforms" - }, + "{ getTransforms: ", + "StorageContextGetTransformFn", "; }" ], "path": "src/plugins/content_management/server/core/types.ts", diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index ca04b10302b92..86adcb31a05df 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 143 | 0 | 124 | 6 | +| 143 | 0 | 124 | 7 | ## Client diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 548f9e945a2de..3288f1d17baab 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 226c536e9ffb5..e13ca8288243b 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.devdocs.json b/api_docs/dashboard.devdocs.json index f2323237ed7b0..a4f01007d7d75 100644 --- a/api_docs/dashboard.devdocs.json +++ b/api_docs/dashboard.devdocs.json @@ -1515,6 +1515,41 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardListingTable", + "type": "Function", + "tags": [], + "label": "DashboardListingTable", + "description": [], + "signature": [ + "(props: ", + "DashboardListingProps", + ") => JSX.Element" + ], + "path": "src/plugins/dashboard/public/dashboard_listing/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dashboard", + "id": "def-public.DashboardListingTable.$1", + "type": "CompoundType", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "DashboardListingProps" + ], + "path": "src/plugins/dashboard/public/dashboard_listing/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "dashboard", "id": "def-public.LazyDashboardContainerRenderer", diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 16dfb4d39fbae..1cf38f45d5ed3 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 188 | 0 | 179 | 13 | +| 190 | 0 | 181 | 14 | ## Client diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 81d8cee4b4846..30a849b2cd99a 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index a5fc6da9e9a3d..db375b8cd5665 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -13035,7 +13035,7 @@ "section": "def-common.FieldFormatsRegistry", "text": "FieldFormatsRegistry" }, - ", \"register\" | \"init\"> & { deserialize: ", + ", \"init\" | \"register\"> & { deserialize: ", { "pluginId": "fieldFormats", "scope": "common", @@ -13737,18 +13737,6 @@ "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/dataview_picker/change_dataview.tsx" }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" - }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "controls", "path": "src/plugins/controls/public/services/options_list/options_list_service.ts" @@ -13781,6 +13769,18 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "dataViews", + "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" + }, + { + "plugin": "dataViews", + "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "aiops", "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx" @@ -13821,22 +13821,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -13853,6 +13837,22 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" + }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts" @@ -21367,18 +21367,6 @@ "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/dataview_picker/change_dataview.tsx" }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" - }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" - }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "controls", "path": "src/plugins/controls/public/services/options_list/options_list_service.ts" @@ -21411,6 +21399,18 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "dataViews", + "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" + }, + { + "plugin": "dataViews", + "path": "src/plugins/data_views/server/rest_api_routes/update_data_view.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "aiops", "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx" @@ -21451,22 +21451,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -21483,6 +21467,22 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" + }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts" diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 1ad37ebf9d75f..6d7ecdba213c3 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 45fd446b374e3..89f562f1f76aa 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 495cd12ea6f32..3f27dab1ede57 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -3253,7 +3253,7 @@ "section": "def-common.SavedObjectsFindOptionsReference", "text": "SavedObjectsFindOptionsReference" }, - "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; }" + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; migrationVersionCompatibility?: \"raw\" | \"compatible\" | undefined; }" ], "path": "src/plugins/data/server/search/session/types.ts", "deprecated": false, @@ -24992,7 +24992,7 @@ "label": "fieldFormats", "description": [], "signature": [ - "{ has: (id: string) => boolean; deserialize: ", + "{ deserialize: ", { "pluginId": "fieldFormats", "scope": "common", @@ -25232,7 +25232,7 @@ "section": "def-common.FieldFormatConfig", "text": "FieldFormatConfig" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "src/plugins/data/common/search/aggs/aggs_service.ts", "deprecated": false, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index f3beb45c98f29..e134bb5db798f 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 7e4b38cc6e36e..aaa7085b8fc33 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 0f81dcc9ff2b6..fa2bd9ba306da 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 1e4bce498ea78..d70538091beeb 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 2007675704479..7d419403e8ded 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -303,10 +303,6 @@ "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/dataview_picker/change_dataview.tsx" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "controls", "path": "src/plugins/controls/public/services/options_list/options_list_service.ts" @@ -339,6 +335,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "aiops", "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx" @@ -379,22 +379,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -411,6 +395,22 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" + }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts" @@ -6593,7 +6593,7 @@ "\nField formats" ], "signature": [ - "{ has: (id: string) => boolean; register: (fieldFormats: ", + "{ register: (fieldFormats: ", { "pluginId": "fieldFormats", "scope": "common", @@ -6601,7 +6601,7 @@ "section": "def-common.FieldFormatInstanceType", "text": "FieldFormatInstanceType" }, - "[]) => void; }" + "[]) => void; has: (id: string) => boolean; }" ], "path": "src/plugins/data_views/public/types.ts", "deprecated": false, @@ -6641,7 +6641,7 @@ "section": "def-common.FieldFormatsRegistry", "text": "FieldFormatsRegistry" }, - ", \"register\" | \"init\"> & { deserialize: ", + ", \"init\" | \"register\"> & { deserialize: ", { "pluginId": "fieldFormats", "scope": "common", @@ -8560,10 +8560,6 @@ "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/dataview_picker/change_dataview.tsx" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "controls", "path": "src/plugins/controls/public/services/options_list/options_list_service.ts" @@ -8596,6 +8592,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "aiops", "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx" @@ -8636,22 +8636,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -8668,6 +8652,22 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" + }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts" @@ -15874,10 +15874,6 @@ "plugin": "unifiedSearch", "path": "src/plugins/unified_search/public/dataview_picker/change_dataview.tsx" }, - { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" - }, { "plugin": "controls", "path": "src/plugins/controls/public/services/options_list/options_list_service.ts" @@ -15910,6 +15906,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx" }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" + }, { "plugin": "aiops", "path": "x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx" @@ -15950,22 +15950,6 @@ "plugin": "apm", "path": "x-pack/plugins/apm/server/routes/data_view/create_static_data_view.ts" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/utils/observability_data_views/observability_data_views.ts" @@ -15982,6 +15966,22 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx" + }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts" @@ -21364,7 +21364,7 @@ "\nField formats service" ], "signature": [ - "{ has: (id: string) => boolean; deserialize: ", + "{ deserialize: ", { "pluginId": "fieldFormats", "scope": "common", @@ -21604,7 +21604,7 @@ "section": "def-common.FieldFormatConfig", "text": "FieldFormatConfig" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "src/plugins/data_views/common/data_views/data_views.ts", "deprecated": false, diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 5d2e47c1b4991..c7a761b939ded 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 77adfe044f770..c62fc0e0da847 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 68042bfb1f404..36b1791990c4b 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -17,17 +17,17 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Referencing plugin(s) | Remove By | | ---------------|-----------|-----------| | | ml, stackAlerts | - | -| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, savedObjectsManagement, unifiedSearch, triggersActionsUi, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, exploratoryView, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | -| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, savedObjectsManagement, unifiedSearch, triggersActionsUi, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, exploratoryView, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | -| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, data, savedObjectsManagement, unifiedSearch, triggersActionsUi, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, exploratoryView, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover | - | -| | home, data, esUiShared, spaces, savedObjectsManagement, observability, exploratoryView, fleet, ml, apm, enterpriseSearch, indexLifecycleManagement, synthetics, upgradeAssistant, ux, kibanaOverview | - | +| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, savedObjectsManagement, unifiedSearch, controls, unifiedFieldList, lens, triggersActionsUi, aiops, ml, infra, visTypeTimeseries, apm, exploratoryView, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | +| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, savedObjectsManagement, unifiedSearch, controls, unifiedFieldList, lens, triggersActionsUi, aiops, ml, infra, visTypeTimeseries, apm, exploratoryView, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | +| | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, data, savedObjectsManagement, unifiedSearch, controls, unifiedFieldList, lens, triggersActionsUi, aiops, ml, infra, visTypeTimeseries, apm, exploratoryView, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover | - | +| | home, data, esUiShared, spaces, savedObjectsManagement, exploratoryView, fleet, observability, ml, apm, enterpriseSearch, indexLifecycleManagement, synthetics, upgradeAssistant, ux, kibanaOverview | - | | | encryptedSavedObjects, actions, data, ml, logstash, securitySolution, cloudChat | - | | | actions, ml, savedObjectsTagging, enterpriseSearch | - | -| | @kbn/core-plugins-browser-internal, @kbn/core-root-browser-internal, dataViews, home, data, savedObjects, unifiedSearch, presentationUtil, visualizations, dashboard, lens, discover, fileUpload, maps, ml, infra, fleet, canvas, dashboardEnhanced, graph, monitoring, synthetics, transform, dataVisualizer, cloudSecurityPosture, securitySolution | - | +| | @kbn/core-plugins-browser-internal, @kbn/core-root-browser-internal, dataViews, home, data, savedObjects, unifiedSearch, presentationUtil, visualizations, dashboard, lens, discover, fileUpload, ml, infra, fleet, canvas, dashboardEnhanced, graph, monitoring, synthetics, transform, dataVisualizer, cloudSecurityPosture, securitySolution | - | | | @kbn/core-saved-objects-browser, @kbn/core-saved-objects-browser-internal, @kbn/core, dataViews, home, savedObjects, savedSearch, visualizations, dashboard, lens, ml, canvas, graph, visTypeTimeseries, @kbn/core-saved-objects-browser-mocks | - | -| | @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, presentationUtil, savedSearch, visualizations, dashboard, lens, maps, ml, infra, cloudSecurityPosture, dashboardEnhanced, graph, securitySolution, synthetics, @kbn/core-saved-objects-browser-internal | - | +| | @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, presentationUtil, savedSearch, visualizations, dashboard, lens, ml, infra, cloudSecurityPosture, dashboardEnhanced, graph, securitySolution, synthetics, @kbn/core-saved-objects-browser-internal | - | | | @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, visualizations, dashboard, ml, infra, cloudSecurityPosture, dashboardEnhanced, monitoring, @kbn/core-saved-objects-browser-internal | - | -| | @kbn/core-saved-objects-browser-internal, @kbn/core, dataViews, savedObjects, embeddable, presentationUtil, visualizations, dashboard, lens, aiops, ml, maps, dataVisualizer, infra, fleet, cloudSecurityPosture, dashboardEnhanced, graph, synthetics, securitySolution, @kbn/core-saved-objects-browser-mocks | - | +| | @kbn/core-saved-objects-browser-internal, @kbn/core, dataViews, savedObjects, embeddable, presentationUtil, visualizations, dashboard, lens, aiops, ml, dataVisualizer, infra, fleet, cloudSecurityPosture, dashboardEnhanced, graph, synthetics, securitySolution, @kbn/core-saved-objects-browser-mocks | - | | | @kbn/core-lifecycle-browser-mocks, @kbn/core, ml, dashboard, dataViews, savedSearch, @kbn/core-plugins-browser-internal | - | | | @kbn/core, savedObjects, embeddable, visualizations, dashboard, fleet, infra, canvas, graph, ml, @kbn/core-saved-objects-common, @kbn/core-saved-objects-server, actions, alerting, savedSearch, enterpriseSearch, securitySolution, taskManager, @kbn/core-saved-objects-server-internal, @kbn/core-saved-objects-api-server | - | | | stackAlerts, alerting, securitySolution, inputControlVis | - | @@ -59,14 +59,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | monitoring | - | | | @kbn/core-saved-objects-api-browser, @kbn/core, savedObjects, savedObjectsManagement, visualizations, savedObjectsTagging, lens, fleet, graph, dashboard, savedObjectsTaggingOss, kibanaUtils, expressions, dataViews, data, embeddable, controls, uiActionsEnhanced, cases, maps, canvas, dashboardEnhanced, globalSearchProviders, infra | - | -| | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, savedSearch, visualizations, dashboard, lens, maps, infra, graph | - | +| | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, savedSearch, visualizations, dashboard, lens, infra, graph | - | | | @kbn/core-saved-objects-browser-mocks, home, @kbn/core-saved-objects-browser-internal | - | -| | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, visualizations, dashboard, maps, infra, graph | - | +| | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, visualizations, dashboard, infra, graph | - | | | @kbn/core-saved-objects-browser-mocks, synthetics, @kbn/core-saved-objects-browser-internal | - | | | @kbn/core-saved-objects-browser-mocks, dashboard, savedObjects, @kbn/core-saved-objects-browser-internal | - | -| | @kbn/core-saved-objects-browser-mocks, dataViews, savedSearch, visualizations, dashboard, lens, maps, graph, @kbn/core-saved-objects-browser-internal | - | +| | @kbn/core-saved-objects-browser-mocks, dataViews, savedSearch, visualizations, dashboard, lens, graph, @kbn/core-saved-objects-browser-internal | - | | | @kbn/core-saved-objects-browser-mocks, fleet, synthetics, @kbn/core-saved-objects-browser-internal | - | -| | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedSearch, maps, infra, savedObjects | - | +| | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedSearch, infra, savedObjects | - | | | @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-browser-internal | - | | | @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-browser-internal | - | | | @kbn/core-saved-objects-browser-internal | - | @@ -83,12 +83,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/core-saved-objects-browser-internal, @kbn/core | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core | - | | | @kbn/core-saved-objects-browser-internal | - | -| | @kbn/core-lifecycle-browser, @kbn/core-saved-objects-browser-internal, @kbn/core, savedSearch, visualizations, dashboard, lens, observability, exploratoryView, canvas, transform, @kbn/core-saved-objects-browser-mocks | - | +| | @kbn/core-lifecycle-browser, @kbn/core-saved-objects-browser-internal, @kbn/core, savedSearch, visualizations, dashboard, lens, exploratoryView, observability, canvas, transform, @kbn/core-saved-objects-browser-mocks | - | | | @kbn/core | - | | | @kbn/core | - | | | @kbn/core, lens, savedObjects, visualizations | - | | | @kbn/core | - | -| | @kbn/core, advancedSettings, triggersActionsUi, visualizations | - | +| | @kbn/core, advancedSettings, visualizations, triggersActionsUi | - | | | home, canvas, osquery | - | | | dataViews, maps | - | | | dataViewManagement, dataViews | - | @@ -98,7 +98,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | | | dataViews, dataViewManagement | - | -| | observability, exploratoryView, dataVisualizer, fleet, cloudSecurityPosture, discoverEnhanced, osquery, synthetics | - | +| | exploratoryView, observability, dataVisualizer, fleet, cloudSecurityPosture, discoverEnhanced, osquery, synthetics | - | | | @kbn/core-saved-objects-api-server-internal, fleet | - | | | canvas | - | | | canvas | - | @@ -126,7 +126,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/core-elasticsearch-server-internal, @kbn/core-plugins-server-internal, console | - | | | @kbn/core-plugins-server-internal | - | | | @kbn/core-saved-objects-api-server-internal | - | -| | security, licenseManagement, ml, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | +| | security, licenseManagement, ml, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher, profiling | 8.8.0 | | | spaces, security, actions, alerting, ml, remoteClusters, graph, indexLifecycleManagement, mapsEms, painlessLab, rollup, searchprofiler, securitySolution, snapshotRestore, transform, upgradeAssistant | 8.8.0 | | | spaces, security, alerting | 8.8.0 | | | embeddable, presentationUtil, dashboard, discover, graph | 8.8.0 | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 80da35ef39f6d..747673056fa15 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -343,7 +343,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/plugin.test.ts#:~:text=getKibanaFeatures) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/plugin.ts#:~:text=license%24), [license_state.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/lib/license_state.test.ts#:~:text=license%24), [license_state.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/lib/license_state.test.ts#:~:text=license%24) | 8.8.0 | | | [task.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/usage/task.ts#:~:text=index) | - | -| | [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes)+ 42 more | - | +| | [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes)+ 44 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/saved_objects/index.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | @@ -464,12 +464,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [dashboard_saved_object_service.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object_service.ts#:~:text=savedObjects), [index.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/index.ts#:~:text=savedObjects) | - | | | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectsClientContract), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectsClientContract), [save_dashboard_state_to_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts#:~:text=SavedObjectsClientContract), [save_dashboard_state_to_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts#:~:text=SavedObjectsClientContract), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SavedObjectsClientContract), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SavedObjectsClientContract), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SavedObjectsClientContract), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SavedObjectsClientContract), [check_for_duplicate_dashboard_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.ts#:~:text=SavedObjectsClientContract), [check_for_duplicate_dashboard_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.ts#:~:text=SavedObjectsClientContract)+ 4 more | - | | | [save_dashboard_state_to_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts#:~:text=create), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=create) | - | -| | [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx#:~:text=delete) | - | +| | [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx#:~:text=delete) | - | | | [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=find), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=find), [check_for_duplicate_dashboard_title.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/check_for_duplicate_dashboard_title.ts#:~:text=find), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=find) | - | | | [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=get) | - | | | [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=bulkGet) | - | | | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=resolve) | - | -| | [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SimpleSavedObject), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SimpleSavedObject), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx#:~:text=SimpleSavedObject), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx#:~:text=SimpleSavedObject) | - | +| | [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SimpleSavedObject), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SimpleSavedObject), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx#:~:text=SimpleSavedObject), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx#:~:text=SimpleSavedObject) | - | | | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=ResolvedSimpleSavedObject), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=ResolvedSimpleSavedObject) | - | | | [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes)+ 20 more | - | | | [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=SavedObjectsStart), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=SavedObjectsStart) | - | @@ -896,15 +896,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [es_search_source.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit), [es_search_source.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit), [es_search_source.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit), [es_search_source.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit) | - | | | [es_tooltip_property.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.test.ts#:~:text=title) | - | | | [es_search_source.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit), [es_search_source.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx#:~:text=flattenHit) | - | -| | [kibana_services.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/kibana_services.ts#:~:text=savedObjects) | - | -| | [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=create) | - | -| | [maps_list_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=delete) | - | -| | [maps_list_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=find), [load_list_and_render.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx#:~:text=find) | - | -| | [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=resolve) | - | -| | [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=update) | - | -| | [maps_vis_type_alias.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/maps_vis_type_alias.ts#:~:text=SimpleSavedObject), [maps_vis_type_alias.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/maps_vis_type_alias.ts#:~:text=SimpleSavedObject), [maps_vis_type_alias.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/maps_vis_type_alias.ts#:~:text=SimpleSavedObject), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=SimpleSavedObject), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=SimpleSavedObject) | - | | | [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=ResolvedSimpleSavedObject), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=ResolvedSimpleSavedObject), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=ResolvedSimpleSavedObject), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=ResolvedSimpleSavedObject) | - | -| | [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=SavedObjectReference), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=SavedObjectReference), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=SavedObjectReference), [maps_list_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx#:~:text=SavedObjectReference) | - | +| | [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [references.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/common/migrations/references.ts#:~:text=SavedObjectReference), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=SavedObjectReference), [map_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/public/map_attribute_service.ts#:~:text=SavedObjectReference) | - | | | [setup_saved_objects.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | @@ -934,7 +927,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/app.tsx#:~:text=savedObjects), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/app.tsx#:~:text=savedObjects), [dashboard_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.ts#:~:text=savedObjects) | - | | | [dependency_cache.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/util/dependency_cache.ts#:~:text=SavedObjectsClientContract), [dependency_cache.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/util/dependency_cache.ts#:~:text=SavedObjectsClientContract), [use_resolver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/routing/use_resolver.ts#:~:text=SavedObjectsClientContract), [use_resolver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/routing/use_resolver.ts#:~:text=SavedObjectsClientContract), [use_resolver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/routing/use_resolver.ts#:~:text=SavedObjectsClientContract), [dashboard_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.ts#:~:text=SavedObjectsClientContract), [dashboard_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.ts#:~:text=SavedObjectsClientContract), [router.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/routing/router.tsx#:~:text=SavedObjectsClientContract), [router.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/routing/router.tsx#:~:text=SavedObjectsClientContract) | - | | | [index_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/util/index_utils.ts#:~:text=find), [resolvers.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/recognize/resolvers.ts#:~:text=find), [dashboard_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.ts#:~:text=find), [dashboard_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.test.ts#:~:text=find) | - | -| | [index_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/util/index_utils.ts#:~:text=get), [route_resolver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts#:~:text=get) | - | +| | [index_utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/util/index_utils.ts#:~:text=get), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts#:~:text=get), [route_resolver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts#:~:text=get) | - | | | [kibana.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/common/types/kibana.ts#:~:text=SimpleSavedObject), [kibana.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/common/types/kibana.ts#:~:text=SimpleSavedObject) | - | | | [dashboard_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.test.ts#:~:text=savedObjectsServiceMock), [dashboard_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/services/dashboard_service.test.ts#:~:text=savedObjectsServiceMock) | - | | | [modules.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/common/types/modules.ts#:~:text=SavedObjectAttributes), [modules.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/common/types/modules.ts#:~:text=SavedObjectAttributes) | - | @@ -999,6 +992,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] +## profiling + +| Deprecated API | Reference location(s) | Remove By | +| ---------------|-----------|-----------| +| | [license_context.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/profiling/public/components/contexts/license/license_context.tsx#:~:text=license%24) | 8.8.0 | + + + ## remoteClusters | Deprecated API | Reference location(s) | Remove By | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 5c233f0397524..f5763c23b9b37 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -139,6 +139,14 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ +## @elastic/profiling-ui + +| Plugin | Deprecated API | Reference location(s) | Remove By | +| --------|-------|-----------|-----------| +| profiling | | [license_context.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/profiling/public/components/contexts/license/license_context.tsx#:~:text=license%24) | 8.8.0 | + + + ## @elastic/response-ops | Plugin | Deprecated API | Reference location(s) | Remove By | diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 1e21e06959f82..bcf610652648e 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 61aa5a32721fb..71254ce683ac4 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -1158,14 +1158,14 @@ "deprecated": true, "trackAdoption": false, "references": [ - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/hooks/use_discover_link.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx" + }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/common/components/results_links/results_links.tsx" diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index eb08bd8067900..5fbb097256f14 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 5c8ed280e703b..54a9f327c9f01 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 8b4ede5e9dcf8..846c5ca955d1a 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index a9e311e6c1154..9bb3fe64f3e52 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -8194,6 +8194,27 @@ "path": "src/plugins/embeddable/public/plugin.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddableStartDependencies.savedObjectsTaggingOss", + "type": "Object", + "tags": [], + "label": "savedObjectsTaggingOss", + "description": [], + "signature": [ + { + "pluginId": "savedObjectsTaggingOss", + "scope": "public", + "docId": "kibSavedObjectsTaggingOssPluginApi", + "section": "def-public.SavedObjectTaggingOssPluginStart", + "text": "SavedObjectTaggingOssPluginStart" + }, + " | undefined" + ], + "path": "src/plugins/embeddable/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 1e55796cf1301..02977e3729e9f 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 544 | 11 | 441 | 4 | +| 545 | 11 | 442 | 4 | ## Client diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index deaf28e156244..14506c9223e2f 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 8c27e2cb53287..6b95e96118bbe 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 460e6d2bb5e61..103ba9e65a7f3 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index aa002119d6e3f..e35fd39160a86 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index c81317430a390..e516bb6a05bb9 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 5f111fb5577b1..b56a56228963f 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.devdocs.json b/api_docs/exploratory_view.devdocs.json index e3a6af7a52ef5..8d7c0c15654a4 100644 --- a/api_docs/exploratory_view.devdocs.json +++ b/api_docs/exploratory_view.devdocs.json @@ -1884,66 +1884,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "exploratoryView", - "id": "def-public.ExploratoryViewPublicPluginsSetup.observability", - "type": "Object", - "tags": [], - "label": "observability", - "description": [], - "signature": [ - "{ dashboard: { register: ({ appName, fetchData, hasData, }: { appName: T; } & ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.DataHandler", - "text": "DataHandler" - }, - ") => void; }; observabilityRuleTypeRegistry: { register: (type: ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.ObservabilityRuleTypeModel", - "text": "ObservabilityRuleTypeModel" - }, - ") => void; getFormatter: (typeId: string) => ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.ObservabilityRuleTypeFormatter", - "text": "ObservabilityRuleTypeFormatter" - }, - " | undefined; list: () => string[]; }; navigation: { registerSections: (sections$: ", - "Observable", - "<", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.NavigationSection", - "text": "NavigationSection" - }, - "[]>) => void; }; useRulesLink: (options?: ", - "Options", - ") => ", - "LinkProps", - "; }" - ], - "path": "x-pack/plugins/exploratory_view/public/plugin.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "exploratoryView", "id": "def-public.ExploratoryViewPublicPluginsSetup.share", @@ -2252,63 +2192,33 @@ }, { "parentPluginId": "exploratoryView", - "id": "def-public.ExploratoryViewPublicPluginsStart.observability", + "id": "def-public.ExploratoryViewPublicPluginsStart.observabilityShared", "type": "Object", "tags": [], - "label": "observability", + "label": "observabilityShared", "description": [], "signature": [ - "{ observabilityRuleTypeRegistry: { register: (type: ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.ObservabilityRuleTypeModel", - "text": "ObservabilityRuleTypeModel" - }, - ") => void; getFormatter: (typeId: string) => ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.ObservabilityRuleTypeFormatter", - "text": "ObservabilityRuleTypeFormatter" - }, - " | undefined; list: () => string[]; }; navigation: { PageTemplate: (pageTemplateProps: ", + "{ navigation: { PageTemplate: (pageTemplateProps: ", "WrappedPageTemplateProps", - ") => JSX.Element; }; createExploratoryViewUrl: ({ reportType, allSeries }: { reportType: ", - "ReportViewType", - "; allSeries: ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.AllSeries", - "text": "AllSeries" - }, - "; }, baseHref?: string, appId?: string) => string; getAppDataView: (appId: ", - "AppDataType", - ", indexPattern?: string | undefined) => Promise<", + ") => JSX.Element; }; updateGlobalNavigation: ({ capabilities, deepLinks, updater$, }: { capabilities: Readonly<{ [x: string]: Readonly<{ [x: string]: boolean | Readonly<{ [x: string]: boolean; }>; }>; navLinks: Readonly<{ [x: string]: boolean; }>; management: Readonly<{ [x: string]: Readonly<{ [x: string]: boolean; }>; }>; catalogue: Readonly<{ [x: string]: boolean; }>; }>; deepLinks: ", { - "pluginId": "dataViews", + "pluginId": "@kbn/core-application-browser", "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.AppDeepLink", + "text": "AppDeepLink" }, - " | null | undefined>; ExploratoryViewEmbeddable: (props: ", + "[]; updater$: ", + "Subject", + "<", { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.ExploratoryEmbeddableProps", - "text": "ExploratoryEmbeddableProps" + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.AppUpdater", + "text": "AppUpdater" }, - ") => JSX.Element | null; useRulesLink: (options?: ", - "Options", - ") => ", - "LinkProps", - "; }" + ">; }) => void; }" ], "path": "x-pack/plugins/exploratory_view/public/plugin.ts", "deprecated": false, diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index bd8d3f2132d20..849453338e010 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/uptime](https://github.com/orgs/elastic/teams/uptime) for ques | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 352 | 4 | 349 | 22 | +| 351 | 4 | 348 | 22 | ## Client diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 88e50817ecc8c..45f3eb5cbf61f 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 1525bf64fab4e..c0bccdf35a0fe 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index df8efb6e1cd98..d934e55b7f2c6 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index d7cdf231a390e..f64aa1581a3c5 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 0f41ccb3df82d..22e421096034b 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 7b5cb5b850c57..b5e8ad43772ee 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 96f4e86302a55..b83a4acedad45 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 7a7f01bd21ff1..613f702138868 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 0bdcb5eff84ff..3f5cb2ac0f28b 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index adb9bbef10a9d..b3235f04d4e6d 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index dc400f797573e..dbb0961004d02 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index b0ef577733241..f10426042f970 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 9ece3347295ae..1fb52cf6c77eb 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 227b721ce9ca1..1bd11ec87fad7 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 344d851004fda..21867f9dcb7c3 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.devdocs.json b/api_docs/field_formats.devdocs.json index 309316f1deb28..d4f8c33a70b9e 100644 --- a/api_docs/field_formats.devdocs.json +++ b/api_docs/field_formats.devdocs.json @@ -352,7 +352,7 @@ "label": "FieldFormatsSetup", "description": [], "signature": [ - "{ has: (id: string) => boolean; register: (fieldFormats: ", + "{ register: (fieldFormats: ", { "pluginId": "fieldFormats", "scope": "common", @@ -360,7 +360,7 @@ "section": "def-common.FieldFormatInstanceType", "text": "FieldFormatInstanceType" }, - "[]) => void; }" + "[]) => void; has: (id: string) => boolean; }" ], "path": "src/plugins/field_formats/public/plugin.ts", "deprecated": false, @@ -384,7 +384,7 @@ "section": "def-common.FieldFormatsRegistry", "text": "FieldFormatsRegistry" }, - ", \"register\" | \"init\"> & { deserialize: ", + ", \"init\" | \"register\"> & { deserialize: ", { "pluginId": "fieldFormats", "scope": "common", @@ -5748,7 +5748,7 @@ "label": "FieldFormatsStartCommon", "description": [], "signature": [ - "{ has: (id: string) => boolean; deserialize: ", + "{ deserialize: ", { "pluginId": "fieldFormats", "scope": "common", @@ -5988,7 +5988,7 @@ "section": "def-common.FieldFormatConfig", "text": "FieldFormatConfig" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "src/plugins/field_formats/common/types.ts", "deprecated": false, @@ -6172,15 +6172,7 @@ "label": "IFieldFormatsRegistry", "description": [], "signature": [ - "{ has: (id: string) => boolean; register: (fieldFormats: ", - { - "pluginId": "fieldFormats", - "scope": "common", - "docId": "kibFieldFormatsPluginApi", - "section": "def-common.FieldFormatInstanceType", - "text": "FieldFormatInstanceType" - }, - "[]) => void; init: (getConfig: ", + "{ init: (getConfig: ", { "pluginId": "fieldFormats", "scope": "common", @@ -6212,6 +6204,14 @@ "section": "def-common.FieldFormatInstanceType", "text": "FieldFormatInstanceType" }, + "[]) => void; register: (fieldFormats: ", + { + "pluginId": "fieldFormats", + "scope": "common", + "docId": "kibFieldFormatsPluginApi", + "section": "def-common.FieldFormatInstanceType", + "text": "FieldFormatInstanceType" + }, "[]) => void; deserialize: ", { "pluginId": "fieldFormats", @@ -6452,7 +6452,7 @@ "section": "def-common.FieldFormatConfig", "text": "FieldFormatConfig" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "src/plugins/field_formats/common/index.ts", "deprecated": false, diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index a1a009f567856..03df4c3fcfb44 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 4cc6fd705adf5..cef26e17b6d33 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 7f34684f9d5ad..284fc310bee93 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -553,6 +553,30 @@ "deprecated": false, "trackAdoption": true, "references": [ + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/application.tsx" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.ts" + }, + { + "plugin": "cases", + "path": "x-pack/plugins/cases/public/plugin.ts" + }, { "plugin": "imageEmbeddable", "path": "src/plugins/image_embeddable/public/plugin.ts" diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 9621bd24cc1e7..9a81f60a70ba5 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index b96c3e34ac3bd..e25bf2cf7fee0 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index 942a2edd93055..c8b3355a17f38 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -20188,7 +20188,7 @@ "label": "data_stream", "description": [], "signature": [ - "{ dataset: string; type: string; elasticsearch?: { privileges?: { indices?: string[] | undefined; } | undefined; index_mode?: string | undefined; source_mode?: string | undefined; } | undefined; }" + "{ dataset: string; type: string; elasticsearch?: { dynamic_dataset?: boolean | undefined; dynamic_namespace?: boolean | undefined; privileges?: { indices?: string[] | undefined; } | undefined; index_mode?: string | undefined; source_mode?: string | undefined; } | undefined; }" ], "path": "x-pack/plugins/fleet/common/types/models/package_policy.ts", "deprecated": false, @@ -21511,6 +21511,34 @@ "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.RegistryElasticsearch.dynamic_dataset", + "type": "CompoundType", + "tags": [], + "label": "dynamic_dataset", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.RegistryElasticsearch.dynamic_namespace", + "type": "CompoundType", + "tags": [], + "label": "dynamic_namespace", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -25192,6 +25220,21 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "fleet", + "id": "def-common.DASHBOARD_LOCATORS_IDS", + "type": "Object", + "tags": [], + "label": "DASHBOARD_LOCATORS_IDS", + "description": [], + "signature": [ + "{ readonly ELASTIC_AGENT_OVERVIEW: \"elastic_agent-a148dc70-6b3c-11ed-98de-67bdecd21824\"; readonly ELASTIC_AGENT_AGENT_INFO: \"elastic_agent-0600ffa0-6b5e-11ed-98de-67bdecd21824\"; readonly ELASTIC_AGENT_AGENT_METRICS: \"elastic_agent-f47f18cc-9c7d-4278-b2ea-a6dee816d395\"; readonly ELASTIC_AGENT_INTEGRATIONS: \"elastic_agent-1a4e7280-6b5e-11ed-98de-67bdecd21824\"; }" + ], + "path": "x-pack/plugins/fleet/common/constants/locators.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-common.ENDPOINT_PRIVILEGES", diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 878fbaeb53506..765d621169bd4 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1098 | 3 | 993 | 27 | +| 1101 | 3 | 996 | 27 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 283491beeb860..0b55b1f3091c7 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 3bd3fed200874..4ae55190ce38e 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 07dd95fad4770..bc635db196102 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 1220fb81438f7..dda6824fbe32f 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index e0173a5ee044d..d7eb6a2fdad96 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 091e75c81d4b6..089c8288851a9 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 5d9f94749555d..9bd4b9bb764dd 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 74a9f64bc3d71..42ee86d841d19 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 2bc01bdc4172b..2759a7bf7be21 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 434b7185a4617..e4d2390ade27b 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 442d12ea088a0..f4e8bc70d1518 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index d15acf69818ab..c73719f74b85f 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 6e7d7caba89d1..a7333dbad536a 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index f665ce2a0fb96..c15074ba938bf 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 5383c1d30436f..41cbc25fe19e0 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 80ed228ace44e..f0910f5fc35a6 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 9bce723a5c71a..ac5cff2d62a3b 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 4f5f5977f24f4..8f9f6554e4150 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 7ae0d214b6039..f4161c633a6c0 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index ba46ab18c7160..974386ed1b8e4 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 6d8aa5e6556ce..5fc1dc2f5919a 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index bfacabaae4b23..79b0ae9090ca8 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 6f7d46b53d175..9867bcd91ff48 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 944f84956381e..eeda8b4391283 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 1a91a50bbc5ac..1f768ab557e33 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index ca561ad7f0ade..ef2fe4abe9747 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index c2e8449d53290..2d1f6245bdf03 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 1f3393549ae79..a43abcdbeded7 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index f9afe193fd7be..802c542ff1d9d 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index ed7572b727594..d14cdd10cb5cd 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index f6e991d3e66a4..c2d0094e972d7 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 06a301c99a6ca..1d3086cc5fa5a 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 82b93218fd11d..d4fe8d7090728 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 9123036bcf4f4..eb4a00a5ef79f 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 0ad85679a49e5..abe5feff261ca 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index cc8fee4781c28..273214fcd1e9c 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 878515d179c31..a2ca9df0c6105 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index 50d4a907ba07e..5e946e9d7a68b 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 1125c2acab586..119058d250f7f 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 336db93a703f4..5056c3647e515 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index ac0bd68b504fa..4dec3e2cd3f5d 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 7abf1996be2e3..368b3abc63350 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 8d9d7e34feb4b..b0ab27ae78600 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index cb7d982cd31d8..3ecf51fd33244 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index b3e6260f625ef..f30b5bae17d46 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 81c0e1e9a2283..28e92d54f0efb 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index c1cce8a1ee188..67b75e7d10057 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 717db918a79a0..8189da0441012 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index c6a7ab9382fef..58893459be1ef 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 4ddb598ff8b18..aca802c739677 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index f01dc8106c28f..d8ff0064c33a3 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index b0715d9e16ce4..48946a11b73d9 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index c989c4452abb9..b7d1bd73c2ad6 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 96b5a33f307ba..7c641e8a33eab 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 24c6af49de31d..67c181dbe353c 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index da540d81e7044..3e937416907f7 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 6184a7d3b584d..0fb69311a41d3 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 59f2721e259fe..723403c04a56b 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 6df1e068fac51..38c200dd4fc4e 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index f538c6422e411..2822cebc8f2b6 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 8fd3cf6648d61..85f67738b5127 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 1419d2833ba69..a195e14f8eb08 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index 7ca49aff777f8..dee08ccafaa3b 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index e780f1f276a02..0500817634ff9 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index a32b5d39a6295..69b03a9701053 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 1df62d7008412..5d39c7f82fc91 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 3486c81b24563..a7455a3d124fe 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index be1dda08a8c13..f148b51bf4f79 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index f12eb1f116bd8..a8931e398dba4 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index c0aae512ed027..7e42f2af72f22 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index bf294faffc954..6e16eb883e32b 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index bee7683fc12c6..fd22accfa2fd9 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 69039df5749d8..69a6b20efc1f4 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index c0dfdf10fd75b..096bf2db5c03c 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 0c90a15b4fde7..06faf0dd23001 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index b47543386f26d..903e4f01a2028 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 2161ad3b1e3b5..4a90ce31147d8 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index db9f3515c682c..f0c9aff947736 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 575f061be9d43..11e72887b3c3c 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 5834766c5c994..832893355aa18 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index a522a6c550215..e13e0e5865ab0 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index aa6a9a8c6ca2e..5155aeece5ccc 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 66b3633524ee5..e75e82dadd7b5 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index d79f7c8cd0271..270978b9a289b 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index e0b0422fbba0b..eb15cc5893e3a 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 84ea5fdba678c..38a2642f3ca7f 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 0248997f7bbb4..c06dbb2c4cabf 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index b594554e680d0..855c92977a56a 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index 30d6428f9fd2d..72e01b8ab8af3 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index ba0bcd44e83cc..34f856af0a5b0 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 0f2c950cb4fea..6525743a6df56 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 808e836c6ce49..e947629304916 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 2949bad925d3c..6dbf86aff88ba 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 1110f55dc8c84..3d31ac3c5a93f 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 0547cce12ebe0..14668296fd09b 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 53cd4b8137ef8..bac6725f95815 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index e608f340539a1..3a75e07c2d942 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index fcc38fc003801..d647820be221a 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 8d335c91b44a6..e6599896f5008 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 9286038b86b53..78898bec9c262 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 32cf610a8fbd6..2cdc139e6a958 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index c7d01b545aab7..71ad5cf9a4746 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.devdocs.json b/api_docs/kbn_core_http_browser.devdocs.json index 40375138fb419..54e7d408afd32 100644 --- a/api_docs/kbn_core_http_browser.devdocs.json +++ b/api_docs/kbn_core_http_browser.devdocs.json @@ -217,14 +217,14 @@ { "parentPluginId": "@kbn/core-http-browser", "id": "def-common.HttpFetchOptions.version", - "type": "Uncategorized", + "type": "string", "tags": [ "experimental" ], "label": "version", "description": [], "signature": [ - "`${number}` | undefined" + "string | undefined" ], "path": "packages/core/http/core-http-browser/src/types.ts", "deprecated": false, @@ -857,7 +857,7 @@ "section": "def-common.KibanaExecutionContext", "text": "KibanaExecutionContext" }, - " | undefined; readonly version?: `${number}` | undefined; readonly body?: BodyInit | null | undefined; readonly cache?: RequestCache | undefined; readonly credentials?: RequestCredentials | undefined; readonly integrity?: string | undefined; readonly keepalive?: boolean | undefined; readonly method?: string | undefined; readonly mode?: RequestMode | undefined; readonly redirect?: RequestRedirect | undefined; readonly referrer?: string | undefined; readonly referrerPolicy?: ReferrerPolicy | undefined; readonly signal?: AbortSignal | null | undefined; readonly window?: null | undefined; }" + " | undefined; readonly version?: string | undefined; readonly body?: BodyInit | null | undefined; readonly cache?: RequestCache | undefined; readonly credentials?: RequestCredentials | undefined; readonly integrity?: string | undefined; readonly keepalive?: boolean | undefined; readonly method?: string | undefined; readonly mode?: RequestMode | undefined; readonly redirect?: RequestRedirect | undefined; readonly referrer?: string | undefined; readonly referrerPolicy?: ReferrerPolicy | undefined; readonly signal?: AbortSignal | null | undefined; readonly window?: null | undefined; }" ], "path": "packages/core/http/core-http-browser/src/types.ts", "deprecated": false, @@ -1234,7 +1234,7 @@ "section": "def-common.KibanaExecutionContext", "text": "KibanaExecutionContext" }, - " | undefined; readonly version?: `${number}` | undefined; readonly body?: BodyInit | null | undefined; readonly cache?: RequestCache | undefined; readonly credentials?: RequestCredentials | undefined; readonly integrity?: string | undefined; readonly keepalive?: boolean | undefined; readonly method?: string | undefined; readonly mode?: RequestMode | undefined; readonly redirect?: RequestRedirect | undefined; readonly referrer?: string | undefined; readonly referrerPolicy?: ReferrerPolicy | undefined; readonly signal?: AbortSignal | null | undefined; readonly window?: null | undefined; }" + " | undefined; readonly version?: string | undefined; readonly body?: BodyInit | null | undefined; readonly cache?: RequestCache | undefined; readonly credentials?: RequestCredentials | undefined; readonly integrity?: string | undefined; readonly keepalive?: boolean | undefined; readonly method?: string | undefined; readonly mode?: RequestMode | undefined; readonly redirect?: RequestRedirect | undefined; readonly referrer?: string | undefined; readonly referrerPolicy?: ReferrerPolicy | undefined; readonly signal?: AbortSignal | null | undefined; readonly window?: null | undefined; }" ], "path": "packages/core/http/core-http-browser/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 604ef830c63d1..f46e20977cc14 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index f701456d9350b..2c2a798a89c6c 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index fde74cf13f92f..dc84030777a3f 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.devdocs.json b/api_docs/kbn_core_http_common.devdocs.json index 4036271e20634..714824b78d573 100644 --- a/api_docs/kbn_core_http_common.devdocs.json +++ b/api_docs/kbn_core_http_common.devdocs.json @@ -89,15 +89,16 @@ "id": "def-common.ApiVersion", "type": "Type", "tags": [ + "note", "note", "experimental" ], "label": "ApiVersion", "description": [ - "\nA Kibana HTTP API version" + "\nA Kibana HTTP API version\n" ], "signature": [ - "`${number}`" + "string" ], "path": "packages/core/http/core-http-common/src/versioning.ts", "deprecated": false, diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index e63d21e085482..f46f80f157990 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 99c9de1b19bb4..2bcecde44aae2 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index 0469748f43cba..728e77b24337f 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 2fb34f64f96f3..15d62f1afa04a 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index e47424f71efbe..753a64b907940 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 6c55f34bf7687..af0d51dc7dcb2 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 2b1df4b53ab2a..b18c188eb87ba 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 6d999b2755dc1..ce5cbd31fa38c 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 81a98233b6228..f9faf5a74ef10 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -129,7 +129,7 @@ { "parentPluginId": "@kbn/core-http-server", "id": "def-common.AddVersionOpts.version", - "type": "Uncategorized", + "type": "string", "tags": [ "experimental" ], @@ -137,9 +137,6 @@ "description": [ "\nVersion to assign to this route" ], - "signature": [ - "`${number}`" - ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, "trackAdoption": false @@ -7367,7 +7364,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -7432,7 +7429,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -7497,7 +7494,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -7562,7 +7559,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -7627,7 +7624,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -7757,15 +7754,16 @@ "id": "def-common.ApiVersion", "type": "Type", "tags": [ + "note", "note", "experimental" ], "label": "ApiVersion", "description": [ - "\nA Kibana HTTP API version" + "\nA Kibana HTTP API version\n" ], "signature": [ - "`${number}`" + "string" ], "path": "packages/core/http/core-http-common/src/versioning.ts", "deprecated": false, @@ -10110,7 +10108,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, @@ -10180,7 +10178,7 @@ "section": "def-common.RouteConfigOptions", "text": "RouteConfigOptions" }, - ", \"access\"> | undefined; access: \"internal\" | \"public\" | undefined; }" + ", \"access\"> | undefined; access: \"internal\" | \"public\"; }" ], "path": "packages/core/http/core-http-server/src/versioning/types.ts", "deprecated": false, diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 27876c38a4fce..edea32f36d0a3 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 9a9017577db9f..7f9c6838f648f 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 7929ef63efd2f..8f98589ac9bb2 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index e6d6dfd817b2f..48717a4928ceb 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 3563d41cbc7c7..91c88b93b9899 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 709d55040b220..dfd6759f82c3e 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index b7bfba80d30a0..2fb875c29aedb 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 7b9f94932ae65..d7374f1d2ea55 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index d8b10cbbd5db8..106e6b27d00fb 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 373dd5f8a943d..6c8f8c341950d 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 24f19347c4fba..b753450d5d6ce 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.devdocs.json b/api_docs/kbn_core_lifecycle_browser.devdocs.json index 76b224bf54d29..385975f4c6e4c 100644 --- a/api_docs/kbn_core_lifecycle_browser.devdocs.json +++ b/api_docs/kbn_core_lifecycle_browser.devdocs.json @@ -681,10 +681,6 @@ "plugin": "fileUpload", "path": "x-pack/plugins/file_upload/public/kibana_services.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/kibana_services.ts" - }, { "plugin": "ml", "path": "x-pack/plugins/ml/public/application/app.tsx" diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 372b3d03033b1..3a36ca3942e7a 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index d426fbb6511cc..475eb48ccbccb 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 3b9dd67096adf..0ef4bf8d43a32 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index e7efae42279a0..3649461b7c5a6 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 4f084caa77ae5..052bf0ba74258 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 6c26f2c40d338..07e96d1422f6a 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index e9ac299d97c49..ce0ac13ded0d6 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 5f3ec65d16973..55bed0b817434 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 9e0705dac8043..1425aa6b5cd5d 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 8620452cb0d6c..6e0c42f2ccb83 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 508c8afb58d57..5cac745fb95e6 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 3976b1ff3c5d3..8b0e0e1d6c1ca 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index cfd4082a9876f..1c31017866648 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 17792afc4b2c5..768a293980d04 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 5cfe4c2f4990a..48dc25bf2cf39 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 56e109e8cefe2..9fb1afd0b61fb 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index e050b202e2ba6..55ae8a712881b 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 0448d67223c7b..63681b4273d7a 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 1cc2de79ccd47..20ab26c0bc749 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index c94faa43fcf78..d824c45cc7aac 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 42fc1db7b90ed..82241455ede13 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index a306a5b40886c..bd6e525f0fb93 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index b9b42c74b16aa..023c363df1f90 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 1819df559b59a..82863e8d7c246 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 0e2b3b7f6f46e..79c15731be923 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index f17b05cb96586..98c3abd58f3a3 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index c9358db10cb78..186767ae5b0ca 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 557af3827de65..5c1c91d0d9180 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 470786e32ea05..3fccd50f19133 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 7001673dac98f..5fa6b166396df 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index b66874696c6be..91c5d2eaf7349 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 00d2b145dcfa6..1181c8e829960 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index d7f4ead5fddd8..541950376bd3a 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 682b1bc80d441..515e475f7ea60 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json index 1782399f2118f..c6ab4101d6c7a 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_browser.devdocs.json @@ -1482,10 +1482,6 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/persistence/saved_object_store.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/map_attribute_service.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx" @@ -1872,11 +1868,7 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" + "path": "src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx" }, { "plugin": "infra", @@ -2171,14 +2163,6 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx" - }, { "plugin": "ml", "path": "x-pack/plugins/ml/public/application/util/index_utils.ts" @@ -2415,6 +2399,10 @@ "plugin": "ml", "path": "x-pack/plugins/ml/public/application/util/index_utils.ts" }, + { + "plugin": "ml", + "path": "x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts" @@ -2688,10 +2676,6 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/persistence/saved_object_store.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/map_attribute_service.ts" - }, { "plugin": "graph", "path": "x-pack/plugins/graph/public/helpers/saved_workspace_utils.ts" @@ -2966,10 +2950,6 @@ "plugin": "savedSearch", "path": "src/plugins/saved_search/public/services/saved_searches/save_saved_searches.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/map_attribute_service.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/infra/public/hooks/use_update_saved_object.tsx" @@ -3881,11 +3861,11 @@ }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx" + "path": "src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx" }, { "plugin": "dashboard", - "path": "src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx" + "path": "src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx" }, { "plugin": "lens", @@ -3911,18 +3891,6 @@ "plugin": "ml", "path": "x-pack/plugins/ml/common/types/kibana.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/maps_vis_type_alias.ts" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/maps_vis_type_alias.ts" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/maps_vis_type_alias.ts" - }, { "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/common/types/index.ts" @@ -4110,14 +4078,6 @@ { "plugin": "@kbn/core-saved-objects-browser-mocks", "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" } ], "children": [ diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 1d9c1a9905ca6..bbb5648505e86 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index 7f6ad1aac0afe..34b6a5eec8838 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -111,6 +111,7 @@ "property", "property", "property", + "property", "property" ], "label": "create", @@ -208,6 +209,7 @@ "id": "def-common.ISavedObjectsRepository.bulkCreate", "type": "Function", "tags": [ + "property", "property", "property" ], @@ -742,6 +744,7 @@ "id": "def-common.ISavedObjectsRepository.bulkGet", "type": "Function", "tags": [ + "property", "property" ], "label": "bulkGet", @@ -762,8 +765,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined) => Promise<", { @@ -810,15 +813,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the bulk get operation" + "{@link SavedObjectsGetOptions } - options for the bulk get operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined" ], @@ -837,6 +840,7 @@ "id": "def-common.ISavedObjectsRepository.bulkResolve", "type": "Function", "tags": [ + "property", "property" ], "label": "bulkResolve", @@ -857,8 +861,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -905,15 +909,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the bulk resolve operation" + "{@link SavedObjectsResolveOptions } - options for the bulk resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -932,6 +936,7 @@ "id": "def-common.ISavedObjectsRepository.get", "type": "Function", "tags": [ + "property", "property" ], "label": "get", @@ -944,8 +949,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined) => Promise<", { @@ -1002,15 +1007,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the get operation" + "{@link SavedObjectsGetOptions } - options for the get operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined" ], @@ -1029,6 +1034,7 @@ "id": "def-common.ISavedObjectsRepository.resolve", "type": "Function", "tags": [ + "property", "property" ], "label": "resolve", @@ -1041,8 +1047,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -1099,15 +1105,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the resolve operation" + "{@link SavedObjectsResolveOptions } - options for the resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -2696,6 +2702,10 @@ "plugin": "alerting", "path": "x-pack/plugins/alerting/common/rule.ts" }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" @@ -4601,8 +4611,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined) => Promise<", { @@ -4649,15 +4659,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the bulk get operation" + "{@link SavedObjectsGetOptions } - options for the bulk get operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined" ], @@ -4686,8 +4696,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined) => Promise<", { @@ -4744,15 +4754,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the get operation" + "{@link SavedObjectsGetOptions } - options for the get operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, " | undefined" ], @@ -4789,8 +4799,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -4837,15 +4847,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the bulk resolve operation" + "{@link SavedObjectsResolveOptions } - options for the bulk resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -4874,8 +4884,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined) => Promise<", { @@ -4932,15 +4942,15 @@ "tags": [], "label": "options", "description": [ - "{@link SavedObjectsBaseOptions } - options for the resolve operation" + "{@link SavedObjectsResolveOptions } - options for the resolve operation" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, " | undefined" ], @@ -6141,6 +6151,22 @@ "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsCreateOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [], + "label": "migrationVersionCompatibility", + "description": [ + "{@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility}" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -6743,6 +6769,22 @@ "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsFindOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [], + "label": "migrationVersionCompatibility", + "description": [ + "{@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility}" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -6973,6 +7015,55 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsGetOptions", + "type": "Interface", + "tags": [], + "label": "SavedObjectsGetOptions", + "description": [ + "\nOptions for the saved objects get operation\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsBaseOptions", + "text": "SavedObjectsBaseOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsGetOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [], + "label": "migrationVersionCompatibility", + "description": [ + "{@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility}" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/get.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-api-server", "id": "def-common.SavedObjectsIncrementCounterField", @@ -7390,6 +7481,55 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsResolveOptions", + "type": "Interface", + "tags": [], + "label": "SavedObjectsResolveOptions", + "description": [ + "\nOptions for the saved objects get operation\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsBaseOptions", + "text": "SavedObjectsBaseOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsResolveOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [], + "label": "migrationVersionCompatibility", + "description": [ + "{@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility}" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-api-server", "id": "def-common.SavedObjectsResolveResponse", @@ -8051,7 +8191,7 @@ "section": "def-common.SavedObjectsFindOptionsReference", "text": "SavedObjectsFindOptionsReference" }, - "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; }" + "[] | undefined; hasNoReferenceOperator?: \"AND\" | \"OR\" | undefined; typeToNamespacesMap?: Map | undefined; migrationVersionCompatibility?: \"raw\" | \"compatible\" | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create_point_in_time_finder.ts", "deprecated": false, diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 94acfc8d639c9..80723a578e801 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 341 | 1 | 5 | 1 | +| 347 | 1 | 5 | 1 | ## Common diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_api_server_internal.devdocs.json index d059303baa3b0..b9cd4f7b0dab6 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server_internal.devdocs.json @@ -629,8 +629,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, ") => Promise<", { @@ -680,8 +680,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" } ], "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts", @@ -715,8 +715,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, ") => Promise<", { @@ -766,8 +766,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" } ], "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts", @@ -793,8 +793,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" }, ") => Promise<", { @@ -852,8 +852,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsGetOptions", + "text": "SavedObjectsGetOptions" } ], "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts", @@ -879,8 +879,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" }, ") => Promise<", { @@ -938,8 +938,8 @@ "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", - "section": "def-common.SavedObjectsBaseOptions", - "text": "SavedObjectsBaseOptions" + "section": "def-common.SavedObjectsResolveOptions", + "text": "SavedObjectsResolveOptions" } ], "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts", diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 2fd310a6635ad..364e3d6c2e0d0 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index d5b42586c3c1d..9b2c6c049793a 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 7ca9b7f3aa37b..2dbf03e97e737 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 17115151d62f9..784bad9b0340a 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.devdocs.json b/api_docs/kbn_core_saved_objects_browser.devdocs.json index b7d377b2da608..1a9ced3fa1030 100644 --- a/api_docs/kbn_core_saved_objects_browser.devdocs.json +++ b/api_docs/kbn_core_saved_objects_browser.devdocs.json @@ -89,14 +89,6 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/types.ts" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/application/types.ts" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/application/types.ts" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/application/types.ts" @@ -105,6 +97,14 @@ "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/application/types.ts" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/application/types.ts" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/application/types.ts" + }, { "plugin": "canvas", "path": "x-pack/plugins/canvas/public/services/platform.ts" diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 7f48b4ef02d29..ac17cc0f50ddc 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 565c2b72832c9..35eb402d34bad 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index a2045e6fcd94d..655b42e41a076 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.devdocs.json b/api_docs/kbn_core_saved_objects_common.devdocs.json index 8ede2d77bb796..7b868ee8aa205 100644 --- a/api_docs/kbn_core_saved_objects_common.devdocs.json +++ b/api_docs/kbn_core_saved_objects_common.devdocs.json @@ -1858,12 +1858,12 @@ "path": "src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts" }, { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts" + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts" }, { - "plugin": "triggersActionsUi", - "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts" + "plugin": "visualizations", + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts" }, { "plugin": "triggersActionsUi", @@ -1874,12 +1874,12 @@ "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts" }, { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts" + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts" }, { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts" + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts" }, { "plugin": "@kbn/core", @@ -3068,14 +3068,6 @@ "plugin": "maps", "path": "x-pack/plugins/maps/public/map_attribute_service.ts" }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, - { - "plugin": "maps", - "path": "x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx" - }, { "plugin": "canvas", "path": "x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts" diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index fc0e8b9f69e04..6aa791f02b305 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 349199bb1947e..ef9c2eee7dda8 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 2fcbec98d44c0..9c2ac13d123a4 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index a54cf9bcc2273..9211f55ae686f 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index f83e88d03e5f4..6ab358dddacee 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.devdocs.json b/api_docs/kbn_core_saved_objects_server.devdocs.json index 72eb4c4b41a5c..04e306480463a 100644 --- a/api_docs/kbn_core_saved_objects_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_server.devdocs.json @@ -6167,6 +6167,10 @@ "plugin": "alerting", "path": "x-pack/plugins/alerting/common/rule.ts" }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, { "plugin": "alerting", "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" @@ -8047,6 +8051,24 @@ "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsRawDocParseOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [ + "default" + ], + "label": "migrationVersionCompatibility", + "description": [ + "\nOptional setting to allow compatible handling of the `migrationVersion` field.\nThis is needed to return the `migrationVersion` field in the same format as it was before migrating to the `typeMigrationVersion` property.\n" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index db73e7ffa37b9..0ab1681109f8b 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 493 | 1 | 99 | 4 | +| 494 | 1 | 99 | 4 | ## Common diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index a5c11d0097485..2bfef7896ed9e 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 392f7bdb629da..5969fa3fec42b 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 82edc39a34e04..f03b52f175605 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 0c611d52e9384..98e14db6a9771 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index c67aa167405af..138054ebf6074 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index c810b6d44f989..cec848d089f1a 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 2ad03105b2f78..6dd568e68595b 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 58314c5b63a23..b1b3f3c84cf7f 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index ffa6d736c383a..b9e2efa25823f 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 0f40f245041ee..d89c14bb7d6f3 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 96a2df4c31991..8bba1c8faaadd 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index a97caf1bdec87..9e3173f339a99 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index e07ac71ddef94..737f37c0f6807 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index ed13120670252..8af892b1926aa 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 8e7ca4a56d80e..ec571030356a6 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index b1bfc1226f39a..2334b44813bba 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 459ad81a8e563..cdb58a7f10ff6 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index c5f4df5ddc5d1..40806c06351df 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 831d88b9c9905..d917f1e1aad65 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.devdocs.json b/api_docs/kbn_core_ui_settings_common.devdocs.json index 7fd88652aec3a..6f31370eab373 100644 --- a/api_docs/kbn_core_ui_settings_common.devdocs.json +++ b/api_docs/kbn_core_ui_settings_common.devdocs.json @@ -431,6 +431,21 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/core-ui-settings-common", + "id": "def-common.TIMEZONE_OPTIONS", + "type": "Array", + "tags": [], + "label": "TIMEZONE_OPTIONS", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/core/ui-settings/core-ui-settings-common/src/timezones.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-ui-settings-common", "id": "def-common.UiSettingsScope", diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index dfa1b1ffd63a5..e018d2c0411e5 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 24 | 0 | 3 | 0 | +| 25 | 0 | 4 | 0 | ## Common diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 7e16b455c8c84..b7ea799469138 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index b290141bbcc2f..6f49e6af8e8cc 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index b1cf3bf32de68..fbb82389c6e58 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 3661214796037..40ce42a44cf15 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 4038f4d75e56c..f53a1d696836e 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 758cebde2f579..63c67653c704c 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 0e7dbcc0eb264..e579c0b88b82b 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 8d69b3ea312df..c6ac7c1e36a6f 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index d2db68db398c1..425ea8f8ebd24 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 8ad2c05ff6c09..63d561e44cf27 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index f8fd5aa676363..0d605704eab6e 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index a8bb7cd609ac3..a14f794d444e4 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 81329fb8afd00..6c434f94f2b97 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index f0a2e8bb06387..a74d64dd14608 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 67b05ab28b6cb..022b4c600076b 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 385dcd4395e56..dd54a131f03c0 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 6296dffd7abc6..d97372d79ebdd 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 20a04ffa8136e..c4e26078f26c2 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 942642120858a..589a982102229 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 200153c1a6da9..11f7705253010 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 12acd8bd473fd..19d33d2cc2bf6 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 15578555ab7fa..ea829b18afe8c 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 502676d747448..baedea81274c2 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index c1c4c0030fad3..98d2f9264c732 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 173e9a677dda5..2c4e5b793efdd 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index b45989ceba045..b09632ba7c10f 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 5ed1617614154..1cb4678323b20 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 6d7011ffbec46..33c796182f8d8 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 9ce26ffbe0784..22e741bcea6c2 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 20b7c43f128cf..e2b7a8557341b 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 13c93c1623e3f..c367835ac3fe0 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.devdocs.json b/api_docs/kbn_generate_csv.devdocs.json new file mode 100644 index 0000000000000..2b04c8389060c --- /dev/null +++ b/api_docs/kbn_generate_csv.devdocs.json @@ -0,0 +1,214 @@ +{ + "id": "@kbn/generate-csv", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator", + "type": "Class", + "tags": [], + "label": "CsvGenerator", + "description": [], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "job", + "description": [], + "signature": [ + "Omit<", + { + "pluginId": "@kbn/generate-csv-types", + "scope": "common", + "docId": "kibKbnGenerateCsvTypesPluginApi", + "section": "def-common.JobParams", + "text": "JobParams" + }, + ", \"version\">" + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + { + "pluginId": "@kbn/generate-csv-types", + "scope": "common", + "docId": "kibKbnGenerateCsvTypesPluginApi", + "section": "def-common.CsvConfig", + "text": "CsvConfig" + } + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$3", + "type": "Object", + "tags": [], + "label": "clients", + "description": [], + "signature": [ + "Clients" + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$4", + "type": "Object", + "tags": [], + "label": "dependencies", + "description": [], + "signature": [ + "Dependencies" + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$5", + "type": "Object", + "tags": [], + "label": "cancellationToken", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.CancellationToken", + "text": "CancellationToken" + } + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$6", + "type": "Object", + "tags": [], + "label": "logger", + "description": [], + "signature": [ + { + "pluginId": "@kbn/logging", + "scope": "common", + "docId": "kibKbnLoggingPluginApi", + "section": "def-common.Logger", + "text": "Logger" + } + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.Unnamed.$7", + "type": "Object", + "tags": [], + "label": "stream", + "description": [], + "signature": [ + "Writable" + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/generate-csv", + "id": "def-common.CsvGenerator.generateData", + "type": "Function", + "tags": [], + "label": "generateData", + "description": [], + "signature": [ + "() => Promise<", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.TaskRunResult", + "text": "TaskRunResult" + }, + ">" + ], + "path": "packages/kbn-generate-csv/src/generate_csv.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx new file mode 100644 index 0000000000000..e61ee026319bb --- /dev/null +++ b/api_docs/kbn_generate_csv.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnGenerateCsvPluginApi +slug: /kibana-dev-docs/api/kbn-generate-csv +title: "@kbn/generate-csv" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/generate-csv plugin +date: 2023-04-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] +--- +import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 10 | 0 | 10 | 0 | + +## Common + +### Classes + + diff --git a/api_docs/kbn_generate_csv_types.devdocs.json b/api_docs/kbn_generate_csv_types.devdocs.json new file mode 100644 index 0000000000000..c816650afc29a --- /dev/null +++ b/api_docs/kbn_generate_csv_types.devdocs.json @@ -0,0 +1,244 @@ +{ + "id": "@kbn/generate-csv-types", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.CsvConfig", + "type": "Interface", + "tags": [], + "label": "CsvConfig", + "description": [], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.CsvConfig.checkForFormulas", + "type": "boolean", + "tags": [], + "label": "checkForFormulas", + "description": [], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.CsvConfig.escapeFormulaValues", + "type": "boolean", + "tags": [], + "label": "escapeFormulaValues", + "description": [], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.CsvConfig.maxSizeBytes", + "type": "CompoundType", + "tags": [], + "label": "maxSizeBytes", + "description": [], + "signature": [ + "number | ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ByteSizeValue", + "text": "ByteSizeValue" + } + ], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.CsvConfig.useByteOrderMarkEncoding", + "type": "boolean", + "tags": [], + "label": "useByteOrderMarkEncoding", + "description": [], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.CsvConfig.scroll", + "type": "Object", + "tags": [], + "label": "scroll", + "description": [], + "signature": [ + "{ duration: string; size: number; }" + ], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.JobParams", + "type": "Interface", + "tags": [], + "label": "JobParams", + "description": [], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.JobParams.searchSource", + "type": "Object", + "tags": [], + "label": "searchSource", + "description": [], + "signature": [ + "{ type?: string | undefined; query?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Query", + "text": "Query" + }, + " | ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined; filter?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[] | undefined; sort?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.EsQuerySortValue", + "text": "EsQuerySortValue" + }, + "[] | undefined; highlight?: ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.SerializableRecord", + "text": "SerializableRecord" + }, + " | undefined; highlightAll?: boolean | undefined; trackTotalHits?: number | boolean | undefined; aggs?: { type: string; enabled?: boolean | undefined; id?: string | undefined; params?: {} | ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.SerializableRecord", + "text": "SerializableRecord" + }, + " | undefined; schema?: string | undefined; }[] | undefined; from?: number | undefined; size?: number | undefined; source?: boolean | ", + "Fields", + " | undefined; version?: boolean | undefined; fields?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchFieldValue", + "text": "SearchFieldValue" + }, + "[] | undefined; fieldsFromSource?: ", + "Fields", + " | undefined; index?: string | ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewSpec", + "text": "DataViewSpec" + }, + " | undefined; searchAfter?: ", + "SortResults", + " | undefined; timeout?: string | undefined; terminate_after?: number | undefined; parent?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SerializedSearchSourceFields", + "text": "SerializedSearchSourceFields" + }, + " | undefined; }" + ], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.JobParams.columns", + "type": "Array", + "tags": [], + "label": "columns", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/generate-csv-types", + "id": "def-common.JobParams.browserTimezone", + "type": "string", + "tags": [], + "label": "browserTimezone", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-generate-csv-types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx new file mode 100644 index 0000000000000..bc547e035a7c9 --- /dev/null +++ b/api_docs/kbn_generate_csv_types.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnGenerateCsvTypesPluginApi +slug: /kibana-dev-docs/api/kbn-generate-csv-types +title: "@kbn/generate-csv-types" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/generate-csv-types plugin +date: 2023-04-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] +--- +import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 10 | 0 | 10 | 0 | + +## Common + +### Interfaces + + diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index ed6b377aae47a..6c1f189614fab 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index fa041406102b2..ebddb3c829bea 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index d6e4ebfd4f900..7e931a6919a3c 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 67104696dac6e..09cda43a18ac0 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 3f1604c00763d..3b51d62c6a7a5 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 19c39fad52c5a..54626a8c45b6f 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index d39060d99b4bd..85a07c8287f09 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index a4c704cc376ee..7159df8ea8c7c 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 583be7ee902ca..ce0e9d0f943da 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 919a97a602dcb..ff307710d9591 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 549f7d02d77cc..5513baf02af0f 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 018ad9191c9cb..01f83e2645e3c 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 380f971ad022a..7bb8caca77b03 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index c326d1ea1b22f..df2ecccb26274 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index e1a71779426ee..b370b7075838f 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index 2e7c600f0f3c5..f71c8943b6932 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index eb2db31c5e989..6ccc85fc8b17d 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 6c259ceac8296..be471478b0784 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 602685a3b3d34..62bfc0653e309 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index df0e18c3dc898..59d73c4659fc7 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.devdocs.json b/api_docs/kbn_ml_agg_utils.devdocs.json index a03c944a44643..7b5e9220bf728 100644 --- a/api_docs/kbn_ml_agg_utils.devdocs.json +++ b/api_docs/kbn_ml_agg_utils.devdocs.json @@ -621,6 +621,45 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.isSignificantTerm", + "type": "Function", + "tags": [], + "label": "isSignificantTerm", + "description": [ + "\nType guard for a significant term.\nNote this is used as a custom type within Explain Log Rate Spikes\nfor a p-value based variant, not a generic significant terms\naggregation type." + ], + "signature": [ + "(arg: unknown) => boolean" + ], + "path": "x-pack/packages/ml/agg_utils/src/type_guards.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-agg-utils", + "id": "def-common.isSignificantTerm.$1", + "type": "Unknown", + "tags": [], + "label": "arg", + "description": [ + "The unknown type to be evaluated" + ], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/agg_utils/src/type_guards.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "whether arg is of type SignificantTerm" + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/ml-agg-utils", "id": "def-common.numberValidator", @@ -1069,7 +1108,7 @@ "tags": [], "label": "SignificantTerm", "description": [ - "\nSignificant term meta data for a field/value pair." + "\nSignificant term meta data for a field/value pair.\nNote this is used as a custom type within Explain Log Rate Spikes\nfor a p-value based variant, not a generic significant terms\naggregation type." ], "signature": [ { diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index b7670bf79ec4d..e044be4e2d738 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 91 | 2 | 61 | 0 | +| 93 | 2 | 61 | 0 | ## Common diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index d6c6e15309b45..c8f1b2857f741 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 95f5cf455746a..c882708c87d62 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index ae9bbb243240c..35cac8263fb52 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index 4f670ecda907b..bfc1083f66c9a 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index e42b661b4ef76..7a96b5d544120 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.devdocs.json b/api_docs/kbn_ml_number_utils.devdocs.json new file mode 100644 index 0000000000000..9529028d9506f --- /dev/null +++ b/api_docs/kbn_ml_number_utils.devdocs.json @@ -0,0 +1,84 @@ +{ + "id": "@kbn/ml-number-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/ml-number-utils", + "id": "def-common.roundToDecimalPlace", + "type": "Function", + "tags": [], + "label": "roundToDecimalPlace", + "description": [ + "\nRounds a number to a specified decimal place.\nIf the specified number is undefined, an empty string is returned.\n" + ], + "signature": [ + "(num: number | undefined, dp: number) => string | number" + ], + "path": "x-pack/packages/ml/number_utils/src/round_to_decimal_place.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-number-utils", + "id": "def-common.roundToDecimalPlace.$1", + "type": "number", + "tags": [], + "label": "num", + "description": [ + "- number to round" + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/ml/number_utils/src/round_to_decimal_place.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/ml-number-utils", + "id": "def-common.roundToDecimalPlace.$2", + "type": "number", + "tags": [], + "label": "dp", + "description": [ + "- decimal place, default value is 2" + ], + "signature": [ + "number" + ], + "path": "x-pack/packages/ml/number_utils/src/round_to_decimal_place.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "rounded number" + ], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx new file mode 100644 index 0000000000000..b9b82f44e2f05 --- /dev/null +++ b/api_docs/kbn_ml_number_utils.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnMlNumberUtilsPluginApi +slug: /kibana-dev-docs/api/kbn-ml-number-utils +title: "@kbn/ml-number-utils" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/ml-number-utils plugin +date: 2023-04-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] +--- +import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; + + + +Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 3 | 0 | 0 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 9b1d0f5a27976..65b228df42293 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 3edcc324d2bc4..c01e9794722c5 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index aebc938fcf5d1..849f20fba0795 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index b44b7462b2658..7e786976d78b9 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index bcf40455bf9f7..51dec0f93ddeb 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 3acf1acf3bb7f..0b1bf48cd584b 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 181df252b22b9..f753592a95739 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.devdocs.json b/api_docs/kbn_object_versioning.devdocs.json index 72a1ca0e87465..bb1210067600d 100644 --- a/api_docs/kbn_object_versioning.devdocs.json +++ b/api_docs/kbn_object_versioning.devdocs.json @@ -205,15 +205,15 @@ "section": "def-common.ObjectMigrationDefinition", "text": "ObjectMigrationDefinition" }, - ") => ", + ") => { up: (obj: I, to?: number | \"latest\", { validate }?: { validate?: boolean | undefined; }) => { error: Error; value: null; } | { value: O; error: null; }; down: (obj: I, from?: number | \"latest\", { validate }?: { validate?: boolean | undefined; }) => { error: Error; value: null; } | { value: any; error: null; }; validate: (value: unknown, version?: number) => ", { - "pluginId": "@kbn/object-versioning", + "pluginId": "@kbn/config-schema", "scope": "common", - "docId": "kibKbnObjectVersioningPluginApi", - "section": "def-common.ObjectTransforms", - "text": "ObjectTransforms" + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ValidationError", + "text": "ValidationError" }, - "" + " | null; }" ], "path": "packages/kbn-object-versioning/lib/object_transform.ts", "deprecated": false, @@ -309,7 +309,7 @@ "label": "up", "description": [], "signature": [ - "(obj: UpIn, version?: number | \"latest\" | undefined, options?: { validate?: boolean | undefined; } | undefined) => ", + "(obj: I, version?: number | \"latest\" | undefined, options?: { validate?: boolean | undefined; } | undefined) => ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -317,7 +317,7 @@ "section": "def-common.TransformReturn", "text": "TransformReturn" }, - "" + "" ], "path": "packages/kbn-object-versioning/lib/types.ts", "deprecated": false, @@ -331,7 +331,7 @@ "label": "obj", "description": [], "signature": [ - "UpIn" + "I" ], "path": "packages/kbn-object-versioning/lib/types.ts", "deprecated": false, @@ -393,7 +393,7 @@ "label": "down", "description": [], "signature": [ - "(obj: DownIn, version?: number | \"latest\" | undefined, options?: { validate?: boolean | undefined; } | undefined) => ", + "(obj: DownIn, version?: number | \"latest\" | undefined, options?: { validate?: boolean | undefined; } | undefined) => ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -401,7 +401,7 @@ "section": "def-common.TransformReturn", "text": "TransformReturn" }, - "" + "" ], "path": "packages/kbn-object-versioning/lib/types.ts", "deprecated": false, @@ -799,7 +799,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; out: { result: ", + "; }; out: { result: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -807,7 +807,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; }" + "; }; }" ], "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, @@ -829,7 +829,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; out: { result: ", + "; }; out: { result: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -837,7 +837,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; }" + "; }; }" ], "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, @@ -859,7 +859,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; options: ", + "; options: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -867,7 +867,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; out: { result: ", + "; }; out: { result: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -875,7 +875,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; }" + "; }; }" ], "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, @@ -897,7 +897,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; options: ", + "; options: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -905,7 +905,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; out: { result: ", + "; }; out: { result: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -913,7 +913,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; }" + "; }; }" ], "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, @@ -935,7 +935,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; out: { result: ", + "; }; out: { result: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -943,7 +943,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; }" + "; }; }" ], "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, @@ -965,7 +965,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; out: { result: ", + "; }; out: { result: ", { "pluginId": "@kbn/object-versioning", "scope": "common", @@ -973,7 +973,7 @@ "section": "def-common.ObjectTransforms", "text": "ObjectTransforms" }, - "; }; }" + "; }; }" ], "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 0485de6dfd926..a70891f0e72f4 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index fd6199d51ab05..6d25386de3158 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 7afc545253956..2a345e6b1aabd 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index f6f60cd3a87a7..1255de26b275f 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 5adfa3b38dd10..e8886fb80a281 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index fd4cca8adfa84..be186b8aefbf7 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 9bd2f1afd042f..1c2bd87c3a2fb 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index a57d0a816d6cf..6957d39392ab0 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index caee0adcd43d5..3f9c876bec28d 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 71b86a45a6869..50ffb132d5ee4 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 0d765be2c8cde..64d76681a94c2 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index 51add88518911..726d4775a7541 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index e56d56d480655..ebc905fe80095 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.devdocs.json b/api_docs/kbn_reporting_common.devdocs.json new file mode 100644 index 0000000000000..0413d5a18cb0c --- /dev/null +++ b/api_docs/kbn_reporting_common.devdocs.json @@ -0,0 +1,1306 @@ +{ + "id": "@kbn/reporting-common", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.AuthenticationExpiredError", + "type": "Class", + "tags": [], + "label": "AuthenticationExpiredError", + "description": [ + "\nWhile performing some reporting action, like fetching data from ES, our\naccess token expired." + ], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.AuthenticationExpiredError", + "text": "AuthenticationExpiredError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.AuthenticationExpiredError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"authentication_expired_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.AuthenticationExpiredError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserCouldNotLaunchError", + "type": "Class", + "tags": [], + "label": "BrowserCouldNotLaunchError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.BrowserCouldNotLaunchError", + "text": "BrowserCouldNotLaunchError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserCouldNotLaunchError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"browser_could_not_launch_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserCouldNotLaunchError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserCouldNotLaunchError.humanFriendlyMessage", + "type": "Function", + "tags": [], + "label": "humanFriendlyMessage", + "description": [], + "signature": [ + "() => any" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserScreenshotError", + "type": "Class", + "tags": [], + "label": "BrowserScreenshotError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.BrowserScreenshotError", + "text": "BrowserScreenshotError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserScreenshotError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"browser_screenshot_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserScreenshotError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserUnexpectedlyClosedError", + "type": "Class", + "tags": [], + "label": "BrowserUnexpectedlyClosedError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.BrowserUnexpectedlyClosedError", + "text": "BrowserUnexpectedlyClosedError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserUnexpectedlyClosedError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"browser_unexpectedly_closed_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.BrowserUnexpectedlyClosedError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CancellationToken", + "type": "Class", + "tags": [], + "label": "CancellationToken", + "description": [], + "path": "packages/kbn-reporting/common/cancellation_token.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CancellationToken.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-reporting/common/cancellation_token.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CancellationToken.on", + "type": "Function", + "tags": [], + "label": "on", + "description": [], + "signature": [ + "(callback: Function) => void" + ], + "path": "packages/kbn-reporting/common/cancellation_token.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CancellationToken.on.$1", + "type": "Object", + "tags": [], + "label": "callback", + "description": [], + "signature": [ + "Function" + ], + "path": "packages/kbn-reporting/common/cancellation_token.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CancellationToken.cancel", + "type": "Function", + "tags": [], + "label": "cancel", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/kbn-reporting/common/cancellation_token.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CancellationToken.isCancelled", + "type": "Function", + "tags": [], + "label": "isCancelled", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "packages/kbn-reporting/common/cancellation_token.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.DisallowedOutgoingUrl", + "type": "Class", + "tags": [], + "label": "DisallowedOutgoingUrl", + "description": [ + "\nWhile loading requests in the Kibana app, a URL was encountered that the network policy did not allow." + ], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.DisallowedOutgoingUrl", + "text": "DisallowedOutgoingUrl" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.DisallowedOutgoingUrl.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"disallowed_outgoing_url_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.DisallowedOutgoingUrl.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"disallowed_outgoing_url_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.InvalidLayoutParametersError", + "type": "Class", + "tags": [], + "label": "InvalidLayoutParametersError", + "description": [ + "\nWhile validating the page layout parameters for a screenshot type report job" + ], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.InvalidLayoutParametersError", + "text": "InvalidLayoutParametersError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.InvalidLayoutParametersError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"invalid_layout_parameters_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.InvalidLayoutParametersError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"invalid_layout_parameters_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.KibanaShuttingDownError", + "type": "Class", + "tags": [], + "label": "KibanaShuttingDownError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.KibanaShuttingDownError", + "text": "KibanaShuttingDownError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.KibanaShuttingDownError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"kibana_shutting_down_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.KibanaShuttingDownError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.PdfWorkerOutOfMemoryError", + "type": "Class", + "tags": [], + "label": "PdfWorkerOutOfMemoryError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.PdfWorkerOutOfMemoryError", + "text": "PdfWorkerOutOfMemoryError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.PdfWorkerOutOfMemoryError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"pdf_worker_out_of_memory_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.PdfWorkerOutOfMemoryError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.PdfWorkerOutOfMemoryError.humanFriendlyMessage", + "type": "Function", + "tags": [], + "label": "humanFriendlyMessage", + "description": [], + "signature": [ + "() => any" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.QueueTimeoutError", + "type": "Class", + "tags": [], + "label": "QueueTimeoutError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.QueueTimeoutError", + "text": "QueueTimeoutError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.QueueTimeoutError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"queue_timeout_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.QueueTimeoutError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError", + "type": "Class", + "tags": [], + "label": "ReportingError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + }, + " extends Error" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError.code", + "type": "string", + "tags": [ + "note" + ], + "label": "code", + "description": [ + "\nA string that uniquely brands an error type. This is used to power telemetry\nabout reporting failures.\n" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError.Unnamed.$1", + "type": "string", + "tags": [], + "label": "details", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError.message", + "type": "string", + "tags": [], + "label": "message", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError.toString", + "type": "Function", + "tags": [], + "label": "toString", + "description": [], + "signature": [ + "() => string" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.UnknownError", + "type": "Class", + "tags": [], + "label": "UnknownError", + "description": [ + "\nAn unknown error has occurred. See details." + ], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.UnknownError", + "text": "UnknownError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.UnknownError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"unknown_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.UnknownError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.VisualReportingSoftDisabledError", + "type": "Class", + "tags": [], + "label": "VisualReportingSoftDisabledError", + "description": [ + "\nSpecial error case that should only occur on Cloud when trying to generate\na report on a Kibana instance that is too small to be running Chromium." + ], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.VisualReportingSoftDisabledError", + "text": "VisualReportingSoftDisabledError" + }, + " extends ", + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.ReportingError", + "text": "ReportingError" + } + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.VisualReportingSoftDisabledError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "\"visual_reporting_soft_disabled_error\"" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.VisualReportingSoftDisabledError.code", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.VisualReportingSoftDisabledError.humanFriendlyMessage", + "type": "Function", + "tags": [], + "label": "humanFriendlyMessage", + "description": [], + "signature": [ + "() => any" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.byteSizeValueToNumber", + "type": "Function", + "tags": [], + "label": "byteSizeValueToNumber", + "description": [], + "signature": [ + "(value: number | ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ByteSizeValue", + "text": "ByteSizeValue" + }, + ") => number" + ], + "path": "packages/kbn-reporting/common/schema_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.byteSizeValueToNumber.$1", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "number | ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ByteSizeValue", + "text": "ByteSizeValue" + } + ], + "path": "packages/kbn-reporting/common/schema_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.durationToNumber", + "type": "Function", + "tags": [], + "label": "durationToNumber", + "description": [], + "signature": [ + "(value: number | moment.Duration) => number" + ], + "path": "packages/kbn-reporting/common/schema_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.durationToNumber.$1", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "number | moment.Duration" + ], + "path": "packages/kbn-reporting/common/schema_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.numberToDuration", + "type": "Function", + "tags": [], + "label": "numberToDuration", + "description": [], + "signature": [ + "(value: number | moment.Duration) => moment.Duration" + ], + "path": "packages/kbn-reporting/common/schema_utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.numberToDuration.$1", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "number | moment.Duration" + ], + "path": "packages/kbn-reporting/common/schema_utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CsvMetrics", + "type": "Interface", + "tags": [], + "label": "CsvMetrics", + "description": [], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CsvMetrics.rows", + "type": "number", + "tags": [], + "label": "rows", + "description": [], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError", + "type": "Interface", + "tags": [], + "label": "ReportingError", + "description": [], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.ReportingError.humanFriendlyMessage", + "type": "Function", + "tags": [], + "label": "humanFriendlyMessage", + "description": [ + "\nReturn a message describing the error that is human friendly" + ], + "signature": [ + "(() => string) | undefined" + ], + "path": "packages/kbn-reporting/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunMetrics", + "type": "Interface", + "tags": [], + "label": "TaskRunMetrics", + "description": [], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunMetrics.csv", + "type": "Object", + "tags": [], + "label": "csv", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.CsvMetrics", + "text": "CsvMetrics" + }, + " | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunMetrics.png", + "type": "Object", + "tags": [], + "label": "png", + "description": [], + "signature": [ + "PerformanceMetrics", + " | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunMetrics.pdf", + "type": "Object", + "tags": [], + "label": "pdf", + "description": [], + "signature": [ + "PdfScreenshotMetrics", + " | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult", + "type": "Interface", + "tags": [], + "label": "TaskRunResult", + "description": [], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult.content_type", + "type": "CompoundType", + "tags": [], + "label": "content_type", + "description": [], + "signature": [ + "string | null" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult.csv_contains_formulas", + "type": "CompoundType", + "tags": [], + "label": "csv_contains_formulas", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult.max_size_reached", + "type": "CompoundType", + "tags": [], + "label": "max_size_reached", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult.warnings", + "type": "Array", + "tags": [], + "label": "warnings", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult.metrics", + "type": "Object", + "tags": [], + "label": "metrics", + "description": [], + "signature": [ + { + "pluginId": "@kbn/reporting-common", + "scope": "common", + "docId": "kibKbnReportingCommonPluginApi", + "section": "def-common.TaskRunMetrics", + "text": "TaskRunMetrics" + }, + " | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.TaskRunResult.error_code", + "type": "string", + "tags": [], + "label": "error_code", + "description": [ + "\nWhen running a report task we may finish with warnings that were triggered\nby an error. We can pass the error code via the task run result to the\ntask runner so that it can be recorded for telemetry.\n\nAlternatively, this field can be populated in the event that the task does\nnot complete in the task runner's error handler." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-reporting/common/metrics.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.CSV_REPORTING_ACTION", + "type": "string", + "tags": [], + "label": "CSV_REPORTING_ACTION", + "description": [], + "signature": [ + "\"downloadCsvReport\"" + ], + "path": "packages/kbn-reporting/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.UI_SETTINGS_CUSTOM_PDF_LOGO", + "type": "string", + "tags": [], + "label": "UI_SETTINGS_CUSTOM_PDF_LOGO", + "description": [], + "signature": [ + "\"xpackReporting:customPdfLogo\"" + ], + "path": "packages/kbn-reporting/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.UI_SETTINGS_DATEFORMAT_TZ", + "type": "string", + "tags": [], + "label": "UI_SETTINGS_DATEFORMAT_TZ", + "description": [], + "signature": [ + "\"dateFormat:tz\"" + ], + "path": "packages/kbn-reporting/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-common", + "id": "def-common.UI_SETTINGS_SEARCH_INCLUDE_FROZEN", + "type": "string", + "tags": [], + "label": "UI_SETTINGS_SEARCH_INCLUDE_FROZEN", + "description": [], + "signature": [ + "\"search:includeFrozen\"" + ], + "path": "packages/kbn-reporting/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx new file mode 100644 index 0000000000000..f16d5d346aa8f --- /dev/null +++ b/api_docs/kbn_reporting_common.mdx @@ -0,0 +1,39 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnReportingCommonPluginApi +slug: /kibana-dev-docs/api/kbn-reporting-common +title: "@kbn/reporting-common" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/reporting-common plugin +date: 2023-04-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] +--- +import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 73 | 0 | 65 | 0 | + +## Common + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 9939c2e0f1dc1..ffabeef0c0662 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 19c9af09f958d..d9d1a037f64b1 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.devdocs.json b/api_docs/kbn_saved_objects_settings.devdocs.json new file mode 100644 index 0000000000000..f6d64f51a049d --- /dev/null +++ b/api_docs/kbn_saved_objects_settings.devdocs.json @@ -0,0 +1,58 @@ +{ + "id": "@kbn/saved-objects-settings", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/saved-objects-settings", + "id": "def-common.LISTING_LIMIT_SETTING", + "type": "string", + "tags": [], + "label": "LISTING_LIMIT_SETTING", + "description": [], + "signature": [ + "\"savedObjects:listingLimit\"" + ], + "path": "packages/kbn-saved-objects-settings/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/saved-objects-settings", + "id": "def-common.PER_PAGE_SETTING", + "type": "string", + "tags": [], + "label": "PER_PAGE_SETTING", + "description": [], + "signature": [ + "\"savedObjects:perPage\"" + ], + "path": "packages/kbn-saved-objects-settings/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx new file mode 100644 index 0000000000000..6a8a7b7b45d2f --- /dev/null +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnSavedObjectsSettingsPluginApi +slug: /kibana-dev-docs/api/kbn-saved-objects-settings +title: "@kbn/saved-objects-settings" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/saved-objects-settings plugin +date: 2023-04-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] +--- +import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 2 | 0 | + +## Common + +### Consts, variables and types + + diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 56b7a05e99fd4..6c4e47f1a4351 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index a680dd17116b2..0003d9f974b86 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index d67b3b2449b34..30b6726407b20 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.devdocs.json b/api_docs/kbn_securitysolution_data_table.devdocs.json new file mode 100644 index 0000000000000..2aa15fe5580f4 --- /dev/null +++ b/api_docs/kbn_securitysolution_data_table.devdocs.json @@ -0,0 +1,1862 @@ +{ + "id": "@kbn/securitysolution-data-table", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.addBuildingBlockStyle", + "type": "Function", + "tags": [], + "label": "addBuildingBlockStyle", + "description": [], + "signature": [ + "(ecs: ", + { + "pluginId": "@kbn/securitysolution-ecs", + "scope": "common", + "docId": "kibKbnSecuritysolutionEcsPluginApi", + "section": "def-common.EcsSecurityExtension", + "text": "EcsSecurityExtension" + }, + ", theme: ", + { + "pluginId": "kibanaReact", + "scope": "common", + "docId": "kibKibanaReactPluginApi", + "section": "def-common.EuiTheme", + "text": "EuiTheme" + }, + ", setCellProps: (props: ", + "EuiDataGridSetCellProps", + ") => void, defaultStyles?: React.CSSProperties | undefined) => void" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.addBuildingBlockStyle.$1", + "type": "Object", + "tags": [], + "label": "ecs", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-ecs", + "scope": "common", + "docId": "kibKbnSecuritysolutionEcsPluginApi", + "section": "def-common.EcsSecurityExtension", + "text": "EcsSecurityExtension" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.addBuildingBlockStyle.$2", + "type": "Object", + "tags": [], + "label": "theme", + "description": [], + "signature": [ + { + "pluginId": "kibanaReact", + "scope": "common", + "docId": "kibKibanaReactPluginApi", + "section": "def-common.EuiTheme", + "text": "EuiTheme" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.addBuildingBlockStyle.$3", + "type": "Function", + "tags": [], + "label": "setCellProps", + "description": [], + "signature": [ + "(props: ", + "EuiDataGridSetCellProps", + ") => void" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.addBuildingBlockStyle.$4", + "type": "Object", + "tags": [], + "label": "defaultStyles", + "description": [], + "signature": [ + "React.CSSProperties | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableComponent", + "type": "Function", + "tags": [], + "label": "DataTableComponent", + "description": [], + "signature": [ + "React.NamedExoticComponent<", + "DataTableProps", + ">" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableComponent.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.dataTableReducer", + "type": "Function", + "tags": [], + "label": "dataTableReducer", + "description": [ + "The reducer for all data table actions" + ], + "signature": [ + "(state: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableState", + "text": "TableState" + }, + " | undefined, action: { type: any; }) => ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableState", + "text": "TableState" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/reducer.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.dataTableReducer.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "PassedS" + ], + "path": "node_modules/typescript-fsa-reducers/dist/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.dataTableReducer.$2", + "type": "Object", + "tags": [], + "label": "action", + "description": [], + "signature": [ + "{ type: any; }" + ], + "path": "node_modules/typescript-fsa-reducers/dist/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getColumnHeaders", + "type": "Function", + "tags": [], + "label": "getColumnHeaders", + "description": [ + "Enriches the column headers with field details from the specified browserFields" + ], + "signature": [ + "(headers: ", + "ColumnHeaderOptions", + "[], browserFields: Readonly>>, isEventRenderedView?: boolean | undefined) => ", + "ColumnHeaderOptions", + "[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getColumnHeaders.$1", + "type": "Array", + "tags": [], + "label": "headers", + "description": [], + "signature": [ + "ColumnHeaderOptions", + "[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getColumnHeaders.$2", + "type": "Object", + "tags": [], + "label": "browserFields", + "description": [], + "signature": [ + "Readonly>>" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getColumnHeaders.$3", + "type": "CompoundType", + "tags": [], + "label": "isEventRenderedView", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getEventIdToDataMapping", + "type": "Function", + "tags": [], + "label": "getEventIdToDataMapping", + "description": [ + "\nCreates mapping of eventID -> fieldData for given fieldsToKeep. Used to store additional field\ndata necessary for custom timeline actions in conjunction with selection state" + ], + "signature": [ + "(timelineData: ", + { + "pluginId": "timelines", + "scope": "common", + "docId": "kibTimelinesPluginApi", + "section": "def-common.TimelineItem", + "text": "TimelineItem" + }, + "[], eventIds: string[], fieldsToKeep: string[], hasAlertsCrud: boolean) => Record" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getEventIdToDataMapping.$1", + "type": "Array", + "tags": [], + "label": "timelineData", + "description": [], + "signature": [ + { + "pluginId": "timelines", + "scope": "common", + "docId": "kibTimelinesPluginApi", + "section": "def-common.TimelineItem", + "text": "TimelineItem" + }, + "[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getEventIdToDataMapping.$2", + "type": "Array", + "tags": [], + "label": "eventIds", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getEventIdToDataMapping.$3", + "type": "Array", + "tags": [], + "label": "fieldsToKeep", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getEventIdToDataMapping.$4", + "type": "boolean", + "tags": [], + "label": "hasAlertsCrud", + "description": [], + "signature": [ + "boolean" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getPageRowIndex", + "type": "Function", + "tags": [], + "label": "getPageRowIndex", + "description": [ + "\nrowIndex is bigger than `data.length` for pages with page numbers bigger than one.\nFor that reason, we must calculate `rowIndex % itemsPerPage`.\n\nEx:\nGiven `rowIndex` is `13` and `itemsPerPage` is `10`.\nIt means that the `activePage` is `2` and the `pageRowIndex` is `3`\n\n**Warning**:\nBe careful with array out of bounds. `pageRowIndex` can be bigger or equal to `data.length`\n in the scenario where the user changes the event status (Open, Acknowledged, Closed)." + ], + "signature": [ + "(rowIndex: number, itemsPerPage: number) => number" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/pagination.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getPageRowIndex.$1", + "type": "number", + "tags": [], + "label": "rowIndex", + "description": [], + "signature": [ + "number" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/pagination.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getPageRowIndex.$2", + "type": "number", + "tags": [], + "label": "itemsPerPage", + "description": [], + "signature": [ + "number" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/pagination.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.getTableByIdSelector", + "type": "Function", + "tags": [], + "label": "getTableByIdSelector", + "description": [], + "signature": [ + "() => ((state: { dataTable: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableState", + "text": "TableState" + }, + "; }, params_0: string) => ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.DataTableModel", + "text": "DataTableModel" + }, + ") & ", + "OutputSelectorFields", + "<(args_0: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.DataTableModel", + "text": "DataTableModel" + }, + ") => ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.DataTableModel", + "text": "DataTableModel" + }, + " & { clearCache: () => void; }> & { clearCache: () => void; }" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/selectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.isEventBuildingBlockType", + "type": "Function", + "tags": [], + "label": "isEventBuildingBlockType", + "description": [], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/securitysolution-ecs", + "scope": "common", + "docId": "kibKbnSecuritysolutionEcsPluginApi", + "section": "def-common.EcsSecurityExtension", + "text": "EcsSecurityExtension" + }, + ") => boolean" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.isEventBuildingBlockType.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-ecs", + "scope": "common", + "docId": "kibKbnSecuritysolutionEcsPluginApi", + "section": "def-common.EcsSecurityExtension", + "text": "EcsSecurityExtension" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel", + "type": "Interface", + "tags": [], + "label": "DataTableModel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.DataTableModel", + "text": "DataTableModel" + }, + " extends ", + "DataTableModelSettings" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.columns", + "type": "Array", + "tags": [], + "label": "columns", + "description": [ + "The columns displayed in the data table" + ], + "signature": [ + "(Pick<", + "EuiDataGridColumn", + ", \"id\" | \"display\" | \"displayAsText\" | \"initialWidth\"> & Pick<", + "EuiDataGridColumn", + ", \"id\" | \"actions\" | \"schema\" | \"display\" | \"defaultSortDirection\" | \"displayAsText\" | \"initialWidth\" | \"isSortable\" | \"isExpandable\" | \"isResizable\"> & { aggregatable?: boolean | undefined; category?: string | undefined; columnHeaderType: ", + "ColumnHeaderType", + "; description?: string | null | undefined; esTypes?: string[] | undefined; example?: string | number | null | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubType", + "text": "IFieldSubType" + }, + " | undefined; type?: string | undefined; })[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.dataViewId", + "type": "CompoundType", + "tags": [], + "label": "dataViewId", + "description": [ + "Kibana data view id" + ], + "signature": [ + "string | null" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.deletedEventIds", + "type": "Array", + "tags": [], + "label": "deletedEventIds", + "description": [ + "Events to not be rendered" + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.expandedDetail", + "type": "Object", + "tags": [], + "label": "expandedDetail", + "description": [ + "This holds the view information for the flyout when viewing data in a consuming view (i.e. hosts page) or the side panel in the primary data view" + ], + "signature": [ + "{ [x: string]: ", + "ExpandedDetailType", + " | undefined; }" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.filters", + "type": "Array", + "tags": [], + "label": "filters", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[] | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.graphEventId", + "type": "string", + "tags": [], + "label": "graphEventId", + "description": [ + "When non-empty, display a graph view for this event" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "Uniquely identifies the data table" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.indexNames", + "type": "Array", + "tags": [], + "label": "indexNames", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.isLoading", + "type": "boolean", + "tags": [], + "label": "isLoading", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.isSelectAllChecked", + "type": "boolean", + "tags": [], + "label": "isSelectAllChecked", + "description": [ + "If selectAll checkbox in header is checked" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.itemsPerPage", + "type": "number", + "tags": [], + "label": "itemsPerPage", + "description": [ + "The number of items to show in a single page of results" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.itemsPerPageOptions", + "type": "Array", + "tags": [], + "label": "itemsPerPageOptions", + "description": [ + "Displays a series of choices that when selected, become the value of `itemsPerPage`" + ], + "signature": [ + "number[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.loadingEventIds", + "type": "Array", + "tags": [], + "label": "loadingEventIds", + "description": [ + "Events to be rendered as loading" + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.selectedEventIds", + "type": "Object", + "tags": [], + "label": "selectedEventIds", + "description": [ + "Events selected on this timeline -- eventId to TimelineNonEcsData[] mapping of data required for bulk actions" + ], + "signature": [ + "{ [x: string]: ", + { + "pluginId": "timelines", + "scope": "common", + "docId": "kibTimelinesPluginApi", + "section": "def-common.TimelineNonEcsData", + "text": "TimelineNonEcsData" + }, + "[]; }" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.initialized", + "type": "CompoundType", + "tags": [], + "label": "initialized", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.sessionViewConfig", + "type": "CompoundType", + "tags": [], + "label": "sessionViewConfig", + "description": [], + "signature": [ + "SessionViewConfig", + " | null" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.updated", + "type": "number", + "tags": [], + "label": "updated", + "description": [ + "updated saved object timestamp" + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.totalCount", + "type": "number", + "tags": [], + "label": "totalCount", + "description": [ + "Total number of fetched events/alerts" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.viewMode", + "type": "CompoundType", + "tags": [], + "label": "viewMode", + "description": [], + "signature": [ + "\"gridView\" | \"eventRenderedView\"" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableModel.additionalFilters", + "type": "Object", + "tags": [], + "label": "additionalFilters", + "description": [], + "signature": [ + "{ showOnlyThreatIndicatorAlerts: boolean; showBuildingBlockAlerts: boolean; }" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableState", + "type": "Interface", + "tags": [], + "label": "DataTableState", + "description": [ + "The state of all timelines is stored here" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.DataTableState.dataTable", + "type": "Object", + "tags": [], + "label": "dataTable", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableState", + "text": "TableState" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SortColumnTable", + "type": "Interface", + "tags": [], + "label": "SortColumnTable", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SortColumnTable.columnId", + "type": "string", + "tags": [], + "label": "columnId", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SortColumnTable.columnType", + "type": "string", + "tags": [], + "label": "columnType", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SortColumnTable.esTypes", + "type": "Array", + "tags": [], + "label": "esTypes", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SortColumnTable.sortDirection", + "type": "CompoundType", + "tags": [], + "label": "sortDirection", + "description": [], + "signature": [ + "\"asc\" | \"desc\" | \"none\" | ", + { + "pluginId": "timelines", + "scope": "common", + "docId": "kibTimelinesPluginApi", + "section": "def-common.Direction", + "text": "Direction" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableById", + "type": "Interface", + "tags": [], + "label": "TableById", + "description": [ + "A map of id to data table" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableById.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[id: string]: DataTableModel", + "description": [], + "signature": [ + "[id: string]: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.DataTableModel", + "text": "DataTableModel" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableState", + "type": "Interface", + "tags": [], + "label": "TableState", + "description": [ + "The state of all data tables is stored here" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableState.tableById", + "type": "Object", + "tags": [], + "label": "tableById", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableById", + "text": "TableById" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.Direction", + "type": "Enum", + "tags": [], + "label": "Direction", + "description": [], + "path": "x-pack/plugins/timelines/common/search_strategy/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableEntityType", + "type": "Enum", + "tags": [], + "label": "TableEntityType", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableId", + "type": "Enum", + "tags": [], + "label": "TableId", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TimelineTabs", + "type": "Enum", + "tags": [], + "label": "TimelineTabs", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/detail_panel.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.defaultColumnHeaderType", + "type": "CompoundType", + "tags": [], + "label": "defaultColumnHeaderType", + "description": [], + "signature": [ + "\"not-filtered\" | \"text-filter\"" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.defaultHeaders", + "type": "Array", + "tags": [], + "label": "defaultHeaders", + "description": [], + "signature": [ + "ColumnHeaderOptions", + "[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.FILTER_OPEN", + "type": "string", + "tags": [], + "label": "FILTER_OPEN", + "description": [], + "signature": [ + "\"open\"" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SortDirectionTable", + "type": "Type", + "tags": [], + "label": "SortDirectionTable", + "description": [], + "signature": [ + "\"asc\" | \"desc\" | \"none\" | ", + { + "pluginId": "timelines", + "scope": "common", + "docId": "kibTimelinesPluginApi", + "section": "def-common.Direction", + "text": "Direction" + } + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.SubsetDataTableModel", + "type": "Type", + "tags": [], + "label": "SubsetDataTableModel", + "description": [], + "signature": [ + "{ readonly sort: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.SortColumnTable", + "text": "SortColumnTable" + }, + "[]; readonly title: string; readonly filters?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[] | undefined; readonly columns: (Pick<", + "EuiDataGridColumn", + ", \"id\" | \"display\" | \"displayAsText\" | \"initialWidth\"> & Pick<", + "EuiDataGridColumn", + ", \"id\" | \"actions\" | \"schema\" | \"display\" | \"defaultSortDirection\" | \"displayAsText\" | \"initialWidth\" | \"isSortable\" | \"isExpandable\" | \"isResizable\"> & { aggregatable?: boolean | undefined; category?: string | undefined; columnHeaderType: ", + "ColumnHeaderType", + "; description?: string | null | undefined; esTypes?: string[] | undefined; example?: string | number | null | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubType", + "text": "IFieldSubType" + }, + " | undefined; type?: string | undefined; })[]; readonly isLoading: boolean; readonly viewMode: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.ViewSelection", + "text": "ViewSelection" + }, + "; readonly dataViewId: string | null; readonly defaultColumns: (Pick<", + "EuiDataGridColumn", + ", \"id\" | \"display\" | \"displayAsText\" | \"initialWidth\"> & Pick<", + "EuiDataGridColumn", + ", \"id\" | \"actions\" | \"schema\" | \"display\" | \"defaultSortDirection\" | \"displayAsText\" | \"initialWidth\" | \"isSortable\" | \"isExpandable\" | \"isResizable\"> & { aggregatable?: boolean | undefined; category?: string | undefined; columnHeaderType: ", + "ColumnHeaderType", + "; description?: string | null | undefined; esTypes?: string[] | undefined; example?: string | number | null | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubType", + "text": "IFieldSubType" + }, + " | undefined; type?: string | undefined; })[]; readonly initialized?: boolean | undefined; readonly queryFields: string[]; readonly selectAll: boolean; readonly showCheckboxes: boolean; readonly deletedEventIds: string[]; readonly expandedDetail: Partial>; readonly graphEventId?: string | undefined; readonly indexNames: string[]; readonly isSelectAllChecked: boolean; readonly itemsPerPage: number; readonly itemsPerPageOptions: number[]; readonly loadingEventIds: string[]; readonly selectedEventIds: Record; readonly sessionViewConfig: ", + "SessionViewConfig", + " | null; readonly totalCount: number; readonly additionalFilters: Record<", + "AlertPageFilterType", + ", boolean>; }" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.TableIdLiteral", + "type": "Type", + "tags": [], + "label": "TableIdLiteral", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".usersPageEvents | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".hostsPageEvents | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".networkPageEvents | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".hostsPageSessions | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".alertsOnRuleDetailsPage | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".alertsOnAlertsPage | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".test | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".rulePreview | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".kubernetesPageSessions | ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableId", + "text": "TableId" + }, + ".alertsOnCasePage" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.ViewSelection", + "type": "Type", + "tags": [], + "label": "ViewSelection", + "description": [], + "signature": [ + "\"gridView\" | \"eventRenderedView\"" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults", + "type": "Object", + "tags": [], + "label": "tableDefaults", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.defaultColumns", + "type": "Array", + "tags": [], + "label": "defaultColumns", + "description": [], + "signature": [ + "ColumnHeaderOptions", + "[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.dataViewId", + "type": "Uncategorized", + "tags": [], + "label": "dataViewId", + "description": [], + "signature": [ + "null" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.deletedEventIds", + "type": "Array", + "tags": [], + "label": "deletedEventIds", + "description": [], + "signature": [ + "never[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.expandedDetail", + "type": "Object", + "tags": [], + "label": "expandedDetail", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false, + "children": [] + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.filters", + "type": "Array", + "tags": [], + "label": "filters", + "description": [], + "signature": [ + "never[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.indexNames", + "type": "Array", + "tags": [], + "label": "indexNames", + "description": [], + "signature": [ + "never[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.isSelectAllChecked", + "type": "boolean", + "tags": [], + "label": "isSelectAllChecked", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.isLoading", + "type": "boolean", + "tags": [], + "label": "isLoading", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.itemsPerPage", + "type": "number", + "tags": [], + "label": "itemsPerPage", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.itemsPerPageOptions", + "type": "Array", + "tags": [], + "label": "itemsPerPageOptions", + "description": [], + "signature": [ + "number[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.loadingEventIds", + "type": "Array", + "tags": [], + "label": "loadingEventIds", + "description": [], + "signature": [ + "never[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.selectedEventIds", + "type": "Object", + "tags": [], + "label": "selectedEventIds", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false, + "children": [] + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.showCheckboxes", + "type": "boolean", + "tags": [], + "label": "showCheckboxes", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.sort", + "type": "Array", + "tags": [], + "label": "sort", + "description": [], + "signature": [ + "{ columnId: string; columnType: string; esTypes: string[]; sortDirection: \"desc\"; }[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.selectAll", + "type": "boolean", + "tags": [], + "label": "selectAll", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.graphEventId", + "type": "string", + "tags": [], + "label": "graphEventId", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.sessionViewConfig", + "type": "Uncategorized", + "tags": [], + "label": "sessionViewConfig", + "description": [], + "signature": [ + "null" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.columns", + "type": "Array", + "tags": [], + "label": "columns", + "description": [], + "signature": [ + "ColumnHeaderOptions", + "[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.queryFields", + "type": "Array", + "tags": [], + "label": "queryFields", + "description": [], + "signature": [ + "never[]" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.totalCount", + "type": "number", + "tags": [], + "label": "totalCount", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.viewMode", + "type": "string", + "tags": [], + "label": "viewMode", + "description": [], + "signature": [ + "\"gridView\"" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.additionalFilters", + "type": "Object", + "tags": [], + "label": "additionalFilters", + "description": [], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.additionalFilters.showBuildingBlockAlerts", + "type": "boolean", + "tags": [], + "label": "showBuildingBlockAlerts", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableDefaults.additionalFilters.showOnlyThreatIndicatorAlerts", + "type": "boolean", + "tags": [], + "label": "showOnlyThreatIndicatorAlerts", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-data-table", + "id": "def-common.tableEntity", + "type": "Object", + "tags": [], + "label": "tableEntity", + "description": [], + "signature": [ + "{ \"users-page-events\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"hosts-page-events\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"network-page-events\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"hosts-page-sessions-v2\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"alerts-rules-details-page\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"alerts-page\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"table-test\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; alternateTest: ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"rule-preview\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"kubernetes-page-sessions\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; \"alerts-case-page\": ", + { + "pluginId": "@kbn/securitysolution-data-table", + "scope": "common", + "docId": "kibKbnSecuritysolutionDataTablePluginApi", + "section": "def-common.TableEntityType", + "text": "TableEntityType" + }, + "; }" + ], + "path": "x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx new file mode 100644 index 0000000000000..93f74959bb28e --- /dev/null +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -0,0 +1,42 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnSecuritysolutionDataTablePluginApi +slug: /kibana-dev-docs/api/kbn-securitysolution-data-table +title: "@kbn/securitysolution-data-table" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/securitysolution-data-table plugin +date: 2023-04-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] +--- +import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; + + + +Contact [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 95 | 0 | 72 | 7 | + +## Common + +### Objects + + +### Functions + + +### Interfaces + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index ccf154cb67c86..c6864d53f5987 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 2b69b7998ea8c..0683ce18f1779 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 1165d3f0adfeb..fa2c635cb5987 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index 3c846c0521417..6f5cade1143a6 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index c5bfd59dae699..144d4664cd8d2 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 89b34982d83c3..2fb1367fc08ad 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 6c4dc819fea22..b5109765d9b12 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 9c6d333dc9146..24a04a2bd93b6 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 4a3573e734dbf..47c3625cc4e59 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index ef899c2a35c12..04b3393bdfa7f 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 59ede52e65f8e..9e574e3409680 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 1833493811a82..b674110dcacb4 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.devdocs.json b/api_docs/kbn_securitysolution_list_utils.devdocs.json index ae9d7b8f6fc72..af3d575d05602 100644 --- a/api_docs/kbn_securitysolution_list_utils.devdocs.json +++ b/api_docs/kbn_securitysolution_list_utils.devdocs.json @@ -196,6 +196,53 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-list-utils", + "id": "def-common.fieldSupportsMatches", + "type": "Function", + "tags": [], + "label": "fieldSupportsMatches", + "description": [], + "signature": [ + "(field: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ") => boolean | undefined" + ], + "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-list-utils", + "id": "def-common.fieldSupportsMatches.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + } + ], + "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-list-utils", "id": "def-common.filterExceptionItems", @@ -2330,6 +2377,39 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-list-utils", + "id": "def-common.isKibanaStringType", + "type": "Function", + "tags": [], + "label": "isKibanaStringType", + "description": [], + "signature": [ + "(type: string) => boolean" + ], + "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-list-utils", + "id": "def-common.isKibanaStringType.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-securitysolution-list-utils/src/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -2743,11 +2823,11 @@ "description": [], "signature": [ { - "pluginId": "@kbn/es-query", + "pluginId": "@kbn/securitysolution-list-utils", "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.DataViewFieldBase", - "text": "DataViewFieldBase" + "docId": "kibKbnSecuritysolutionListUtilsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" }, " | undefined" ], diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 3c6df7388e172..c2aa79d1226d9 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 206 | 10 | 159 | 0 | +| 210 | 10 | 163 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 6a28231da0481..0477dce73217b 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index d2c72d0772e19..2f9f9263ae18e 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 1125681b0971f..bf30718757312 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 0bd85175242ec..f05a9f838751d 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 6669a02222e93..33001595efbb3 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 27b601b490b83..b2aae28dfd413 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index fcc9f127760ab..8d727e343ce46 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index c3f57b199f9ff..1d96f50090471 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 30a0148e2e8f3..c238eebf30ce3 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index 70b219338a20e..e571e864814a7 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 8f9f80d7b3913..17d863ad15b80 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 031e48822bbee..c7560cd21c835 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 5d5631733ede7..7c3cbca6bfa65 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 1204c8e54dfc0..3458dd32b4b5c 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index c1308d02512ea..c5b10b3e93361 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 4e458537cb972..03765102bad1c 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 46501527af39b..c2a6b2898f267 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 341c7347279cc..a3072335000dd 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index ecb231fd1f4f7..73dffa9666505 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 954750cc64c2d..b44a3ebd0cf57 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 709a6d1e47cde..29f8177f6a683 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 943ab8cbedcfe..bfeb8a5123374 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index d93bbdf9320f1..124de09fbb6b2 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index ef62df1c54e45..ffef3842a8f49 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 5fcb1c5611849..12253c541138d 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 8e162852299b6..521e7afeffc28 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 36ef41e9b981c..d94f87f474a06 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 9509faff3ef85..9256b249ddec3 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 8daec4391fc6c..98c666dc7ac30 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 159d8d0b0fe55..c7e445662d405 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index ab2a30ad96e08..16e2e693823a6 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 58d5ec89cf249..dd303a55b92d1 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 442ee9563c4f0..730c8b3b4a670 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index ec808997f66f0..b9a5faef6e24d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index 7c3b0b6b885c4..eb3cdb4b62c34 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 0707ca083e707..50643295597d3 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index d71ef08c3ca12..a3b0893d962cc 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index abcb2e6274398..0419eea5c2561 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 5e56c85446ac2..8d93e65e58a7a 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index ac3d4b7dd5e01..7b06d8fedf3bc 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 8683b5f6910a7..ba35535d9c82d 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 0e6b935d6e815..b80a811065383 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index c7187e4b0da1a..fdd01db6e7893 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index a3e682d142670..002e032035f2d 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index f7d5c14c7b67d..909cbc86dbb9a 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index e0f66d57dd61c..32a0175b0ae11 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.devdocs.json b/api_docs/kbn_std.devdocs.json index 65acb05e4f8b1..f574a699ece38 100644 --- a/api_docs/kbn_std.devdocs.json +++ b/api_docs/kbn_std.devdocs.json @@ -452,6 +452,62 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/std", + "id": "def-common.ensureDeepObject", + "type": "Function", + "tags": [], + "label": "ensureDeepObject", + "description": [ + "\nRecursively traverses through the object's properties and expands ones with\ndot-separated names into nested objects (eg. { a.b: 'c'} -> { a: { b: 'c' })." + ], + "signature": [ + "(obj: any, path: string[]) => any" + ], + "path": "packages/kbn-std/src/ensure_deep_object.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/std", + "id": "def-common.ensureDeepObject.$1", + "type": "Any", + "tags": [], + "label": "obj", + "description": [ + "Object to traverse through." + ], + "signature": [ + "any" + ], + "path": "packages/kbn-std/src/ensure_deep_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/std", + "id": "def-common.ensureDeepObject.$2", + "type": "Array", + "tags": [], + "label": "path", + "description": [ + "The current path of the traversal" + ], + "signature": [ + "string[]" + ], + "path": "packages/kbn-std/src/ensure_deep_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "Same object instance with expanded properties." + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/std", "id": "def-common.ensureNoUnsafeProperties", diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index b9046dbf144ae..760fb637dca1f 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 97 | 1 | 64 | 1 | +| 100 | 2 | 64 | 1 | ## Common diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 40bd1e5c0a717..c7c0f0be1c9a8 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index ff14992f83ed1..a8aaf772dfbeb 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index f3bc65872a7af..e6fb061b3c4e2 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 7126d1c159255..9bc5f62ac0fb9 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index d7d02ef7774e6..7cdb7653f1365 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 3a65c1dc1293e..fa0e562873fca 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 9fbc8195ada3c..06c7351858830 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 8b2f4925556b6..2b501f8c55bdb 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index eb9a7fe028a40..1d9e624fb0ea9 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index b29354f0fcf90..2b2bdd60e4ce7 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index d4e9f21979baf..2461a2ae517a5 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 0855e3c50be75..889b19937ba67 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 8b5e076143a7e..c68316050f7c4 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 7b5770a36fa1e..27d8eed321f38 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 563ecd0768335..23117d51a44f0 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index b8fb14db2eb37..dd420e4509c04 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 43b4873bef6dd..eb3b59e92004e 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index b78925b43bfd1..92633990126eb 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index 30afa8507031f..1dbaea8a030f5 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -1268,18 +1268,6 @@ "plugin": "savedObjectsManagement", "path": "src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx" }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/application/index.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/application/index.tsx" - }, - { - "plugin": "observability", - "path": "x-pack/plugins/observability/public/application/index.tsx" - }, { "plugin": "exploratoryView", "path": "x-pack/plugins/exploratory_view/public/application/index.tsx" @@ -1316,6 +1304,18 @@ "plugin": "fleet", "path": "x-pack/plugins/fleet/public/components/custom_assets_accordion.tsx" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/application/index.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/application/index.tsx" + }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/application/index.tsx" + }, { "plugin": "ml", "path": "x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx" diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 2d697f9a8efc2..b521c0367a597 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.devdocs.json b/api_docs/kibana_utils.devdocs.json index 5aef227d6e93f..a9f8c8bbeaced 100644 --- a/api_docs/kibana_utils.devdocs.json +++ b/api_docs/kibana_utils.devdocs.json @@ -4191,7 +4191,7 @@ "section": "def-common.IToasts", "text": "IToasts" }, - ") => { onGetError: (error: Error) => void; onSetError: (error: Error) => void; }" + ") => { onGetError: (e: Error) => void; onSetError: (e: Error) => void; }" ], "path": "src/plugins/kibana_utils/public/state_management/url/errors.ts", "deprecated": false, diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index eab1359a98acf..8dd98ee82cd67 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index fb531b8ebd0a8..edea19a333163 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; -Contact [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) for questions regarding this plugin. +Contact [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index 4e7a2c648a02e..802b5f80050e6 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -6585,7 +6585,7 @@ "signature": [ "((props: ", "VisualizationConfigProps", - ") => boolean) | undefined" + ") => Record<\"data\" | \"appearance\", boolean>) | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -6619,8 +6619,8 @@ "description": [], "signature": [ "((domElement: Element, props: ", - "VisualizationLayerSettingsProps", - ") => void | ((cleanupElement: Element) => void)) | undefined" + "VisualizationConfigProps", + " & { setState(newState: T | ((currState: T) => T)): void; panelRef: React.MutableRefObject; } & { section: \"data\" | \"appearance\"; }) => void | ((cleanupElement: Element) => void)) | undefined" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -6649,8 +6649,8 @@ "label": "props", "description": [], "signature": [ - "VisualizationLayerSettingsProps", - "" + "VisualizationConfigProps", + " & { setState(newState: T | ((currState: T) => T)): void; panelRef: React.MutableRefObject; } & { section: \"data\" | \"appearance\"; }" ], "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, @@ -7602,6 +7602,40 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getReportingLayout", + "type": "Function", + "tags": [], + "label": "getReportingLayout", + "description": [ + "\nA visualization can return custom dimensions for the reporting tool" + ], + "signature": [ + "((state: T) => { height: number; width: number; }) | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Visualization.getReportingLayout.$1", + "type": "Uncategorized", + "tags": [], + "label": "state", + "description": [], + "signature": [ + "T" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 7ce2b18535b66..67003127c5a66 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 608 | 0 | 513 | 53 | +| 610 | 0 | 514 | 52 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index d5ced7548224d..523a22146062a 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 4ac20c9c09658..f8f04c8ea317a 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.devdocs.json b/api_docs/licensing.devdocs.json index f83a3f0c03d25..7c6a18a6c2d30 100644 --- a/api_docs/licensing.devdocs.json +++ b/api_docs/licensing.devdocs.json @@ -833,6 +833,10 @@ { "plugin": "watcher", "path": "x-pack/plugins/watcher/public/plugin.ts" + }, + { + "plugin": "profiling", + "path": "x-pack/plugins/profiling/public/components/contexts/license/license_context.tsx" } ] }, diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 1a3861056e1a2..5d218b97477d5 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 2ab9399953019..3fa35bd4fee29 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 1587215a43e6f..869b213005de0 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.devdocs.json b/api_docs/maps.devdocs.json index 327c896765d86..5b7625d514738 100644 --- a/api_docs/maps.devdocs.json +++ b/api_docs/maps.devdocs.json @@ -3976,7 +3976,7 @@ "signature": [ "\"__kbn__feature_id__\"" ], - "path": "x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts", + "path": "x-pack/plugins/maps/common/constants.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -4101,7 +4101,7 @@ "signature": [ "\"MAPS_APP_LOCATOR\"" ], - "path": "x-pack/plugins/maps/public/locators.ts", + "path": "x-pack/plugins/maps/public/locators/map_locator/locator_definition.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 18773e302454b..c61e46893d581 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 357a7ff1ce584..880d15fc8021b 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 912535168923f..3f870e60e045f 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index f3fa2c68ca7b9..52cfc7d67cff5 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 5dc6cf38ce3a7..0dd111f72ef78 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 1e859449e5ccf..3121830a9f2a7 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index fff2dfb44dcf9..37887d4843b4c 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index a861b6224e6d6..f9bde1b340e02 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 09a129e9ca3dc..7a97a26b790af 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -4242,6 +4242,24 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsSetup.observabilityShared", + "type": "Object", + "tags": [], + "label": "observabilityShared", + "description": [], + "signature": [ + "{ navigation: { registerSections: (sections$: ", + "Observable", + "<", + "NavigationSection", + "[]>) => void; }; }" + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "observability", "id": "def-public.ObservabilityPublicPluginsSetup.share", @@ -4389,7 +4407,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - "[]; has: (id: string) => boolean; register: (objectType: ", + "[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -4397,7 +4415,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - ") => void; }" + ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/observability/public/plugin.ts", "deprecated": false, @@ -4526,6 +4544,26 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsStart.exploratoryView", + "type": "Object", + "tags": [], + "label": "exploratoryView", + "description": [], + "signature": [ + { + "pluginId": "exploratoryView", + "scope": "public", + "docId": "kibExploratoryViewPluginApi", + "section": "def-public.ExploratoryViewPublicPluginsStart", + "text": "ExploratoryViewPublicPluginsStart" + } + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "observability", "id": "def-public.ObservabilityPublicPluginsStart.guidedOnboarding", @@ -4586,6 +4624,40 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsStart.observabilityShared", + "type": "Object", + "tags": [], + "label": "observabilityShared", + "description": [], + "signature": [ + "{ navigation: { PageTemplate: (pageTemplateProps: ", + "WrappedPageTemplateProps", + ") => JSX.Element; }; updateGlobalNavigation: ({ capabilities, deepLinks, updater$, }: { capabilities: Readonly<{ [x: string]: Readonly<{ [x: string]: boolean | Readonly<{ [x: string]: boolean; }>; }>; navLinks: Readonly<{ [x: string]: boolean; }>; management: Readonly<{ [x: string]: Readonly<{ [x: string]: boolean; }>; }>; catalogue: Readonly<{ [x: string]: boolean; }>; }>; deepLinks: ", + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.AppDeepLink", + "text": "AppDeepLink" + }, + "[]; updater$: ", + "Subject", + "<", + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.AppUpdater", + "text": "AppUpdater" + }, + ">; }) => void; }" + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "observability", "id": "def-public.ObservabilityPublicPluginsStart.ruleTypeRegistry", @@ -4626,7 +4698,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">[]; has: (id: string) => boolean; register: (objectType: ", + ">[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -4642,7 +4714,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/observability/public/plugin.ts", "deprecated": false, @@ -6845,17 +6917,7 @@ "section": "def-public.ObservabilityRuleTypeFormatter", "text": "ObservabilityRuleTypeFormatter" }, - " | undefined; list: () => string[]; }; navigation: { registerSections: (sections$: ", - "Observable", - "<", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.NavigationSection", - "text": "NavigationSection" - }, - "[]>) => void; }; useRulesLink: (options?: ", + " | undefined; list: () => string[]; }; useRulesLink: (options?: ", "Options", ") => ", "LinkProps", @@ -6891,37 +6953,7 @@ "section": "def-public.ObservabilityRuleTypeFormatter", "text": "ObservabilityRuleTypeFormatter" }, - " | undefined; list: () => string[]; }; navigation: { PageTemplate: (pageTemplateProps: ", - "WrappedPageTemplateProps", - ") => JSX.Element; }; createExploratoryViewUrl: ({ reportType, allSeries }: { reportType: ", - "ReportViewType", - "; allSeries: ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.AllSeries", - "text": "AllSeries" - }, - "; }, baseHref?: string, appId?: string) => string; getAppDataView: (appId: ", - "AppDataType", - ", indexPattern?: string | undefined) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - " | null | undefined>; ExploratoryViewEmbeddable: (props: ", - { - "pluginId": "observability", - "scope": "public", - "docId": "kibObservabilityPluginApi", - "section": "def-public.ExploratoryEmbeddableProps", - "text": "ExploratoryEmbeddableProps" - }, - ") => JSX.Element | null; useRulesLink: (options?: ", + " | undefined; list: () => string[]; }; useRulesLink: (options?: ", "Options", ") => ", "LinkProps", @@ -10272,7 +10304,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { sloResources: { [x: string]: string; \"slo-observability.sli\": string; \"slo-observability.sli-mappings\": string; \"slo-observability.sli-settings\": string; }; sloSavedObject: string | ", + ", { sloResources: { [x: string]: string; \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; }; sloSavedObject: string | ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -10314,7 +10346,7 @@ "PublicLicenseJSON", "; userPrivileges: ", "SecurityGetUserPrivilegesResponse", - "; sloResources: { [x: string]: string; \"slo-observability.sli\": string; \"slo-observability.sli-mappings\": string; \"slo-observability.sli-settings\": string; }; }, ", + "; sloResources: { [x: string]: string; \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; }; }, ", { "pluginId": "observability", "scope": "server", @@ -11074,7 +11106,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { sloResources: { [x: string]: string; \"slo-observability.sli\": string; \"slo-observability.sli-mappings\": string; \"slo-observability.sli-settings\": string; }; sloSavedObject: string | ", + ", { sloResources: { [x: string]: string; \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; }; sloSavedObject: string | ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11116,7 +11148,7 @@ "PublicLicenseJSON", "; userPrivileges: ", "SecurityGetUserPrivilegesResponse", - "; sloResources: { [x: string]: string; \"slo-observability.sli\": string; \"slo-observability.sli-mappings\": string; \"slo-observability.sli-settings\": string; }; }, ", + "; sloResources: { [x: string]: string; \".slo-observability.sli\": string; \".slo-observability.sli-mappings\": string; \".slo-observability.sli-settings\": string; }; }, ", { "pluginId": "observability", "scope": "server", @@ -15054,6 +15086,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-common.syntheticsSettingsLocatorID", + "type": "string", + "tags": [], + "label": "syntheticsSettingsLocatorID", + "description": [], + "signature": [ + "\"SYNTHETICS_SETTINGS\"" + ], + "path": "x-pack/plugins/observability/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.TimeFormatter", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index a705788d2d4f0..3e7b223414a79 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 652 | 44 | 646 | 34 | +| 656 | 44 | 650 | 33 | ## Client diff --git a/api_docs/observability_shared.devdocs.json b/api_docs/observability_shared.devdocs.json index cc911b709261d..703f3db5c4d04 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -154,7 +154,26 @@ "initialIsOpen": false } ], - "functions": [], + "functions": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.useObservabilityTourContext", + "type": "Function", + "tags": [], + "label": "useObservabilityTourContext", + "description": [], + "signature": [ + "() => ", + "ObservabilityTourContextValue" + ], + "path": "x-pack/plugins/observability_shared/public/components/tour/tour.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [ { "parentPluginId": "observabilityShared", @@ -376,6 +395,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.observabilityAppId", + "type": "string", + "tags": [], + "label": "observabilityAppId", + "description": [], + "signature": [ + "\"observability-overview\"" + ], + "path": "x-pack/plugins/observability_shared/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.observabilityFeatureId", diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index fde074cfefd67..4056665de1ed6 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; @@ -21,10 +21,13 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 30 | 0 | 30 | 4 | +| 32 | 0 | 32 | 5 | ## Client +### Functions + + ### Classes diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 0aa053144ee22..4663fb00ad3f8 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index be1d891a6b5bb..9c0fa8d2d15ae 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 589 | 485 | 38 | +| 595 | 491 | 37 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 68706 | 525 | 59352 | 1328 | +| 68995 | 526 | 59590 | 1337 | ## Plugin Directory @@ -29,14 +29,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] |--------------|----------------|-----------|--------------|----------|---------------|--------| | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 259 | 8 | 254 | 26 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 12 | 0 | 1 | 2 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 567 | 1 | 546 | 41 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 37 | 0 | 22 | 0 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 601 | 1 | 580 | 42 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 43 | 0 | 43 | 109 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | Asset manager plugin for entity assets (inventory, topology, etc) | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 91 | 1 | 75 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 96 | 0 | 79 | 31 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 96 | 0 | 79 | 32 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 271 | 16 | 256 | 10 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 41 | 0 | 11 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Chat available on Elastic Cloud deployments for quicker assistance. | 1 | 0 | 0 | 0 | @@ -48,12 +48,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | cloudLinks | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 13 | 0 | 13 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 143 | 0 | 124 | 6 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 143 | 0 | 124 | 7 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 273 | 0 | 269 | 10 | | crossClusterReplication | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | customBranding | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Enables customization of Kibana | 0 | 0 | 0 | 0 | | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 271 | 0 | 252 | 1 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 188 | 0 | 179 | 13 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 190 | 0 | 181 | 14 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3271 | 119 | 2577 | 27 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 16 | 0 | 7 | 0 | @@ -65,14 +65,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 97 | 0 | 78 | 7 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 544 | 11 | 441 | 4 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 545 | 11 | 442 | 4 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 51 | 0 | 44 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 7 | 0 | 7 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 114 | 3 | 110 | 5 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The Event Annotation service contains expressions for event annotations | 172 | 30 | 172 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 116 | 0 | 116 | 11 | -| | [@elastic/uptime](https://github.com/orgs/elastic/teams/uptime) | - | 352 | 4 | 349 | 22 | +| | [@elastic/uptime](https://github.com/orgs/elastic/teams/uptime) | - | 351 | 4 | 348 | 22 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 17 | 0 | 15 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 59 | 0 | 59 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Heatmap plugin adds a `heatmap` renderer and function to the expression plugin. The renderer will display the `heatmap` chart. | 112 | 14 | 108 | 2 | @@ -92,7 +92,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 62 | 0 | 62 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 215 | 0 | 10 | 5 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 1 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1098 | 3 | 993 | 27 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1101 | 3 | 996 | 27 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -113,8 +113,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 185 | 1 | 153 | 5 | | kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 609 | 3 | 416 | 9 | -| | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 3 | 0 | 3 | 1 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 608 | 0 | 513 | 53 | +| | [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) | - | 3 | 0 | 3 | 1 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 610 | 0 | 514 | 52 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -129,8 +129,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 652 | 44 | 646 | 34 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 30 | 0 | 30 | 4 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 656 | 44 | 650 | 33 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 32 | 0 | 32 | 5 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 6 | | painlessLab | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 207 | 8 | 151 | 12 | @@ -138,7 +138,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Reporting Services enables applications to feature reports that the user can automate with Watcher and download later. | 36 | 0 | 16 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 21 | 0 | 21 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 260 | 0 | 231 | 14 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 263 | 0 | 234 | 14 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 24 | 0 | 19 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 127 | 2 | 116 | 4 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 44 | 0 | 44 | 0 | @@ -147,11 +147,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 100 | 0 | 52 | 1 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the definition and helper methods around saved searches, used by discover and visualizations. | 55 | 0 | 55 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 0 | 13 | 0 | -| | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 4 | +| | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 5 | | searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 280 | 0 | 94 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 117 | 0 | 76 | 27 | -| | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 7 | 0 | 7 | 1 | +| | [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) | - | 7 | 0 | 7 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 22 | 1 | 22 | 1 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 253 | 0 | 65 | 0 | @@ -167,7 +167,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 257 | 1 | 214 | 20 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 536 | 10 | 507 | 49 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 540 | 10 | 511 | 49 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 134 | 2 | 92 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 276 | 0 | 250 | 7 | @@ -348,7 +348,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 25 | 1 | 24 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 109 | 1 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 341 | 1 | 5 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 347 | 1 | 5 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 77 | 0 | 55 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 72 | 0 | 52 | 8 | @@ -361,7 +361,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 120 | 0 | 85 | 46 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 493 | 1 | 99 | 4 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 494 | 1 | 99 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 69 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 14 | 0 | 14 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 30 | 0 | 6 | 0 | @@ -381,7 +381,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 30 | 1 | 18 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 11 | 1 | 11 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 24 | 0 | 3 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 25 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 1 | 16 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 18 | 1 | 17 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 6 | 0 | @@ -413,6 +413,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 0 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 29 | 0 | 29 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 0 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | | | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | - | 52 | 0 | 50 | 3 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 33 | 3 | 24 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | @@ -433,12 +435,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 8 | 0 | 8 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 1 | 1 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 534 | 1 | 1 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 91 | 2 | 61 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 93 | 2 | 61 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 52 | 0 | 4 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 2 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 2 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 3 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 8 | 2 | 8 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 10 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 0 | 0 | @@ -459,11 +462,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 40 | 0 | 38 | 5 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 13 | 0 | 9 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 6 | 1 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 73 | 0 | 65 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 13 | 2 | 8 | 0 | | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 102 | 0 | 99 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 30 | 0 | 30 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 2 | 0 | 0 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 56 | 1 | 41 | 1 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 95 | 0 | 72 | 7 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 341 | 1 | 337 | 32 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 68 | 0 | 62 | 1 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 104 | 0 | 93 | 1 | @@ -476,7 +482,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 67 | 0 | 64 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 35 | 0 | 23 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 60 | 0 | 47 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 206 | 10 | 159 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 210 | 10 | 163 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 26 | 0 | 23 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 120 | 0 | 116 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 31 | 2 | 29 | 0 | @@ -523,7 +529,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 3 | 0 | | | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 78 | 0 | 78 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 20 | 0 | 12 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 97 | 1 | 64 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 100 | 2 | 64 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 4 | 0 | 2 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 41 | 2 | 21 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 15feefbbfa0a8..63d78b82deb52 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 0b340911d3e28..3c12612495706 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 7ae92b7ce6a07..a33d94e833ade 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index bd6811e2df43c..f38277af4ee88 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 7bf79bc3030b2..90c95d189b35b 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index 6b966ac060602..a7c14ef74b66d 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -314,7 +314,7 @@ "description": [], "signature": [ "({ caseId, alerts }: ", - "RemoveAlertsFromCaseOptions", + "RemoveCaseIdFromAlertsOptions", ") => Promise" ], "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", @@ -329,7 +329,7 @@ "label": "{ caseId, alerts }", "description": [], "signature": [ - "RemoveAlertsFromCaseOptions" + "RemoveCaseIdFromAlertsOptions" ], "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", "deprecated": false, @@ -339,6 +339,50 @@ ], "returnComment": [] }, + { + "parentPluginId": "ruleRegistry", + "id": "def-server.AlertsClient.removeCaseIdsFromAllAlerts", + "type": "Function", + "tags": [], + "label": "removeCaseIdsFromAllAlerts", + "description": [], + "signature": [ + "({ caseIds }: { caseIds: string[]; }) => Promise" + ], + "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "ruleRegistry", + "id": "def-server.AlertsClient.removeCaseIdsFromAllAlerts.$1", + "type": "Object", + "tags": [], + "label": "{ caseIds }", + "description": [], + "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "ruleRegistry", + "id": "def-server.AlertsClient.removeCaseIdsFromAllAlerts.$1.caseIds", + "type": "Array", + "tags": [], + "label": "caseIds", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, { "parentPluginId": "ruleRegistry", "id": "def-server.AlertsClient.find", diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index b3c1c61d08fd8..03e511b5dd320 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 260 | 0 | 231 | 14 | +| 263 | 0 | 234 | 14 | ## Server diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 618efcf510834..abc334737e1d2 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 86d6ab1cc2223..1f7640d60a3e5 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.devdocs.json b/api_docs/saved_objects_finder.devdocs.json index 08991612ff496..9ae6255f9aefe 100644 --- a/api_docs/saved_objects_finder.devdocs.json +++ b/api_docs/saved_objects_finder.devdocs.json @@ -789,7 +789,7 @@ "signature": [ "\"savedObjects:listingLimit\"" ], - "path": "src/plugins/saved_objects_finder/common/index.ts", + "path": "packages/kbn-saved-objects-settings/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -804,7 +804,7 @@ "signature": [ "\"savedObjects:perPage\"" ], - "path": "src/plugins/saved_objects_finder/common/index.ts", + "path": "packages/kbn-saved-objects-settings/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 8cc4fee5ed1bd..9f9007d746036 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 2aedecd401080..cafcf387064c8 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index f8a00e23f3799..73795b54fde91 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index f2b148e54da5c..8fe115ad61b6b 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 6af67096f96c0..b20decae874f6 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 5e1406a7a5ce7..5d6890995a537 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index eac55db77d8dc..1a81510cb27e3 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/tea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 27 | 0 | 8 | 4 | +| 27 | 0 | 8 | 5 | ## Client diff --git a/api_docs/security.mdx b/api_docs/security.mdx index d01e0e2721699..dad65436a8394 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index 3c468e74d4b85..7ceeedb3fb56d 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -95,7 +95,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly policyResponseInFleetEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly endpointRbacEnabled: boolean; readonly endpointRbacV1Enabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionGetFileEnabled: boolean; readonly responseActionExecuteEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly securityFlyoutEnabled: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly disableIsolationUIPendingStatuses: boolean; readonly pendingActionResponsesWithAck: boolean; readonly policyListEnabled: boolean; readonly policyResponseInFleetEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly responseActionsConsoleEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly endpointRbacEnabled: boolean; readonly endpointRbacV1Enabled: boolean; readonly alertDetailsPageEnabled: boolean; readonly responseActionGetFileEnabled: boolean; readonly responseActionExecuteEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly securityFlyoutEnabled: boolean; readonly riskyHostsEnabled: boolean; readonly riskyUsersEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly newUserDetailsFlyout: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 9b81e1bacacdb..8ad48aa527f9a 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index bfe94812f71cd..a9735544749f3 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; -Contact [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) for questions regarding this plugin. +Contact [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 1a5c6a9b4d3f8..b747319f1e8dc 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 0105176a92698..6db34a3fb6340 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 3e4c951e24c5c..a0abb5235fc22 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index f825a6b7d6677..54ceca2baa1ac 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 2707ff15af58e..f253101036027 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 76f5b825373c7..fa4754530cfe1 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 46c4339f69620..1129c23641775 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index d78a5806a3cbb..2bd33bd8db740 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 2d030a3b46390..3536a151b1b87 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 34c1bad6f7a97..ffa3e18a9de78 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 2ab455b675f53..5dc2189325209 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index b4d1faa32da91..f139696fae67d 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index f0fc798b2bbaf..3a23c501e9939 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index e747db4d48838..957f8e44345a9 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -3593,6 +3593,63 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.NotifyWhenSelectOptions", + "type": "Interface", + "tags": [], + "label": "NotifyWhenSelectOptions", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.NotifyWhenSelectOptions.isSummaryOption", + "type": "CompoundType", + "tags": [], + "label": "isSummaryOption", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.NotifyWhenSelectOptions.isForEachAlertOption", + "type": "CompoundType", + "tags": [], + "label": "isForEachAlertOption", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.NotifyWhenSelectOptions.value", + "type": "Object", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "EuiSuperSelectOption", + "<\"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\">" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-public.RuleAction", @@ -3679,7 +3736,14 @@ "label": "frequency", "description": [], "signature": [ - "{ summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined" + { + "pluginId": "alerting", + "scope": "common", + "docId": "kibAlertingPluginApi", + "section": "def-common.RuleActionFrequency", + "text": "RuleActionFrequency" + }, + " | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -3790,7 +3854,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">[]; has: (id: string) => boolean; register: (objectType: ", + ">[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -3806,7 +3870,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -3836,7 +3900,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - "[]; has: (id: string) => boolean; register: (objectType: ", + "[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -3844,7 +3908,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - ") => void; }" + ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -5151,7 +5215,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - "[]; has: (id: string) => boolean; register: (objectType: ", + "[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -5159,7 +5223,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - ") => void; }" + ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", "deprecated": false, @@ -5205,7 +5269,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">[]; has: (id: string) => boolean; register: (objectType: ", + ">[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -5221,7 +5285,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", "deprecated": false, @@ -5239,9 +5303,9 @@ "AlertsTableConfigurationRegistry", "; list: () => ", "AlertsTableConfigurationRegistry", - "[]; has: (id: string) => boolean; register: (objectType: ", + "[]; register: (objectType: ", "AlertsTableConfigurationRegistry", - ") => void; }" + ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", "deprecated": false, @@ -5505,7 +5569,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - "[]; has: (id: string) => boolean; register: (objectType: ", + "[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -5513,7 +5577,7 @@ "section": "def-public.ActionTypeModel", "text": "ActionTypeModel" }, - ") => void; }" + ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -5604,9 +5668,9 @@ "AlertsTableConfigurationRegistry", "; list: () => ", "AlertsTableConfigurationRegistry", - "[]; has: (id: string) => boolean; register: (objectType: ", + "[]; register: (objectType: ", "AlertsTableConfigurationRegistry", - ") => void; }" + ") => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, @@ -5940,7 +6004,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">[]; has: (id: string) => boolean; register: (objectType: ", + ">[]; register: (objectType: ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -5956,7 +6020,7 @@ "section": "def-common.RuleTypeParams", "text": "RuleTypeParams" }, - ">) => void; }" + ">) => void; has: (id: string) => boolean; }" ], "path": "x-pack/plugins/triggers_actions_ui/public/types.ts", "deprecated": false, diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index fb2415841988a..8d2457325cc8f 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 536 | 10 | 507 | 49 | +| 540 | 10 | 511 | 49 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index d3a36a14c5d1e..d5cc70c49e339 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index e67c3ee123141..205ef372f2cf8 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.devdocs.json b/api_docs/unified_field_list.devdocs.json index 22e319c1b515a..c21a3c52f5e02 100644 --- a/api_docs/unified_field_list.devdocs.json +++ b/api_docs/unified_field_list.devdocs.json @@ -3560,7 +3560,7 @@ "section": "def-common.FieldFormatsRegistry", "text": "FieldFormatsRegistry" }, - ", \"register\" | \"init\"> & { deserialize: ", + ", \"init\" | \"register\"> & { deserialize: ", { "pluginId": "fieldFormats", "scope": "common", diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index d040691008637..b87c428bf726f 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.devdocs.json b/api_docs/unified_histogram.devdocs.json index f88fd244420f4..7550ec5192e72 100644 --- a/api_docs/unified_histogram.devdocs.json +++ b/api_docs/unified_histogram.devdocs.json @@ -745,7 +745,7 @@ "section": "def-common.FieldFormatsRegistry", "text": "FieldFormatsRegistry" }, - ", \"register\" | \"init\"> & { deserialize: ", + ", \"init\" | \"register\"> & { deserialize: ", { "pluginId": "fieldFormats", "scope": "common", diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index ee7f5dc732c1d..78d219b601783 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 286efba92cc75..decb9376b9b01 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index e5660f7584a3d..23dac2d6d06ae 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 969fabb8f9cca..a131918660baf 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 198c3a7e07fe4..5323eb431a1db 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 689f037d4b3b0..55579684c8294 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 55ec42563fdbb..19e4daeb84ec2 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 3f158507a60b3..5cbc1fabc9bdf 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index a2c2aa51116a4..ede12c188e2e3 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index fd7cad52ed4bd..d9697ae33c35d 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 9a8ffe1dae592..9177c2197fbb7 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 2caa4bc8b5e61..502100b2541ef 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index f8b2859fadec7..01ec3973ced2f 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 2a496a6a90480..2a75cae2ee36d 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 75a1368de094e..88c97e8638f13 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index f02182bc1a778..b3f1e332f5e8a 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index a6f1d6bd12dd3..1d18208d4c2fd 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-04-13 +date: 2023-04-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/dev_docs/tutorials/versioning_http_apis.mdx b/dev_docs/tutorials/versioning_http_apis.mdx index c8a3625fb4977..81bfed4f4dc4e 100644 --- a/dev_docs/tutorials/versioning_http_apis.mdx +++ b/dev_docs/tutorials/versioning_http_apis.mdx @@ -41,7 +41,7 @@ router.get( ); ``` -#### Why is this problemetic for versioning? +#### Why is this problematic for versioning? Whenever we perform a data migration the body of this endpoint will change for all clients. This prevents us from being able to maintain past interfaces and gracefully introduce new ones. @@ -119,7 +119,7 @@ router.post( } ); ``` -#### Why is this problemetic for versioning? +#### Why is this problematic for versioning? This HTTP API currently accepts all numbers and strings as input which allows for unexpected inputs like negative numbers or non-URL friendly characters. This may break future migrations or integrations that assume your data will always be within certain parameters. @@ -141,7 +141,7 @@ This HTTP API currently accepts all numbers and strings as input which allows fo Adding this validation we negate the risk of unexpected values. It is not necessary to use `@kbn/config-schema`, as long as your validation mechanism provides finer grained controls than "number" or "string". -In summary: think about the acceptable paramaters for every input your HTTP API expects. +In summary: think about the acceptable parameters for every input your HTTP API expects. ### 3. Keep interfaces as "narrow" as possible @@ -170,7 +170,7 @@ router.get( The above code follows guidelines from steps 1 and 2, but it allows clients to specify ANY string by which to sort. This is a far "wider" API than we need for this endpoint. -#### Why is this problemetic for versioning? +#### Why is this problematic for versioning? Without telemetry it is impossible to know what values clients might be passing through — and what type of sort behaviour they are expecting. @@ -207,9 +207,112 @@ router.get( The changes are: -1. New input validation accepts a known set of values. This makes our HTTP API far _narrower_ and specific to our use case. It does not matter that our `sortSchema` has the same values as our persistence schema, what matters is that we created a **translation layer** between our HTTP API and our internal schema. This faclitates easily versioning this endpoint. +1. New input validation accepts a known set of values. This makes our HTTP API far _narrower_ and specific to our use case. It does not matter that our `sortSchema` has the same values as our persistence schema, what matters is that we created a **translation layer** between our HTTP API and our internal schema. This facilitates easily versioning this endpoint. 2. **Bonus point**: we use the `escapeKuery` utility to defend against KQL injection attacks. -### 4. Use the versioned API spec +### 4. Adhere to the HTTP versioning specification + +#### Choosing the right version + +##### Public endpoints +Public endpoints include any endpoint that is intended for users to directly integrate with via HTTP. + +Choose a date string in the format `YYYY-MM-DD`. This date should be the date that a (group) of APIs was made available. + +##### Internal endpoints +Internal endpoints are all non-public endpoints (see definition above). + +If you need to maintain backwards-compatibility for an internal endpoint use a single, larger-than-zero number. Ex. `1`. + + +#### Use the versioned router + +Core exposes a versioned router that ensures your endpoint's behaviour and formatting all conforms to the versioning specification. + +```typescript + router.versioned. + .post({ + access: 'public', // This endpoint is intended for a public audience + path: '/api/my-app/foo/{id?}', + options: { timeout: { payload: 60000 } }, + }) + .addVersion( + { + version: '2023-01-01', // The public version of this API + validate: { + request: { + query: schema.object({ + name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })), + }), + params: schema.object({ + id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })), + }), + body: schema.object({ foo: schema.string() }), + }, + response: { + 200: { // In development environments, this validation will run against 200 responses + body: schema.object({ foo: schema.string() }), + }, + }, + }, + }, + async (ctx, req, res) => { + await ctx.fooService.create(req.body.foo, req.params.id, req.query.name); + return res.ok({ body: { foo: req.body.foo } }); + } + ) + // BREAKING CHANGE: { foo: string } => { fooString: string } in response body + .addVersion( + { + version: '2023-02-01', + validate: { + request: { + query: schema.object({ + name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })), + }), + params: schema.object({ + id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })), + }), + body: schema.object({ fooString: schema.string() }), + }, + response: { + 200: { + body: schema.object({ fooName: schema.string() }), + }, + }, + }, + }, + async (ctx, req, res) => { + await ctx.fooService.create(req.body.fooString, req.params.id, req.query.name); + return res.ok({ body: { fooName: req.body.fooString } }); + } + ) + // BREAKING CHANGES: Enforce min/max length on fooString + .addVersion( + { + version: '2023-03-01', + validate: { + request: { + query: schema.object({ + name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })), + }), + params: schema.object({ + id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })), + }), + body: schema.object({ fooString: schema.string({ minLength: 0, maxLength: 1000 }) }), + }, + response: { + 200: { + body: schema.object({ fooName: schema.string() }), + }, + }, + }, + }, + async (ctx, req, res) => { + await ctx.fooService.create(req.body.fooString, req.params.id, req.query.name); + return res.ok({ body: { fooName: req.body.fooString } }); + } +``` -_Under construction, check back here soon!_ \ No newline at end of file +#### Additional reading +For a more details on the versioning specification see [this document](https://docs.google.com/document/d/1YpF6hXIHZaHvwNaQAxWFzexUF1nbqACTtH2IfDu0ldA/edit?usp=sharing). \ No newline at end of file diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 572452cec368c..1755f65193276 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -431,7 +431,7 @@ The plugin exposes the static DefaultEditorController class to consume. |{kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm] -|undefined +|This plugin provides access to App Monitoring features provided by Elastic. It allows you to monitor your software services and applications in real-time; visualize detailed performance information on your services, identify and analyze errors, and monitor host-level and APM agent-specific metrics like JVM and Go runtime metrics. |{kib-repo}blob/{branch}/x-pack/plugins/asset_manager/README.md[assetManager] diff --git a/docs/index-custom-title-page.html b/docs/index-custom-title-page.html index 87405c783a0a7..5d768eab3484e 100644 --- a/docs/index-custom-title-page.html +++ b/docs/index-custom-title-page.html @@ -45,6 +45,13 @@ -moz-columns: 2; } } + #guide h3.gtk { + margin-top: 0; +} + +.mb-4, .my-4 { + margin-bottom: 0!important; +}
@@ -63,53 +70,17 @@

Bring your data to life

- How-to videos -

+ What's new + Release notes + Install +

-

Explore by Elastic solution

- - - -

Get to know Kibana

+

Get to know Kibana

@@ -134,6 +105,28 @@

+
+
+

+ + Install and upgrade +

+
+ +
+
-
+

Explore by Elastic solution

+ +

View all Elastic docs

diff --git a/docs/management/connectors/action-types/email.asciidoc b/docs/management/connectors/action-types/email.asciidoc index 6b2684c1a15b4..65bc85a7d7133 100644 --- a/docs/management/connectors/action-types/email.asciidoc +++ b/docs/management/connectors/action-types/email.asciidoc @@ -234,14 +234,12 @@ is considered `false`. Typically, `port: 465` uses `secure: true`, and [float] [[elasticcloud]] ==== Sending email from Elastic Cloud - -IMPORTANT: To receive notifications, the email addresses must be added to an - link:{cloud}/ec-watcher.html#ec-watcher-allowlist[allowlist] in the - Elasticsearch Service Console. Use the preconfigured email connector (`Elastic-Cloud-SMTP`) to send emails from Elastic Cloud. +NOTE: For more information on the preconfigured email connector, see link:{cloud}/ec-watcher.html#ec-cloud-email-service-limits[Elastic Cloud email service limits]. + [float] [[gmail]] ==== Sending email from Gmail diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index a2ac56be64ebf..7fa35c0df57d7 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -130,6 +130,9 @@ A list of allowed email domains which can be used with the email connector. When WARNING: This feature is available in {kib} 7.17.4 and 8.3.0 onwards but is not supported in {kib} 8.0, 8.1 or 8.2. As such, this setting should be removed before upgrading from 7.17 to 8.0, 8.1 or 8.2. It is possible to configure the settings in 7.17.4 and then upgrade to 8.3.0 directly. +`xpack.actions.enableFooterInEmail` {ess-icon}:: +A boolean value indicating that a footer with a relevant link should be added to emails sent as alerting actions. Default: true. + `xpack.actions.enabledActionTypes` {ess-icon}:: A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.email`, `.index`, `.jira`, `.opsgenie`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, .`servicenow-itom`, `.servicenow-sir`, `.slack`, `.swimlane`, `.teams`, `.tines`, `.torq`, `.xmatters`, and `.webhook`. An empty list `[]` will disable all action types. + diff --git a/examples/portable_dashboards_example/public/app.tsx b/examples/portable_dashboards_example/public/app.tsx index 052c74bd9b61d..a99ecc6274795 100644 --- a/examples/portable_dashboards_example/public/app.tsx +++ b/examples/portable_dashboards_example/public/app.tsx @@ -6,11 +6,15 @@ * Side Public License, v 1. */ -import React from 'react'; import ReactDOM from 'react-dom'; +import React, { useMemo } from 'react'; +import { useAsync } from 'react-use/lib'; +import { Router, Redirect, Switch } from 'react-router-dom'; -import { EuiSpacer } from '@elastic/eui'; +import { Route } from '@kbn/shared-ux-router'; import { AppMountParameters } from '@kbn/core/public'; +import { EuiButton, EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { DashboardListingTable } from '@kbn/dashboard-plugin/public'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { DualReduxExample } from './dual_redux_example'; @@ -20,17 +24,70 @@ import { StaticByReferenceExample } from './static_by_reference_example'; import { DynamicByReferenceExample } from './dynamically_add_panels_example'; import { DashboardWithControlsExample } from './dashboard_with_controls_example'; +const DASHBOARD_DEMO_PATH = '/dashboardDemo'; +const DASHBOARD_LIST_PATH = '/listingDemo'; + export const renderApp = async ( { data, dashboard }: PortableDashboardsExampleStartDeps, - { element }: AppMountParameters + { element, history }: AppMountParameters ) => { - const dataViews = await data.dataViews.find('kibana_sample_data_logs'); - const findDashboardsService = await dashboard.findDashboardsService(); - const logsSampleDashboardId = (await findDashboardsService?.findByTitle('[Logs] Web Traffic')) - ?.id; + ReactDOM.render( + , + element + ); + return () => ReactDOM.unmountComponentAtNode(element); +}; + +const PortableDashboardsDemos = ({ + data, + dashboard, + history, +}: { + data: PortableDashboardsExampleStartDeps['data']; + dashboard: PortableDashboardsExampleStartDeps['dashboard']; + history: AppMountParameters['history']; +}) => { + return ( + + + + + + + + + + + + + + ); +}; - const examples = - dataViews.length > 0 ? ( +const DashboardsDemo = ({ + data, + history, + dashboard, +}: { + history: AppMountParameters['history']; + data: PortableDashboardsExampleStartDeps['data']; + dashboard: PortableDashboardsExampleStartDeps['dashboard']; +}) => { + const { loading, value: dataviewResults } = useAsync(async () => { + const dataViews = await data.dataViews.find('kibana_sample_data_logs'); + const findDashboardsService = await dashboard.findDashboardsService(); + const logsSampleDashboardId = (await findDashboardsService?.findByTitle('[Logs] Web Traffic')) + ?.id; + return { dataViews, logsSampleDashboardId }; + }, []); + + const usageDemos = useMemo(() => { + if (loading || !dataviewResults) return null; + if (dataviewResults?.dataViews.length === 0) { +
{'Install web logs sample data to run the embeddable dashboard examples.'}
; + } + const { dataViews, logsSampleDashboardId } = dataviewResults; + return ( <> @@ -42,16 +99,37 @@ export const renderApp = async ( - ) : ( -
{'Install web logs sample data to run the embeddable dashboard examples.'}
); + }, [dataviewResults, loading]); - ReactDOM.render( + return ( - - {examples} - , - element + + + history.push(DASHBOARD_LIST_PATH)}> + View portable dashboard listing page + + + {usageDemos} + + + ); +}; + +const PortableDashboardListingDemo = ({ history }: { history: AppMountParameters['history'] }) => { + return ( + + alert(`Here's where I would redirect you to ${dashboardId ?? 'a new Dashboard'}`) + } + getDashboardUrl={() => 'https://www.elastic.co/'} + > + history.push(DASHBOARD_DEMO_PATH)}> + Go back to usage demos + + + + + ); - return () => ReactDOM.unmountComponentAtNode(element); }; diff --git a/examples/portable_dashboards_example/tsconfig.json b/examples/portable_dashboards_example/tsconfig.json index 3b96a17c085bf..338fd93863aa5 100644 --- a/examples/portable_dashboards_example/tsconfig.json +++ b/examples/portable_dashboards_example/tsconfig.json @@ -25,6 +25,7 @@ "@kbn/embeddable-examples-plugin", "@kbn/shared-ux-page-kibana-template", "@kbn/shared-ux-utility", - "@kbn/controls-plugin" + "@kbn/controls-plugin", + "@kbn/shared-ux-router" ] } diff --git a/fleet_packages.json b/fleet_packages.json index ae7bdca17b4c6..e78e1fbe6451f 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -46,6 +46,6 @@ }, { "name": "security_detection_engine", - "version": "8.7.1" + "version": "8.7.2" } ] \ No newline at end of file diff --git a/package.json b/package.json index 2b2f4ff6326f1..d36fc1284122b 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.6.0-canary.3", "@elastic/ems-client": "8.4.0", - "@elastic/eui": "77.0.0", + "@elastic/eui": "77.1.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -531,6 +531,7 @@ "@kbn/saved-objects-hidden-type-plugin": "link:test/plugin_functional/plugins/saved_objects_hidden_type", "@kbn/saved-objects-management-plugin": "link:src/plugins/saved_objects_management", "@kbn/saved-objects-plugin": "link:src/plugins/saved_objects", + "@kbn/saved-objects-settings": "link:packages/kbn-saved-objects-settings", "@kbn/saved-objects-tagging-oss-plugin": "link:src/plugins/saved_objects_tagging_oss", "@kbn/saved-objects-tagging-plugin": "link:x-pack/plugins/saved_objects_tagging", "@kbn/saved-search-plugin": "link:src/plugins/saved_search", @@ -547,6 +548,7 @@ "@kbn/security-solution-storybook-config": "link:packages/security-solution/storybook/config", "@kbn/security-test-endpoints-plugin": "link:x-pack/test/security_functional/plugins/test_endpoints", "@kbn/securitysolution-autocomplete": "link:packages/kbn-securitysolution-autocomplete", + "@kbn/securitysolution-data-table": "link:x-pack/packages/kbn-securitysolution-data-table", "@kbn/securitysolution-ecs": "link:packages/kbn-securitysolution-ecs", "@kbn/securitysolution-es-utils": "link:packages/kbn-securitysolution-es-utils", "@kbn/securitysolution-exception-list-components": "link:packages/kbn-securitysolution-exception-list-components", @@ -768,7 +770,7 @@ "deepmerge": "^4.2.2", "del": "^6.1.0", "elastic-apm-http-client": "^11.0.1", - "elastic-apm-node": "^3.44.0", + "elastic-apm-node": "^3.44.1", "email-addresses": "^5.0.0", "execa": "^4.0.2", "expiry-js": "0.1.7", diff --git a/packages/analytics/shippers/fullstory/src/load_snippet.ts b/packages/analytics/shippers/fullstory/src/load_snippet.ts index 2c37eee6608e9..1cbcc96e201ef 100644 --- a/packages/analytics/shippers/fullstory/src/load_snippet.ts +++ b/packages/analytics/shippers/fullstory/src/load_snippet.ts @@ -36,7 +36,7 @@ export interface FullStorySnippetConfig { } export function loadSnippet({ - scriptUrl = 'edge.fullstory.com/s/fs.js', + scriptUrl = 'https://edge.fullstory.com/s/fs.js', fullStoryOrgId, host = 'fullstory.com', namespace = 'FS', diff --git a/packages/core/http/core-http-common/index.ts b/packages/core/http/core-http-common/index.ts index 8554801f90301..1a2c19b4ea6b2 100644 --- a/packages/core/http/core-http-common/index.ts +++ b/packages/core/http/core-http-common/index.ts @@ -9,4 +9,4 @@ export type { IExternalUrlPolicy } from './src/external_url_policy'; export type { ApiVersion } from './src/versioning'; -export { ELASTIC_HTTP_VERSION_HEADER } from './src/versioning'; +export { ELASTIC_HTTP_VERSION_HEADER } from './src/constants'; diff --git a/src/plugins/dashboard/public/dashboard_app/listing/index.ts b/packages/core/http/core-http-common/src/constants.ts similarity index 79% rename from src/plugins/dashboard/public/dashboard_app/listing/index.ts rename to packages/core/http/core-http-common/src/constants.ts index 5b3caaf4d391b..b231fdd745b36 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing/index.ts +++ b/packages/core/http/core-http-common/src/constants.ts @@ -6,4 +6,5 @@ * Side Public License, v 1. */ -export { DashboardListing } from './dashboard_listing'; +/** @internal */ +export const ELASTIC_HTTP_VERSION_HEADER = 'elastic-api-version' as const; diff --git a/packages/core/http/core-http-common/src/versioning.ts b/packages/core/http/core-http-common/src/versioning.ts index 188db476af1be..438b186f0922b 100644 --- a/packages/core/http/core-http-common/src/versioning.ts +++ b/packages/core/http/core-http-common/src/versioning.ts @@ -8,10 +8,15 @@ /** * A Kibana HTTP API version - * @note assumption that version will be monotonically increasing number where: version > 0. + * + * @note + * For public APIs: conforms to the Elastic API version specification APIs as a date string formatted as YYYY-MM-DD. + * Ex. 2021-01-01 -> 2022-02-02 + * + * @note + * For internal APIs: follow the convention of monotonic increasing integers. + * Ex. 1 -> 2 -> 3 + * * @experimental */ -export type ApiVersion = `${number}`; - -/** @internal */ -export const ELASTIC_HTTP_VERSION_HEADER = 'elastic-api-version' as const; +export type ApiVersion = string; diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts index d189b04d332a2..4b2519c6bc80f 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.ts @@ -119,6 +119,12 @@ function validOptions( return { ...options, body }; } +/** @internal */ +interface RouterOptions { + /** Whether we are running in development */ + isDev: boolean; +} + /** * @internal */ @@ -135,7 +141,8 @@ export class Router + private readonly enhanceWithContext: ContextEnhancer, + private readonly options: RouterOptions = { isDev: false } ) { const buildMethod = (method: Method) => @@ -209,7 +216,7 @@ export class Router = undefined; public get versioned(): VersionedRouter { if (this.versionedRouter === undefined) { - this.versionedRouter = CoreVersionedRouter.from({ router: this }); + this.versionedRouter = CoreVersionedRouter.from({ router: this, isDev: this.options.isDev }); } return this.versionedRouter; } diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts index 43af728b36a53..e95f4cf82a309 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts @@ -86,7 +86,7 @@ describe('Versioned route', () => { ); }); - it('only allows versions that are numbers greater than 0', () => { + it('only allows versions that are numbers greater than 0 for internal APIs', () => { const versionedRouter = CoreVersionedRouter.from({ router }); expect(() => versionedRouter @@ -109,6 +109,35 @@ describe('Versioned route', () => { ).toThrowError( `Invalid version number. Received "1.1", expected any finite, whole number greater than 0.` ); + expect(() => + versionedRouter + .get({ path: '/test/{id}', access: 'internal' }) + .addVersion({ version: '1', validate: false }, handlerFn) + ).not.toThrow(); + }); + + it('only allows versions date strings for public APIs', () => { + const versionedRouter = CoreVersionedRouter.from({ router }); + expect(() => + versionedRouter + .get({ path: '/test/{id}', access: 'public' }) + .addVersion({ version: '1-1-2020' as ApiVersion, validate: false }, handlerFn) + ).toThrowError(/Invalid version/); + expect(() => + versionedRouter + .get({ path: '/test/{id}', access: 'public' }) + .addVersion({ version: '', validate: false }, handlerFn) + ).toThrowError(/Invalid version/); + expect(() => + versionedRouter + .get({ path: '/test/{id}', access: 'public' }) + .addVersion({ version: 'abc', validate: false }, handlerFn) + ).toThrowError(/Invalid version/); + expect(() => + versionedRouter + .get({ path: '/test/{id}', access: 'public' }) + .addVersion({ version: '2020-02-02', validate: false }, handlerFn) + ).not.toThrow(); }); it('runs request and response validations', async () => { @@ -120,7 +149,7 @@ describe('Versioned route', () => { let validatedOutputBody = false; (router.post as jest.Mock).mockImplementation((opts: unknown, fn) => (handler = fn)); - const versionedRouter = CoreVersionedRouter.from({ router, validateResponses: true }); + const versionedRouter = CoreVersionedRouter.from({ router, isDev: true }); versionedRouter.post({ path: '/test/{id}', access: 'internal' }).addVersion( { version: '1', @@ -165,7 +194,7 @@ describe('Versioned route', () => { ); const kibanaResponse = await handler!( - {} as any, + { core: { env: { mode: { dev: true } } } } as any, createRequest({ version: '1', body: { foo: 1 }, @@ -181,66 +210,4 @@ describe('Versioned route', () => { expect(validatedQuery).toBe(true); expect(validatedOutputBody).toBe(true); }); - - it('returns the expected output for non-existent versions', async () => { - let handler: RequestHandler; - const versionedRouter = CoreVersionedRouter.from({ router }); - (router.post as jest.Mock).mockImplementation((opts: unknown, fn) => (handler = fn)); - versionedRouter.post({ access: 'internal', path: '/test/{id}' }); - - await expect( - handler!({} as any, createRequest({ version: '999' }), responseFactory) - ).resolves.toEqual( - expect.objectContaining({ - payload: - 'No version "999" available for [post] [/test/{id}]. Available versions are: ', - status: 406, - }) - ); - }); - - it('returns the expected output if no version was provided to versioned route', async () => { - let handler: RequestHandler; - const versionedRouter = CoreVersionedRouter.from({ router }); - (router.post as jest.Mock).mockImplementation((opts: unknown, fn) => (handler = fn)); - - versionedRouter - .post({ access: 'internal', path: '/test/{id}' }) - .addVersion({ validate: false, version: '1' }, handlerFn); - - await expect( - handler!({} as any, createRequest({ version: undefined }), responseFactory) - ).resolves.toEqual({ - options: {}, - payload: `Version expected at [post] [/test/{id}]. Please specify a version using the "${ELASTIC_HTTP_VERSION_HEADER}" header. Available versions are: [1]`, - status: 406, - }); - }); - it('returns the expected output for failed validation', async () => { - let handler: RequestHandler; - const versionedRouter = CoreVersionedRouter.from({ router }); - (router.post as jest.Mock).mockImplementation((opts: unknown, fn) => (handler = fn)); - - versionedRouter - .post({ access: 'internal', path: '/test/{id}' }) - .addVersion( - { validate: { request: { body: schema.object({ foo: schema.number() }) } }, version: '1' }, - handlerFn - ); - - await expect( - handler!( - {} as any, - createRequest({ - version: '1', - body: {}, - }), - responseFactory - ) - ).resolves.toEqual({ - options: {}, - payload: expect.any(String), - status: 400, - }); - }); }); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts index 4eafc5d54bde0..4d73b09563077 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts @@ -10,7 +10,6 @@ import { schema } from '@kbn/config-schema'; import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { RequestHandler, - IRouter, RequestHandlerContextBase, KibanaRequest, KibanaResponseFactory, @@ -18,12 +17,17 @@ import type { AddVersionOpts, VersionedRoute, VersionedRouteConfig, + IKibanaResponse, } from '@kbn/core-http-server'; import type { Mutable } from 'utility-types'; import type { Method } from './types'; +import type { CoreVersionedRouter } from './core_versioned_router'; import { validate } from './validate'; import { isValidRouteVersion } from './is_valid_route_version'; +import { injectResponseHeaders } from './inject_response_headers'; + +import { resolvers } from './handler_resolvers'; type Options = AddVersionOpts; @@ -48,25 +52,24 @@ export class CoreVersionedRoute implements VersionedRoute { method, path, options, - validateResponses = false, }: { - router: IRouter; + router: CoreVersionedRouter; method: Method; path: string; options: VersionedRouteConfig; - validateResponses?: boolean; }) { - return new CoreVersionedRoute(router, method, path, options, validateResponses); + return new CoreVersionedRoute(router, method, path, options); } + private isPublic: boolean; private constructor( - private readonly router: IRouter, + private readonly router: CoreVersionedRouter, public readonly method: Method, public readonly path: string, - public readonly options: VersionedRouteConfig, - private readonly validateResponses: boolean = false + public readonly options: VersionedRouteConfig ) { - this.router[this.method]( + this.isPublic = this.options.access === 'public'; + this.router.router[this.method]( { path: this.path, validate: passThroughValidation, @@ -76,6 +79,11 @@ export class CoreVersionedRoute implements VersionedRoute { ); } + /** This method assumes that one or more versions handlers are registered */ + private getDefaultVersion(): ApiVersion { + return resolvers[this.router.defaultHandlerResolutionStrategy]([...this.handlers.keys()]); + } + private getAvailableVersionsMessage(): string { const versions = [...this.handlers.keys()]; return `Available versions are: ${ @@ -83,26 +91,27 @@ export class CoreVersionedRoute implements VersionedRoute { }`; } - /** This is where we must implement the versioned spec once it is available */ private requestHandler = async ( ctx: RequestHandlerContextBase, req: KibanaRequest, res: KibanaResponseFactory - ) => { - const version = req.headers?.[ELASTIC_HTTP_VERSION_HEADER] as undefined | ApiVersion; - if (!version) { + ): Promise => { + if (this.handlers.size <= 0) { return res.custom({ - statusCode: 406, - body: `Version expected at [${this.method}] [${ - this.path - }]. Please specify a version using the "${ELASTIC_HTTP_VERSION_HEADER}" header. ${this.getAvailableVersionsMessage()}`, + statusCode: 500, + body: `No handlers registered for [${this.method}] [${this.path}].`, }); } + const version = this.getVersion(req); + + const invalidVersionMessage = isValidRouteVersion(this.isPublic, version); + if (invalidVersionMessage) { + return res.badRequest({ body: invalidVersionMessage }); + } const handler = this.handlers.get(version); if (!handler) { - return res.custom({ - statusCode: 406, + return res.badRequest({ body: `No version "${version}" available for [${this.method}] [${ this.path }]. ${this.getAvailableVersionsMessage()}`, @@ -137,13 +146,13 @@ export class CoreVersionedRoute implements VersionedRoute { mutableCoreKibanaRequest.query = {}; } - const result = await handler.fn(ctx, mutableCoreKibanaRequest, res); + const response = await handler.fn(ctx, mutableCoreKibanaRequest, res); - if (this.validateResponses && validation?.response?.[result.status]) { - const responseValidation = validation.response[result.status]; + if (this.router.isDev && validation?.response?.[response.status]) { + const responseValidation = validation.response[response.status]; try { validate( - { body: result.payload }, + { body: response.payload }, { body: responseValidation.body, unsafe: { body: validation.response.unsafe?.body } }, handler.options.version ); @@ -155,14 +164,23 @@ export class CoreVersionedRoute implements VersionedRoute { } } - return result; + return injectResponseHeaders( + { + [ELASTIC_HTTP_VERSION_HEADER]: version, + }, + response + ); }; + private getVersion(request: KibanaRequest): ApiVersion { + const versions = request.headers?.[ELASTIC_HTTP_VERSION_HEADER]; + return Array.isArray(versions) ? versions[0] : versions ?? this.getDefaultVersion(); + } + private validateVersion(version: string) { - if (!isValidRouteVersion(version)) { - throw new Error( - `Invalid version number. Received "${version}", expected any finite, whole number greater than 0.` - ); + const message = isValidRouteVersion(this.isPublic, version); + if (message) { + throw new Error(message); } if (this.handlers.has(version as ApiVersion)) { diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts index 58545babf8abc..2d148ab461a6e 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts @@ -9,33 +9,35 @@ import type { IRouter } from '@kbn/core-http-server'; import type { VersionedRouter, VersionedRoute, VersionedRouteConfig } from '@kbn/core-http-server'; import { CoreVersionedRoute } from './core_versioned_route'; -import { Method, VersionedRouterRoute } from './types'; +import { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types'; + +/** @internal */ +interface Dependencies { + router: IRouter; + defaultHandlerResolutionStrategy?: HandlerResolutionStrategy; + /** Whether Kibana is running in a dev environment */ + isDev?: boolean; +} export class CoreVersionedRouter implements VersionedRouter { private readonly routes = new Set(); - public static from({ - router, - validateResponses, - }: { - router: IRouter; - validateResponses?: boolean; - }) { - return new CoreVersionedRouter(router, validateResponses); + public static from({ router, defaultHandlerResolutionStrategy, isDev }: Dependencies) { + return new CoreVersionedRouter(router, defaultHandlerResolutionStrategy, isDev); } private constructor( - private readonly router: IRouter, - private readonly validateResponses: boolean = false + public readonly router: IRouter, + public readonly defaultHandlerResolutionStrategy: HandlerResolutionStrategy = 'oldest', + public readonly isDev: boolean = false ) {} private registerVersionedRoute = (routeMethod: Method) => (options: VersionedRouteConfig): VersionedRoute => { const route = CoreVersionedRoute.from({ - router: this.router, + router: this, method: routeMethod, path: options.path, options, - validateResponses: this.validateResponses, }); this.routes.add(route); return route; diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.test.ts new file mode 100644 index 0000000000000..a40b2831948a7 --- /dev/null +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.test.ts @@ -0,0 +1,35 @@ +/* + * 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 { resolvers } from './handler_resolvers'; + +describe('default handler resolvers', () => { + describe('oldest', () => { + test.each([ + { versions: ['2002-02-02', '2022-02-02', '2021-02-02'], expected: '2002-02-02' }, + { versions: ['abc', 'def', 'ghi'], expected: 'abc' }, + { versions: ['1', '2', '400'], expected: '1' }, + { versions: ['2002-02-02'], expected: '2002-02-02' }, + { versions: [], expected: undefined }, + ])(`$versions returns $expected`, ({ versions, expected }) => { + expect(resolvers.oldest(versions)).toBe(expected); + }); + }); + + describe('newest', () => { + test.each([ + { versions: ['2002-02-02', '2022-02-02', '2021-02-02'], expected: '2022-02-02' }, + { versions: ['abc', 'def', 'ghi'], expected: 'ghi' }, + { versions: ['1', '2', '400'], expected: '400' }, + { versions: ['2002-02-02'], expected: '2002-02-02' }, + { versions: [], expected: undefined }, + ])(`$versions returns $expected`, ({ versions, expected }) => { + expect(resolvers.newest(versions)).toBe(expected); + }); + }); +}); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts new file mode 100644 index 0000000000000..376116c02607b --- /dev/null +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.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. + */ + +/** + * Assumes that there is at least one version in the array. + * @internal + */ +type Resolver = (versions: string[]) => string; + +const oldest: Resolver = (versions) => [...versions].sort((a, b) => a.localeCompare(b))[0]; + +const newest: Resolver = (versions) => [...versions].sort((a, b) => b.localeCompare(a))[0]; + +export const resolvers = { + oldest, + newest, +}; diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/inject_response_headers.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/inject_response_headers.ts new file mode 100644 index 0000000000000..6430ef2f30a9f --- /dev/null +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/inject_response_headers.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 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 type { Mutable } from 'utility-types'; +import type { IKibanaResponse } from '@kbn/core-http-server'; + +/** + * @note mutates the response object + * @internal + */ +export function injectResponseHeaders(headers: object, response: IKibanaResponse): IKibanaResponse { + const mutableResponse = response as Mutable; + mutableResponse.options = { + ...mutableResponse.options, + headers: { + ...mutableResponse.options.headers, + ...headers, + }, + }; + return mutableResponse; +} diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.test.ts index 3930ad7c06df1..8062371f9a0c4 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.test.ts @@ -9,11 +9,24 @@ import { isValidRouteVersion } from './is_valid_route_version'; describe('isValidRouteVersion', () => { - test('valid numbers return "true"', () => { - expect(isValidRouteVersion('1')).toBe(true); + describe('public', () => { + test('allows valid dates', () => { + expect(isValidRouteVersion(true, '2010-02-01')).toBe(undefined); + }); + test.each([['2020.02.01'], ['2020-99-99'], [''], ['abc']])( + '%p returns an error message', + (value: string) => { + expect(isValidRouteVersion(true, value)).toMatch(/Invalid version/); + } + ); }); + describe('internal', () => { + test('allows valid numbers', () => { + expect(isValidRouteVersion(false, '1')).toBe(undefined); + }); - test.each([['1.1'], [''], ['abc']])('%p returns "false"', (value: string) => { - expect(isValidRouteVersion(value)).toBe(false); + test.each([['1.1'], [''], ['abc']])('%p returns an error message', (value: string) => { + expect(isValidRouteVersion(false, value)).toMatch(/Invalid version number/); + }); }); }); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.ts index a07301a4f9936..d903fd015a1e5 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/is_valid_route_version.ts @@ -6,7 +6,23 @@ * Side Public License, v 1. */ -export function isValidRouteVersion(version: string): boolean { +import moment from 'moment'; + +const VERSION_REGEX = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/; + +/** + * For public routes we must check that the version is a string that is YYYY-MM-DD. + * For internal routes we must check that the version is a number. + * @internal + */ +export function isValidRouteVersion(isPublicApi: boolean, version: string): undefined | string { + if (isPublicApi) { + return VERSION_REGEX.test(version) && moment(version, 'YYYY-MM-DD').isValid() + ? undefined + : `Invalid version. Received "${version}", expected a valid date string formatted as YYYY-MM-DD.`; + } const float = parseFloat(version); - return isFinite(float) && !isNaN(float) && float > 0 && Math.round(float) === float; + return isFinite(float) && !isNaN(float) && float > 0 && Math.round(float) === float + ? undefined + : `Invalid version number. Received "${version}", expected any finite, whole number greater than 0.`; } diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts index f5f8d1c6f67dc..6aa818d2e663a 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts @@ -29,3 +29,17 @@ export interface VersionedRouterRoute { options: AddVersionOpts; }>; } + +/** + * Specifies resolution strategy to use if a request does not provide a version. + * + * This strategy assumes that a handler is represented by a version string [0-9\-]+ that is + * alphanumerically sortable. + * + * @internal + */ +export type HandlerResolutionStrategy = + /** Use the oldest available version by default */ + | 'oldest' + /** Use the newest available version by default */ + | 'newest'; diff --git a/packages/core/http/core-http-router-server-internal/tsconfig.json b/packages/core/http/core-http-router-server-internal/tsconfig.json index 067fd1a5173f4..e4a70cbcddeec 100644 --- a/packages/core/http/core-http-router-server-internal/tsconfig.json +++ b/packages/core/http/core-http-router-server-internal/tsconfig.json @@ -17,7 +17,7 @@ "@kbn/hapi-mocks", "@kbn/core-logging-server-mocks", "@kbn/logging", - "@kbn/core-http-common", + "@kbn/core-http-common" ], "exclude": [ "target/**/*", diff --git a/packages/core/http/core-http-server-internal/src/http_service.ts b/packages/core/http/core-http-server-internal/src/http_service.ts index 45841b72bdbee..5c1ec864b2141 100644 --- a/packages/core/http/core-http-server-internal/src/http_service.ts +++ b/packages/core/http/core-http-server-internal/src/http_service.ts @@ -128,7 +128,8 @@ export class HttpService const router = new Router( path, this.log, - prebootServerRequestHandlerContext.createHandler.bind(null, this.coreContext.coreId) + prebootServerRequestHandlerContext.createHandler.bind(null, this.coreContext.coreId), + { isDev: this.env.mode.dev } ); registerCallback(router); @@ -172,7 +173,9 @@ export class HttpService pluginId: PluginOpaqueId = this.coreContext.coreId ) => { const enhanceHandler = this.requestHandlerContext!.createHandler.bind(null, pluginId); - const router = new Router(path, this.log, enhanceHandler); + const router = new Router(path, this.log, enhanceHandler, { + isDev: this.env.mode.dev, + }); registerRouter(router); return router; }, diff --git a/packages/core/http/core-http-server/src/versioning/types.ts b/packages/core/http/core-http-server/src/versioning/types.ts index 0fa6e167f9e0d..4a63d23e10b96 100644 --- a/packages/core/http/core-http-server/src/versioning/types.ts +++ b/packages/core/http/core-http-server/src/versioning/types.ts @@ -34,7 +34,7 @@ export type VersionedRouteConfig = Omit< > & { options?: Omit, 'access'>; /** See {@link RouteConfigOptions['access']} */ - access: RouteConfigOptions['access']; + access: Exclude['access'], undefined>; }; /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.test.ts index 0120ecf75c797..0b5d750e57040 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.test.ts @@ -522,4 +522,21 @@ describe('validateAndConvertAggregations', () => { '"[aggName.cardinality.field] Invalid attribute path: alert.alert.actions.group"' ); }); + + it('allows aggregations for root fields', () => { + const aggregations: AggsMap = { + types: { + terms: { + field: 'type', + }, + }, + }; + expect(validateAndConvertAggregations(['foo'], aggregations, mockMappings)).toEqual({ + types: { + terms: { + field: 'type', + }, + }, + }); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.ts index b3a6bbae5e956..c673f73d8d844 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/aggregations/validation.ts @@ -18,6 +18,7 @@ import { rewriteRootLevelAttribute, } from './validation_utils'; import { aggregationSchemas } from './aggs_types'; +import { getRootFields } from '../included_fields'; const aggregationKeys = ['aggs', 'aggregations']; @@ -226,6 +227,10 @@ const isAttributeValue = (fieldName: string, fieldValue: unknown): boolean => { return attributeFields.includes(fieldName) && typeof fieldValue === 'string'; }; +const isRootField = (fieldName: string): boolean => { + return getRootFields().includes(fieldName); +}; + const validateAndRewriteAttributePath = ( attributePath: string, { allowedTypes, indexMapping, currentPath }: ValidationContext @@ -236,5 +241,8 @@ const validateAndRewriteAttributePath = ( if (isObjectTypeAttribute(attributePath, indexMapping, allowedTypes)) { return rewriteObjectTypeAttribute(attributePath); } + if (isRootField(attributePath)) { + return attributePath; + } throw new Error(`[${currentPath.join('.')}] Invalid attribute path: ${attributePath}`); }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap index 352df1947d6eb..efbdf0da12f26 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap @@ -8,7 +8,6 @@ Object { "bmap": "510f1f0adb69830cf8a1c5ce2923ed82", "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "created_at": "00da57df13e94e9d98437d13ace4bfe0", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", "originId": "2f4316de49999235636386fe51dc06c1", @@ -40,10 +39,6 @@ Object { "created_at": Object { "type": "date", }, - "migrationVersion": Object { - "dynamic": "true", - "type": "object", - }, "namespace": Object { "type": "keyword", }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap index c634524f06d66..42aaff1b7f0df 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap @@ -8,7 +8,6 @@ Object { "bbb": "18c78c995965207ed3f6e7fc5c6e55fe", "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "created_at": "00da57df13e94e9d98437d13ace4bfe0", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", "originId": "2f4316de49999235636386fe51dc06c1", @@ -32,10 +31,6 @@ Object { "created_at": Object { "type": "date", }, - "migrationVersion": Object { - "dynamic": "true", - "type": "object", - }, "namespace": Object { "type": "keyword", }, @@ -79,7 +74,6 @@ Object { "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "created_at": "00da57df13e94e9d98437d13ace4bfe0", "firstType": "635418ab953d81d93f1190b70a8d3f57", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", "originId": "2f4316de49999235636386fe51dc06c1", @@ -107,10 +101,6 @@ Object { }, }, }, - "migrationVersion": Object { - "dynamic": "true", - "type": "object", - }, "namespace": Object { "type": "keyword", }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts index 3d2cd8b8f1a6f..12ed0931ce0c3 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts @@ -118,12 +118,6 @@ export function getBaseMappings(): IndexMapping { return { dynamic: 'strict', properties: { - migrationVersion: { - // Saved Objects can't redefine dynamic, but we cheat here to support migrations - // @ts-expect-error - dynamic: 'true', - type: 'object', - }, type: { type: 'keyword', }, diff --git a/packages/core/ui-settings/core-ui-settings-common/index.ts b/packages/core/ui-settings/core-ui-settings-common/index.ts index 3f94ad9799415..f04a41613f76f 100644 --- a/packages/core/ui-settings/core-ui-settings-common/index.ts +++ b/packages/core/ui-settings/core-ui-settings-common/index.ts @@ -13,3 +13,5 @@ export type { UserProvidedValues, UiSettingsScope, } from './src/ui_settings'; + +export { TIMEZONE_OPTIONS } from './src/timezones'; diff --git a/packages/core/ui-settings/core-ui-settings-common/src/timezones.ts b/packages/core/ui-settings/core-ui-settings-common/src/timezones.ts new file mode 100644 index 0000000000000..f77d36ab0c227 --- /dev/null +++ b/packages/core/ui-settings/core-ui-settings-common/src/timezones.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 moment from 'moment-timezone'; + +export const TIMEZONE_OPTIONS = [ + ...moment.tz + .names() + // We need to filter out some time zones, that moment.js knows about, but Elasticsearch + // does not understand and would fail thus with a 400 bad request when using them. + .filter((tz) => !['America/Nuuk', 'EST', 'HST', 'ROC', 'MST'].includes(tz)), +]; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/settings/date_formats.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/settings/date_formats.ts index c13eae4965a54..947f7fbac11b7 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/settings/date_formats.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/settings/date_formats.ts @@ -9,20 +9,13 @@ import moment from 'moment-timezone'; import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import type { UiSettingsParams } from '@kbn/core-ui-settings-common'; +import { UiSettingsParams, TIMEZONE_OPTIONS } from '@kbn/core-ui-settings-common'; export const getDateFormatSettings = (): Record => { const weekdays = moment.weekdays().slice(); const [defaultWeekday] = weekdays; - const timezones = [ - 'Browser', - ...moment.tz - .names() - // We need to filter out some time zones, that moment.js knows about, but Elasticsearch - // does not understand and would fail thus with a 400 bad request when using them. - .filter((tz) => !['America/Nuuk', 'EST', 'HST', 'ROC', 'MST'].includes(tz)), - ]; + const timezones = ['Browser', ...TIMEZONE_OPTIONS]; return { dateFormat: { diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts index 9294a12b4ce50..81c95d2130018 100644 --- a/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/ecs_field_map.ts @@ -9,8 +9,16 @@ import { EcsFlat } from '@kbn/ecs'; import { EcsMetadata, FieldMap } from './types'; +const EXCLUDED_TYPES = ['constant_keyword']; + export const ecsFieldMap: FieldMap = Object.keys(EcsFlat).reduce((acc, currKey) => { const value: EcsMetadata = EcsFlat[currKey as keyof typeof EcsFlat]; + + // Exclude excluded types + if (EXCLUDED_TYPES.includes(value.type)) { + return acc; + } + return { ...acc, [currKey]: { diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 74d7df9394153..93d5507c53a1e 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -7,7 +7,7 @@ pageLoadAssetSize: banners: 17946 bfetch: 22837 canvas: 1066647 - cases: 144442 + cases: 170000 charts: 55000 cloud: 21076 cloudChat: 19894 diff --git a/packages/kbn-saved-objects-settings/README.md b/packages/kbn-saved-objects-settings/README.md new file mode 100644 index 0000000000000..48e2630354b02 --- /dev/null +++ b/packages/kbn-saved-objects-settings/README.md @@ -0,0 +1,3 @@ +# @kbn/saved-objects-settings + +Contains constants for some of saved objects related ui settings that had to be re-used between plugins without causing circular dependencies. diff --git a/packages/kbn-saved-objects-settings/index.ts b/packages/kbn-saved-objects-settings/index.ts new file mode 100644 index 0000000000000..f2a1aa7c06e54 --- /dev/null +++ b/packages/kbn-saved-objects-settings/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 const PER_PAGE_SETTING = 'savedObjects:perPage'; +export const LISTING_LIMIT_SETTING = 'savedObjects:listingLimit'; diff --git a/packages/kbn-saved-objects-settings/jest.config.js b/packages/kbn-saved-objects-settings/jest.config.js new file mode 100644 index 0000000000000..562e1f12ecceb --- /dev/null +++ b/packages/kbn-saved-objects-settings/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-saved-objects-settings'], +}; diff --git a/packages/kbn-saved-objects-settings/kibana.jsonc b/packages/kbn-saved-objects-settings/kibana.jsonc new file mode 100644 index 0000000000000..40486e1ef0cf1 --- /dev/null +++ b/packages/kbn-saved-objects-settings/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/saved-objects-settings", + "owner": "@elastic/appex-sharedux" +} diff --git a/packages/kbn-saved-objects-settings/package.json b/packages/kbn-saved-objects-settings/package.json new file mode 100644 index 0000000000000..38ef61439d47d --- /dev/null +++ b/packages/kbn-saved-objects-settings/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/saved-objects-settings", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-saved-objects-settings/tsconfig.json b/packages/kbn-saved-objects-settings/tsconfig.json new file mode 100644 index 0000000000000..2f9ddddbeea23 --- /dev/null +++ b/packages/kbn-saved-objects-settings/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [] +} diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap index daaf9c66c453f..23a56e38ba458 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap @@ -16,7 +16,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -33,6 +33,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" id="generated-id__eui-combobox-id" role="combobox" @@ -89,7 +90,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -106,6 +107,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" id="generated-id__eui-combobox-id" role="combobox" @@ -219,7 +221,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -236,6 +238,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" disabled="" id="generated-id__eui-combobox-id" @@ -283,7 +286,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -300,6 +303,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" disabled="" id="generated-id__eui-combobox-id" @@ -404,7 +408,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -421,6 +425,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" id="generated-id__eui-combobox-id" role="combobox" @@ -466,7 +471,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -483,6 +488,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" id="generated-id__eui-combobox-id" role="combobox" @@ -585,7 +591,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -602,6 +608,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" disabled="" id="generated-id__eui-combobox-id" @@ -654,7 +661,7 @@ Object { class="euiFormControlLayout__childrenWrapper" >
@@ -671,6 +678,7 @@ Object { aria-autocomplete="list" aria-controls="" aria-expanded="false" + aria-invalid="false" data-test-subj="comboBoxSearchInput" disabled="" id="generated-id__eui-combobox-id" diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx index 8fd88fe6a910b..24bb72c3058ac 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx @@ -142,11 +142,7 @@ describe('AutocompleteFieldMatchComponent', () => { /> ); - expect( - wrapper - .find('[data-test-subj="comboBoxInput"]') - .hasClass('euiComboBox__inputWrap-isClearable') - ).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); }); test('it correctly displays selected value', () => { diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx index bfa4aa6de4d24..2fa8446bfa8b9 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.test.tsx @@ -131,11 +131,7 @@ describe('AutocompleteFieldMatchAnyComponent', () => { /> ); - expect( - wrapper - .find(`[data-test-subj="comboBoxInput"]`) - .hasClass('euiComboBox__inputWrap-isClearable') - ).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); }); test('it correctly displays selected value', () => { diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx index bad9f619b4994..f0b5931e54436 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx @@ -143,11 +143,7 @@ describe('AutocompleteFieldWildcardComponent', () => { /> ); - expect( - wrapper - .find('[data-test-subj="comboBoxInput"]') - .hasClass('euiComboBox__inputWrap-isClearable') - ).toBeTruthy(); + expect(wrapper.find(`[data-test-subj="comboBoxClearButton"]`)).toBeTruthy(); }); test('it correctly displays selected value', () => { diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap index 976401785f88a..871e3ea311cd6 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap @@ -331,15 +331,23 @@ Object {
- +
+ +
+
diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap index ed991c34d8870..4ab6fed810c0a 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/edit_modal/__snapshots__/edit_modal.test.tsx.snap @@ -107,15 +107,23 @@ Object {
- +
+ +
+
diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx index 2ddf166306ef0..e3b1c45b2733d 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx @@ -7,13 +7,13 @@ */ import type { FieldSpec } from '@kbn/data-views-plugin/common'; +import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import React, { useCallback, useEffect } from 'react'; -import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import { GroupSelector, isNoneGroup } from '..'; import { groupActions, groupByIdSelector } from './state'; import type { GroupOption } from './types'; import { Action, defaultGroup, GroupMap } from './types'; +import { GroupSelector, isNoneGroup } from '..'; import { getTelemetryEvent } from '../telemetry/const'; export interface UseGetGroupSelectorArgs { diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts index 31a5ba496647f..6e06731798cc5 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { getMappingConflictsInfo } from '.'; +import { getMappingConflictsInfo, fieldSupportsMatches } from '.'; describe('Helpers', () => { describe('getMappingConflictsInfo', () => { @@ -143,4 +143,42 @@ describe('Helpers', () => { ]); }); }); + + describe('fieldSupportsMatches', () => { + test('it returns true if esTypes is keyword', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword'] }) + ).toBeTruthy(); + }); + + test('it returns true if one of the esTypes is kibana type string and another is not', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'object'] }) + ).toBeTruthy(); + }); + + test('it returns true if one of the esTypes is keyword', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'unmapped'] }) + ).toBeTruthy(); + }); + + test('it returns true if one of the esTypes is text', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'unmapped'] }) + ).toBeTruthy(); + }); + + test('it returns true if all of the esTypes is map to kibana type string', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'keyword'] }) + ).toBeTruthy(); + }); + + test('it returns false if none of the esTypes map to kibana type string', () => { + expect( + fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['bool', 'unmapped'] }) + ).toBeFalsy(); + }); + }); }); diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts index 403b07c0ba058..42a442d7e3be1 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts @@ -35,6 +35,7 @@ import { getDataViewFieldSubtypeNested, isDataViewFieldSubtypeNested, } from '@kbn/es-query'; +import { castEsToKbnFieldTypeName, KBN_FIELD_TYPES } from '@kbn/field-types'; import { ALL_OPERATORS, @@ -676,8 +677,13 @@ export const getEntryOnOperatorChange = ( } }; -const fieldSupportsMatches = (field: DataViewFieldBase) => { - return field.type === 'string'; +export const isKibanaStringType = (type: string) => { + const kbnFieldType = castEsToKbnFieldTypeName(type); + return kbnFieldType === KBN_FIELD_TYPES.STRING; +}; + +export const fieldSupportsMatches = (field: DataViewFieldBase) => { + return field.esTypes?.some(isKibanaStringType); }; /** diff --git a/packages/kbn-securitysolution-list-utils/src/types/index.ts b/packages/kbn-securitysolution-list-utils/src/types/index.ts index ccd0ab68799bb..e73381fc2d151 100644 --- a/packages/kbn-securitysolution-list-utils/src/types/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/types/index.ts @@ -26,6 +26,10 @@ import { EXCEPTION_LIST_NAMESPACE_AGNOSTIC, } from '@kbn/securitysolution-list-constants'; +export interface DataViewField extends DataViewFieldBase { + conflictDescriptions?: Record; +} + export interface OperatorOption { message: string; value: string; @@ -35,7 +39,7 @@ export interface OperatorOption { export interface FormattedBuilderEntry { id: string; - field: DataViewFieldBase | undefined; + field: DataViewField | undefined; operator: OperatorOption; value: string | string[] | undefined; nested: 'parent' | 'child' | undefined; @@ -117,7 +121,3 @@ export const exceptionListAgnosticSavedObjectType = EXCEPTION_LIST_NAMESPACE_AGN export type SavedObjectType = | typeof EXCEPTION_LIST_NAMESPACE | typeof EXCEPTION_LIST_NAMESPACE_AGNOSTIC; - -export interface DataViewField extends DataViewFieldBase { - conflictDescriptions?: Record; -} diff --git a/packages/kbn-securitysolution-list-utils/tsconfig.json b/packages/kbn-securitysolution-list-utils/tsconfig.json index c0aaedaafd845..d75eb1f98314c 100644 --- a/packages/kbn-securitysolution-list-utils/tsconfig.json +++ b/packages/kbn-securitysolution-list-utils/tsconfig.json @@ -16,7 +16,8 @@ "@kbn/securitysolution-io-ts-list-types", "@kbn/securitysolution-io-ts-utils", "@kbn/securitysolution-list-constants", - "@kbn/securitysolution-utils" + "@kbn/securitysolution-utils", + "@kbn/field-types" ], "exclude": [ "target/**/*", diff --git a/packages/shared-ux/file/file_upload/impl/src/file_upload.tsx b/packages/shared-ux/file/file_upload/impl/src/file_upload.tsx index 498a4a93b5fe4..45e74312e1e55 100644 --- a/packages/shared-ux/file/file_upload/impl/src/file_upload.tsx +++ b/packages/shared-ux/file/file_upload/impl/src/file_upload.tsx @@ -19,7 +19,7 @@ import { context } from './context'; /** * An object representing an uploaded file */ -interface UploadedFile { +export interface UploadedFile { /** * The ID that was generated for the uploaded file */ diff --git a/renovate.json b/renovate.json index ed5c6ee554f6a..bb1d043c30f48 100644 --- a/renovate.json +++ b/renovate.json @@ -270,7 +270,7 @@ { "groupName": "TTY Output", "matchPackageNames": ["xterm", "byte-size", "@types/byte-size"], - "reviewers": ["team:awp-viz"], + "reviewers": ["team:sec-cloudnative-integrations"], "matchBaseBranches": ["main"], "labels": ["Team: AWP: Visualization", "release_note:skip", "backport:skip"], "enabled": true, diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 4a875d6955428..9facf94408235 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -78,25 +78,43 @@ const configPathCollector = pathCollector(); const pluginPathCollector = pathCollector(); /** - * @param {string} name - * @param {string[]} configs - * @param {'push' | 'unshift'} method + * @param {string} name The config file name + * @returns {boolean} Whether the file exists */ -function maybeAddConfig(name, configs, method) { +function configFileExists(name) { const path = resolve(getConfigDirectory(), name); try { - if (statSync(path).isFile()) { - configs[method](path); - } + return statSync(path).isFile(); } catch (err) { if (err.code === 'ENOENT') { - return; + return false; } throw err; } } +/** + * @returns {boolean} Whether the distribution can run in Serverless mode + */ +function isServerlessCapableDistribution() { + // For now, checking if the `serverless.yml` config file exists should be enough + // We could also check the following as well, but I don't think it's necessary: + // VALID_SERVERLESS_PROJECT_MODE.some((projectType) => configFileExists(`serverless.${projectType}.yml`)) + return configFileExists('serverless.yml'); +} + +/** + * @param {string} name + * @param {string[]} configs + * @param {'push' | 'unshift'} method + */ +function maybeAddConfig(name, configs, method) { + if (configFileExists(name)) { + configs[method](resolve(getConfigDirectory(), name)); + } +} + /** * @returns {string[]} */ @@ -233,8 +251,11 @@ export default function (program) { .option( '--run-examples', 'Adds plugin paths for all the Kibana example plugins and runs with no base path' - ) - .option('--serverless ', 'Start Kibana in a serverless project mode'); + ); + } + + if (isServerlessCapableDistribution()) { + command.option('--serverless ', 'Start Kibana in a serverless project mode'); } if (DEV_MODE_SUPPORTED) { diff --git a/src/core/server/integration_tests/http/versioned_router.test.ts b/src/core/server/integration_tests/http/versioned_router.test.ts index b2179bea4d1f7..4206bed642bba 100644 --- a/src/core/server/integration_tests/http/versioned_router.test.ts +++ b/src/core/server/integration_tests/http/versioned_router.test.ts @@ -7,38 +7,41 @@ */ import Supertest from 'supertest'; +import { createTestEnv, getEnvOptions } from '@kbn/config-mocks'; +import { schema } from '@kbn/config-schema'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks'; import { contextServiceMock } from '@kbn/core-http-context-server-mocks'; import { createHttpServer } from '@kbn/core-http-server-mocks'; import type { HttpService } from '@kbn/core-http-server-internal'; +import type { IRouterWithVersion } from '@kbn/core-http-server'; let server: HttpService; let logger: ReturnType; -const contextSetup = contextServiceMock.createSetupContract(); - -const setupDeps = { - context: contextSetup, - executionContext: executionContextServiceMock.createInternalSetupContract(), -}; - -beforeEach(async () => { - logger = loggingSystemMock.create(); - server = createHttpServer({ logger }); - await server.preboot({ context: contextServiceMock.createPrebootContract() }); -}); - -afterEach(async () => { - await server.stop(); -}); - describe('Routing versioned requests', () => { - it('routes requests to the expected handlers', async () => { + let router: IRouterWithVersion; + let supertest: Supertest.SuperTest; + + const setupDeps = { + context: contextServiceMock.createSetupContract(), + executionContext: executionContextServiceMock.createInternalSetupContract(), + }; + + beforeEach(async () => { + logger = loggingSystemMock.create(); + server = createHttpServer({ logger }); + await server.preboot({ context: contextServiceMock.createPrebootContract() }); const { server: innerServer, createRouter } = await server.setup(setupDeps); - const router = createRouter('/'); - const supertest = Supertest(innerServer.listener); + router = createRouter('/'); + supertest = Supertest(innerServer.listener); + }); + + afterEach(async () => { + await server.stop(); + }); + it('routes requests to the expected handlers', async () => { router.versioned .get({ path: '/my-path', access: 'internal' }) .addVersion({ validate: false, version: '1' }, async (ctx, req, res) => { @@ -67,29 +70,88 @@ describe('Routing versioned requests', () => { ).resolves.toBe('2'); }); - it('handles non-existent version', async () => { - const { server: innerServer, createRouter } = await server.setup(setupDeps); - const router = createRouter('/'); - const supertest = Supertest(innerServer.listener); + it('handles missing version header (defaults to oldest)', async () => { + router.versioned + .get({ path: '/my-path', access: 'public' }) + .addVersion({ validate: false, version: '2020-02-02' }, async (ctx, req, res) => { + return res.ok({ body: { v: '1' } }); + }) + .addVersion({ validate: false, version: '2022-02-02' }, async (ctx, req, res) => { + return res.ok({ body: { v: '2' } }); + }); - router.versioned.get({ path: '/my-path', access: 'internal' }); // do not actually register any versions await server.start(); - await supertest.get('/my-path').set('Elastic-Api-Version', '2').expect(406); + await expect(supertest.get('/my-path').expect(200)).resolves.toEqual( + expect.objectContaining({ + body: { v: '1' }, + header: expect.objectContaining({ + 'elastic-api-version': '2020-02-02', + }), + }) + ); }); - it('handles missing version header', async () => { - const { server: innerServer, createRouter } = await server.setup(setupDeps); - const router = createRouter('/'); - const supertest = Supertest(innerServer.listener); - + it('returns the expected output for badly formatted versions', async () => { router.versioned .get({ path: '/my-path', access: 'internal' }) .addVersion({ validate: false, version: '1' }, async (ctx, req, res) => { return res.ok({ body: { v: '1' } }); + }); + + await server.start(); + + await expect( + supertest + .get('/my-path') + .set('Elastic-Api-Version', 'abc') + .expect(400) + .then(({ body }) => body) + ).resolves.toEqual( + expect.objectContaining({ + message: expect.stringMatching(/Invalid version/), }) - .addVersion({ validate: false, version: '2' }, async (ctx, req, res) => { - return res.ok({ body: { v: '2' } }); + ); + }); + + it('returns the expected responses for failed validation', async () => { + router.versioned + .post({ path: '/my-path', access: 'internal' }) + // Bad request validation + .addVersion( + { + validate: { + request: { body: schema.object({ foo: schema.number() }) }, + }, + version: '1', + }, + async (ctx, req, res) => { + return res.ok({ body: { v: '1' } }); + } + ); + + await server.start(); + + await expect( + supertest + .post('/my-path') + .send({}) + .set('Elastic-Api-Version', '1') + .expect(400) + .then(({ body }) => body) + ).resolves.toEqual( + expect.objectContaining({ + error: 'Bad Request', + message: expect.stringMatching(/expected value of type/), + }) + ); + }); + + it('returns the version in response headers', async () => { + router.versioned + .get({ path: '/my-path', access: 'public' }) + .addVersion({ validate: false, version: '2020-02-02' }, async (ctx, req, res) => { + return res.ok({ body: { v: '2020-02-02' } }); }); await server.start(); @@ -97,12 +159,82 @@ describe('Routing versioned requests', () => { await expect( supertest .get('/my-path') - .expect(406) + .set('Elastic-Api-Version', '2020-02-02') + .expect(200) + .then(({ header }) => header) + ).resolves.toEqual(expect.objectContaining({ 'elastic-api-version': '2020-02-02' })); + }); + + it('runs response validation when in dev', async () => { + router.versioned + .get({ path: '/my-path', access: 'internal' }) + .addVersion( + { validate: { response: { 200: { body: schema.number() } } }, version: '1' }, + async (ctx, req, res) => { + return res.ok({ body: { v: '1' } }); + } + ); + + await server.start(); + + await expect( + supertest + .get('/my-path') + .set('Elastic-Api-Version', '1') + .expect(500) .then(({ body }) => body) ).resolves.toEqual( expect.objectContaining({ - message: expect.stringMatching(/Version expected at/), + message: expect.stringMatching(/Failed output validation/), }) ); }); + + it('does not run response validation in prod', async () => { + logger = loggingSystemMock.create(); + await server.stop(); // stop the already started server + server = createHttpServer({ + logger, + env: createTestEnv({ envOptions: getEnvOptions({ cliArgs: { dev: false } }) }), + }); + await server.preboot({ context: contextServiceMock.createPrebootContract() }); + const { server: innerServer, createRouter } = await server.setup(setupDeps); + + router = createRouter('/'); + supertest = Supertest(innerServer.listener); + router.versioned + .get({ path: '/my-path', access: 'internal' }) + .addVersion( + { validate: { response: { 200: { body: schema.number() } } }, version: '1' }, + async (ctx, req, res) => { + return res.ok({ body: { v: '1' } }); + } + ); + + await server.start(); + + await expect( + supertest + .get('/my-path') + .set('Elastic-Api-Version', '1') + .expect(200) + .then(({ body }) => body.v) + ).resolves.toEqual('1'); + }); + + it('errors when no handler could be found', async () => { + router.versioned.get({ path: '/my-path', access: 'public' }); + + await server.start(); + + await expect( + supertest + .get('/my-path') + .set('Elastic-Api-Version', '2020-02-02') + .expect(500) + .then(({ body }) => body) + ).resolves.toEqual( + expect.objectContaining({ message: expect.stringMatching(/No handlers registered/) }) + ); + }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 541bfa046b0d3..a6428387f65f3 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -55,9 +55,9 @@ describe('checking migration metadata changes on all registered SO types', () => expect(hashMap).toMatchInlineSnapshot(` Object { - "action": "6cfc277ed3211639e37546ac625f4a68f2494215", - "action_task_params": "5f419caba96dd8c77d0f94013e71d43890e3d5d6", - "alert": "7bec97d7775a025ecf36a33baf17386b9e7b4c3c", + "action": "12c6b25ef1fffb36d8de893318f8a2bc5d6a46a6", + "action_task_params": "c725c37de66135934150465f9b1e76fe349e8a23", + "alert": "0cd1f1921014004a9ff5c0a9333ca9bde14bf748", "api_key_pending_invalidation": "16e7bcf8e78764102d7f525542d5b616809a21ee", "apm-indices": "d19dd7fb51f2d2cbc1f8769481721e0953f9a6d2", "apm-server-schema": "1d42f17eff9ec6c16d3a9324d9539e2d123d0a9a", @@ -77,7 +77,7 @@ describe('checking migration metadata changes on all registered SO types', () => "cases-user-actions": "8ad74294b71edffa58fad7a40eea2388209477c9", "config": "97e16b8f5dc10c404fd3b201ef36bc6c3c63dc80", "config-global": "d9791e8f73edee884630e1cb6e4954ae513ce75e", - "connector_token": "fb05ff5afdcb6e2f20c9c6513ff7a1ab12b66f36", + "connector_token": "aff1aa0ebc0a6b44b570057972af25178b0bca42", "core-usage-stats": "b3c04da317c957741ebcdedfea4524049fdc79ff", "csp-rule-template": "099c229bf97578d9ca72b3a672d397559b84ee0b", "dashboard": "71e3f8dfcffeb5fbd410dec81ce46f5691763c43", @@ -123,8 +123,8 @@ describe('checking migration metadata changes on all registered SO types', () => "osquery-pack": "edd84b2c59ef36214ece0676706da8f22175c660", "osquery-pack-asset": "18e08979d46ee7e5538f54c080aec4d8c58516ca", "osquery-saved-query": "f5e4e303f65c7607248ea8b2672f1ee30e4fb15e", - "query": "ec6000b775f06f81470df42d23f7a88cb31d64ba", - "rules-settings": "9854495c3b54b16a6625fb250c35e5504da72266", + "query": "cfc049e1f0574fb4fdb2d653d7c10bdc970a2610", + "rules-settings": "eb8d40b7d60aeffe66831f7d5d83de28d85776d8", "sample-data-telemetry": "c38daf1a49ed24f2a4fb091e6e1e833fccf19935", "search": "ed3a9b1681b57d69560909d51933fdf17576ea68", "search-session": "fae0dfc63274d6a3b90ca583802c48cab8760637", @@ -142,8 +142,8 @@ describe('checking migration metadata changes on all registered SO types', () => "synthetics-param": "9776c9b571d35f0d0397e8915e035ea1dc026db7", "synthetics-privates-locations": "7d032fc788905e32152029ae7ab3d6038c48ae44", "tag": "87f21f07df9cc37001b15a26e413c18f50d1fbfe", - "task": "ff760534a44c4cfabcf4baf8cfe8283f717cab02", - "telemetry": "561b329aaed3c15b91aaf2075645be3097247612", + "task": "533ee80c50c47f0505846bfac73fc10962c5bc45", + "telemetry": "3b3b89cf411a2a2e60487cef6ccdbc5df691aeb9", "ui-metric": "410a8ad28e0f44b161c960ff0ce950c712b17c52", "upgrade-assistant-ml-upgrade-operation": "d8816e5ce32649e7a3a43e2c406c632319ff84bb", "upgrade-assistant-reindex-operation": "09ac8ed9c9acf7e8ece8eafe47d7019ea1472144", diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 8196e1f9f621e..6801686f90f57 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -205,6 +205,7 @@ kibana_vars=( xpack.actions.allowedHosts xpack.actions.customHostSettings xpack.actions.email.domain_allowlist + xpack.actions.enableFooterInEmail xpack.actions.enabledActionTypes xpack.actions.maxResponseContentLength xpack.actions.preconfigured diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 72c269ae3bd2d..75a12b0884f8a 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -68,6 +68,7 @@ export const LICENSE_ALLOWED = [ 'Nuclide software', 'Python-2.0', '(Apache-2.0 AND MIT)', + 'BlueOak-1.0.0', ]; // The following list only applies to licenses that @@ -84,6 +85,6 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.4.0': ['Elastic License 2.0'], - '@elastic/eui@77.0.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@77.1.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 3ad6852936aed..da762f8f55725 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -45,6 +45,7 @@ export const storybookAliases = { presentation: 'src/plugins/presentation_util/storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', security_solution_packages: 'packages/security-solution/storybook/config', + security_solution_data_table: 'x-pack/packages/kbn-securitysolution-data-table/.storybook', shared_ux: 'packages/shared-ux/storybook/config', threat_intelligence: 'x-pack/plugins/threat_intelligence/.storybook', triggers_actions_ui: 'x-pack/plugins/triggers_actions_ui/.storybook', diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 94512bd8b43b7..4f7a2e3305191 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -226,12 +226,13 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` ariaUseDefaultSummary={true} baseTheme={Object {}} debugState={false} - flatLegend={false} + flatLegend={true} legendAction={[Function]} legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" legendSize={130} + legendSort={[Function]} noResults={ { [visData.rows, metricColumn] ); - const flatLegend = isLegendFlat(visType, splitChartDimension); + const flatLegend = !visParams.nestedLegend || isLegendFlat(visType, splitChartDimension); const canShowPieChart = !isEmpty && !isMetricEmpty && !isAllZeros && !hasNegative; @@ -425,6 +425,32 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { const partitionType = getPartitionType(visType); + const customLegendSort = useMemo(() => { + if (!showLegend || !flatLegend) { + return; + } + const [bucketColumn] = bucketColumns; + if (!bucketColumn.id) { + return; + } + const lookup: Record = {}; + visData.rows.forEach((row, i) => { + const category = row[bucketColumn.id!]; + if (!(category in lookup)) { + lookup[category] = i; + } + }); + return (a: SeriesIdentifier, b: SeriesIdentifier) => { + if (a.key == null) { + return 1; + } + if (b.key == null) { + return -1; + } + return lookup[a.key] - lookup[b.key]; + }; + }, [bucketColumns, flatLegend, showLegend, visData.rows]); + return (
{!canShowPieChart ? ( @@ -471,6 +497,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { legendMaxDepth={visParams.nestedLegend ? undefined : 1} legendColorPicker={props.uiState ? LegendColorPickerWrapper : undefined} flatLegend={flatLegend} + legendSort={customLegendSort} tooltip={tooltip} showLegendExtra={visParams.showValuesInLegend} onElementClick={([elementEvent]) => { diff --git a/src/plugins/content_management/server/rpc/routes/routes.ts b/src/plugins/content_management/server/rpc/routes/routes.ts index 6d40e37d533c8..ea529aae11188 100644 --- a/src/plugins/content_management/server/rpc/routes/routes.ts +++ b/src/plugins/content_management/server/rpc/routes/routes.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; -import { LISTING_LIMIT_SETTING, PER_PAGE_SETTING } from '@kbn/saved-objects-finder-plugin/common'; +import { LISTING_LIMIT_SETTING, PER_PAGE_SETTING } from '@kbn/saved-objects-settings'; import { ProcedureName } from '../../../common'; import type { ContentRegistry } from '../../core'; import { MSearchService } from '../../core/msearch'; diff --git a/src/plugins/content_management/tsconfig.json b/src/plugins/content_management/tsconfig.json index 93aacaecff0c9..dd87f182de476 100644 --- a/src/plugins/content_management/tsconfig.json +++ b/src/plugins/content_management/tsconfig.json @@ -14,7 +14,7 @@ "@kbn/object-versioning", "@kbn/core-saved-objects-api-server-mocks", "@kbn/core-saved-objects-api-server", - "@kbn/saved-objects-finder-plugin", + "@kbn/saved-objects-settings", ], "exclude": [ "target/**/*", diff --git a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts index 3da1585405305..f3b4ea598b886 100644 --- a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts +++ b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts @@ -8,6 +8,7 @@ import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; import { CoreSetup } from '@kbn/core/server'; +import { errors } from '@elastic/elasticsearch'; export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { const router = http.createRouter(); @@ -39,6 +40,17 @@ export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { }, }); } catch (e) { + if (e instanceof errors.ResponseError && e.body.error.type === 'security_exception') { + /** + * in cases where the user does not have the 'monitor' permission this check will fail. In these cases, we will + * fall back to assume that the allowExpensiveQueries setting is on, because it defaults to true. + */ + return response.ok({ + body: { + allowExpensiveQueries: true, + }, + }); + } const kbnErr = getKbnServerError(e); return reportServerError(response, kbnErr); } diff --git a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts index d34bc51343cd3..e87a74d428f9a 100644 --- a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts +++ b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts @@ -101,136 +101,6 @@ export const getPanelTooOldErrorString = () => defaultMessage: 'Cannot load panels from a URL created in a version older than 7.3', }); -/* - Dashboard Listing Page -*/ -export const discardConfirmStrings = { - getDiscardTitle: () => - i18n.translate('dashboard.discardChangesConfirmModal.discardChangesTitle', { - defaultMessage: 'Discard changes to dashboard?', - }), - getDiscardSubtitle: () => - i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { - defaultMessage: `Once you discard your changes, there's no getting them back.`, - }), - getDiscardConfirmButtonText: () => - i18n.translate('dashboard.discardChangesConfirmModal.confirmButtonLabel', { - defaultMessage: 'Discard changes', - }), - getDiscardCancelButtonText: () => - i18n.translate('dashboard.discardChangesConfirmModal.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), -}; - -export const createConfirmStrings = { - getCreateTitle: () => - i18n.translate('dashboard.createConfirmModal.unsavedChangesTitle', { - defaultMessage: 'New dashboard already in progress', - }), - getCreateSubtitle: () => - i18n.translate('dashboard.createConfirmModal.unsavedChangesSubtitle', { - defaultMessage: 'Continue editing or start over with a blank dashboard.', - }), - getStartOverButtonText: () => - i18n.translate('dashboard.createConfirmModal.confirmButtonLabel', { - defaultMessage: 'Start over', - }), - getContinueButtonText: () => - i18n.translate('dashboard.createConfirmModal.continueButtonLabel', { - defaultMessage: 'Continue editing', - }), - getCancelButtonText: () => - i18n.translate('dashboard.createConfirmModal.cancelButtonLabel', { - defaultMessage: 'Cancel', - }), -}; - -export const dashboardListingErrorStrings = { - getErrorDeletingDashboardToast: () => - i18n.translate('dashboard.deleteError.toastDescription', { - defaultMessage: 'Error encountered while deleting dashboard', - }), -}; - -export const dashboardListingTableStrings = { - getEntityName: () => - i18n.translate('dashboard.listing.table.entityName', { - defaultMessage: 'dashboard', - }), - getEntityNamePlural: () => - i18n.translate('dashboard.listing.table.entityNamePlural', { - defaultMessage: 'dashboards', - }), - getTableListTitle: () => getDashboardPageTitle(), -}; - -export const noItemsStrings = { - getReadonlyTitle: () => - i18n.translate('dashboard.listing.readonlyNoItemsTitle', { - defaultMessage: 'No dashboards to view', - }), - getReadonlyBody: () => - i18n.translate('dashboard.listing.readonlyNoItemsBody', { - defaultMessage: `There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator.`, - }), - getReadEditTitle: () => - i18n.translate('dashboard.listing.createNewDashboard.title', { - defaultMessage: 'Create your first dashboard', - }), - getReadEditInProgressTitle: () => - i18n.translate('dashboard.listing.createNewDashboard.inProgressTitle', { - defaultMessage: 'Dashboard in progress', - }), - getReadEditDashboardDescription: () => - i18n.translate('dashboard.listing.createNewDashboard.combineDataViewFromKibanaAppDescription', { - defaultMessage: - 'Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations.', - }), - getSampleDataLinkText: () => - i18n.translate('dashboard.listing.createNewDashboard.sampleDataInstallLinkText', { - defaultMessage: `Add some sample data`, - }), - getCreateNewDashboardText: () => - i18n.translate('dashboard.listing.createNewDashboard.createButtonLabel', { - defaultMessage: `Create a dashboard`, - }), -}; - -export const dashboardUnsavedListingStrings = { - getUnsavedChangesTitle: (plural = false) => - i18n.translate('dashboard.listing.unsaved.unsavedChangesTitle', { - defaultMessage: 'You have unsaved changes in the following {dash}:', - values: { - dash: plural - ? dashboardListingTableStrings.getEntityNamePlural() - : dashboardListingTableStrings.getEntityName(), - }, - }), - getLoadingTitle: () => - i18n.translate('dashboard.listing.unsaved.loading', { - defaultMessage: 'Loading', - }), - getEditAriaLabel: (title: string) => - i18n.translate('dashboard.listing.unsaved.editAria', { - defaultMessage: 'Continue editing {title}', - values: { title }, - }), - getEditTitle: () => - i18n.translate('dashboard.listing.unsaved.editTitle', { - defaultMessage: 'Continue editing', - }), - getDiscardAriaLabel: (title: string) => - i18n.translate('dashboard.listing.unsaved.discardAria', { - defaultMessage: 'Discard changes to {title}', - values: { title }, - }), - getDiscardTitle: () => - i18n.translate('dashboard.listing.unsaved.discardTitle', { - defaultMessage: 'Discard changes', - }), -}; - /* Share Modal */ diff --git a/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx b/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx index 31c6caf84020a..6956ea7024a32 100644 --- a/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx +++ b/src/plugins/dashboard/public/dashboard_app/dashboard_router.tsx @@ -15,13 +15,8 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { Switch, RouteComponentProps, HashRouter, Redirect } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; -import { - TableListViewKibanaDependencies, - TableListViewKibanaProvider, -} from '@kbn/content-management-table-list'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { I18nProvider } from '@kbn/i18n-react'; import { AppMountParameters, CoreSetup } from '@kbn/core/public'; -import { I18nProvider, FormattedRelative } from '@kbn/i18n-react'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { createKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; @@ -32,14 +27,13 @@ import { LANDING_PAGE_PATH, VIEW_DASHBOARD_URL, } from '../dashboard_constants'; -import { DashboardListing } from './listing'; import { DashboardApp } from './dashboard_app'; import { pluginServices } from '../services/plugin_services'; -import { DashboardNoMatch } from './listing/dashboard_no_match'; +import { DashboardNoMatch } from './listing_page/dashboard_no_match'; import { createDashboardEditUrl } from '../dashboard_constants'; import { DashboardStart, DashboardStartDependencies } from '../plugin'; import { DashboardMountContext } from './hooks/dashboard_mount_context'; -import { DashboardApplicationService } from '../services/application/types'; +import { DashboardListingPage } from './listing_page/dashboard_listing_page'; import { dashboardReadonlyBadge, getDashboardPageTitle } from './_dashboard_app_strings'; import { DashboardEmbedSettings, DashboardMountContextProps, RedirectToProps } from './types'; @@ -57,25 +51,15 @@ export interface DashboardMountProps { mountContext: DashboardMountContextProps; } -// because the type of `application.capabilities.advancedSettings` is so generic, the provider -// requiring the `save` key to be part of it is causing type issues - so, creating a custom type -type TableListViewApplicationService = DashboardApplicationService & { - capabilities: { advancedSettings: { save: boolean } }; -}; - export async function mountApp({ core, element, appUnMounted, mountContext }: DashboardMountProps) { const { - application, chrome: { setBadge, docTitle, setHelpExtension }, dashboardCapabilities: { showWriteControls }, documentationLinks: { dashboardDocLink }, settings: { uiSettings }, - savedObjectsTagging, data: dataStart, notifications, embeddable, - overlays, - http, } = pluginServices.getServices(); let globalEmbedSettings: DashboardEmbedSettings | undefined; @@ -140,7 +124,7 @@ export async function mountApp({ core, element, appUnMounted, mountContext }: Da routerHistory = routeProps.history; } return ( - - - - - - - - - - - - - + + + + + + + + + + diff --git a/src/plugins/dashboard/public/dashboard_app/listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/dashboard_app/listing/__snapshots__/dashboard_listing.test.tsx.snap deleted file mode 100644 index 074697e980d1b..0000000000000 --- a/src/plugins/dashboard/public/dashboard_app/listing/__snapshots__/dashboard_listing.test.tsx.snap +++ /dev/null @@ -1,543 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`after fetch When given a title that matches multiple dashboards, filter on the title 1`] = ` - - - Create a dashboard - - } - body={ - -

- Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations. -

-

- - Add some sample data - , - } - } - /> -

-
- } - iconType="dashboardApp" - title={ -

- Create your first dashboard -

- } - /> - } - entityName="dashboard" - entityNamePlural="dashboards" - findItems={[Function]} - getDetailViewLink={[Function]} - headingId="dashboardListingHeading" - id="dashboard" - initialFilter="search by title" - tableListTitle="Dashboards" - > - -
-
-`; - -exports[`after fetch initialFilter 1`] = ` - - - Create a dashboard - - } - body={ - -

- Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations. -

-

- - Add some sample data - , - } - } - /> -

-
- } - iconType="dashboardApp" - title={ -

- Create your first dashboard -

- } - /> - } - entityName="dashboard" - entityNamePlural="dashboards" - findItems={[Function]} - getDetailViewLink={[Function]} - headingId="dashboardListingHeading" - id="dashboard" - initialFilter="" - tableListTitle="Dashboards" - > - -
-
-`; - -exports[`after fetch renders all table rows 1`] = ` - - - Create a dashboard - - } - body={ - -

- Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations. -

-

- - Add some sample data - , - } - } - /> -

-
- } - iconType="dashboardApp" - title={ -

- Create your first dashboard -

- } - /> - } - entityName="dashboard" - entityNamePlural="dashboards" - findItems={[Function]} - getDetailViewLink={[Function]} - headingId="dashboardListingHeading" - id="dashboard" - initialFilter="" - tableListTitle="Dashboards" - > - -
-
-`; - -exports[`after fetch renders call to action when no dashboards exist 1`] = ` - - - Create a dashboard - - } - body={ - -

- Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations. -

-

- - Add some sample data - , - } - } - /> -

-
- } - iconType="dashboardApp" - title={ -

- Create your first dashboard -

- } - /> - } - entityName="dashboard" - entityNamePlural="dashboards" - findItems={[Function]} - getDetailViewLink={[Function]} - headingId="dashboardListingHeading" - id="dashboard" - initialFilter="" - tableListTitle="Dashboards" - > - -
-
-`; - -exports[`after fetch renders call to action with continue when no dashboards exist but one is in progress 1`] = ` - - - - - Discard changes - - - - - Continue editing - - - - } - body={ - -

- Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations. -

-
- } - iconType="dashboardApp" - title={ -

- Dashboard in progress -

- } - /> - } - entityName="dashboard" - entityNamePlural="dashboards" - findItems={[Function]} - getDetailViewLink={[Function]} - headingId="dashboardListingHeading" - id="dashboard" - initialFilter="" - tableListTitle="Dashboards" - > - -
-
-`; - -exports[`after fetch showWriteControls 1`] = ` - - - There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator. -

- } - iconType="glasses" - title={ -

- No dashboards to view -

- } - /> - } - entityName="dashboard" - entityNamePlural="dashboards" - findItems={[Function]} - getDetailViewLink={[Function]} - headingId="dashboardListingHeading" - id="dashboard" - initialFilter="" - tableListTitle="Dashboards" - > - -
-
-`; diff --git a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.test.tsx deleted file mode 100644 index e638fbca60f05..0000000000000 --- a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.test.tsx +++ /dev/null @@ -1,212 +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 React from 'react'; -import { mount, ReactWrapper } from 'enzyme'; -import { act } from 'react-dom/test-utils'; -import { - TableListViewKibanaDependencies, - TableListViewKibanaProvider, -} from '@kbn/content-management-table-list'; -import { I18nProvider, FormattedRelative } from '@kbn/i18n-react'; -import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; - -import { pluginServices } from '../../services/plugin_services'; -import { DashboardListing, DashboardListingProps } from './dashboard_listing'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; - -jest.mock('react-router-dom', () => { - return { - useLocation: () => ({ - search: '', - }), - useHistory: () => ({ - push: () => undefined, - }), - }; -}); - -function makeDefaultProps(): DashboardListingProps { - return { - redirectTo: jest.fn(), - kbnUrlStateStorage: createKbnUrlStateStorage(), - }; -} - -function mountWith({ props: incomingProps }: { props?: DashboardListingProps }) { - const props = incomingProps ?? makeDefaultProps(); - const wrappingComponent: React.FC<{ - children: React.ReactNode; - }> = ({ children }) => { - const { application, notifications, savedObjectsTagging, http, overlays } = - pluginServices.getServices(); - - return ( - - ({ - searchTerm: '', - tagReferences: [], - tagReferencesToExclude: [], - }), - components: { - TagList: () => null, - }, - }, - } as unknown as TableListViewKibanaDependencies['savedObjectsTagging'] - } - FormattedRelative={FormattedRelative} - toMountPoint={() => () => () => undefined} - > - {children} - - - ); - }; - const component = mount(, { wrappingComponent }); - return { component, props }; -} - -describe('after fetch', () => { - test('renders all table rows', async () => { - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({})); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(component!).toMatchSnapshot(); - }); - - test('renders call to action when no dashboards exist', async () => { - ( - pluginServices.getServices().dashboardSavedObject.findDashboards.findSavedObjects as jest.Mock - ).mockResolvedValue({ - total: 0, - hits: [], - }); - - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({})); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(component!).toMatchSnapshot(); - }); - - test('renders call to action with continue when no dashboards exist but one is in progress', async () => { - pluginServices.getServices().dashboardSessionStorage.getDashboardIdsWithUnsavedChanges = jest - .fn() - .mockReturnValueOnce([DASHBOARD_PANELS_UNSAVED_ID]) - .mockReturnValue(['dashboardUnsavedOne', 'dashboardUnsavedTwo']); - ( - pluginServices.getServices().dashboardSavedObject.findDashboards.findSavedObjects as jest.Mock - ).mockResolvedValue({ - total: 0, - hits: [], - }); - - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({})); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(component!).toMatchSnapshot(); - }); - - test('initialFilter', async () => { - const props = makeDefaultProps(); - props.initialFilter = 'testFilter'; - - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({})); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(component!).toMatchSnapshot(); - }); - - test('When given a title that matches multiple dashboards, filter on the title', async () => { - const title = 'search by title'; - const props = makeDefaultProps(); - props.title = title; - ( - pluginServices.getServices().dashboardSavedObject.findDashboards.findByTitle as jest.Mock - ).mockResolvedValue(undefined); - - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({ props })); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(component!).toMatchSnapshot(); - expect(props.redirectTo).not.toHaveBeenCalled(); - }); - - test('When given a title that matches one dashboard, redirect to dashboard', async () => { - const title = 'search by title'; - const props = makeDefaultProps(); - props.title = title; - ( - pluginServices.getServices().dashboardSavedObject.findDashboards.findByTitle as jest.Mock - ).mockResolvedValue({ id: 'you_found_me' }); - - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({ props })); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(props.redirectTo).toHaveBeenCalledWith({ - destination: 'dashboard', - id: 'you_found_me', - useReplace: true, - }); - }); - - test('showWriteControls', async () => { - pluginServices.getServices().dashboardCapabilities.showWriteControls = false; - - let component: ReactWrapper; - - await act(async () => { - ({ component } = mountWith({})); - }); - - // Ensure the state changes are reflected - component!.update(); - expect(component!).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx deleted file mode 100644 index d857e640c04da..0000000000000 --- a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx +++ /dev/null @@ -1,383 +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 useMount from 'react-use/lib/useMount'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; - -import { - EuiLink, - EuiButton, - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, -} from '@elastic/eui'; -import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; -import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; -import type { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; -import type { SavedObjectsFindOptionsReference, SimpleSavedObject } from '@kbn/core/public'; -import { TableListView, type UserContentCommonSchema } from '@kbn/content-management-table-list'; - -import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; -import { SAVED_OBJECT_DELETE_TIME, SAVED_OBJECT_LOADED_TIME } from '../../dashboard_constants'; - -import { - getDashboardBreadcrumb, - dashboardListingTableStrings, - noItemsStrings, - dashboardUnsavedListingStrings, - getNewDashboardTitle, - dashboardListingErrorStrings, -} from '../_dashboard_app_strings'; -import { - DashboardAppNoDataPage, - isDashboardAppInNoDataState, -} from '../no_data/dashboard_app_no_data'; -import { DashboardRedirect } from '../types'; -import { DashboardAttributes } from '../../../common'; -import { pluginServices } from '../../services/plugin_services'; -import { DashboardUnsavedListing } from './dashboard_unsaved_listing'; -import { DASHBOARD_SAVED_OBJECT_TYPE } from '../../dashboard_constants'; -import { getDashboardListItemLink } from './get_dashboard_list_item_link'; -import { confirmCreateWithUnsaved, confirmDiscardUnsavedChanges } from './confirm_overlays'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; - -const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; -const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; - -interface DashboardSavedObjectUserContent extends UserContentCommonSchema { - attributes: { - title: string; - description?: string; - timeRestore: boolean; - }; -} - -const toTableListViewSavedObject = ( - savedObject: SimpleSavedObject -): DashboardSavedObjectUserContent => { - const { title, description, timeRestore } = savedObject.attributes; - return { - type: 'dashboard', - id: savedObject.id, - updatedAt: savedObject.updatedAt!, - references: savedObject.references, - attributes: { - title, - description, - timeRestore, - }, - }; -}; - -export interface DashboardListingProps { - kbnUrlStateStorage: IKbnUrlStateStorage; - redirectTo: DashboardRedirect; - initialFilter?: string; - title?: string; -} - -export const DashboardListing = ({ - title, - redirectTo, - initialFilter, - kbnUrlStateStorage, -}: DashboardListingProps) => { - const { - application, - data: { query }, - dashboardSessionStorage, - settings: { uiSettings }, - notifications: { toasts }, - chrome: { setBreadcrumbs }, - coreContext: { executionContext }, - dashboardCapabilities: { showWriteControls }, - dashboardSavedObject: { findDashboards, savedObjectsClient }, - } = pluginServices.getServices(); - - const [showNoDataPage, setShowNoDataPage] = useState(false); - useMount(() => { - (async () => setShowNoDataPage(await isDashboardAppInNoDataState()))(); - }); - - const [unsavedDashboardIds, setUnsavedDashboardIds] = useState( - dashboardSessionStorage.getDashboardIdsWithUnsavedChanges() - ); - - useExecutionContext(executionContext, { - type: 'application', - page: 'list', - }); - - // Set breadcrumbs useEffect - useEffect(() => { - setBreadcrumbs([ - { - text: getDashboardBreadcrumb(), - }, - ]); - }, [setBreadcrumbs]); - - useEffect(() => { - // syncs `_g` portion of url with query services - const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( - query, - kbnUrlStateStorage - ); - if (title) { - findDashboards.findByTitle(title).then((result) => { - if (!result) return; - redirectTo({ - destination: 'dashboard', - id: result.id, - useReplace: true, - }); - }); - } - - return () => { - stopSyncingQueryServiceStateWithUrl(); - }; - }, [title, redirectTo, query, kbnUrlStateStorage, findDashboards]); - - const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); - const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); - const defaultFilter = title ? `${title}` : ''; - - const createItem = useCallback(() => { - if (!dashboardSessionStorage.dashboardHasUnsavedEdits()) { - redirectTo({ destination: 'dashboard' }); - } else { - confirmCreateWithUnsaved( - () => { - dashboardSessionStorage.clearState(); - redirectTo({ destination: 'dashboard' }); - }, - () => redirectTo({ destination: 'dashboard' }) - ); - } - }, [dashboardSessionStorage, redirectTo]); - - const emptyPrompt = useMemo(() => { - if (!showWriteControls) { - return ( - - {noItemsStrings.getReadonlyTitle()} - - } - body={

{noItemsStrings.getReadonlyBody()}

} - /> - ); - } - - const isEditingFirstDashboard = unsavedDashboardIds.length === 1; - - const emptyAction = isEditingFirstDashboard ? ( - - - - confirmDiscardUnsavedChanges(() => { - dashboardSessionStorage.clearState(DASHBOARD_PANELS_UNSAVED_ID); - setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()); - }) - } - data-test-subj="discardDashboardPromptButton" - aria-label={dashboardUnsavedListingStrings.getDiscardAriaLabel(getNewDashboardTitle())} - > - {dashboardUnsavedListingStrings.getDiscardTitle()} - - - - redirectTo({ destination: 'dashboard' })} - data-test-subj="newItemButton" - aria-label={dashboardUnsavedListingStrings.getEditAriaLabel(getNewDashboardTitle())} - > - {dashboardUnsavedListingStrings.getEditTitle()} - - - - ) : ( - - {noItemsStrings.getCreateNewDashboardText()} - - ); - - return ( - - {isEditingFirstDashboard - ? noItemsStrings.getReadEditInProgressTitle() - : noItemsStrings.getReadEditTitle()} - - } - body={ - <> -

{noItemsStrings.getReadEditDashboardDescription()}

- {!isEditingFirstDashboard && ( -

- - application.navigateToApp('home', { - path: '#/tutorial_directory/sampleData', - }) - } - > - {noItemsStrings.getSampleDataLinkText()} - - ), - }} - /> -

- )} - - } - actions={emptyAction} - /> - ); - }, [ - redirectTo, - createItem, - application, - showWriteControls, - unsavedDashboardIds, - dashboardSessionStorage, - ]); - - const fetchItems = useCallback( - ( - searchTerm: string, - { - references, - referencesToExclude, - }: { - references?: SavedObjectsFindOptionsReference[]; - referencesToExclude?: SavedObjectsFindOptionsReference[]; - } = {} - ) => { - const searchStartTime = window.performance.now(); - return findDashboards - .findSavedObjects({ - search: searchTerm, - size: listingLimit, - hasReference: references, - hasNoReference: referencesToExclude, - }) - .then(({ total, hits }) => { - const searchEndTime = window.performance.now(); - const searchDuration = searchEndTime - searchStartTime; - reportPerformanceMetricEvent(pluginServices.getServices().analytics, { - eventName: SAVED_OBJECT_LOADED_TIME, - duration: searchDuration, - meta: { - saved_object_type: DASHBOARD_SAVED_OBJECT_TYPE, - }, - }); - return { - total, - hits: hits.map(toTableListViewSavedObject), - }; - }); - }, - [findDashboards, listingLimit] - ); - - const deleteItems = useCallback( - async (dashboardsToDelete: Array<{ id: string }>) => { - try { - const deleteStartTime = window.performance.now(); - - await Promise.all( - dashboardsToDelete.map(({ id }) => { - dashboardSessionStorage.clearState(id); - return savedObjectsClient.delete(DASHBOARD_SAVED_OBJECT_TYPE, id); - }) - ); - - const deleteDuration = window.performance.now() - deleteStartTime; - reportPerformanceMetricEvent(pluginServices.getServices().analytics, { - eventName: SAVED_OBJECT_DELETE_TIME, - duration: deleteDuration, - meta: { - saved_object_type: DASHBOARD_SAVED_OBJECT_TYPE, - total: dashboardsToDelete.length, - }, - }); - } catch (error) { - toasts.addError(error, { - title: dashboardListingErrorStrings.getErrorDeletingDashboardToast(), - }); - } - - setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()); - }, - [savedObjectsClient, dashboardSessionStorage, toasts] - ); - - const editItem = useCallback( - ({ id }: { id: string | undefined }) => - redirectTo({ destination: 'dashboard', id, editMode: true }), - [redirectTo] - ); - - const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings; - return ( - <> - {showNoDataPage && ( - setShowNoDataPage(false)} /> - )} - {!showNoDataPage && ( - - createItem={!showWriteControls ? undefined : createItem} - deleteItems={!showWriteControls ? undefined : deleteItems} - initialPageSize={initialPageSize} - editItem={!showWriteControls ? undefined : editItem} - initialFilter={initialFilter ?? defaultFilter} - headingId="dashboardListingHeading" - findItems={fetchItems} - entityNamePlural={getEntityNamePlural()} - tableListTitle={getTableListTitle()} - entityName={getEntityName()} - {...{ - emptyPrompt, - listingLimit, - }} - id="dashboard" - getDetailViewLink={({ id, attributes: { timeRestore } }) => - getDashboardListItemLink(kbnUrlStateStorage, id, timeRestore) - } - > - - setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()) - } - /> - - )} - - ); -}; diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx new file mode 100644 index 0000000000000..3ae147d9e7e40 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.test.tsx @@ -0,0 +1,127 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { mount, ReactWrapper } from 'enzyme'; +import { I18nProvider } from '@kbn/i18n-react'; +import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; + +import { pluginServices } from '../../services/plugin_services'; +import { DashboardListingPage, DashboardListingPageProps } from './dashboard_listing_page'; + +// Mock child components. The Dashboard listing page mostly passes down props to shared UX components which are tested in their own packages. +import { DashboardListing } from '../../dashboard_listing/dashboard_listing'; +jest.mock('../../dashboard_listing/dashboard_listing', () => { + return { + __esModule: true, + DashboardListing: jest.fn().mockReturnValue(null), + }; +}); + +import { DashboardAppNoDataPage } from '../no_data/dashboard_app_no_data'; +jest.mock('../no_data/dashboard_app_no_data', () => { + const originalModule = jest.requireActual('../no_data/dashboard_app_no_data'); + return { + __esModule: true, + ...originalModule, + DashboardAppNoDataPage: jest.fn().mockReturnValue(null), + }; +}); + +function makeDefaultProps(): DashboardListingPageProps { + return { + redirectTo: jest.fn(), + kbnUrlStateStorage: createKbnUrlStateStorage(), + }; +} + +function mountWith({ props: incomingProps }: { props?: DashboardListingPageProps }) { + const props = incomingProps ?? makeDefaultProps(); + const wrappingComponent: React.FC<{ + children: React.ReactNode; + }> = ({ children }) => { + return {children}; + }; + const component = mount(, { wrappingComponent }); + return { component, props }; +} + +test('renders analytics no data page when the user has no data view', async () => { + pluginServices.getServices().data.dataViews.hasData.hasUserDataView = jest + .fn() + .mockResolvedValue(false); + + let component: ReactWrapper; + await act(async () => { + ({ component } = mountWith({})); + }); + component!.update(); + expect(DashboardAppNoDataPage).toHaveBeenCalled(); +}); + +test('initialFilter is passed through if title is not provided', async () => { + const props = makeDefaultProps(); + props.initialFilter = 'filterPassThrough'; + + let component: ReactWrapper; + + await act(async () => { + ({ component } = mountWith({ props })); + }); + + component!.update(); + expect(DashboardListing).toHaveBeenCalledWith( + expect.objectContaining({ initialFilter: 'filterPassThrough' }), + expect.any(Object) // react context + ); +}); + +test('When given a title that matches multiple dashboards, filter on the title', async () => { + const title = 'search by title'; + const props = makeDefaultProps(); + props.title = title; + + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByTitle as jest.Mock + ).mockResolvedValue(undefined); + + let component: ReactWrapper; + + await act(async () => { + ({ component } = mountWith({ props })); + }); + component!.update(); + + expect(props.redirectTo).not.toHaveBeenCalled(); + expect(DashboardListing).toHaveBeenCalledWith( + expect.objectContaining({ initialFilter: 'search by title' }), + expect.any(Object) // react context + ); +}); + +test('When given a title that matches one dashboard, redirect to dashboard', async () => { + const title = 'search by title'; + const props = makeDefaultProps(); + props.title = title; + ( + pluginServices.getServices().dashboardSavedObject.findDashboards.findByTitle as jest.Mock + ).mockResolvedValue({ id: 'you_found_me' }); + + let component: ReactWrapper; + + await act(async () => { + ({ component } = mountWith({ props })); + }); + component!.update(); + expect(props.redirectTo).toHaveBeenCalledWith({ + destination: 'dashboard', + id: 'you_found_me', + useReplace: true, + }); +}); diff --git a/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx new file mode 100644 index 0000000000000..9e46bb1c3cabe --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_listing_page.tsx @@ -0,0 +1,107 @@ +/* + * 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, { useEffect, useState } from 'react'; + +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; +import type { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; + +import { + DashboardAppNoDataPage, + isDashboardAppInNoDataState, +} from '../no_data/dashboard_app_no_data'; +import { DashboardRedirect } from '../types'; +import { pluginServices } from '../../services/plugin_services'; +import { getDashboardBreadcrumb } from '../_dashboard_app_strings'; +import { getDashboardListItemLink } from './get_dashboard_list_item_link'; +import { DashboardListing } from '../../dashboard_listing/dashboard_listing'; + +export interface DashboardListingPageProps { + kbnUrlStateStorage: IKbnUrlStateStorage; + redirectTo: DashboardRedirect; + initialFilter?: string; + title?: string; +} + +export const DashboardListingPage = ({ + title, + redirectTo, + initialFilter, + kbnUrlStateStorage, +}: DashboardListingPageProps) => { + const { + data: { query }, + chrome: { setBreadcrumbs }, + dashboardSavedObject: { findDashboards }, + } = pluginServices.getServices(); + + const [showNoDataPage, setShowNoDataPage] = useState(false); + useEffect(() => { + let isMounted = true; + (async () => { + const isInNoDataState = await isDashboardAppInNoDataState(); + if (isInNoDataState && isMounted) setShowNoDataPage(true); + })(); + return () => { + isMounted = false; + }; + }, []); + + useEffect(() => { + setBreadcrumbs([ + { + text: getDashboardBreadcrumb(), + }, + ]); + }, [setBreadcrumbs]); + + useEffect(() => { + // syncs `_g` portion of url with query services + const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( + query, + kbnUrlStateStorage + ); + if (title) { + findDashboards.findByTitle(title).then((result) => { + if (!result) return; + redirectTo({ + destination: 'dashboard', + id: result.id, + useReplace: true, + }); + }); + } + + return () => { + stopSyncingQueryServiceStateWithUrl(); + }; + }, [title, redirectTo, query, kbnUrlStateStorage, findDashboards]); + + const titleFilter = title ? `${title}` : ''; + + return ( + <> + {showNoDataPage && ( + setShowNoDataPage(false)} /> + )} + {!showNoDataPage && ( + { + redirectTo({ destination: 'dashboard', id, editMode: viewMode === ViewMode.EDIT }); + }} + getDashboardUrl={(id, timeRestore) => { + return getDashboardListItemLink(kbnUrlStateStorage, id, timeRestore); + }} + /> + )} + + ); +}; diff --git a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_no_match.tsx b/src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_no_match.tsx similarity index 100% rename from src/plugins/dashboard/public/dashboard_app/listing/dashboard_no_match.tsx rename to src/plugins/dashboard/public/dashboard_app/listing_page/dashboard_no_match.tsx diff --git a/src/plugins/dashboard/public/dashboard_app/listing/get_dashboard_list_item_link.test.ts b/src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.test.ts similarity index 100% rename from src/plugins/dashboard/public/dashboard_app/listing/get_dashboard_list_item_link.test.ts rename to src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.test.ts diff --git a/src/plugins/dashboard/public/dashboard_app/listing/get_dashboard_list_item_link.ts b/src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.ts similarity index 100% rename from src/plugins/dashboard/public/dashboard_app/listing/get_dashboard_list_item_link.ts rename to src/plugins/dashboard/public/dashboard_app/listing_page/get_dashboard_list_item_link.ts diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx index 645aa42ec918d..b548d2e39afc5 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx @@ -19,8 +19,8 @@ import { ShowShareModal } from './share/show_share_modal'; import { pluginServices } from '../../services/plugin_services'; import { CHANGE_CHECK_DEBOUNCE } from '../../dashboard_constants'; import { SaveDashboardReturn } from '../../services/dashboard_saved_object/types'; +import { confirmDiscardUnsavedChanges } from '../../dashboard_listing/confirm_overlays'; import { useDashboardContainerContext } from '../../dashboard_container/dashboard_container_context'; -import { confirmDiscardUnsavedChanges } from '../listing/confirm_overlays'; export const useDashboardMenuItems = ({ redirectTo, diff --git a/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts b/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts new file mode 100644 index 0000000000000..df036a01fcf95 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts @@ -0,0 +1,144 @@ +/* + * 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'; + +export const dashboardListingErrorStrings = { + getErrorDeletingDashboardToast: () => + i18n.translate('dashboard.deleteError.toastDescription', { + defaultMessage: 'Error encountered while deleting dashboard', + }), +}; + +export const getNewDashboardTitle = () => + i18n.translate('dashboard.listing.newDashboardTitle', { + defaultMessage: 'New Dashboard', + }); + +export const dashboardListingTableStrings = { + getEntityName: () => + i18n.translate('dashboard.listing.table.entityName', { + defaultMessage: 'dashboard', + }), + getEntityNamePlural: () => + i18n.translate('dashboard.listing.table.entityNamePlural', { + defaultMessage: 'dashboards', + }), + getTableListTitle: () => + i18n.translate('dashboard.listing.tableListTitle', { + defaultMessage: 'Dashboards', + }), +}; + +export const noItemsStrings = { + getReadonlyTitle: () => + i18n.translate('dashboard.listing.readonlyNoItemsTitle', { + defaultMessage: 'No dashboards to view', + }), + getReadonlyBody: () => + i18n.translate('dashboard.listing.readonlyNoItemsBody', { + defaultMessage: `There are no available dashboards. To change your permissions to view the dashboards in this space, contact your administrator.`, + }), + getReadEditTitle: () => + i18n.translate('dashboard.listing.createNewDashboard.title', { + defaultMessage: 'Create your first dashboard', + }), + getReadEditInProgressTitle: () => + i18n.translate('dashboard.listing.createNewDashboard.inProgressTitle', { + defaultMessage: 'Dashboard in progress', + }), + getReadEditDashboardDescription: () => + i18n.translate('dashboard.listing.createNewDashboard.combineDataViewFromKibanaAppDescription', { + defaultMessage: + 'Analyze all of your Elastic data in one place by creating a dashboard and adding visualizations.', + }), + getSampleDataLinkText: () => + i18n.translate('dashboard.listing.createNewDashboard.sampleDataInstallLinkText', { + defaultMessage: `Add some sample data`, + }), + getCreateNewDashboardText: () => + i18n.translate('dashboard.listing.createNewDashboard.createButtonLabel', { + defaultMessage: `Create a dashboard`, + }), +}; + +export const dashboardUnsavedListingStrings = { + getUnsavedChangesTitle: (plural = false) => + i18n.translate('dashboard.listing.unsaved.unsavedChangesTitle', { + defaultMessage: 'You have unsaved changes in the following {dash}:', + values: { + dash: plural + ? dashboardListingTableStrings.getEntityNamePlural() + : dashboardListingTableStrings.getEntityName(), + }, + }), + getLoadingTitle: () => + i18n.translate('dashboard.listing.unsaved.loading', { + defaultMessage: 'Loading', + }), + getEditAriaLabel: (title: string) => + i18n.translate('dashboard.listing.unsaved.editAria', { + defaultMessage: 'Continue editing {title}', + values: { title }, + }), + getEditTitle: () => + i18n.translate('dashboard.listing.unsaved.editTitle', { + defaultMessage: 'Continue editing', + }), + getDiscardAriaLabel: (title: string) => + i18n.translate('dashboard.listing.unsaved.discardAria', { + defaultMessage: 'Discard changes to {title}', + values: { title }, + }), + getDiscardTitle: () => + i18n.translate('dashboard.listing.unsaved.discardTitle', { + defaultMessage: 'Discard changes', + }), +}; + +export const discardConfirmStrings = { + getDiscardTitle: () => + i18n.translate('dashboard.discardChangesConfirmModal.discardChangesTitle', { + defaultMessage: 'Discard changes to dashboard?', + }), + getDiscardSubtitle: () => + i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { + defaultMessage: `Once you discard your changes, there's no getting them back.`, + }), + getDiscardConfirmButtonText: () => + i18n.translate('dashboard.discardChangesConfirmModal.confirmButtonLabel', { + defaultMessage: 'Discard changes', + }), + getDiscardCancelButtonText: () => + i18n.translate('dashboard.discardChangesConfirmModal.cancelButtonLabel', { + defaultMessage: 'Cancel', + }), +}; + +export const createConfirmStrings = { + getCreateTitle: () => + i18n.translate('dashboard.createConfirmModal.unsavedChangesTitle', { + defaultMessage: 'New dashboard already in progress', + }), + getCreateSubtitle: () => + i18n.translate('dashboard.createConfirmModal.unsavedChangesSubtitle', { + defaultMessage: 'Continue editing or start over with a blank dashboard.', + }), + getStartOverButtonText: () => + i18n.translate('dashboard.createConfirmModal.confirmButtonLabel', { + defaultMessage: 'Start over', + }), + getContinueButtonText: () => + i18n.translate('dashboard.createConfirmModal.continueButtonLabel', { + defaultMessage: 'Continue editing', + }), + getCancelButtonText: () => + i18n.translate('dashboard.createConfirmModal.cancelButtonLabel', { + defaultMessage: 'Cancel', + }), +}; diff --git a/src/plugins/dashboard/public/dashboard_app/listing/confirm_overlays.tsx b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx similarity index 96% rename from src/plugins/dashboard/public/dashboard_app/listing/confirm_overlays.tsx rename to src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx index b8d6f436c38ba..03027cda242b9 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing/confirm_overlays.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx @@ -22,8 +22,8 @@ import { } from '@elastic/eui'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { pluginServices } from '../../services/plugin_services'; -import { createConfirmStrings, discardConfirmStrings } from '../_dashboard_app_strings'; +import { pluginServices } from '../services/plugin_services'; +import { createConfirmStrings, discardConfirmStrings } from './_dashboard_listing_strings'; export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep'; diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx new file mode 100644 index 0000000000000..383b51fbd65fa --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx @@ -0,0 +1,105 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { mount, ReactWrapper } from 'enzyme'; +import { I18nProvider } from '@kbn/i18n-react'; + +import { pluginServices } from '../services/plugin_services'; +import { DashboardListing, DashboardListingProps } from './dashboard_listing'; + +/** + * Mock Table List view. This dashboard component is a wrapper around the shared UX table List view. We + * need to ensure we're passing down the correct props, but the table list view itself doesn't need to be rendered + * in our tests because it is covered in its package. + */ +import { TableListView } from '@kbn/content-management-table-list'; +// import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +jest.mock('@kbn/content-management-table-list', () => { + const originalModule = jest.requireActual('@kbn/content-management-table-list'); + return { + __esModule: true, + ...originalModule, + TableListViewKibanaProvider: jest.fn().mockImplementation(({ children }) => { + return <>{children}; + }), + TableListView: jest.fn().mockReturnValue(null), + }; +}); + +function makeDefaultProps(): DashboardListingProps { + return { + goToDashboard: jest.fn(), + getDashboardUrl: jest.fn(), + }; +} + +function mountWith({ props: incomingProps }: { props?: Partial }) { + const props = { ...makeDefaultProps(), ...incomingProps }; + const wrappingComponent: React.FC<{ + children: React.ReactNode; + }> = ({ children }) => { + return {children}; + }; + const component = mount(, { wrappingComponent }); + return { component, props }; +} + +test('initial filter is passed through', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + + await act(async () => { + ({ component } = mountWith({ props: { initialFilter: 'kibanana' } })); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ initialFilter: 'kibanana' }), + expect.any(Object) // react context + ); +}); + +test('when showWriteControls is true, table list view is passed editing functions', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + + let component: ReactWrapper; + + await act(async () => { + ({ component } = mountWith({})); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ + createItem: expect.any(Function), + deleteItems: expect.any(Function), + editItem: expect.any(Function), + }), + expect.any(Object) // react context + ); +}); + +test('when showWriteControls is false, table list view is not passed editing functions', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + + await act(async () => { + ({ component } = mountWith({})); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ + createItem: undefined, + deleteItems: undefined, + editItem: undefined, + }), + expect.any(Object) // react context + ); +}); diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx new file mode 100644 index 0000000000000..8572356687fd6 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -0,0 +1,264 @@ +/* + * 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 { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; +import React, { PropsWithChildren, useCallback, useState } from 'react'; + +import { + TableListView, + TableListViewKibanaDependencies, + TableListViewKibanaProvider, + type UserContentCommonSchema, +} from '@kbn/content-management-table-list'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; +import { toMountPoint, useExecutionContext } from '@kbn/kibana-react-plugin/public'; +import type { SavedObjectsFindOptionsReference, SimpleSavedObject } from '@kbn/core/public'; + +import { + SAVED_OBJECT_DELETE_TIME, + SAVED_OBJECT_LOADED_TIME, + DASHBOARD_SAVED_OBJECT_TYPE, +} from '../dashboard_constants'; +import { + dashboardListingTableStrings, + dashboardListingErrorStrings, +} from './_dashboard_listing_strings'; +import { DashboardAttributes } from '../../common'; +import { pluginServices } from '../services/plugin_services'; +import { confirmCreateWithUnsaved } from './confirm_overlays'; +import { DashboardUnsavedListing } from './dashboard_unsaved_listing'; +import { DashboardApplicationService } from '../services/application/types'; +import { DashboardListingEmptyPrompt } from './dashboard_listing_empty_prompt'; + +// because the type of `application.capabilities.advancedSettings` is so generic, the provider +// requiring the `save` key to be part of it is causing type issues - so, creating a custom type +type TableListViewApplicationService = DashboardApplicationService & { + capabilities: { advancedSettings: { save: boolean } }; +}; + +const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; +const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; + +interface DashboardSavedObjectUserContent extends UserContentCommonSchema { + attributes: { + title: string; + description?: string; + timeRestore: boolean; + }; +} + +const toTableListViewSavedObject = ( + savedObject: SimpleSavedObject +): DashboardSavedObjectUserContent => { + const { title, description, timeRestore } = savedObject.attributes; + return { + type: 'dashboard', + id: savedObject.id, + updatedAt: savedObject.updatedAt!, + references: savedObject.references, + attributes: { + title, + description, + timeRestore, + }, + }; +}; + +export type DashboardListingProps = PropsWithChildren<{ + initialFilter?: string; + useSessionStorageIntegration?: boolean; + goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; + getDashboardUrl: (dashboardId: string, usesTimeRestore: boolean) => string; +}>; + +export const DashboardListing = ({ + children, + initialFilter, + goToDashboard, + getDashboardUrl, + useSessionStorageIntegration, +}: DashboardListingProps) => { + const { + application, + notifications, + overlays, + http, + savedObjectsTagging, + dashboardSessionStorage, + settings: { uiSettings }, + notifications: { toasts }, + coreContext: { executionContext }, + dashboardCapabilities: { showWriteControls }, + dashboardSavedObject: { findDashboards, savedObjectsClient }, + } = pluginServices.getServices(); + + const [unsavedDashboardIds, setUnsavedDashboardIds] = useState( + dashboardSessionStorage.getDashboardIdsWithUnsavedChanges() + ); + + useExecutionContext(executionContext, { + type: 'application', + page: 'list', + }); + + const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); + const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + + const createItem = useCallback(() => { + if (useSessionStorageIntegration && dashboardSessionStorage.dashboardHasUnsavedEdits()) { + confirmCreateWithUnsaved(() => { + dashboardSessionStorage.clearState(); + goToDashboard(); + }, goToDashboard); + return; + } + goToDashboard(); + }, [dashboardSessionStorage, goToDashboard, useSessionStorageIntegration]); + + const fetchItems = useCallback( + ( + searchTerm: string, + { + references, + referencesToExclude, + }: { + references?: SavedObjectsFindOptionsReference[]; + referencesToExclude?: SavedObjectsFindOptionsReference[]; + } = {} + ) => { + const searchStartTime = window.performance.now(); + return findDashboards + .findSavedObjects({ + search: searchTerm, + size: listingLimit, + hasReference: references, + hasNoReference: referencesToExclude, + }) + .then(({ total, hits }) => { + const searchEndTime = window.performance.now(); + const searchDuration = searchEndTime - searchStartTime; + reportPerformanceMetricEvent(pluginServices.getServices().analytics, { + eventName: SAVED_OBJECT_LOADED_TIME, + duration: searchDuration, + meta: { + saved_object_type: DASHBOARD_SAVED_OBJECT_TYPE, + }, + }); + return { + total, + hits: hits.map(toTableListViewSavedObject), + }; + }); + }, + [findDashboards, listingLimit] + ); + + const deleteItems = useCallback( + async (dashboardsToDelete: Array<{ id: string }>) => { + try { + const deleteStartTime = window.performance.now(); + + await Promise.all( + dashboardsToDelete.map(({ id }) => { + dashboardSessionStorage.clearState(id); + return savedObjectsClient.delete(DASHBOARD_SAVED_OBJECT_TYPE, id); + }) + ); + + const deleteDuration = window.performance.now() - deleteStartTime; + reportPerformanceMetricEvent(pluginServices.getServices().analytics, { + eventName: SAVED_OBJECT_DELETE_TIME, + duration: deleteDuration, + meta: { + saved_object_type: DASHBOARD_SAVED_OBJECT_TYPE, + total: dashboardsToDelete.length, + }, + }); + } catch (error) { + toasts.addError(error, { + title: dashboardListingErrorStrings.getErrorDeletingDashboardToast(), + }); + } + + setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()); + }, + [savedObjectsClient, dashboardSessionStorage, toasts] + ); + + const editItem = useCallback( + ({ id }: { id: string | undefined }) => goToDashboard(id, ViewMode.EDIT), + [goToDashboard] + ); + const emptyPrompt = ( + + ); + + const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings; + + return ( + + + + getDetailViewLink={({ id, attributes: { timeRestore } }) => + getDashboardUrl(id, timeRestore) + } + deleteItems={!showWriteControls ? undefined : deleteItems} + createItem={!showWriteControls ? undefined : createItem} + editItem={!showWriteControls ? undefined : editItem} + entityNamePlural={getEntityNamePlural()} + tableListTitle={getTableListTitle()} + headingId="dashboardListingHeading" + initialPageSize={initialPageSize} + initialFilter={initialFilter} + entityName={getEntityName()} + listingLimit={listingLimit} + emptyPrompt={emptyPrompt} + findItems={fetchItems} + id="dashboard" + > + <> + {children} + + setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()) + } + /> + + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default DashboardListing; diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx new file mode 100644 index 0000000000000..886d43a1db6d9 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx @@ -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 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 { act } from 'react-dom/test-utils'; +import { mount, ReactWrapper } from 'enzyme'; + +import { I18nProvider } from '@kbn/i18n-react'; + +import { + DashboardListingEmptyPrompt, + DashboardListingEmptyPromptProps, +} from './dashboard_listing_empty_prompt'; +import { pluginServices } from '../services/plugin_services'; +import { confirmDiscardUnsavedChanges } from './confirm_overlays'; + +jest.mock('./confirm_overlays', () => { + const originalModule = jest.requireActual('./confirm_overlays'); + return { + __esModule: true, + ...originalModule, + confirmDiscardUnsavedChanges: jest.fn(), + }; +}); + +const makeDefaultProps = (): DashboardListingEmptyPromptProps => ({ + createItem: jest.fn(), + unsavedDashboardIds: [], + goToDashboard: jest.fn(), + setUnsavedDashboardIds: jest.fn(), + useSessionStorageIntegration: true, +}); + +function mountWith({ + props: incomingProps, +}: { + props?: Partial; +}) { + const props = { ...makeDefaultProps(), ...incomingProps }; + const wrappingComponent: React.FC<{ + children: React.ReactNode; + }> = ({ children }) => { + return {children}; + }; + const component = mount(, { wrappingComponent }); + return { component, props }; +} + +test('renders readonly empty prompt when showWriteControls is off', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + await act(async () => { + ({ component } = mountWith({})); + }); + + component!.update(); + expect(component!.find('EuiLink').length).toBe(0); +}); + +test('renders empty prompt with link when showWriteControls is on', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + + let component: ReactWrapper; + await act(async () => { + ({ component } = mountWith({})); + }); + + component!.update(); + expect(component!.find('EuiLink').length).toBe(1); +}); + +test('renders continue button when no dashboards exist but one is in progress', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + let component: ReactWrapper; + let props: DashboardListingEmptyPromptProps; + await act(async () => { + ({ component, props } = mountWith({ + props: { unsavedDashboardIds: ['newDashboard'], useSessionStorageIntegration: true }, + })); + }); + component!.update(); + await act(async () => { + // EuiButton is used for the Continue button + const continueButton = component!.find('EuiButton'); + expect(continueButton.length).toBe(1); + continueButton.find('button').simulate('click'); + }); + expect(props!.goToDashboard).toHaveBeenCalled(); +}); + +test('renders discard button when no dashboards exist but one is in progress', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + let component: ReactWrapper; + await act(async () => { + ({ component } = mountWith({ + props: { unsavedDashboardIds: ['coolId'], useSessionStorageIntegration: true }, + })); + }); + component!.update(); + await act(async () => { + // EuiButtonEmpty is used for the discard button + component!.find('EuiButtonEmpty').simulate('click'); + }); + expect(confirmDiscardUnsavedChanges).toHaveBeenCalled(); +}); diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx new file mode 100644 index 0000000000000..a518c520bcbd8 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx @@ -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 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 { + EuiLink, + EuiButton, + EuiFlexItem, + EuiFlexGroup, + EuiButtonEmpty, + EuiEmptyPrompt, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useCallback, useMemo } from 'react'; + +import { + noItemsStrings, + getNewDashboardTitle, + dashboardUnsavedListingStrings, +} from './_dashboard_listing_strings'; +import { DashboardListingProps } from './dashboard_listing'; +import { pluginServices } from '../services/plugin_services'; +import { confirmDiscardUnsavedChanges } from './confirm_overlays'; +import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_session_storage/dashboard_session_storage_service'; + +export interface DashboardListingEmptyPromptProps { + createItem: () => void; + unsavedDashboardIds: string[]; + goToDashboard: DashboardListingProps['goToDashboard']; + setUnsavedDashboardIds: React.Dispatch>; + useSessionStorageIntegration: DashboardListingProps['useSessionStorageIntegration']; +} + +export const DashboardListingEmptyPrompt = ({ + useSessionStorageIntegration, + setUnsavedDashboardIds, + unsavedDashboardIds, + goToDashboard, + createItem, +}: DashboardListingEmptyPromptProps) => { + const { + application, + dashboardSessionStorage, + dashboardCapabilities: { showWriteControls }, + } = pluginServices.getServices(); + + const isEditingFirstDashboard = useMemo( + () => useSessionStorageIntegration && unsavedDashboardIds.length === 1, + [unsavedDashboardIds.length, useSessionStorageIntegration] + ); + + const getEmptyAction = useCallback(() => { + if (!isEditingFirstDashboard) { + return ( + + {noItemsStrings.getCreateNewDashboardText()} + + ); + } + return ( + + + + confirmDiscardUnsavedChanges(() => { + dashboardSessionStorage.clearState(DASHBOARD_PANELS_UNSAVED_ID); + setUnsavedDashboardIds(dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()); + }) + } + data-test-subj="discardDashboardPromptButton" + aria-label={dashboardUnsavedListingStrings.getDiscardAriaLabel(getNewDashboardTitle())} + > + {dashboardUnsavedListingStrings.getDiscardTitle()} + + + + goToDashboard()} + aria-label={dashboardUnsavedListingStrings.getEditAriaLabel(getNewDashboardTitle())} + > + {dashboardUnsavedListingStrings.getEditTitle()} + + + + ); + }, [ + dashboardSessionStorage, + isEditingFirstDashboard, + setUnsavedDashboardIds, + goToDashboard, + createItem, + ]); + + if (!showWriteControls) { + return ( + + {noItemsStrings.getReadonlyTitle()} + + } + body={

{noItemsStrings.getReadonlyBody()}

} + /> + ); + } + + return ( + + {isEditingFirstDashboard + ? noItemsStrings.getReadEditInProgressTitle() + : noItemsStrings.getReadEditTitle()} + + } + body={ + <> +

{noItemsStrings.getReadEditDashboardDescription()}

+ {!isEditingFirstDashboard && ( +

+ + application.navigateToApp('home', { + path: '#/tutorial_directory/sampleData', + }) + } + > + {noItemsStrings.getSampleDataLinkText()} + + ), + }} + /> +

+ )} + + } + actions={getEmptyAction()} + /> + ); +}; diff --git a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_unsaved_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx similarity index 88% rename from src/plugins/dashboard/public/dashboard_app/listing/dashboard_unsaved_listing.test.tsx rename to src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx index 34a78c5181b92..6ae8050b0df51 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_unsaved_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.test.tsx @@ -12,18 +12,19 @@ import { I18nProvider } from '@kbn/i18n-react'; import { waitFor } from '@testing-library/react'; import { findTestSubject } from '@elastic/eui/lib/test'; +import { pluginServices } from '../services/plugin_services'; import { DashboardUnsavedListing, DashboardUnsavedListingProps } from './dashboard_unsaved_listing'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; -import { pluginServices } from '../../services/plugin_services'; +import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_session_storage/dashboard_session_storage_service'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; const makeDefaultProps = (): DashboardUnsavedListingProps => ({ - redirectTo: jest.fn(), + goToDashboard: jest.fn(), unsavedDashboardIds: ['dashboardUnsavedOne', 'dashboardUnsavedTwo', 'dashboardUnsavedThree'], refreshUnsavedDashboards: jest.fn(), }); -function mountWith({ props: incomingProps }: { props?: DashboardUnsavedListingProps }) { - const props = incomingProps ?? makeDefaultProps(); +function mountWith({ props: incomingProps }: { props?: Partial }) { + const props = { ...makeDefaultProps(), ...incomingProps }; const wrappingComponent: React.FC<{ children: React.ReactNode; }> = ({ children }) => { @@ -62,11 +63,7 @@ describe('Unsaved listing', () => { expect(getEditButton().length).toEqual(1); }); getEditButton().simulate('click'); - expect(props.redirectTo).toHaveBeenCalledWith({ - destination: 'dashboard', - id: 'dashboardUnsavedOne', - editMode: true, - }); + expect(props.goToDashboard).toHaveBeenCalledWith('dashboardUnsavedOne', ViewMode.EDIT); }); it('Redirects to new dashboard when continue editing clicked', async () => { @@ -79,11 +76,7 @@ describe('Unsaved listing', () => { expect(getEditButton().length).toBe(1); }); getEditButton().simulate('click'); - expect(props.redirectTo).toHaveBeenCalledWith({ - destination: 'dashboard', - id: undefined, - editMode: true, - }); + expect(props.goToDashboard).toHaveBeenCalledWith(undefined, ViewMode.EDIT); }); it('Shows a warning then clears changes when delete unsaved changes is pressed', async () => { diff --git a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_unsaved_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx similarity index 92% rename from src/plugins/dashboard/public/dashboard_app/listing/dashboard_unsaved_listing.tsx rename to src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx index 2cf39d8ccb0d0..f4b7f91db77d3 100644 --- a/src/plugins/dashboard/public/dashboard_app/listing/dashboard_unsaved_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_unsaved_listing.tsx @@ -17,12 +17,13 @@ import { } from '@elastic/eui'; import React, { useCallback, useEffect, useState } from 'react'; -import type { DashboardRedirect } from '../types'; -import { DashboardAttributes } from '../../../common'; -import { pluginServices } from '../../services/plugin_services'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; + +import { DashboardAttributes } from '../../common'; +import { pluginServices } from '../services/plugin_services'; import { confirmDiscardUnsavedChanges } from './confirm_overlays'; -import { dashboardUnsavedListingStrings, getNewDashboardTitle } from '../_dashboard_app_strings'; -import { DASHBOARD_PANELS_UNSAVED_ID } from '../../services/dashboard_session_storage/dashboard_session_storage_service'; +import { dashboardUnsavedListingStrings, getNewDashboardTitle } from './_dashboard_listing_strings'; +import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_session_storage/dashboard_session_storage_service'; const DashboardUnsavedItem = ({ id, @@ -104,13 +105,13 @@ interface UnsavedItemMap { } export interface DashboardUnsavedListingProps { - refreshUnsavedDashboards: () => void; - redirectTo: DashboardRedirect; unsavedDashboardIds: string[]; + refreshUnsavedDashboards: () => void; + goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; } export const DashboardUnsavedListing = ({ - redirectTo, + goToDashboard, unsavedDashboardIds, refreshUnsavedDashboards, }: DashboardUnsavedListingProps) => { @@ -123,9 +124,9 @@ export const DashboardUnsavedListing = ({ const onOpen = useCallback( (id?: string) => { - redirectTo({ destination: 'dashboard', id, editMode: true }); + goToDashboard(id, ViewMode.EDIT); }, - [redirectTo] + [goToDashboard] ); const onDiscard = useCallback( diff --git a/src/plugins/dashboard/public/dashboard_listing/index.tsx b/src/plugins/dashboard/public/dashboard_listing/index.tsx new file mode 100644 index 0000000000000..92febf2904bdd --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_listing/index.tsx @@ -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 React, { Suspense } from 'react'; +import { EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; + +import { servicesReady } from '../plugin'; +import { DashboardListingProps } from './dashboard_listing'; + +const ListingTableLoadingIndicator = () => { + return } />; +}; + +const LazyDashboardListing = React.lazy(() => + (async () => { + const modulePromise = import('./dashboard_listing'); + const [module] = await Promise.all([modulePromise, servicesReady]); + + return { + default: module.DashboardListing, + }; + })().then((module) => module) +); + +export const DashboardListingTable = (props: DashboardListingProps) => { + return ( + }> + + + ); +}; diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index 4179de9e4f899..af7c9a307957d 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -23,6 +23,8 @@ export { } from './dashboard_container'; export type { DashboardSetup, DashboardStart, DashboardFeatureFlagConfig } from './plugin'; +export { DashboardListingTable } from './dashboard_listing'; + export { type DashboardAppLocator, type DashboardAppLocatorParams, diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 3c4031072ca92..093e879280d6f 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -62,8 +62,8 @@ import { LEGACY_DASHBOARD_APP_ID, SEARCH_SESSION_ID, } from './dashboard_constants'; -import { PlaceholderEmbeddableFactory } from './placeholder_embeddable'; import { DashboardMountContextProps } from './dashboard_app/types'; +import { PlaceholderEmbeddableFactory } from './placeholder_embeddable'; import type { FindDashboardsService } from './services/dashboard_saved_object/types'; export interface DashboardFeatureFlagConfig { @@ -114,6 +114,9 @@ export interface DashboardStart { findDashboardsService: () => Promise; } +export let resolveServicesReady: () => void; +export const servicesReady = new Promise((resolve) => (resolveServicesReady = resolve)); + export class DashboardPlugin implements Plugin @@ -133,6 +136,7 @@ export class DashboardPlugin ) { const { registry, pluginServices } = await import('./services/plugin_services'); pluginServices.setRegistry(registry.start({ coreStart, startPlugins, initContext })); + resolveServicesReady(); } public setup( diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts index 5c3148743a322..4e9fbdb31dca4 100644 --- a/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts @@ -37,6 +37,7 @@ export const dashboardSavedObjectServiceFactory: DashboardSavedObjectServiceFact description: `dashboard${i} desc`, title: `dashboard${i} - ${search} - title`, }, + references: [] as FindDashboardSavedObjectsResponse['hits'][0]['references'], } as FindDashboardSavedObjectsResponse['hits'][0]); } return Promise.resolve({ diff --git a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index f65b08fc632bc..0c60d3b0606be 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -204,7 +204,7 @@ Array [ class="euiSpacer euiSpacer--s emotion-euiSpacer-s" />,
@@ -255,7 +255,7 @@ Array [
@@ -546,7 +546,7 @@ Array [ class="euiSpacer euiSpacer--s emotion-euiSpacer-s" />,
@@ -597,7 +597,7 @@ Array [
diff --git a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx index 307c074ce5c6f..3deee5fe0bda9 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx +++ b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; - +import { css } from '@emotion/react'; import { EuiButtonIcon, EuiFlexGroup, @@ -192,12 +192,25 @@ export class DataTableFormat extends Component div:last-child { + position: sticky; + left: 0; + } + `} /> ); } diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts index c32a13e85888a..cf75c28743d4f 100644 --- a/src/plugins/data/server/saved_objects/query.ts +++ b/src/plugins/data/server/saved_objects/query.ts @@ -8,6 +8,7 @@ import { SavedObjectsType } from '@kbn/core/server'; import { savedQueryMigrations } from './migrations/query'; +import { SCHEMA_QUERY_V8_8_0 } from './schemas/query'; export const querySavedObjectType: SavedObjectsType = { name: 'query', @@ -29,21 +30,14 @@ export const querySavedObjectType: SavedObjectsType = { }, }, mappings: { + dynamic: false, properties: { title: { type: 'text' }, description: { type: 'text' }, - query: { - dynamic: false, - properties: { - language: { type: 'keyword' }, - }, - }, - filters: { - dynamic: false, - properties: {}, - }, - timefilter: { dynamic: false, properties: {} }, }, }, migrations: savedQueryMigrations, + schemas: { + '8.8.0': SCHEMA_QUERY_V8_8_0, + }, }; diff --git a/src/plugins/data/server/saved_objects/schemas/query.ts b/src/plugins/data/server/saved_objects/schemas/query.ts new file mode 100644 index 0000000000000..c460a06b9727a --- /dev/null +++ b/src/plugins/data/server/saved_objects/schemas/query.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 { schema } from '@kbn/config-schema'; + +// As per `SavedQueryAttributes` +export const SCHEMA_QUERY_V8_8_0 = schema.object({ + title: schema.string(), + description: schema.string({ defaultValue: '' }), + query: schema.object({ + language: schema.string(), + query: schema.oneOf([schema.string(), schema.object({}, { unknowns: 'allow' })]), + }), + filters: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + timefilter: schema.maybe( + schema.object({ + from: schema.string(), + to: schema.string(), + refreshInterval: schema.maybe( + schema.object({ + value: schema.number(), + pause: schema.boolean(), + }) + ), + }) + ), +}); diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx index 6a53ae14186de..fddf14c864743 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx @@ -59,6 +59,9 @@ const currentDocumentSelector = (state: PreviewState) => state.documents[state.c const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadingDocuments; const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => { + const { + validation: { setScriptEditorValidation }, + } = useFieldPreviewContext(); const monacoEditor = useRef(null); const editorValidationSubscription = useRef(); const fieldCurrentValue = useRef(''); @@ -143,7 +146,7 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr editorValidationSubscription.current = PainlessLang.validation$().subscribe( ({ isValid, isValidating, errors }) => { - controller.setScriptEditorValidation({ + setScriptEditorValidation({ isValid, isValidating, message: errors[0]?.message ?? null, @@ -151,7 +154,7 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr } ); }, - [controller] + [setScriptEditorValidation] ); const updateMonacoMarkers = useCallback((markers: monaco.editor.IMarkerData[]) => { diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index 3addd448f1e7e..f554025ce9f4b 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -75,8 +75,6 @@ const documentsSelector = (state: PreviewState) => { }; }; -const scriptEditorValidationSelector = (state: PreviewState) => state.scriptEditorValidation; - export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewController }> = ({ controller, children, @@ -121,6 +119,12 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro /** The parameters required for the Painless _execute API */ const [params, setParams] = useState(defaultParams); + const [scriptEditorValidation, setScriptEditorValidation] = useState<{ + isValidating: boolean; + isValid: boolean; + message: string | null; + }>({ isValidating: false, isValid: true, message: null }); + /** Flag to show/hide the preview panel */ const [isPanelVisible, setIsPanelVisible] = useState(true); /** Flag to indicate if we are loading document from cluster */ @@ -133,10 +137,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const { currentDocument, currentDocIndex, currentDocId, totalDocs, currentIdx } = useStateSelector(controller.state$, documentsSelector); - const scriptEditorValidation = useStateSelector( - controller.state$, - scriptEditorValidationSelector - ); let isPreviewAvailable = true; @@ -513,6 +513,9 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro isVisible: isPanelVisible, setIsVisible: setIsPanelVisible, }, + validation: { + setScriptEditorValidation, + }, reset, }), [ diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts index 80b11e74597aa..b572827eac06d 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts @@ -95,9 +95,11 @@ export class PreviewController { } }; + /* disabled while investigating issues with painless script editor setScriptEditorValidation = (scriptEditorValidation: PreviewState['scriptEditorValidation']) => { this.updateState({ scriptEditorValidation }); }; + */ setCustomId = (customId?: string) => { this.updateState({ customId }); diff --git a/src/plugins/data_view_field_editor/public/components/preview/types.ts b/src/plugins/data_view_field_editor/public/components/preview/types.ts index 377aed627ba54..347e0a709cf28 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/types.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/types.ts @@ -133,6 +133,11 @@ export interface Context { isLastDoc: boolean; }; reset: () => void; + validation: { + setScriptEditorValidation: React.Dispatch< + React.SetStateAction<{ isValid: boolean; isValidating: boolean; message: string | null }> + >; + }; } export type PainlessExecuteContext = diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.test.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.test.ts index 173453e9e2420..f83200f56f27c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.test.ts @@ -6,48 +6,41 @@ * Side Public License, v 1. */ -import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { createCollectorFetchContextMock } from '@kbn/usage-collection-plugin/server/mocks'; import { getSavedObjectsCounts } from './get_saved_object_counts'; -function mockGetSavedObjectsCounts(params: TBody) { - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - // @ts-expect-error arbitrary type - esClient.search.mockResponse(params); - return esClient; -} +const soEmptyResponse = { total: 0, saved_objects: [], per_page: 0, page: 1 }; describe('getSavedObjectsCounts', () => { + const fetchContextMock = createCollectorFetchContextMock(); + const soClient = fetchContextMock.soClient as jest.Mocked; + + beforeEach(() => { + soClient.find.mockReset(); + }); + test('should not fail if no body returned', async () => { - const esClient = mockGetSavedObjectsCounts({}); + soClient.find.mockResolvedValueOnce(soEmptyResponse); - const results = await getSavedObjectsCounts(esClient, '.kibana', ['type-a']); + const results = await getSavedObjectsCounts(soClient, ['type-a']); // Make sure ES.search is triggered (we'll test the actual params in other specific tests) - expect(esClient.search).toHaveBeenCalledTimes(1); + expect(soClient.find).toHaveBeenCalledTimes(1); expect(results).toStrictEqual({ total: 0, per_type: [], non_expected_types: [], others: 0 }); }); test('should match all and request the `missing` bucket (size + 1) when `exclusive === false`', async () => { - const esClient = mockGetSavedObjectsCounts({}); - await getSavedObjectsCounts(esClient, '.kibana', ['type-a', 'type_2']); - expect(esClient.search).toHaveBeenCalledWith({ - index: '.kibana', - ignore_unavailable: true, - filter_path: [ - 'aggregations.types.buckets', - 'aggregations.types.sum_other_doc_count', - 'hits.total', - ], - body: { - size: 0, - track_total_hits: true, - query: { match_all: {} }, - aggs: { - types: { - terms: { - field: 'type', - size: 3, - missing: 'missing_so_type', - }, + soClient.find.mockResolvedValueOnce(soEmptyResponse); + await getSavedObjectsCounts(soClient, ['type-a', 'type_2']); + expect(soClient.find).toHaveBeenCalledWith({ + type: ['type-a', 'type_2'], + perPage: 0, + aggs: { + types: { + terms: { + field: 'type', + size: 3, + missing: 'missing_so_type', }, }, }, @@ -55,22 +48,12 @@ describe('getSavedObjectsCounts', () => { }); test('should apply the terms query and aggregation with the size matching the length of the list when `exclusive === true`', async () => { - const esClient = mockGetSavedObjectsCounts({}); - await getSavedObjectsCounts(esClient, '.kibana', ['type_one', 'type_two'], true); - expect(esClient.search).toHaveBeenCalledWith({ - index: '.kibana', - ignore_unavailable: true, - filter_path: [ - 'aggregations.types.buckets', - 'aggregations.types.sum_other_doc_count', - 'hits.total', - ], - body: { - size: 0, - track_total_hits: true, - query: { terms: { type: ['type_one', 'type_two'] } }, - aggs: { types: { terms: { field: 'type', size: 2 } } }, - }, + soClient.find.mockResolvedValueOnce(soEmptyResponse); + await getSavedObjectsCounts(soClient, ['type_one', 'type_two'], true); + expect(soClient.find).toHaveBeenCalledWith({ + type: ['type_one', 'type_two'], + perPage: 0, + aggs: { types: { terms: { field: 'type', size: 2 } } }, }); }); @@ -80,39 +63,13 @@ describe('getSavedObjectsCounts', () => { { key: 'type-two', doc_count: 2 }, ]; - const esClient = mockGetSavedObjectsCounts({ - hits: { total: { value: 13 } }, - aggregations: { types: { buckets, sum_other_doc_count: 10 } }, - }); - - const results = await getSavedObjectsCounts(esClient, '.kibana', [ - 'type_one', - 'type-two', - 'type-3', - ]); - expect(results).toStrictEqual({ + soClient.find.mockResolvedValueOnce({ + ...soEmptyResponse, total: 13, - per_type: [ - { key: 'type_one', doc_count: 1 }, - { key: 'type-two', doc_count: 2 }, - ], - non_expected_types: [], - others: 10, - }); - }); - - test('supports ES returning total as a number (just in case)', async () => { - const buckets = [ - { key: 'type_one', doc_count: 1 }, - { key: 'type-two', doc_count: 2 }, - ]; - - const esClient = mockGetSavedObjectsCounts({ - hits: { total: 13 }, aggregations: { types: { buckets, sum_other_doc_count: 10 } }, }); - const results = await getSavedObjectsCounts(esClient, '.kibana', ['type_one', 'type-two']); + const results = await getSavedObjectsCounts(soClient, ['type_one', 'type-two', 'type-3']); expect(results).toStrictEqual({ total: 13, per_type: [ @@ -132,12 +89,13 @@ describe('getSavedObjectsCounts', () => { { key: 'type-four', doc_count: 2 }, ]; - const esClient = mockGetSavedObjectsCounts({ - hits: { total: { value: 13 } }, + soClient.find.mockResolvedValueOnce({ + ...soEmptyResponse, + total: 13, aggregations: { types: { buckets, sum_other_doc_count: 6 } }, }); - const results = await getSavedObjectsCounts(esClient, '.kibana', ['type_one', 'type-two']); + const results = await getSavedObjectsCounts(soClient, ['type_one', 'type-two']); expect(results).toStrictEqual({ total: 13, per_type: [ diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.ts index 812933eeb3e69..cdca68e5d6006 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts/get_saved_object_counts.ts @@ -7,7 +7,7 @@ */ import { estypes } from '@elastic/elasticsearch'; -import { ElasticsearchClient } from '@kbn/core/server'; +import type { SavedObjectsClientContract } from '@kbn/core/server'; const MISSING_TYPE_KEY = 'missing_so_type'; @@ -39,40 +39,28 @@ export interface SavedObjectsCounts { * It also returns a break-down of the document count for all the built-in SOs in Kibana (or the types specified in `soTypes`). * Finally, it completes the information with an `others` counter, that indicates the number of documents that do not match the SO type breakdown. * - * @param esClient The {@link ElasticsearchClient} to use when performing the aggregation. - * @param kibanaIndex The index where SOs are stored. Typically '.kibana'. + * @param soClient The {@link SavedObjectsClientContract} to use when performing the aggregation. * @param soTypes The SO types we want to know about. * @param exclusive If `true`, the results will only contain the breakdown for the specified `soTypes`. Otherwise, it'll also return `missing` and `others` bucket. * @returns {@link SavedObjectsCounts} */ export async function getSavedObjectsCounts( - esClient: ElasticsearchClient, - kibanaIndex: string, // Typically '.kibana'. We might need a way to obtain it from the SavedObjects client (or the SavedObjects client to provide a way to run aggregations?) + soClient: SavedObjectsClientContract, soTypes: string[], exclusive: boolean = false ): Promise { - const body = await esClient.search({ - index: kibanaIndex, - ignore_unavailable: true, - filter_path: [ - 'aggregations.types.buckets', - 'aggregations.types.sum_other_doc_count', - 'hits.total', - ], - body: { - size: 0, - track_total_hits: true, - query: exclusive ? { terms: { type: soTypes } } : { match_all: {} }, - aggs: { - types: { - terms: { - field: 'type', - // If `exclusive == true`, we only care about the strict length of the provided SO types. - // Otherwise, we want to account for the `missing` bucket (size and missing option). - ...(exclusive - ? { size: soTypes.length } - : { missing: MISSING_TYPE_KEY, size: soTypes.length + 1 }), - }, + const body = await soClient.find({ + type: soTypes, + perPage: 0, + aggs: { + types: { + terms: { + field: 'type', + // If `exclusive == true`, we only care about the strict length of the provided SO types. + // Otherwise, we want to account for the `missing` bucket (size and missing option). + ...(exclusive + ? { size: soTypes.length } + : { missing: MISSING_TYPE_KEY, size: soTypes.length + 1 }), }, }, }, @@ -93,7 +81,7 @@ export async function getSavedObjectsCounts( }); return { - total: (typeof body.hits?.total === 'number' ? body.hits?.total : body.hits?.total?.value) ?? 0, + total: body.total, per_type: perType, non_expected_types: nonExpectedTypes, others: body.aggregations?.types?.sum_other_doc_count ?? 0, diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts index 65003ff99c22a..cdf2ca35d6ecc 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { loggingSystemMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { Collector, createCollectorFetchContextMock, @@ -52,7 +52,8 @@ describe('kibana_usage', () => { }); describe('getKibanaSavedObjectCounts', () => { - const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const fetchContextMock = createCollectorFetchContextMock(); + const soClient = fetchContextMock.soClient; test('Get all the saved objects equal to 0 because no results were found', async () => { getSavedObjectsCountsMock.mockResolvedValueOnce({ @@ -61,7 +62,7 @@ describe('getKibanaSavedObjectCounts', () => { non_expected_types: [], others: 0, }); - const results = await getKibanaSavedObjectCounts(esClient, '.kibana'); + const results = await getKibanaSavedObjectCounts(soClient); expect(results).toStrictEqual({ dashboard: { total: 0 }, visualization: { total: 0 }, @@ -83,7 +84,7 @@ describe('getKibanaSavedObjectCounts', () => { others: 0, }); - const results = await getKibanaSavedObjectCounts(esClient, '.kibana'); + const results = await getKibanaSavedObjectCounts(soClient); expect(results).toStrictEqual({ dashboard: { total: 1 }, visualization: { total: 0 }, @@ -93,8 +94,7 @@ describe('getKibanaSavedObjectCounts', () => { }); expect(getSavedObjectsCountsMock).toHaveBeenCalledWith( - esClient, - '.kibana', + soClient, ['dashboard', 'visualization', 'search', 'index-pattern', 'graph-workspace'], true ); diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts index dbff86fb23ad7..9a128888f05d5 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import type { ElasticsearchClient } from '@kbn/core/server'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { snakeCase } from 'lodash'; +import { SavedObjectsClientContract } from '@kbn/core/server'; import { getSavedObjectsCounts } from './get_saved_object_counts'; interface KibanaSavedObjectCounts { @@ -26,10 +26,9 @@ interface KibanaUsage extends KibanaSavedObjectCounts { const TYPES = ['dashboard', 'visualization', 'search', 'index-pattern', 'graph-workspace']; export async function getKibanaSavedObjectCounts( - esClient: ElasticsearchClient, - kibanaIndex: string + soClient: SavedObjectsClientContract ): Promise { - const { per_type: buckets } = await getSavedObjectsCounts(esClient, kibanaIndex, TYPES, true); + const { per_type: buckets } = await getSavedObjectsCounts(soClient, TYPES, true); const allZeros = Object.fromEntries( TYPES.map((type) => [snakeCase(type), { total: 0 }]) @@ -80,10 +79,10 @@ export function registerKibanaUsageCollector( }, }, }, - async fetch({ esClient }) { + async fetch({ soClient }) { return { index: kibanaIndex, - ...(await getKibanaSavedObjectCounts(esClient, kibanaIndex)), + ...(await getKibanaSavedObjectCounts(soClient)), }; }, }) diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.test.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.test.ts index 25ac79f88e523..53168be3c34b6 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.test.ts @@ -17,10 +17,8 @@ describe('saved_objects_count_collector', () => { const usageCollectionMock = createUsageCollectionSetupMock(); const fetchContextMock = createCollectorFetchContextMock(); - const kibanaIndex = '.kibana-tests'; - beforeAll(() => - registerSavedObjectsCountUsageCollector(usageCollectionMock, kibanaIndex, () => + registerSavedObjectsCountUsageCollector(usageCollectionMock, () => Promise.resolve(['type_one', 'type_two', 'type-three', 'type-four']) ) ); @@ -81,8 +79,7 @@ describe('saved_objects_count_collector', () => { }); expect(getSavedObjectsCountsMock).toHaveBeenCalledWith( - fetchContextMock.esClient, - kibanaIndex, + fetchContextMock.soClient, ['type_one', 'type_two', 'type-three', 'type-four'], false ); diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.ts index 376a036a0f24a..49bfb7819389d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/saved_objects_count_collector.ts @@ -23,7 +23,6 @@ interface SavedObjectsCountUsage { export function registerSavedObjectsCountUsageCollector( usageCollection: UsageCollectionSetup, - kibanaIndex: string, getAllSavedObjectTypes: () => Promise ) { usageCollection.registerCollector( @@ -68,14 +67,14 @@ export function registerSavedObjectsCountUsageCollector( }, }, }, - async fetch({ esClient }) { + async fetch({ soClient }) { const allRegisteredSOTypes = await getAllSavedObjectTypes(); const { total, per_type: buckets, non_expected_types: nonRegisteredTypes, others, - } = await getSavedObjectsCounts(esClient, kibanaIndex, allRegisteredSOTypes, false); + } = await getSavedObjectsCounts(soClient, allRegisteredSOTypes, false); return { total, by_type: buckets.map(({ key: type, doc_count: count }) => ({ type, count })), diff --git a/src/plugins/kibana_usage_collection/server/plugin.ts b/src/plugins/kibana_usage_collection/server/plugin.ts index 784ab3ef4af62..8787d085c692e 100644 --- a/src/plugins/kibana_usage_collection/server/plugin.ts +++ b/src/plugins/kibana_usage_collection/server/plugin.ts @@ -148,7 +148,7 @@ export class KibanaUsageCollectionPlugin implements Plugin { .getAllTypes() .map(({ name }) => name); }; - registerSavedObjectsCountUsageCollector(usageCollection, kibanaIndex, getAllSavedObjectTypes); + registerSavedObjectsCountUsageCollector(usageCollection, getAllSavedObjectTypes); registerManagementUsageCollector(usageCollection, getUiSettingsClient); registerUiMetricUsageCollector(usageCollection, registerType, getSavedObjectsClient); registerApplicationUsageCollector( diff --git a/src/plugins/kibana_usage_collection/tsconfig.json b/src/plugins/kibana_usage_collection/tsconfig.json index 271032000d510..d56cb860cd463 100644 --- a/src/plugins/kibana_usage_collection/tsconfig.json +++ b/src/plugins/kibana_usage_collection/tsconfig.json @@ -18,6 +18,7 @@ "@kbn/logging", "@kbn/core-test-helpers-kbn-server", "@kbn/core-usage-data-server", + "@kbn/core-saved-objects-api-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/saved_objects_finder/common/index.ts b/src/plugins/saved_objects_finder/common/index.ts index 056d0b4638efc..8a0964edc100b 100644 --- a/src/plugins/saved_objects_finder/common/index.ts +++ b/src/plugins/saved_objects_finder/common/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export const PER_PAGE_SETTING = 'savedObjects:perPage'; -export const LISTING_LIMIT_SETTING = 'savedObjects:listingLimit'; +export { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '@kbn/saved-objects-settings'; export type { SavedObjectCommon, FindQueryHTTP, FindResponseHTTP, FinderAttributes } from './types'; diff --git a/src/plugins/saved_objects_finder/tsconfig.json b/src/plugins/saved_objects_finder/tsconfig.json index 813379f02a313..217f4f7f0dbe3 100644 --- a/src/plugins/saved_objects_finder/tsconfig.json +++ b/src/plugins/saved_objects_finder/tsconfig.json @@ -14,6 +14,7 @@ "@kbn/config-schema", "@kbn/core-ui-settings-browser", "@kbn/core-http-browser", + "@kbn/saved-objects-settings", ], "exclude": [ "target/**/*", diff --git a/src/plugins/telemetry/server/saved_objects/register_telemetry_saved_object.ts b/src/plugins/telemetry/server/saved_objects/register_telemetry_saved_object.ts index 20b2ab6093514..17cae888c58dd 100644 --- a/src/plugins/telemetry/server/saved_objects/register_telemetry_saved_object.ts +++ b/src/plugins/telemetry/server/saved_objects/register_telemetry_saved_object.ts @@ -17,32 +17,8 @@ export function registerTelemetrySavedObject( hidden: true, namespaceType: 'agnostic', mappings: { - properties: { - enabled: { - type: 'boolean', - }, - sendUsageFrom: { - type: 'keyword', - }, - lastReported: { - type: 'date', - }, - lastVersionChecked: { - type: 'keyword', - }, - userHasSeenNotice: { - type: 'boolean', - }, - reportFailureCount: { - type: 'integer', - }, - reportFailureVersion: { - type: 'keyword', - }, - allowChangingOptInStatus: { - type: 'boolean', - }, - }, + dynamic: false, + properties: {}, }, }); } diff --git a/test/api_integration/apis/telemetry/index.ts b/test/api_integration/apis/telemetry/index.ts index 3afe1ef304b27..a92ae4e1b8481 100644 --- a/test/api_integration/apis/telemetry/index.ts +++ b/test/api_integration/apis/telemetry/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import type { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Telemetry', () => { diff --git a/test/api_integration/apis/telemetry/opt_in.ts b/test/api_integration/apis/telemetry/opt_in.ts index 8d0fb6725bacf..943d7534acc0a 100644 --- a/test/api_integration/apis/telemetry/opt_in.ts +++ b/test/api_integration/apis/telemetry/opt_in.ts @@ -7,17 +7,16 @@ */ import expect from '@kbn/expect'; -import { Client } from '@elastic/elasticsearch'; -import { TelemetrySavedObjectAttributes } from '@kbn/telemetry-plugin/server/saved_objects'; import SuperTest from 'supertest'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import type { KbnClient } from '@kbn/test'; +import type { TelemetrySavedObjectAttributes } from '@kbn/telemetry-plugin/server/saved_objects'; +import type { FtrProviderContext } from '../../ftr_provider_context'; export default function optInTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); - const esClient: Client = getService('es'); describe('/api/telemetry/v2/optIn API', () => { let defaultAttributes: TelemetrySavedObjectAttributes; @@ -26,39 +25,39 @@ export default function optInTest({ getService }: FtrProviderContext) { await esArchiver.emptyKibanaIndex(); const kibanaVersionAccessor = kibanaServer.version; kibanaVersion = await kibanaVersionAccessor.get(); - defaultAttributes = await getSavedObjectAttributes(esClient); + defaultAttributes = await getSavedObjectAttributes(kibanaServer); expect(typeof kibanaVersion).to.eql('string'); expect(kibanaVersion.length).to.be.greaterThan(0); }); afterEach(async () => { - await updateSavedObjectAttributes(esClient, defaultAttributes); + await updateSavedObjectAttributes(kibanaServer, defaultAttributes); }); it('should support sending false with allowChangingOptInStatus true', async () => { - await updateSavedObjectAttributes(esClient, { + await updateSavedObjectAttributes(kibanaServer, { allowChangingOptInStatus: true, }); await postTelemetryV2OptIn(supertest, false, 200); - const { enabled, lastVersionChecked } = await getSavedObjectAttributes(esClient); + const { enabled, lastVersionChecked } = await getSavedObjectAttributes(kibanaServer); expect(enabled).to.be(false); expect(lastVersionChecked).to.be(kibanaVersion); }); it('should support sending true with allowChangingOptInStatus true', async () => { - await updateSavedObjectAttributes(esClient, { + await updateSavedObjectAttributes(kibanaServer, { ...defaultAttributes, allowChangingOptInStatus: true, }); await postTelemetryV2OptIn(supertest, true, 200); - const { enabled, lastVersionChecked } = await getSavedObjectAttributes(esClient); + const { enabled, lastVersionChecked } = await getSavedObjectAttributes(kibanaServer); expect(enabled).to.be(true); expect(lastVersionChecked).to.be(kibanaVersion); }); it('should not support sending false with allowChangingOptInStatus false', async () => { - await updateSavedObjectAttributes(esClient, { + await updateSavedObjectAttributes(kibanaServer, { ...defaultAttributes, allowChangingOptInStatus: false, }); @@ -66,7 +65,7 @@ export default function optInTest({ getService }: FtrProviderContext) { }); it('should not support sending true with allowChangingOptInStatus false', async () => { - await updateSavedObjectAttributes(esClient, { + await updateSavedObjectAttributes(kibanaServer, { ...defaultAttributes, allowChangingOptInStatus: false, }); @@ -98,29 +97,31 @@ async function postTelemetryV2OptIn( } async function updateSavedObjectAttributes( - es: Client, + client: KbnClient, attributes: TelemetrySavedObjectAttributes ): Promise { - // Directly writing to the `.kibana` index because the SO type now is hidden, meaning it's not exposed via the SO HTTP API - await es.update({ - index: '.kibana', - id: 'telemetry:telemetry', - doc: { - telemetry: attributes, - // there are many missing fields in the SO, hopefully it doesn't break Kibana - }, - doc_as_upsert: true, + await client.savedObjects.create({ + type: 'telemetry', + id: 'telemetry', + overwrite: true, + attributes, }); } -async function getSavedObjectAttributes(es: Client): Promise { - // Directly writing to the `.kibana` index because the SO type now is hidden, meaning it's not exposed via the SO HTTP API - const { _source: body } = await es.get<{ telemetry: TelemetrySavedObjectAttributes }>( - { - index: '.kibana', - id: 'telemetry:telemetry', - }, - { ignore: [404] } - ); - return body?.telemetry || {}; +async function getSavedObjectAttributes( + client: KbnClient +): Promise { + try { + const body = await client.savedObjects.get({ + type: 'telemetry', + id: 'telemetry', + }); + + return body.attributes; + } catch (err) { + if (err.response?.status === 404) { + return {}; + } + throw err; + } } diff --git a/test/api_integration/apis/telemetry/telemetry_config.ts b/test/api_integration/apis/telemetry/telemetry_config.ts index 7a03600a35647..f6dd12b0c2a9d 100644 --- a/test/api_integration/apis/telemetry/telemetry_config.ts +++ b/test/api_integration/apis/telemetry/telemetry_config.ts @@ -7,7 +7,7 @@ */ import { AxiosError } from 'axios'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import type { FtrProviderContext } from '../../ftr_provider_context'; const TELEMETRY_SO_TYPE = 'telemetry'; const TELEMETRY_SO_ID = 'telemetry'; diff --git a/test/api_integration/apis/telemetry/telemetry_last_reported.ts b/test/api_integration/apis/telemetry/telemetry_last_reported.ts index 37097c4cbf2f5..e553fa0218aa1 100644 --- a/test/api_integration/apis/telemetry/telemetry_last_reported.ts +++ b/test/api_integration/apis/telemetry/telemetry_last_reported.ts @@ -7,21 +7,15 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import type { FtrProviderContext } from '../../ftr_provider_context'; export default function optInTest({ getService }: FtrProviderContext) { - const client = getService('es'); + const client = getService('kibanaServer'); const supertest = getService('supertest'); describe('/api/telemetry/v2/last_reported API Telemetry lastReported', () => { before(async () => { - await client.delete( - { - index: '.kibana', - id: 'telemetry:telemetry', - }, - { ignore: [404] } - ); + await client.savedObjects.delete({ type: 'telemetry', id: 'telemetry' }); }); it('GET should return undefined when there is no stored telemetry.lastReported value', async () => { @@ -31,22 +25,25 @@ export default function optInTest({ getService }: FtrProviderContext) { it('PUT should update telemetry.lastReported to now', async () => { await supertest.put('/api/telemetry/v2/last_reported').set('kbn-xsrf', 'xxx').expect(200); - const { _source } = await client.get<{ telemetry: { lastReported: number } }>({ - index: '.kibana', - id: 'telemetry:telemetry', + const { + attributes: { lastReported }, + } = await client.savedObjects.get<{ lastReported: number }>({ + type: 'telemetry', + id: 'telemetry', }); - expect(_source?.telemetry.lastReported).to.be.a('number'); + expect(lastReported).to.be.a('number'); }); it('GET should return the previously stored lastReported value', async () => { - const { _source } = await client.get<{ telemetry: { lastReported: number } }>({ - index: '.kibana', - id: 'telemetry:telemetry', + const { + attributes: { lastReported }, + } = await client.savedObjects.get<{ lastReported: number }>({ + type: 'telemetry', + id: 'telemetry', }); - expect(_source?.telemetry.lastReported).to.be.a('number'); - const lastReported = _source?.telemetry.lastReported; + expect(lastReported).to.be.a('number'); await supertest .get('/api/telemetry/v2/last_reported') diff --git a/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts b/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts index a2c48996069b2..53b0d2cadca64 100644 --- a/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts +++ b/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts @@ -7,30 +7,26 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import type { FtrProviderContext } from '../../ftr_provider_context'; export default function optInTest({ getService }: FtrProviderContext) { - const client = getService('es'); + const client = getService('kibanaServer'); const supertest = getService('supertest'); describe('/api/telemetry/v2/userHasSeenNotice API Telemetry User has seen OptIn Notice', () => { it('should update telemetry setting field via PUT', async () => { - await client.delete( - { - index: '.kibana', - id: 'telemetry:telemetry', - }, - { ignore: [404] } - ); + await client.savedObjects.delete({ type: 'telemetry', id: 'telemetry' }); await supertest.put('/api/telemetry/v2/userHasSeenNotice').set('kbn-xsrf', 'xxx').expect(200); - const { _source } = await client.get<{ telemetry: { userHasSeenNotice: boolean } }>({ - index: '.kibana', - id: 'telemetry:telemetry', + const { + attributes: { userHasSeenNotice }, + } = await client.savedObjects.get<{ userHasSeenNotice: boolean }>({ + type: 'telemetry', + id: 'telemetry', }); - expect(_source?.telemetry.userHasSeenNotice).to.be(true); + expect(userHasSeenNotice).to.be(true); }); }); } diff --git a/test/functional/apps/context/_filters.ts b/test/functional/apps/context/_filters.ts index b3b8cc0a1fd62..9aff3f6c805fc 100644 --- a/test/functional/apps/context/_filters.ts +++ b/test/functional/apps/context/_filters.ts @@ -20,12 +20,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const filterBar = getService('filterBar'); const retry = getService('retry'); const browser = getService('browser'); + const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'context']); const testSubjects = getService('testSubjects'); - // FLAKY: https://github.com/elastic/kibana/issues/154387 - describe.skip('context filters', function contextSize() { + describe('context filters', function contextSize() { + before(async function () { + await kibanaServer.uiSettings.update({ + 'discover:rowHeightOption': 0, // to have more grid rows visible at once + }); + }); + beforeEach(async function () { await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, TEST_ANCHOR_ID, { columns: TEST_COLUMN_NAMES, diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index 7fde46031e737..1f022c890b724 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -207,6 +207,8 @@ export class DataGridService extends FtrService { ? '~docTableExpandToggleColumnAnchor' : '~docTableExpandToggleColumn'; const toggle = await row[0].findByTestSubject(testSubj); + + await toggle.scrollIntoViewIfNecessary(); await toggle.click(); } diff --git a/tsconfig.base.json b/tsconfig.base.json index 85fa70f831719..6bede7391b146 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1052,6 +1052,8 @@ "@kbn/saved-objects-management-plugin/*": ["src/plugins/saved_objects_management/*"], "@kbn/saved-objects-plugin": ["src/plugins/saved_objects"], "@kbn/saved-objects-plugin/*": ["src/plugins/saved_objects/*"], + "@kbn/saved-objects-settings": ["packages/kbn-saved-objects-settings"], + "@kbn/saved-objects-settings/*": ["packages/kbn-saved-objects-settings/*"], "@kbn/saved-objects-tagging-oss-plugin": ["src/plugins/saved_objects_tagging_oss"], "@kbn/saved-objects-tagging-oss-plugin/*": ["src/plugins/saved_objects_tagging_oss/*"], "@kbn/saved-objects-tagging-plugin": ["x-pack/plugins/saved_objects_tagging"], @@ -1086,6 +1088,8 @@ "@kbn/security-test-endpoints-plugin/*": ["x-pack/test/security_functional/plugins/test_endpoints/*"], "@kbn/securitysolution-autocomplete": ["packages/kbn-securitysolution-autocomplete"], "@kbn/securitysolution-autocomplete/*": ["packages/kbn-securitysolution-autocomplete/*"], + "@kbn/securitysolution-data-table": ["x-pack/packages/kbn-securitysolution-data-table"], + "@kbn/securitysolution-data-table/*": ["x-pack/packages/kbn-securitysolution-data-table/*"], "@kbn/securitysolution-ecs": ["packages/kbn-securitysolution-ecs"], "@kbn/securitysolution-ecs/*": ["packages/kbn-securitysolution-ecs/*"], "@kbn/securitysolution-es-utils": ["packages/kbn-securitysolution-es-utils"], diff --git a/x-pack/examples/exploratory_view_example/public/app.tsx b/x-pack/examples/exploratory_view_example/public/app.tsx index 96e44f62d2ab5..268f070b40d16 100644 --- a/x-pack/examples/exploratory_view_example/public/app.tsx +++ b/x-pack/examples/exploratory_view_example/public/app.tsx @@ -28,7 +28,7 @@ export const App = (props: { plugins: StartDependencies; defaultIndexPattern: DataView | null; }) => { - const ExploratoryViewComponent = props.plugins.observability.ExploratoryViewEmbeddable; + const ExploratoryViewComponent = props.plugins.exploratoryView.ExploratoryViewEmbeddable; const seriesList: AllSeries = [ { diff --git a/x-pack/packages/kbn-securitysolution-data-table/.storybook/main.js b/x-pack/packages/kbn-securitysolution-data-table/.storybook/main.js new file mode 100644 index 0000000000000..86b48c32f103e --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/.storybook/main.js @@ -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. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/x-pack/packages/kbn-securitysolution-data-table/README.md b/x-pack/packages/kbn-securitysolution-data-table/README.md new file mode 100644 index 0000000000000..1cea501e31e30 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/README.md @@ -0,0 +1,35 @@ +# Security Solutions's Data Table + +## Motivation + +The idea behind this package is to have a reusable data table component, embedding the features +available to alerts table in security solution plugin. + +## How to use this + +Standalone examples will follow. In the meantime: + +Consult the following file to get the idea of what is necessary to reuse the component + +`x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx` + +The following file: +`kibana/x-pack/plugins/security_solution/public/common/store/reducer.ts` + +showcases the redux store setup for the package. + +## The most important public api members + +- DataTableComponent itself +- dataTableReducer + +### Extras + +Be sure to check out provided helpers + +## Storybook + +General look of the component can be checked visually running the following storybook: +`yarn storybook security_solution_data_table` + +Note that all the interactions are mocked. \ No newline at end of file diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/constants.ts b/x-pack/packages/kbn-securitysolution-data-table/common/constants.ts new file mode 100644 index 0000000000000..b2e623eebddb6 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/common/constants.ts @@ -0,0 +1,501 @@ +/* + * 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. + */ + +/** + * as const + * + * The const assertion ensures that type widening does not occur + * https://mariusschulz.com/blog/literal-type-widening-in-typescript + * Please follow this convention when adding to this file + */ + +export const APP_ID = 'securitySolution' as const; +export const APP_UI_ID = 'securitySolutionUI' as const; +export const CASES_FEATURE_ID = 'securitySolutionCases' as const; +export const SERVER_APP_ID = 'siem' as const; +export const APP_NAME = 'Security' as const; +export const APP_ICON = 'securityAnalyticsApp' as const; +export const APP_ICON_SOLUTION = 'logoSecurity' as const; +export const APP_PATH = `/app/security` as const; +export const ADD_DATA_PATH = `/app/integrations/browse/security`; +export const ADD_THREAT_INTELLIGENCE_DATA_PATH = `/app/integrations/browse/threat_intel`; +export const DEFAULT_BYTES_FORMAT = 'format:bytes:defaultPattern' as const; +export const DEFAULT_DATE_FORMAT = 'dateFormat' as const; +export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz' as const; +export const DEFAULT_DARK_MODE = 'theme:darkMode' as const; +export const DEFAULT_INDEX_KEY = 'securitySolution:defaultIndex' as const; +export const DEFAULT_NUMBER_FORMAT = 'format:number:defaultPattern' as const; +export const DEFAULT_DATA_VIEW_ID = 'security-solution' as const; +export const DEFAULT_TIME_FIELD = '@timestamp' as const; +export const DEFAULT_TIME_RANGE = 'timepicker:timeDefaults' as const; +export const DEFAULT_REFRESH_RATE_INTERVAL = 'timepicker:refreshIntervalDefaults' as const; +export const DEFAULT_APP_TIME_RANGE = 'securitySolution:timeDefaults' as const; +export const DEFAULT_APP_REFRESH_INTERVAL = 'securitySolution:refreshIntervalDefaults' as const; +export const DEFAULT_ALERTS_INDEX = '.alerts-security.alerts' as const; +export const DEFAULT_SIGNALS_INDEX = '.siem-signals' as const; +export const DEFAULT_PREVIEW_INDEX = '.preview.alerts-security.alerts' as const; +export const DEFAULT_LISTS_INDEX = '.lists' as const; +export const DEFAULT_ITEMS_INDEX = '.items' as const; +// The DEFAULT_MAX_SIGNALS value exists also in `x-pack/plugins/cases/common/constants.ts` +// If either changes, engineer should ensure both values are updated +export const DEFAULT_MAX_SIGNALS = 100 as const; +export const DEFAULT_SEARCH_AFTER_PAGE_SIZE = 100 as const; +export const DEFAULT_ANOMALY_SCORE = 'securitySolution:defaultAnomalyScore' as const; +export const DEFAULT_MAX_TABLE_QUERY_SIZE = 10000 as const; +export const DEFAULT_FROM = 'now/d' as const; +export const DEFAULT_TO = 'now/d' as const; +export const DEFAULT_INTERVAL_PAUSE = true as const; +export const DEFAULT_INTERVAL_TYPE = 'manual' as const; +export const DEFAULT_INTERVAL_VALUE = 300000 as const; // ms +export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges' as const; +export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled' as const; +export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled' as const; +export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51' as const; +export const ENDPOINT_METADATA_INDEX = 'metrics-endpoint.metadata-*' as const; +export const ENDPOINT_METRICS_INDEX = '.ds-metrics-endpoint.metrics-*' as const; +export const DEFAULT_RULE_REFRESH_INTERVAL_ON = true as const; +export const DEFAULT_RULE_REFRESH_INTERVAL_VALUE = 60000 as const; // ms +export const DEFAULT_RULE_NOTIFICATION_QUERY_SIZE = 100 as const; +export const SECURITY_FEATURE_ID = 'Security' as const; +export const SECURITY_TAG_NAME = 'Security Solution' as const; +export const DEFAULT_SPACE_ID = 'default' as const; +export const DEFAULT_RELATIVE_DATE_THRESHOLD = 24 as const; + +// Document path where threat indicator fields are expected. Fields are used +// to enrich signals, and are copied to threat.enrichments. +export const DEFAULT_INDICATOR_SOURCE_PATH = 'threat.indicator' as const; +export const ENRICHMENT_DESTINATION_PATH = 'threat.enrichments' as const; +export const DEFAULT_THREAT_INDEX_KEY = 'securitySolution:defaultThreatIndex' as const; +export const DEFAULT_THREAT_INDEX_VALUE = ['logs-ti_*'] as const; +export const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"' as const; + +export enum SecurityPageName { + administration = 'administration', + alerts = 'alerts', + blocklist = 'blocklist', + /* + * Warning: Computed values are not permitted in an enum with string valued members + * All Cases page names must match `CasesDeepLinkId` in x-pack/plugins/cases/public/common/navigation/deep_links.ts + */ + case = 'cases', // must match `CasesDeepLinkId.cases` + caseConfigure = 'cases_configure', // must match `CasesDeepLinkId.casesConfigure` + caseCreate = 'cases_create', // must match `CasesDeepLinkId.casesCreate` + /* + * Warning: Computed values are not permitted in an enum with string valued members + * All cloud security posture page names must match `CloudSecurityPosturePageId` in x-pack/plugins/cloud_security_posture/public/common/navigation/types.ts + */ + cloudSecurityPostureBenchmarks = 'cloud_security_posture-benchmarks', + cloudSecurityPostureDashboard = 'cloud_security_posture-dashboard', + cloudSecurityPostureFindings = 'cloud_security_posture-findings', + cloudSecurityPostureRules = 'cloud_security_posture-rules', + dashboardsLanding = 'dashboards', + dataQuality = 'data_quality', + detections = 'detections', + detectionAndResponse = 'detection_response', + endpoints = 'endpoints', + eventFilters = 'event_filters', + exceptions = 'exceptions', + exploreLanding = 'explore', + hostIsolationExceptions = 'host_isolation_exceptions', + hosts = 'hosts', + hostsAnomalies = 'hosts-anomalies', + hostsRisk = 'hosts-risk', + hostsEvents = 'hosts-events', + investigate = 'investigate', + kubernetes = 'kubernetes', + landing = 'get_started', + network = 'network', + networkAnomalies = 'network-anomalies', + networkDns = 'network-dns', + networkEvents = 'network-events', + networkHttp = 'network-http', + networkTls = 'network-tls', + noPage = '', + overview = 'overview', + policies = 'policy', + responseActionsHistory = 'response_actions_history', + rules = 'rules', + rulesCreate = 'rules-create', + sessions = 'sessions', + /* + * Warning: Computed values are not permitted in an enum with string valued members + * All threat intelligence page names must match `TIPageId` in x-pack/plugins/threat_intelligence/public/common/navigation/types.ts + */ + threatIntelligenceIndicators = 'threat_intelligence-indicators', + timelines = 'timelines', + timelinesTemplates = 'timelines-templates', + trustedApps = 'trusted_apps', + uncommonProcesses = 'uncommon_processes', + users = 'users', + usersAnomalies = 'users-anomalies', + usersAuthentications = 'users-authentications', + usersEvents = 'users-events', + usersRisk = 'users-risk', + entityAnalytics = 'entity-analytics', +} + +export const EXPLORE_PATH = '/explore' as const; +export const DASHBOARDS_PATH = '/dashboards' as const; +export const MANAGE_PATH = '/manage' as const; +export const TIMELINES_PATH = '/timelines' as const; +export const CASES_PATH = '/cases' as const; +export const OVERVIEW_PATH = '/overview' as const; +export const LANDING_PATH = '/get_started' as const; +export const DATA_QUALITY_PATH = '/data_quality' as const; +export const DETECTION_RESPONSE_PATH = '/detection_response' as const; +export const DETECTIONS_PATH = '/detections' as const; +export const ALERTS_PATH = '/alerts' as const; +export const RULES_PATH = '/rules' as const; +export const RULES_CREATE_PATH = `${RULES_PATH}/create` as const; +export const EXCEPTIONS_PATH = '/exceptions' as const; +export const EXCEPTION_LIST_DETAIL_PATH = `${EXCEPTIONS_PATH}/details/:detailName` as const; +export const HOSTS_PATH = '/hosts' as const; +export const USERS_PATH = '/users' as const; +export const KUBERNETES_PATH = '/kubernetes' as const; +export const NETWORK_PATH = '/network' as const; +export const MANAGEMENT_PATH = '/administration' as const; +export const THREAT_INTELLIGENCE_PATH = '/threat_intelligence' as const; +export const ENDPOINTS_PATH = `${MANAGEMENT_PATH}/endpoints` as const; +export const POLICIES_PATH = `${MANAGEMENT_PATH}/policy` as const; +export const TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/trusted_apps` as const; +export const EVENT_FILTERS_PATH = `${MANAGEMENT_PATH}/event_filters` as const; +export const HOST_ISOLATION_EXCEPTIONS_PATH = + `${MANAGEMENT_PATH}/host_isolation_exceptions` as const; +export const BLOCKLIST_PATH = `${MANAGEMENT_PATH}/blocklist` as const; +export const RESPONSE_ACTIONS_HISTORY_PATH = `${MANAGEMENT_PATH}/response_actions_history` as const; +export const ENTITY_ANALYTICS_PATH = '/entity_analytics' as const; +export const APP_OVERVIEW_PATH = `${APP_PATH}${OVERVIEW_PATH}` as const; +export const APP_LANDING_PATH = `${APP_PATH}${LANDING_PATH}` as const; +export const APP_DETECTION_RESPONSE_PATH = `${APP_PATH}${DETECTION_RESPONSE_PATH}` as const; +export const APP_MANAGEMENT_PATH = `${APP_PATH}${MANAGEMENT_PATH}` as const; + +export const APP_ALERTS_PATH = `${APP_PATH}${ALERTS_PATH}` as const; +export const APP_RULES_PATH = `${APP_PATH}${RULES_PATH}` as const; +export const APP_EXCEPTIONS_PATH = `${APP_PATH}${EXCEPTIONS_PATH}` as const; + +export const APP_HOSTS_PATH = `${APP_PATH}${HOSTS_PATH}` as const; +export const APP_USERS_PATH = `${APP_PATH}${USERS_PATH}` as const; +export const APP_NETWORK_PATH = `${APP_PATH}${NETWORK_PATH}` as const; +export const APP_KUBERNETES_PATH = `${APP_PATH}${KUBERNETES_PATH}` as const; +export const APP_TIMELINES_PATH = `${APP_PATH}${TIMELINES_PATH}` as const; +export const APP_CASES_PATH = `${APP_PATH}${CASES_PATH}` as const; +export const APP_ENDPOINTS_PATH = `${APP_PATH}${ENDPOINTS_PATH}` as const; +export const APP_POLICIES_PATH = `${APP_PATH}${POLICIES_PATH}` as const; +export const APP_TRUSTED_APPS_PATH = `${APP_PATH}${TRUSTED_APPS_PATH}` as const; +export const APP_EVENT_FILTERS_PATH = `${APP_PATH}${EVENT_FILTERS_PATH}` as const; +export const APP_HOST_ISOLATION_EXCEPTIONS_PATH = + `${APP_PATH}${HOST_ISOLATION_EXCEPTIONS_PATH}` as const; +export const APP_BLOCKLIST_PATH = `${APP_PATH}${BLOCKLIST_PATH}` as const; +export const APP_RESPONSE_ACTIONS_HISTORY_PATH = + `${APP_PATH}${RESPONSE_ACTIONS_HISTORY_PATH}` as const; +export const APP_ENTITY_ANALYTICS_PATH = `${APP_PATH}${ENTITY_ANALYTICS_PATH}` as const; +export const APP_DATA_QUALITY_PATH = `${APP_PATH}${DATA_QUALITY_PATH}` as const; + +// cloud logs to exclude from default index pattern +export const EXCLUDE_ELASTIC_CLOUD_INDICES = ['-*elastic-cloud-logs-*']; + +/** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ +export const INCLUDE_INDEX_PATTERN = [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'traces-apm*', + 'winlogbeat-*', +]; +/** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events, and the exclude index pattern */ +export const DEFAULT_INDEX_PATTERN = [...INCLUDE_INDEX_PATTERN, ...EXCLUDE_ELASTIC_CLOUD_INDICES]; + +/** This Kibana Advanced Setting enables the grouped navigation in Security Solution */ +export const ENABLE_GROUPED_NAVIGATION = 'securitySolution:enableGroupedNav' as const; + +/** 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; + +/** This Kibana Advanced Setting specifies the URL of the News feed widget */ +export const NEWS_FEED_URL_SETTING = 'securitySolution:newsFeedUrl' as const; + +/** The default value for News feed widget */ +export const NEWS_FEED_URL_SETTING_DEFAULT = 'https://feeds.elastic.co/security-solution' as const; + +/** This Kibana Advanced Setting specifies the URLs of `IP Reputation Links`*/ +export const IP_REPUTATION_LINKS_SETTING = 'securitySolution:ipReputationLinks' as const; + +/** The default value for `IP Reputation Links` */ +export const IP_REPUTATION_LINKS_SETTING_DEFAULT = `[ + { "name": "virustotal.com", "url_template": "https://www.virustotal.com/gui/search/{{ip}}" }, + { "name": "talosIntelligence.com", "url_template": "https://talosintelligence.com/reputation_center/lookup?search={{ip}}" } +]`; + +/** This Kibana Advanced Setting shows related integrations on the Rules Table */ +export const SHOW_RELATED_INTEGRATIONS_SETTING = + 'securitySolution:showRelatedIntegrations' as const; + +/** This Kibana Advanced Setting enables extended rule execution logging to Event Log */ +export const EXTENDED_RULE_EXECUTION_LOGGING_ENABLED_SETTING = + 'securitySolution:extendedRuleExecutionLoggingEnabled' as const; + +/** This Kibana Advanced Setting sets minimum log level starting from which execution logs will be written to Event Log */ +export const EXTENDED_RULE_EXECUTION_LOGGING_MIN_LEVEL_SETTING = + 'securitySolution:extendedRuleExecutionLoggingMinLevel' as const; + +/** + * Id for the notifications alerting type + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const LEGACY_NOTIFICATIONS_ID = `siem.notifications` as const; + +/** + * Internal actions route + */ +export const UPDATE_OR_CREATE_LEGACY_ACTIONS = '/internal/api/detection/legacy/notifications'; + +/** + * Exceptions management routes + */ + +export const SHARED_EXCEPTION_LIST_URL = `/api${EXCEPTIONS_PATH}/shared` as const; + +/** + * Detection engine routes + */ +export const DETECTION_ENGINE_URL = '/api/detection_engine' as const; +export const DETECTION_ENGINE_PRIVILEGES_URL = `${DETECTION_ENGINE_URL}/privileges` as const; +export const DETECTION_ENGINE_INDEX_URL = `${DETECTION_ENGINE_URL}/index` as const; + +export const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules` as const; +export const DETECTION_ENGINE_RULES_URL_FIND = `${DETECTION_ENGINE_RULES_URL}/_find` as const; +export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags` as const; +export const DETECTION_ENGINE_RULES_BULK_ACTION = + `${DETECTION_ENGINE_RULES_URL}/_bulk_action` as const; +export const DETECTION_ENGINE_RULES_PREVIEW = `${DETECTION_ENGINE_RULES_URL}/preview` as const; +export const DETECTION_ENGINE_RULES_BULK_DELETE = + `${DETECTION_ENGINE_RULES_URL}/_bulk_delete` as const; +export const DETECTION_ENGINE_RULES_BULK_CREATE = + `${DETECTION_ENGINE_RULES_URL}/_bulk_create` as const; +export const DETECTION_ENGINE_RULES_BULK_UPDATE = + `${DETECTION_ENGINE_RULES_URL}/_bulk_update` as const; + +export const INTERNAL_RISK_SCORE_URL = '/internal/risk_score' as const; +export const DEV_TOOL_PREBUILT_CONTENT = + `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/{console_id}` as const; +export const devToolPrebuiltContentUrl = (spaceId: string, consoleId: string) => + `/s/${spaceId}${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/${consoleId}` as const; +export const PREBUILT_SAVED_OBJECTS_BULK_CREATE = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_create/{template_name}`; +export const prebuiltSavedObjectsBulkCreateUrl = (templateName: string) => + `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_create/${templateName}` as const; +export const PREBUILT_SAVED_OBJECTS_BULK_DELETE = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_delete/{template_name}`; +export const prebuiltSavedObjectsBulkDeleteUrl = (templateName: string) => + `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_delete/${templateName}` as const; +export const RISK_SCORE_CREATE_INDEX = `${INTERNAL_RISK_SCORE_URL}/indices/create`; +export const RISK_SCORE_DELETE_INDICES = `${INTERNAL_RISK_SCORE_URL}/indices/delete`; +export const RISK_SCORE_CREATE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/create`; +export const RISK_SCORE_DELETE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/delete`; +/** + * Internal detection engine routes + */ +export const INTERNAL_DETECTION_ENGINE_URL = '/internal/detection_engine' as const; +export const DETECTION_ENGINE_ALERTS_INDEX_URL = + `${INTERNAL_DETECTION_ENGINE_URL}/signal/index` as const; + +/** + * Telemetry detection endpoint for any previews requested of what data we are + * providing through UI/UX and for e2e tests. + * curl http//localhost:5601/internal/security_solution/telemetry + * to see the contents + */ +export const SECURITY_TELEMETRY_URL = `/internal/security_solution/telemetry` as const; + +export const TIMELINE_RESOLVE_URL = '/api/timeline/resolve' as const; +export const TIMELINE_URL = '/api/timeline' as const; +export const TIMELINES_URL = '/api/timelines' as const; +export const TIMELINE_FAVORITE_URL = '/api/timeline/_favorite' as const; +export const TIMELINE_DRAFT_URL = `${TIMELINE_URL}/_draft` as const; +export const TIMELINE_EXPORT_URL = `${TIMELINE_URL}/_export` as const; +export const TIMELINE_IMPORT_URL = `${TIMELINE_URL}/_import` as const; +export const TIMELINE_PREPACKAGED_URL = `${TIMELINE_URL}/_prepackaged` as const; + +export const NOTE_URL = '/api/note' as const; +export const PINNED_EVENT_URL = '/api/pinned_event' as const; +export const SOURCERER_API_URL = '/internal/security_solution/sourcerer' as const; +export const RISK_SCORE_INDEX_STATUS_API_URL = '/internal/risk_score/index_status' as const; + +/** + * Default signals index key for kibana.dev.yml + */ +export const SIGNALS_INDEX_KEY = 'signalsIndex' as const; + +export const DETECTION_ENGINE_SIGNALS_URL = `${DETECTION_ENGINE_URL}/signals` as const; +export const DETECTION_ENGINE_SIGNALS_STATUS_URL = + `${DETECTION_ENGINE_SIGNALS_URL}/status` as const; +export const DETECTION_ENGINE_QUERY_SIGNALS_URL = `${DETECTION_ENGINE_SIGNALS_URL}/search` as const; +export const DETECTION_ENGINE_SIGNALS_MIGRATION_URL = + `${DETECTION_ENGINE_SIGNALS_URL}/migration` as const; +export const DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL = + `${DETECTION_ENGINE_SIGNALS_URL}/migration_status` as const; +export const DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL = + `${DETECTION_ENGINE_SIGNALS_URL}/finalize_migration` as const; + +export const ALERTS_AS_DATA_URL = '/internal/rac/alerts' as const; +export const ALERTS_AS_DATA_FIND_URL = `${ALERTS_AS_DATA_URL}/find` as const; + +/** + * Common naming convention for an unauthenticated user + */ +export const UNAUTHENTICATED_USER = 'Unauthenticated' as const; + +/* + Licensing requirements + */ +export const MINIMUM_ML_LICENSE = 'platinum' as const; + +/* + Machine Learning constants + */ +export const ML_GROUP_ID = 'security' as const; +export const LEGACY_ML_GROUP_ID = 'siem' as const; +export const ML_GROUP_IDS = [ML_GROUP_ID, LEGACY_ML_GROUP_ID] as const; + +export const NOTIFICATION_THROTTLE_NO_ACTIONS = 'no_actions' as const; +export const NOTIFICATION_THROTTLE_RULE = 'rule' as const; + +export const showAllOthersBucket: string[] = [ + 'destination.ip', + 'event.action', + 'event.category', + 'event.dataset', + 'event.module', + 'signal.rule.threat.tactic.name', + 'source.ip', + 'destination.ip', + 'user.name', +]; + +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', + INDEXING: 'indexing', + STARTED: 'started', + STOPPED: 'stopped', + STOPPING: 'stopping', + WAITING: 'waiting', +}; + +export const WARNING_TRANSFORM_STATES = new Set([ + TRANSFORM_STATES.ABORTING, + TRANSFORM_STATES.FAILED, + TRANSFORM_STATES.STOPPED, + TRANSFORM_STATES.STOPPING, +]); + +export const STARTED_TRANSFORM_STATES = new Set([ + TRANSFORM_STATES.INDEXING, + TRANSFORM_STATES.STARTED, +]); + +/** + * How many rules to update at a time is set to 50 from errors coming from + * the slow environments such as cloud when the rule updates are > 100 we were + * seeing timeout issues. + * + * Since there is not timeout options at the alerting API level right now, we are + * at the mercy of the Elasticsearch server client/server default timeouts and what + * we are doing could be considered a workaround to not being able to increase the timeouts. + * + * However, other bad effects and saturation of connections beyond 50 makes this a "noisy neighbor" + * if we don't limit its number of connections as we increase the number of rules that can be + * installed at a time. + * + * Lastly, we saw weird issues where Chrome on upstream 408 timeouts will re-call the REST route + * which in turn could create additional connections we want to avoid. + * + * See file import_rules_route.ts for another area where 50 was chosen, therefore I chose + * 50 here to mimic it as well. If you see this re-opened or what similar to it, consider + * reducing the 50 above to a lower number. + * + * See the original ticket here: + * https://github.com/elastic/kibana/issues/94418 + */ +export const MAX_RULES_TO_UPDATE_IN_PARALLEL = 50; + +export const LIMITED_CONCURRENCY_ROUTE_TAG_PREFIX = `${APP_ID}:limitedConcurrency`; + +/** + * Max number of rules to display on UI in table, max number of rules that can be edited in a single bulk edit API request + * We limit number of rules in bulk edit API, because rulesClient doesn't support bulkGet of rules by ids. + * Given this limitation, current implementation fetches each rule separately through rulesClient.resolve method. + * As max number of rules displayed on a page is 100, max 100 rules can be bulk edited by passing their ids to API. + * We decided add this limit(number of ids less than 100) in bulk edit API as well, to prevent a huge number of single rule fetches + */ +export const RULES_TABLE_MAX_PAGE_SIZE = 100; + +/** + * Local storage keys we use to store the state of our new features tours we currently show in the app. + * + * NOTE: As soon as we want to show tours for new features in the upcoming release, + * we will need to update these constants with the corresponding version. + */ +export const NEW_FEATURES_TOUR_STORAGE_KEYS = { + RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.6', +}; + +export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY = + 'securitySolution.ruleDetails.ruleExecutionLog.showMetrics.v8.2'; + +// TODO: https://github.com/elastic/kibana/pull/142950 +/** + * Error codes that can be thrown during _bulk_action API dry_run call and be processed and displayed to end user + */ +export enum BulkActionsDryRunErrCode { + IMMUTABLE = 'IMMUTABLE', + MACHINE_LEARNING_AUTH = 'MACHINE_LEARNING_AUTH', + MACHINE_LEARNING_INDEX_PATTERN = 'MACHINE_LEARNING_INDEX_PATTERN', +} + +export const RISKY_HOSTS_DOC_LINK = + 'https://www.elastic.co/guide/en/security/current/host-risk-score.html'; +export const RISKY_USERS_DOC_LINK = + 'https://www.elastic.co/guide/en/security/current/user-risk-score.html'; + +export const MAX_NUMBER_OF_NEW_TERMS_FIELDS = 3; + +export const BULK_ADD_TO_TIMELINE_LIMIT = 2000; + +export const DEFAULT_DETECTION_PAGE_FILTERS = [ + { + title: 'Status', + fieldName: 'kibana.alert.workflow_status', + selectedOptions: ['open'], + hideActionBar: true, + }, + { + title: 'Severity', + fieldName: 'kibana.alert.severity', + selectedOptions: [], + hideActionBar: true, + }, + { + title: 'User', + fieldName: 'user.name', + }, + { + title: 'Host', + fieldName: 'host.name', + }, +]; diff --git a/x-pack/plugins/security_solution/common/types/data_table/index.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts similarity index 91% rename from x-pack/plugins/security_solution/common/types/data_table/index.ts rename to x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts index 0a9c23aebf729..92d84d8026a1d 100644 --- a/x-pack/plugins/security_solution/common/types/data_table/index.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/data_table/index.ts @@ -5,13 +5,10 @@ * 2.0. */ +import { Direction } from '@kbn/timelines-plugin/common'; import * as runtimeTypes from 'io-ts'; -import type { VIEW_SELECTION } from '../../constants'; -export enum Direction { - asc = 'asc', - desc = 'desc', -} +export { Direction }; export type SortDirectionTable = 'none' | 'asc' | 'desc' | Direction; export interface SortColumnTable { @@ -21,8 +18,6 @@ export interface SortColumnTable { sortDirection: SortDirectionTable; } -export type { TableById } from '../../../public/common/store/data_table/types'; - export enum TableId { usersPageEvents = 'users-page-events', hostsPageEvents = 'hosts-page-events', @@ -67,10 +62,16 @@ const TableIdLiteralRt = runtimeTypes.union([ runtimeTypes.literal(TableId.test), runtimeTypes.literal(TableId.rulePreview), runtimeTypes.literal(TableId.kubernetesPageSessions), + runtimeTypes.literal(TableId.alertsOnCasePage), ]); export type TableIdLiteral = runtimeTypes.TypeOf; +export const VIEW_SELECTION = { + gridView: 'gridView', + eventRenderedView: 'eventRenderedView', +} as const; + export type ViewSelectionTypes = keyof typeof VIEW_SELECTION; export type ViewSelection = typeof VIEW_SELECTION[ViewSelectionTypes]; diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/types/detail_panel.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/detail_panel.ts new file mode 100644 index 0000000000000..075f7e5ad7183 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/detail_panel.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. + */ + +type EmptyObject = Record; + +export enum FlowTargetSourceDest { + destination = 'destination', + source = 'source', +} + +export type ExpandedEventType = + | { + panelView?: 'eventDetail'; + params?: { + eventId: string; + indexName: string; + refetch?: () => void; + }; + } + | EmptyObject; + +export type ExpandedHostType = + | { + panelView?: 'hostDetail'; + params?: { + hostName: string; + }; + } + | EmptyObject; + +export type ExpandedNetworkType = + | { + panelView?: 'networkDetail'; + params?: { + ip: string; + flowTarget: FlowTargetSourceDest; + }; + } + | EmptyObject; + +export type ExpandedUserType = + | { + panelView?: 'userDetail'; + params?: { + userName: string; + }; + } + | EmptyObject; + +export type ExpandedDetailType = + | ExpandedEventType + | ExpandedHostType + | ExpandedNetworkType + | ExpandedUserType; + +export enum TimelineTabs { + query = 'query', + graph = 'graph', + notes = 'notes', + pinned = 'pinned', + eql = 'eql', + session = 'session', +} + +export type ExpandedDetailTimeline = { + [tab in TimelineTabs]?: ExpandedDetailType; +}; + +export type ExpandedDetail = Partial>; diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/types/header_actions/index.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/header_actions/index.ts new file mode 100644 index 0000000000000..f8dea6bf3612b --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/header_actions/index.ts @@ -0,0 +1,107 @@ +/* + * 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 { EuiDataGridCellValueElementProps, EuiDataGridColumn } from '@elastic/eui'; +import type { IFieldSubType } from '@kbn/es-query'; +import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public'; +import type { ComponentType, JSXElementConstructor } from 'react'; +import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { BrowserFields } from '@kbn/rule-registry-plugin/common'; +import { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; +import { OnRowSelected } from '../../../components/data_table/types'; +import type { SortColumnTable } from '../data_table'; +import { SetEventsDeleted, SetEventsLoading } from '..'; + +export type ColumnHeaderType = 'not-filtered' | 'text-filter'; + +/** Uniquely identifies a column */ +export type ColumnId = string; + +/** The specification of a column header */ +export type ColumnHeaderOptions = Pick< + EuiDataGridColumn, + | 'actions' + | 'defaultSortDirection' + | 'display' + | 'displayAsText' + | 'id' + | 'initialWidth' + | 'isSortable' + | 'schema' + | 'isExpandable' + | 'isResizable' +> & { + aggregatable?: boolean; + category?: string; + columnHeaderType: ColumnHeaderType; + description?: string | null; + esTypes?: string[]; + example?: string | number | null; + format?: string; + linkField?: string; + placeholder?: string; + subType?: IFieldSubType; + type?: string; +}; + +export interface HeaderActionProps { + width: number; + browserFields: BrowserFields; + columnHeaders: ColumnHeaderOptions[]; + fieldBrowserOptions?: FieldBrowserOptions; + isEventViewer?: boolean; + isSelectAllChecked: boolean; + onSelectAll: ({ isSelected }: { isSelected: boolean }) => void; + showEventsSelect: boolean; + showSelectAllCheckbox: boolean; + sort: SortColumnTable[]; + tabType: string; + timelineId: string; +} + +export type HeaderCellRender = ComponentType | ComponentType; + +type GenericActionRowCellRenderProps = Pick< + EuiDataGridCellValueElementProps, + 'rowIndex' | 'columnId' +>; + +export type RowCellRender = + | JSXElementConstructor + | ((props: GenericActionRowCellRenderProps) => JSX.Element) + | JSXElementConstructor + | ((props: ActionProps) => JSX.Element); + +export interface ActionProps { + action?: RowCellRender; + ariaRowindex: number; + checked: boolean; + columnId: string; + columnValues: string; + data: TimelineNonEcsData[]; + disabled?: boolean; + ecsData: Ecs; + eventId: string; + eventIdToNoteIds?: Readonly>; + index: number; + isEventPinned?: boolean; + isEventViewer?: boolean; + loadingEventIds: Readonly; + onEventDetailsPanelOpened: () => void; + onRowSelected: OnRowSelected; + onRuleChange?: () => void; + refetch?: () => void; + rowIndex: number; + setEventsDeleted: SetEventsDeleted; + setEventsLoading: SetEventsLoading; + showCheckboxes: boolean; + showNotes?: boolean; + tabType?: string; + timelineId: string; + toggleShowNotes?: () => void; + width?: number; +} diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/types/index.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/index.ts new file mode 100644 index 0000000000000..8a10f51754c9b --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/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. + */ + +export * from './data_table'; +export * from './header_actions'; +export * from './session_view'; + +export const FILTER_OPEN = 'open' as const; +export const FILTER_CLOSED = 'closed' as const; +export const FILTER_ACKNOWLEDGED = 'acknowledged' as const; + +export type SetEventsLoading = (params: { eventIds: string[]; isLoading: boolean }) => void; +export type SetEventsDeleted = (params: { eventIds: string[]; isDeleted: boolean }) => void; + +export { TimelineTabs } from './detail_panel'; diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/types/risk_scores.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/risk_scores.ts new file mode 100644 index 0000000000000..810f868e047cb --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/risk_scores.ts @@ -0,0 +1,18 @@ +/* + * 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 interface ESProcessorConfig { + on_failure?: Processor[]; + ignore_failure?: boolean; + if?: string; + tag?: string; + [key: string]: unknown; +} + +export interface Processor { + [typeName: string]: ESProcessorConfig; +} diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts new file mode 100644 index 0000000000000..105e5cc6b1d84 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface SessionViewConfig { + sessionEntityId: string; + jumpToEntityId?: string; + jumpToCursor?: string; + investigatedAlertId?: string; +} diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/default_headers.ts b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/default_headers.ts similarity index 91% rename from x-pack/plugins/security_solution/public/common/components/data_table/column_headers/default_headers.ts rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/default_headers.ts index 8d1736cc172c3..20a36c0803cec 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/default_headers.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/default_headers.ts @@ -5,8 +5,7 @@ * 2.0. */ -import type { ColumnHeaderType } from '../../../../../common/types'; -import type { ColumnHeaderOptions } from '../../../../../common/types/timeline'; +import type { ColumnHeaderOptions, ColumnHeaderType } from '../../../common/types'; import { DEFAULT_TABLE_COLUMN_MIN_WIDTH, DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH } from '../constants'; export const defaultColumnHeaderType: ColumnHeaderType = 'not-filtered'; diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/helpers.test.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.test.tsx similarity index 99% rename from x-pack/plugins/security_solution/public/common/components/data_table/column_headers/helpers.test.tsx rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.test.tsx index 34f3320513b47..b6a4ae9cd0c67 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/helpers.test.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.test.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { mount } from 'enzyme'; import { set } from '@kbn/safer-lodash-set/fp'; import { omit } from 'lodash/fp'; @@ -18,9 +19,9 @@ import { allowSorting, } from './helpers'; import { DEFAULT_TABLE_COLUMN_MIN_WIDTH, DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH } from '../constants'; -import type { ColumnHeaderOptions } from '../../../../../common/types'; -import { mockBrowserFields } from '../../../containers/source/mock'; +import type { ColumnHeaderOptions } from '../../../common/types'; import { defaultHeaders } from '../../../store/data_table/defaults'; +import { mockBrowserFields } from '../../../mock/mock_source'; window.matchMedia = jest.fn().mockImplementation((query) => { return { diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/helpers.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx similarity index 83% rename from x-pack/plugins/security_solution/public/common/components/data_table/column_headers/helpers.tsx rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx index a39a0fc3664c8..95adca844984c 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/helpers.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/helpers.tsx @@ -5,18 +5,15 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import type { EuiDataGridColumnActions } from '@elastic/eui'; import { keyBy } from 'lodash/fp'; import React from 'react'; -import { eventRenderedViewColumns } from '../../../../detections/configurations/security_solution_detections/columns'; -import type { - BrowserField, - BrowserFields, -} from '../../../../../common/search_strategy/index_fields'; -import type { ColumnHeaderOptions } from '../../../../../common/types/timeline'; +import { BrowserField, BrowserFields } from '@kbn/timelines-plugin/common'; import { DEFAULT_TABLE_COLUMN_MIN_WIDTH, DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH } from '../constants'; import { defaultColumnHeaderType } from '../../../store/data_table/defaults'; +import { ColumnHeaderOptions } from '../../../common/types'; const defaultActions: EuiDataGridColumnActions = { showSortAsc: true, @@ -152,6 +149,48 @@ export const getSchema = (type: string | undefined): BUILT_IN_SCHEMA | undefined } }; +const eventRenderedViewColumns: ColumnHeaderOptions[] = [ + { + columnHeaderType: defaultColumnHeaderType, + id: '@timestamp', + displayAsText: i18n.translate( + 'securitySolutionDataTable.EventRenderedView.timestampTitle.column', + { + defaultMessage: 'Timestamp', + } + ), + initialWidth: DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH + 50, + actions: false, + isExpandable: false, + isResizable: false, + }, + { + columnHeaderType: defaultColumnHeaderType, + displayAsText: i18n.translate('securitySolutionDataTable.EventRenderedView.ruleTitle.column', { + defaultMessage: 'Rule', + }), + id: 'kibana.alert.rule.name', + initialWidth: DEFAULT_TABLE_COLUMN_MIN_WIDTH + 50, + linkField: 'kibana.alert.rule.uuid', + actions: false, + isExpandable: false, + isResizable: false, + }, + { + columnHeaderType: defaultColumnHeaderType, + id: 'eventSummary', + displayAsText: i18n.translate( + 'securitySolutionDataTable.EventRenderedView.eventSummary.column', + { + defaultMessage: 'Event Summary', + } + ), + actions: false, + isExpandable: false, + isResizable: false, + }, +]; + /** Enriches the column headers with field details from the specified browserFields */ export const getColumnHeaders = ( headers: ColumnHeaderOptions[], diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/translations.ts b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/translations.ts similarity index 82% rename from x-pack/plugins/security_solution/public/common/components/data_table/column_headers/translations.ts rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/translations.ts index c7e32ef1d15da..48aeb0d0f0d9e 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/column_headers/translations.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/column_headers/translations.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; export const REMOVE_COLUMN = i18n.translate( - 'xpack.securitySolution.columnHeaders.flyout.pane.removeColumnButtonLabel', + 'securitySolutionDataTable.columnHeaders.flyout.pane.removeColumnButtonLabel', { defaultMessage: 'Remove column', } diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/constants.ts b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/constants.ts similarity index 59% rename from x-pack/plugins/security_solution/public/common/components/data_table/constants.ts rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/constants.ts index 3658382879300..a1e5bdbc9bc8e 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/constants.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/constants.ts @@ -10,3 +10,12 @@ export const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 180; // px /** The default minimum width of a column of type `date` */ export const DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH = 190; // px + +/** The default minimum width of a column (when a width for the column type is not specified) */ +export const DEFAULT_COLUMN_MIN_WIDTH = 180; // px + +/** The minimum width of a resized column */ +export const RESIZED_COLUMN_MIN_WITH = 70; // px + +/** The default minimum width of a column of type `date` */ +export const DEFAULT_DATE_COLUMN_MIN_WIDTH = 190; // px diff --git a/x-pack/packages/kbn-securitysolution-data-table/components/data_table/data_table.stories.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/data_table.stories.tsx new file mode 100644 index 0000000000000..29b50a59af86a --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/data_table.stories.tsx @@ -0,0 +1,107 @@ +/* + * 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 { CellActionsProvider } from '@kbn/cell-actions'; +import { I18nProvider } from '@kbn/i18n-react'; +import { CellValueElementProps } from '@kbn/timelines-plugin/common'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import React from 'react'; +import { DragDropContext, DropResult, ResponderProvided } from 'react-beautiful-dnd'; +// eslint-disable-next-line @kbn/eslint/module_migration +import { ThemeProvider } from 'styled-components'; +import { Provider as ReduxStoreProvider } from 'react-redux'; +import { euiDarkVars } from '@kbn/ui-theme'; +import { Store } from 'redux'; +import { createStore as createReduxStore } from 'redux'; +import type { Action } from '@kbn/ui-actions-plugin/public'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { mockGlobalState } from '../../mock/global_state'; +import { getMappedNonEcsValue } from './utils'; +import { TableId } from '../..'; +import { mockTimelineData } from '../../mock/mock_timeline_data'; +import { DataTableComponent } from '.'; + +export default { + component: DataTableComponent, + title: 'DataTableComponent', +}; + +const createStore = (state: any) => createReduxStore(() => state, state); + +interface Props { + children?: React.ReactNode; + store?: Store; + onDragEnd?: (result: DropResult, provided: ResponderProvided) => void; + cellActions?: Action[]; +} + +const StoryCellRenderer: React.FC = ({ columnId, data }) => ( + <> + {getMappedNonEcsValue({ + data, + fieldName: columnId, + })?.reduce((x) => x[0]) ?? ''} + +); + +const StoryProviders: React.FC = ({ children, onDragEnd = () => {}, cellActions = [] }) => { + const store = createStore(mockGlobalState); + const queryClient = new QueryClient(); + + return ( + + + ({ eui: euiDarkVars, darkMode: true })}> + + Promise.resolve(cellActions)}> + {children} + + + + + + ); +}; + +const MockFieldBrowser = () => { + return ( + window.alert('Not implemented')} + > + Field Browser + + ); +}; + +export function Example() { + return ( + + {}, + onChangePage: () => {}, + }} + loadPage={() => {}} + rowRenderers={[]} + totalItems={mockTimelineData.length} + getFieldBrowser={() => } + /> + + ); +} diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/helpers.test.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.test.tsx similarity index 98% rename from x-pack/plugins/security_solution/public/common/components/data_table/helpers.test.tsx rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.test.tsx index db337d3bdaf8f..597f661aa3650 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/helpers.test.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import type { ColumnHeaderOptions } from '../../../../common/types'; +import type { ColumnHeaderOptions } from '../../common/types'; import { hasCellActions, mapSortDirectionToDirection, @@ -14,7 +14,7 @@ import { } from './helpers'; import { euiThemeVars } from '@kbn/ui-theme'; -import { mockDnsEvent } from '../../mock'; +import { mockDnsEvent } from '../../mock/mock_timeline_data'; describe('helpers', () => { describe('mapSortDirectionToDirection', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/helpers.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx similarity index 91% rename from x-pack/plugins/security_solution/public/common/components/data_table/helpers.tsx rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx index a31c40d45dace..38c1384b3468b 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/helpers.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/helpers.tsx @@ -10,9 +10,12 @@ import { isEmpty } from 'lodash/fp'; import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; -import type { SortColumnTable } from '../../../../common/types'; -import type { TimelineItem, TimelineNonEcsData } from '../../../../common/search_strategy'; -import type { ColumnHeaderOptions, SortDirection } from '../../../../common/types/timeline'; +import { + ColumnHeaderOptions, + TimelineItem, + TimelineNonEcsData, +} from '@kbn/timelines-plugin/common'; +import type { SortColumnTable, SortDirectionTable } from '../../common/types'; /** * Creates mapping of eventID -> fieldData for given fieldsToKeep. Used to store additional field @@ -42,7 +45,7 @@ export const isEventBuildingBlockType = (event: Ecs): boolean => !isEmpty(event.kibana?.alert?.building_block_type); /** Maps (Redux) `SortDirection` to the `direction` values used by `EuiDataGrid` */ -export const mapSortDirectionToDirection = (sortDirection: SortDirection): 'asc' | 'desc' => { +export const mapSortDirectionToDirection = (sortDirection: SortDirectionTable): 'asc' | 'desc' => { switch (sortDirection) { case 'asc': // fall through case 'desc': diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.test.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.test.tsx index 86cf4eabeba1b..28217ff931d43 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.test.tsx @@ -13,12 +13,14 @@ import { DataTableComponent } from '.'; import { REMOVE_COLUMN } from './column_headers/translations'; import { useMountAppended } from '../../utils/use_mount_appended'; import type { EuiDataGridColumn } from '@elastic/eui'; -import { defaultHeaders, mockGlobalState, mockTimelineData, TestProviders } from '../../mock'; -import { mockBrowserFields } from '../../containers/source/mock'; -import { getMappedNonEcsValue } from '../../../timelines/components/timeline/body/data_driven_columns'; -import type { CellValueElementProps } from '../../../../common/types'; -import { TableId } from '../../../../common/types'; -import { SecurityCellActionsTrigger } from '../cell_actions'; +import { TableId } from '../../common/types'; +import { defaultHeaders } from '../../mock/header'; +import { mockGlobalState } from '../../mock/global_state'; +import { mockTimelineData } from '../../mock/mock_timeline_data'; +import { TestProviders } from '../../mock/test_providers'; +import { CellValueElementProps } from '@kbn/timelines-plugin/common'; +import { mockBrowserFields } from '../../mock/mock_source'; +import { getMappedNonEcsValue } from './utils'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => ({ @@ -41,20 +43,6 @@ jest.mock('./column_headers/helpers', () => ({ getColumnHeaders: () => mockGetColumnHeaders(), })); -jest.mock('@kbn/kibana-react-plugin/public', () => { - const originalModule = jest.requireActual('@kbn/kibana-react-plugin/public'); - return { - ...originalModule, - useKibana: () => ({ - services: { - triggersActionsUi: { - getFieldBrowser: jest.fn(), - }, - }, - }), - }; -}); - jest.mock('../../hooks/use_selector', () => ({ useShallowEqualSelector: () => mockGlobalState.dataTable.tableById['table-test'], useDeepEqualSelector: () => mockGlobalState.dataTable.tableById['table-test'], @@ -104,6 +92,7 @@ describe('DataTable', () => { onChangeItemsPerPage: jest.fn(), onChangePage: jest.fn(), }, + getFieldBrowser: jest.fn(), }; beforeEach(() => { @@ -178,13 +167,17 @@ describe('DataTable', () => { const data = mockTimelineData.slice(0, 1); const wrapper = mount( - + ); wrapper.update(); expect(mockUseDataGridColumnsCellActions).toHaveBeenCalledWith({ - triggerId: SecurityCellActionsTrigger.DEFAULT, + triggerId: 'mockCellActionsTrigger', fields: [ { name: '@timestamp', @@ -200,10 +193,10 @@ describe('DataTable', () => { }); }); - test('does not render cell actions if disableCellActions is true', () => { + test('does not render cell actions if cellActionsTriggerId is not specified', () => { const wrapper = mount( - + ); wrapper.update(); @@ -219,7 +212,11 @@ describe('DataTable', () => { mockUseDataGridColumnsCellActions.mockReturnValueOnce([]); const wrapper = mount( - + ); wrapper.update(); @@ -237,7 +234,11 @@ describe('DataTable', () => { mockUseDataGridColumnsCellActions.mockReturnValueOnce([[() =>
]]); const wrapper = mount( - + ); wrapper.update(); diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/index.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/common/components/data_table/index.tsx rename to x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.tsx index a48bf61f4ac93..0f1e34099997f 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/index.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/components/data_table/index.tsx @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable @kbn/eslint/module_migration */ + import type { EuiDataGridRefProps, EuiDataGridColumn, @@ -14,6 +16,7 @@ import type { EuiDataGridControlColumn, EuiDataGridPaginationProps, EuiDataGridRowHeightsOptions, + EuiDataGridProps, } from '@elastic/eui'; import { EuiDataGrid, EuiProgress } from '@elastic/eui'; import { getOr } from 'lodash/fp'; @@ -23,44 +26,60 @@ import { useDispatch } from 'react-redux'; import styled, { ThemeContext } from 'styled-components'; import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; -import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public'; +import type { + FieldBrowserOptions, + FieldBrowserProps, +} from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { - useDataGridColumnsSecurityCellActions, - SecurityCellActionsTrigger, - type UseDataGridColumnsSecurityCellActionsProps, -} from '../cell_actions'; -import type { + BrowserFields, CellValueElementProps, ColumnHeaderOptions, RowRenderer, -} from '../../../../common/types/timeline'; - -import type { TimelineItem } from '../../../../common/search_strategy/timeline'; + TimelineItem, +} from '@kbn/timelines-plugin/common'; +import { useDataGridColumnsCellActions } from '@kbn/cell-actions'; +import { DataTableModel, DataTableState } from '../../store/data_table/types'; import { getColumnHeader, getColumnHeaders } from './column_headers/helpers'; import { addBuildingBlockStyle, mapSortDirectionToDirection, mapSortingColumns } from './helpers'; -import type { BrowserFields } from '../../../../common/search_strategy/index_fields'; import { REMOVE_COLUMN } from './column_headers/translations'; import { dataTableActions, dataTableSelectors } from '../../store/data_table'; import type { BulkActionsProp } from '../toolbar/bulk_actions/types'; -import { useKibana } from '../../lib/kibana'; import { getPageRowIndex } from './pagination'; import { UnitCount } from '../toolbar/unit'; import { useShallowEqualSelector } from '../../hooks/use_selector'; import { tableDefaults } from '../../store/data_table/defaults'; -const DATA_TABLE_ARIA_LABEL = i18n.translate('xpack.securitySolution.dataTable.ariaLabel', { +const DATA_TABLE_ARIA_LABEL = i18n.translate('securitySolutionDataTable.dataTable.ariaLabel', { defaultMessage: 'Alerts', }); -export interface DataTableProps { +type GetFieldBrowser = (props: FieldBrowserProps) => void; + +type NonCustomizableGridProps = + | 'id' + | 'data-test-subj' + | 'aria-label' + | 'aria-labelledby' + | 'columns' + | 'columnVisibility' + | 'gridStyle' + | 'leadingControlColumns' + | 'toolbarVisibility' + | 'rowCount' + | 'renderCellValue' + | 'sorting' + | 'onColumnResize' + | 'pagination' + | 'rowHeightsOptions'; + +interface BaseDataTableProps { additionalControls?: React.ReactNode; browserFields: BrowserFields; bulkActions?: BulkActionsProp; data: TimelineItem[]; - disableCellActions?: boolean; fieldBrowserOptions?: FieldBrowserOptions; id: string; leadingControlColumns: EuiDataGridControlColumn[]; @@ -73,8 +92,12 @@ export interface DataTableProps { totalItems: number; rowHeightsOptions?: EuiDataGridRowHeightsOptions; isEventRenderedView?: boolean; + getFieldBrowser: GetFieldBrowser; + cellActionsTriggerId?: string; } +export type DataTableProps = BaseDataTableProps & Omit; + const ES_LIMIT_COUNT = 9999; const gridStyle = (isEventRenderedView: boolean | undefined = false): EuiDataGridStyle => ({ @@ -115,7 +138,6 @@ export const DataTableComponent = React.memo( browserFields, bulkActions = true, data, - disableCellActions = false, fieldBrowserOptions, hasCrudPermissions, id, @@ -128,12 +150,14 @@ export const DataTableComponent = React.memo( totalItems, rowHeightsOptions, isEventRenderedView = false, + getFieldBrowser, + cellActionsTriggerId, + ...otherProps }) => { - const { - triggersActionsUi: { getFieldBrowser }, - } = useKibana().services; const getDataTable = dataTableSelectors.getTableByIdSelector(); - const dataTable = useShallowEqualSelector((state) => getDataTable(state, id) ?? tableDefaults); + const dataTable = useShallowEqualSelector( + (state) => getDataTable(state, id) ?? tableDefaults + ); const { columns, selectedEventIds, showCheckboxes, sort, isLoading, defaultColumns } = dataTable; @@ -303,8 +327,8 @@ export const DataTableComponent = React.memo( [dispatch, id] ); - const columnsCellActionsProps = useMemo(() => { - const fields = disableCellActions + const columnsCellActionsProps = useMemo(() => { + const fields = !cellActionsTriggerId ? [] : columnHeaders.map((column) => ({ name: column.id, @@ -317,16 +341,16 @@ export const DataTableComponent = React.memo( })); return { - triggerId: SecurityCellActionsTrigger.DEFAULT, + triggerId: cellActionsTriggerId || '', fields, metadata: { scopeId: id, }, dataGridRef, }; - }, [disableCellActions, columnHeaders, data, id]); + }, [columnHeaders, cellActionsTriggerId, id, data]); - const columnsCellActions = useDataGridColumnsSecurityCellActions(columnsCellActionsProps); + const columnsCellActions = useDataGridColumnsCellActions(columnsCellActionsProps); const columnsWithCellActions: EuiDataGridColumn[] = useMemo( () => @@ -417,6 +441,7 @@ export const DataTableComponent = React.memo( <> ES_LIMIT_COUNT}> { + const item = data.find((d) => d.field === fieldName); + if (item != null && item.value != null) { + return item.value; + } + return undefined; +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/bulk_actions/types.ts b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/bulk_actions/types.ts new file mode 100644 index 0000000000000..ab556f1908919 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/bulk_actions/types.ts @@ -0,0 +1,39 @@ +/* + * 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 { TimelineItem } from '@kbn/timelines-plugin/common'; + +export type AlertWorkflowStatus = 'open' | 'closed' | 'acknowledged'; + +export interface CustomBulkAction { + key: string; + label: string; + disableOnQuery?: boolean; + disabledLabel?: string; + onClick: (items?: TimelineItem[]) => void; + ['data-test-subj']?: string; +} + +export type CustomBulkActionProp = Omit & { + onClick: (eventIds: string[]) => void; +}; + +export interface BulkActionsObjectProp { + alertStatusActions?: boolean; + onAlertStatusActionSuccess?: OnUpdateAlertStatusSuccess; + onAlertStatusActionFailure?: OnUpdateAlertStatusError; + customBulkActions?: CustomBulkAction[]; +} + +export type OnUpdateAlertStatusSuccess = ( + updated: number, + conflicts: number, + status: AlertWorkflowStatus +) => void; +export type OnUpdateAlertStatusError = (status: AlertWorkflowStatus, error: Error) => void; + +export type BulkActionsProp = boolean | BulkActionsObjectProp; diff --git a/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/index.ts b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/index.ts new file mode 100644 index 0000000000000..ca28e59d4bf51 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as i18n from './translations'; + +export * from './styles'; +export const defaultUnit = (n: number) => i18n.ALERTS_UNIT(n); diff --git a/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/styles.tsx b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/styles.tsx new file mode 100644 index 0000000000000..c135ac935337e --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/styles.tsx @@ -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. + */ + +// eslint-disable-next-line @kbn/eslint/module_migration +import styled from 'styled-components'; + +export const UnitCount = styled.span` + font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; + font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; + border-right: ${({ theme }) => theme.eui.euiBorderThin}; + margin-right: ${({ theme }) => theme.eui.euiSizeS}; + padding-right: ${({ theme }) => theme.eui.euiSizeM}; +`; diff --git a/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/translations.ts b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/translations.ts new file mode 100644 index 0000000000000..0ec18a9e509cd --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/components/toolbar/unit/translations.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. + */ + +import { i18n } from '@kbn/i18n'; + +export const ALERTS_UNIT = (totalCount: number) => + i18n.translate('securitySolutionDataTable.eventsTab.unit', { + values: { totalCount }, + defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`, + }); diff --git a/x-pack/packages/kbn-securitysolution-data-table/hooks/use_selector.tsx b/x-pack/packages/kbn-securitysolution-data-table/hooks/use_selector.tsx new file mode 100644 index 0000000000000..55db6d7f4dfdd --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/hooks/use_selector.tsx @@ -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 { shallowEqual, useSelector } from 'react-redux'; + +export type TypedUseSelectorHook = ( + selector: (state: TState) => TSelected, + equalityFn?: (left: TSelected, right: TSelected) => boolean +) => TSelected; + +export const useShallowEqualSelector: TypedUseSelectorHook = (selector) => + useSelector(selector, shallowEqual); diff --git a/x-pack/packages/kbn-securitysolution-data-table/index.ts b/x-pack/packages/kbn-securitysolution-data-table/index.ts new file mode 100644 index 0000000000000..0f58e52fba4db --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { DataTableComponent } from './components/data_table'; + +export { dataTableActions, dataTableSelectors } from './store/data_table'; +export { getTableByIdSelector } from './store/data_table/selectors'; +export { dataTableReducer } from './store/data_table/reducer'; +export { + tableDefaults, + defaultColumnHeaderType, + defaultHeaders, +} from './store/data_table/defaults'; + +export type { TableState, DataTableState, TableById } from './store/data_table/types'; +export type { DataTableModel, SubsetDataTableModel } from './store/data_table/model'; + +export { + Direction, + tableEntity, + FILTER_OPEN, + TimelineTabs, + TableId, + TableEntityType, +} from './common/types'; +export type { + TableIdLiteral, + ViewSelection, + SortDirectionTable, + SortColumnTable, +} from './common/types'; + +export { getColumnHeaders } from './components/data_table/column_headers/helpers'; +export { + getEventIdToDataMapping, + addBuildingBlockStyle, + isEventBuildingBlockType, +} from './components/data_table/helpers'; +export { getPageRowIndex } from './components/data_table/pagination'; diff --git a/x-pack/plugins/task_manager/server/lib/calculate_health_status.mock.ts b/x-pack/packages/kbn-securitysolution-data-table/jest.config.js similarity index 61% rename from x-pack/plugins/task_manager/server/lib/calculate_health_status.mock.ts rename to x-pack/packages/kbn-securitysolution-data-table/jest.config.js index f34a26560133b..439c6c690765b 100644 --- a/x-pack/plugins/task_manager/server/lib/calculate_health_status.mock.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/jest.config.js @@ -5,10 +5,8 @@ * 2.0. */ -const createCalculateHealthStatusMock = () => { - return jest.fn(); -}; - -export const calculateHealthStatusMock = { - create: createCalculateHealthStatusMock, +module.exports = { + preset: '@kbn/test', + roots: ['/x-pack/packages/kbn-securitysolution-data-table'], + rootDir: '../../..', }; diff --git a/x-pack/packages/kbn-securitysolution-data-table/kibana.jsonc b/x-pack/packages/kbn-securitysolution-data-table/kibana.jsonc new file mode 100644 index 0000000000000..5298db752359f --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/securitysolution-data-table", + "owner": "@elastic/security-threat-hunting-investigations" +} diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/library_load_event.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/library_load_event.ts new file mode 100644 index 0000000000000..a7cd3ec96d22f --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/library_load_event.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; + +export const demoEndpointLibraryLoadEvent: Ecs = { + file: { + path: ['C:\\Windows\\System32\\bcrypt.dll'], + hash: { + md5: ['00439016776de367bad087d739a03797'], + sha1: ['2c4ba5c1482987d50a182bad915f52cd6611ee63'], + sha256: ['e70f5d8f87aab14e3160227d38387889befbe37fa4f8f5adc59eff52804b35fd'], + }, + name: ['bcrypt.dll'], + }, + host: { + os: { + full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + name: ['Windows'], + version: ['1809 (10.0.17763.1697)'], + family: ['windows'], + kernel: ['1809 (10.0.17763.1697)'], + platform: ['windows'], + }, + mac: ['aa:bb:cc:dd:ee:ff'], + name: ['win2019-endpoint-1'], + architecture: ['x86_64'], + ip: ['10.1.2.3'], + id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + }, + event: { + category: ['library'], + kind: ['event'], + created: ['2021-02-05T21:27:23.921Z'], + module: ['endpoint'], + action: ['load'], + type: ['start'], + id: ['LzzWB9jjGmCwGMvk++++Da5H'], + dataset: ['endpoint.events.library'], + }, + process: { + name: ['sshd.exe'], + pid: [9644], + entity_id: [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTk2NDQtMTMyNTcwMzQwNDEuNzgyMTczODAw', + ], + executable: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + }, + agent: { + type: ['endpoint'], + }, + user: { + name: ['SYSTEM'], + domain: ['NT AUTHORITY'], + }, + message: ['Endpoint DLL load event'], + timestamp: '2021-02-05T21:27:23.921Z', + _id: 'IAUYdHcBGrBB52F2zo8Q', +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/process_execution_malware_prevention_alert.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/process_execution_malware_prevention_alert.ts new file mode 100644 index 0000000000000..252d3a8e76568 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/process_execution_malware_prevention_alert.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 type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; + +export const demoEndpointProcessExecutionMalwarePreventionAlert: Ecs = { + process: { + hash: { + md5: ['177afc1eb0be88eb9983fb74111260c4'], + sha256: ['3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb'], + sha1: ['f573b85e9beb32121f1949217947b2adc6749e3d'], + }, + entity_id: [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTY5MjAtMTMyNDg5OTk2OTAuNDgzMzA3NzAw', + ], + executable: [ + 'C:\\Users\\sean\\Downloads\\3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb.exe', + ], + name: [ + 'C:\\Users\\sean\\Downloads\\3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb.exe', + ], + pid: [6920], + args: [ + 'C:\\Users\\sean\\Downloads\\3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb.exe', + ], + }, + host: { + os: { + full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1518)'], + name: ['Windows'], + version: ['1809 (10.0.17763.1518)'], + platform: ['windows'], + family: ['windows'], + kernel: ['1809 (10.0.17763.1518)'], + }, + mac: ['aa:bb:cc:dd:ee:ff'], + architecture: ['x86_64'], + ip: ['10.1.2.3'], + id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + name: ['win2019-endpoint-1'], + }, + file: { + mtime: ['2020-11-04T21:40:51.494Z'], + path: [ + 'C:\\Users\\sean\\Downloads\\3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb.exe', + ], + owner: ['sean'], + hash: { + md5: ['177afc1eb0be88eb9983fb74111260c4'], + sha256: ['3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb'], + sha1: ['f573b85e9beb32121f1949217947b2adc6749e3d'], + }, + name: ['3be13acde2f4dcded4fd8d518a513bfc9882407a6e384ffb17d12710db7d76fb.exe'], + extension: ['exe'], + size: [1604112], + }, + event: { + category: ['malware', 'intrusion_detection', 'process'], + outcome: ['success'], + severity: [73], + code: ['malicious_file'], + action: ['execution'], + id: ['LsuMZVr+sdhvehVM++++Gp2Y'], + kind: ['alert'], + created: ['2020-11-04T21:41:30.533Z'], + module: ['endpoint'], + type: ['info', 'start', 'denied'], + dataset: ['endpoint.alerts'], + }, + agent: { + type: ['endpoint'], + }, + timestamp: '2020-11-04T21:41:30.533Z', + message: ['Malware Prevention Alert'], + _id: '0dA2lXUBn9bLIbfPkY7d', +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/registry_modification_event.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/registry_modification_event.ts new file mode 100644 index 0000000000000..991df10dd77b0 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/endpoint/registry_modification_event.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 { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; + +export const demoEndpointRegistryModificationEvent: Ecs = { + host: { + os: { + full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + name: ['Windows'], + version: ['1809 (10.0.17763.1697)'], + family: ['windows'], + kernel: ['1809 (10.0.17763.1697)'], + platform: ['windows'], + }, + mac: ['aa:bb:cc:dd:ee:ff'], + name: ['win2019-endpoint-1'], + architecture: ['x86_64'], + ip: ['10.1.2.3'], + id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + }, + event: { + category: ['registry'], + kind: ['event'], + created: ['2021-02-04T13:44:31.559Z'], + module: ['endpoint'], + action: ['modification'], + type: ['change'], + id: ['LzzWB9jjGmCwGMvk++++CbOn'], + dataset: ['endpoint.events.registry'], + }, + process: { + name: ['GoogleUpdate.exe'], + pid: [7408], + entity_id: [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTc0MDgtMTMyNTY5MTk4NDguODY4NTI0ODAw', + ], + executable: ['C:\\Program Files (x86)\\Google\\Update\\GoogleUpdate.exe'], + }, + registry: { + hive: ['HKLM'], + key: [ + 'SOFTWARE\\WOW6432Node\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}\\CurrentState', + ], + path: [ + 'HKLM\\SOFTWARE\\WOW6432Node\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}\\CurrentState\\StateValue', + ], + value: ['StateValue'], + }, + agent: { + type: ['endpoint'], + }, + user: { + name: ['SYSTEM'], + domain: ['NT AUTHORITY'], + }, + message: ['Endpoint registry event'], + timestamp: '2021-02-04T13:44:31.559Z', + _id: '4cxLbXcBGrBB52F2uOfF', +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/timeline.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/timeline.ts new file mode 100644 index 0000000000000..ebbe559b07719 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/demo_data/timeline.ts @@ -0,0 +1,1117 @@ +/* + * 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 { TimelineItem } from '@kbn/timelines-plugin/common'; + +export const demoTimelineData: TimelineItem[] = [ + { + _id: '1', + data: [ + { field: '@timestamp', value: ['2018-11-05T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'event.action', value: ['Action'] }, + { field: 'host.name', value: ['apache'] }, + { field: 'source.ip', value: ['192.168.0.1'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['john.dee'] }, + ], + ecs: { + _id: '1', + timestamp: '2018-11-05T19:03:25.937Z', + host: { name: ['apache'], ip: ['192.168.0.1'] }, + event: { + id: ['1'], + action: ['Action'], + category: ['Access'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.1'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['1'], name: ['john.dee'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '3', + data: [ + { field: '@timestamp', value: ['2018-11-07T19:03:25.937Z'] }, + { field: 'event.severity', value: ['1'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['nginx'] }, + { field: 'source.ip', value: ['192.168.0.3'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['evan.davis'] }, + ], + ecs: { + _id: '3', + timestamp: '2018-11-07T19:03:25.937Z', + host: { name: ['nginx'], ip: ['192.168.0.1'] }, + event: { + id: ['3'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [1], + }, + source: { ip: ['192.168.0.3'], port: [443] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['3'], name: ['evan.davis'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '4', + data: [ + { field: '@timestamp', value: ['2018-11-08T19:03:25.937Z'] }, + { field: 'event.severity', value: ['1'] }, + { field: 'event.category', value: ['Attempted Administrator Privilege Gain'] }, + { field: 'host.name', value: ['suricata'] }, + { field: 'source.ip', value: ['192.168.0.3'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jenny.jones'] }, + ], + ecs: { + _id: '4', + timestamp: '2018-11-08T19:03:25.937Z', + host: { name: ['suricata'], ip: ['192.168.0.1'] }, + event: { + id: ['4'], + category: ['Attempted Administrator Privilege Gain'], + type: ['Alert'], + module: ['suricata'], + severity: [1], + }, + source: { ip: ['192.168.0.3'], port: [53] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + suricata: { + eve: { + flow_id: [4], + proto: [''], + alert: { + signature: [ + 'ET EXPLOIT NETGEAR WNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)', + ], + signature_id: [4], + }, + }, + }, + user: { id: ['4'], name: ['jenny.jones'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '5', + data: [ + { field: '@timestamp', value: ['2018-11-09T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.3'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['becky.davis'] }, + ], + ecs: { + _id: '5', + timestamp: '2018-11-09T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['5'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.3'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['5'], name: ['becky.davis'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '6', + data: [ + { field: '@timestamp', value: ['2018-11-10T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['braden.davis'] }, + { field: 'source.ip', value: ['192.168.0.6'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + ], + ecs: { + _id: '6', + timestamp: '2018-11-10T19:03:25.937Z', + host: { name: ['braden.davis'], ip: ['192.168.0.1'] }, + event: { + id: ['6'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.6'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '8', + data: [ + { field: '@timestamp', value: ['2018-11-12T19:03:25.937Z'] }, + { field: 'event.severity', value: ['2'] }, + { field: 'event.category', value: ['Web Application Attack'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.8'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jone.doe'] }, + ], + ecs: { + _id: '8', + timestamp: '2018-11-12T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['8'], + category: ['Web Application Attack'], + type: ['Alert'], + module: ['suricata'], + severity: [2], + }, + suricata: { + eve: { + flow_id: [8], + proto: [''], + alert: { + signature: ['ET WEB_SERVER Possible CVE-2014-6271 Attempt in HTTP Cookie'], + signature_id: [8], + }, + }, + }, + source: { ip: ['192.168.0.8'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['8'], name: ['jone.doe'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '7', + data: [ + { field: '@timestamp', value: ['2018-11-11T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.7'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jone.doe'] }, + ], + ecs: { + _id: '7', + timestamp: '2018-11-11T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['7'], + category: ['Access'], + type: ['HTTP Request'], + module: ['apache'], + severity: [3], + }, + source: { ip: ['192.168.0.7'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['7'], name: ['jone.doe'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '9', + data: [ + { field: '@timestamp', value: ['2018-11-13T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.9'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jone.doe'] }, + ], + ecs: { + _id: '9', + timestamp: '2018-11-13T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['9'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.9'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['9'], name: ['jone.doe'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '10', + data: [ + { field: '@timestamp', value: ['2018-11-14T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.10'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jone.doe'] }, + ], + ecs: { + _id: '10', + timestamp: '2018-11-14T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['10'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.10'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['10'], name: ['jone.doe'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '11', + data: [ + { field: '@timestamp', value: ['2018-11-15T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.11'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jone.doe'] }, + ], + ecs: { + _id: '11', + timestamp: '2018-11-15T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['11'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.11'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['11'], name: ['jone.doe'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '12', + data: [ + { field: '@timestamp', value: ['2018-11-16T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.12'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['jone.doe'] }, + ], + ecs: { + _id: '12', + timestamp: '2018-11-16T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['12'], + category: ['Access'], + type: ['HTTP Request'], + module: ['nginx'], + severity: [3], + }, + source: { ip: ['192.168.0.12'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['12'], name: ['jone.doe'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '2', + data: [ + { field: '@timestamp', value: ['2018-11-06T19:03:25.937Z'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Authentication'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.2'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['joe.bob'] }, + ], + ecs: { + _id: '2', + timestamp: '2018-11-06T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['2'], + category: ['Authentication'], + type: ['Authentication Success'], + module: ['authlog'], + severity: [3], + }, + source: { ip: ['192.168.0.2'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['1'], name: ['joe.bob'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '13', + data: [ + { field: '@timestamp', value: ['2018-13-12T19:03:25.937Z'] }, + { field: 'event.severity', value: ['1'] }, + { field: 'event.category', value: ['Web Application Attack'] }, + { field: 'host.name', value: ['joe.computer'] }, + { field: 'source.ip', value: ['192.168.0.8'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + ], + ecs: { + _id: '13', + timestamp: '2018-12-12T19:03:25.937Z', + host: { name: ['joe.computer'], ip: ['192.168.0.1'] }, + event: { + id: ['13'], + category: ['Web Application Attack'], + type: ['Alert'], + module: ['suricata'], + severity: [1], + }, + suricata: { + eve: { + flow_id: [13], + proto: [''], + alert: { + signature: ['ET WEB_SERVER Possible Attempt in HTTP Cookie'], + signature_id: [13], + }, + }, + }, + source: { ip: ['192.168.0.8'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '14', + data: [ + { field: '@timestamp', value: ['2019-03-07T05:06:51.000Z'] }, + { field: 'host.name', value: ['zeek-franfurt'] }, + { field: 'source.ip', value: ['192.168.26.101'] }, + { field: 'destination.ip', value: ['192.168.238.205'] }, + ], + ecs: { + _id: '14', + timestamp: '2019-03-07T05:06:51.000Z', + event: { + module: ['zeek'], + dataset: ['zeek.connection'], + }, + host: { + id: ['37c81253e0fc4c46839c19b981be5177'], + name: ['zeek-franfurt'], + ip: ['207.154.238.205', '10.19.0.5', 'fe80::d82b:9aff:fe0d:1e12'], + }, + source: { ip: ['185.176.26.101'], port: [44059] }, + destination: { ip: ['207.154.238.205'], port: [11568] }, + geo: { region_name: ['New York'], country_iso_code: ['US'] }, + network: { transport: ['tcp'] }, + zeek: { + session_id: ['C8DRTq362Fios6hw16'], + connection: { + local_resp: [false], + local_orig: [false], + missed_bytes: [0], + state: ['REJ'], + history: ['Sr'], + }, + }, + }, + }, + { + _id: '15', + data: [ + { field: '@timestamp', value: ['2019-03-07T00:51:28.000Z'] }, + { field: 'host.name', value: ['suricata-zeek-singapore'] }, + { field: 'source.ip', value: ['192.168.35.240'] }, + { field: 'destination.ip', value: ['192.168.67.3'] }, + ], + ecs: { + _id: '15', + timestamp: '2019-03-07T00:51:28.000Z', + event: { + module: ['zeek'], + dataset: ['zeek.dns'], + }, + host: { + id: ['af3fddf15f1d47979ce817ba0df10c6e'], + name: ['suricata-zeek-singapore'], + ip: ['206.189.35.240', '10.15.0.5', 'fe80::98c7:eff:fe29:4455'], + }, + source: { ip: ['206.189.35.240'], port: [57475] }, + destination: { ip: ['67.207.67.3'], port: [53] }, + geo: { region_name: ['New York'], country_iso_code: ['US'] }, + network: { transport: ['udp'] }, + zeek: { + session_id: ['CyIrMA1L1JtLqdIuol'], + dns: { + AA: [false], + RD: [false], + trans_id: [65252], + RA: [false], + TC: [false], + }, + }, + }, + }, + { + _id: '16', + data: [ + { field: '@timestamp', value: ['2019-03-05T07:00:20.000Z'] }, + { field: 'host.name', value: ['suricata-zeek-singapore'] }, + { field: 'source.ip', value: ['192.168.35.240'] }, + { field: 'destination.ip', value: ['192.168.164.26'] }, + ], + ecs: { + _id: '16', + timestamp: '2019-03-05T07:00:20.000Z', + event: { + module: ['zeek'], + dataset: ['zeek.http'], + }, + host: { + id: ['af3fddf15f1d47979ce817ba0df10c6e'], + name: ['suricata-zeek-singapore'], + ip: ['206.189.35.240', '10.15.0.5', 'fe80::98c7:eff:fe29:4455'], + }, + source: { ip: ['206.189.35.240'], port: [36220] }, + destination: { ip: ['192.241.164.26'], port: [80] }, + geo: { region_name: ['New York'], country_iso_code: ['US'] }, + http: { + version: ['1.1'], + request: { body: { bytes: [0] } }, + response: { status_code: [302], body: { bytes: [154] } }, + }, + zeek: { + session_id: ['CZLkpC22NquQJOpkwe'], + + http: { + resp_mime_types: ['text/html'], + trans_depth: ['3'], + status_msg: ['Moved Temporarily'], + resp_fuids: ['FzeujEPP7GTHmYPsc'], + tags: [], + }, + }, + }, + }, + { + _id: '17', + data: [ + { field: '@timestamp', value: ['2019-02-28T22:36:28.000Z'] }, + { field: 'host.name', value: ['zeek-franfurt'] }, + { field: 'source.ip', value: ['192.168.77.171'] }, + ], + ecs: { + _id: '17', + timestamp: '2019-02-28T22:36:28.000Z', + event: { + module: ['zeek'], + dataset: ['zeek.notice'], + }, + host: { + id: ['37c81253e0fc4c46839c19b981be5177'], + name: ['zeek-franfurt'], + ip: ['207.154.238.205', '10.19.0.5', 'fe80::d82b:9aff:fe0d:1e12'], + }, + source: { ip: ['8.42.77.171'] }, + zeek: { + notice: { + suppress_for: [3600], + msg: ['8.42.77.171 scanned at least 15 unique ports of host 207.154.238.205 in 0m0s'], + note: ['Scan::Port_Scan'], + sub: ['remote'], + dst: ['207.154.238.205'], + dropped: [false], + peer_descr: ['bro'], + }, + }, + }, + }, + { + _id: '18', + data: [ + { field: '@timestamp', value: ['2019-02-22T21:12:13.000Z'] }, + { field: 'host.name', value: ['zeek-sensor-amsterdam'] }, + { field: 'source.ip', value: ['192.168.66.184'] }, + { field: 'destination.ip', value: ['192.168.95.15'] }, + ], + ecs: { + _id: '18', + timestamp: '2019-02-22T21:12:13.000Z', + event: { + module: ['zeek'], + dataset: ['zeek.ssl'], + }, + host: { id: ['2ce8b1e7d69e4a1d9c6bcddc473da9d9'], name: ['zeek-sensor-amsterdam'] }, + source: { ip: ['188.166.66.184'], port: [34514] }, + destination: { ip: ['91.189.95.15'], port: [443] }, + geo: { region_name: ['England'], country_iso_code: ['GB'] }, + zeek: { + session_id: ['CmTxzt2OVXZLkGDaRe'], + ssl: { + cipher: ['TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'], + established: [false], + resumed: [false], + version: ['TLSv12'], + }, + }, + }, + }, + { + _id: '19', + data: [ + { field: '@timestamp', value: ['2019-03-03T04:26:38.000Z'] }, + { field: 'host.name', value: ['suricata-zeek-singapore'] }, + ], + ecs: { + _id: '19', + timestamp: '2019-03-03T04:26:38.000Z', + event: { + module: ['zeek'], + dataset: ['zeek.files'], + }, + host: { + id: ['af3fddf15f1d47979ce817ba0df10c6e'], + name: ['suricata-zeek-singapore'], + ip: ['206.189.35.240', '10.15.0.5', 'fe80::98c7:eff:fe29:4455'], + }, + zeek: { + session_id: ['Cu0n232QMyvNtzb75j'], + files: { + session_ids: ['Cu0n232QMyvNtzb75j'], + timedout: [false], + local_orig: [false], + tx_host: ['5.101.111.50'], + source: ['HTTP'], + is_orig: [false], + overflow_bytes: [0], + sha1: ['fa5195a5dfacc9d1c68d43600f0e0262cad14dde'], + duration: [0], + depth: [0], + analyzers: ['MD5', 'SHA1'], + mime_type: ['text/plain'], + rx_host: ['206.189.35.240'], + total_bytes: [88722], + fuid: ['FePz1uVEVCZ3I0FQi'], + seen_bytes: [1198], + missing_bytes: [0], + md5: ['f7653f1951693021daa9e6be61226e32'], + }, + }, + }, + }, + { + _id: '20', + data: [ + { field: '@timestamp', value: ['2019-03-13T05:42:11.815Z'] }, + { field: 'event.category', value: ['audit-rule'] }, + { field: 'host.name', value: ['zeek-sanfran'] }, + { field: 'process.args', value: ['gpgconf', '--list-dirs', 'agent-socket'] }, + ], + ecs: { + _id: '20', + timestamp: '2019-03-13T05:42:11.815Z', + event: { + action: ['executed'], + module: ['auditd'], + category: ['audit-rule'], + }, + host: { + id: ['f896741c3b3b44bdb8e351a4ab6d2d7c'], + name: ['zeek-sanfran'], + ip: ['134.209.63.134', '10.46.0.5', 'fe80::a0d9:16ff:fecf:e70b'], + }, + user: { name: ['alice'] }, + process: { + pid: [5402], + name: ['gpgconf'], + ppid: [5401], + args: ['gpgconf', '--list-dirs', 'agent-socket'], + executable: ['/usr/bin/gpgconf'], + title: ['gpgconf --list-dirs agent-socket'], + working_directory: ['/'], + }, + }, + }, + { + _id: '21', + data: [ + { field: '@timestamp', value: ['2019-03-14T22:30:25.527Z'] }, + { field: 'event.category', value: ['user-login'] }, + { field: 'host.name', value: ['zeek-london'] }, + { field: 'source.ip', value: ['192.168.77.171'] }, + { field: 'user.name', value: ['root'] }, + ], + ecs: { + _id: '21', + timestamp: '2019-03-14T22:30:25.527Z', + event: { + action: ['logged-in'], + module: ['auditd'], + category: ['user-login'], + }, + auditd: { + result: ['success'], + session: ['14'], + data: { terminal: ['/dev/pts/0'], op: ['login'] }, + summary: { + actor: { primary: ['alice'], secondary: ['alice'] }, + object: { primary: ['/dev/pts/0'], secondary: ['8.42.77.171'], type: ['user-session'] }, + how: ['/usr/sbin/sshd'], + }, + }, + host: { + id: ['7c21f5ed03b04d0299569d221fe18bbc'], + name: ['zeek-london'], + ip: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + source: { ip: ['8.42.77.171'] }, + user: { name: ['root'] }, + process: { + pid: [17471], + executable: ['/usr/sbin/sshd'], + }, + }, + }, + { + _id: '22', + data: [ + { field: '@timestamp', value: ['2019-03-13T03:35:21.614Z'] }, + { field: 'event.category', value: ['user-login'] }, + { field: 'host.name', value: ['suricata-bangalore'] }, + { field: 'user.name', value: ['root'] }, + ], + ecs: { + _id: '22', + timestamp: '2019-03-13T03:35:21.614Z', + event: { + action: ['disposed-credentials'], + module: ['auditd'], + category: ['user-login'], + }, + auditd: { + result: ['success'], + session: ['340'], + data: { acct: ['alice'], terminal: ['ssh'], op: ['PAM:setcred'] }, + summary: { + actor: { primary: ['alice'], secondary: ['alice'] }, + object: { primary: ['ssh'], secondary: ['8.42.77.171'], type: ['user-session'] }, + how: ['/usr/sbin/sshd'], + }, + }, + host: { + id: ['0a63559c1acf4c419d979c4b4d8b83ff'], + name: ['suricata-bangalore'], + ip: ['139.59.11.147', '10.47.0.5', 'fe80::ec0b:1bff:fe29:80bd'], + }, + user: { name: ['root'] }, + process: { + pid: [21202], + executable: ['/usr/sbin/sshd'], + }, + }, + }, + { + _id: '23', + data: [ + { field: '@timestamp', value: ['2019-03-13T03:35:21.614Z'] }, + { field: 'event.category', value: ['user-login'] }, + { field: 'host.name', value: ['suricata-bangalore'] }, + { field: 'user.name', value: ['root'] }, + ], + ecs: { + _id: '23', + timestamp: '2019-03-13T03:35:21.614Z', + event: { + action: ['ended-session'], + module: ['auditd'], + category: ['user-login'], + }, + auditd: { + result: ['success'], + session: ['340'], + data: { acct: ['alice'], terminal: ['ssh'], op: ['PAM:session_close'] }, + summary: { + actor: { primary: ['alice'], secondary: ['alice'] }, + object: { primary: ['ssh'], secondary: ['8.42.77.171'], type: ['user-session'] }, + how: ['/usr/sbin/sshd'], + }, + }, + host: { + id: ['0a63559c1acf4c419d979c4b4d8b83ff'], + name: ['suricata-bangalore'], + ip: ['139.59.11.147', '10.47.0.5', 'fe80::ec0b:1bff:fe29:80bd'], + }, + user: { name: ['root'] }, + process: { + pid: [21202], + executable: ['/usr/sbin/sshd'], + }, + }, + }, + { + _id: '24', + data: [ + { field: '@timestamp', value: ['2019-03-18T23:17:01.645Z'] }, + { field: 'event.category', value: ['user-login'] }, + { field: 'host.name', value: ['zeek-london'] }, + { field: 'user.name', value: ['root'] }, + ], + ecs: { + _id: '24', + timestamp: '2019-03-18T23:17:01.645Z', + event: { + action: ['acquired-credentials'], + module: ['auditd'], + category: ['user-login'], + }, + auditd: { + result: ['success'], + session: ['unset'], + data: { acct: ['root'], terminal: ['cron'], op: ['PAM:setcred'] }, + summary: { + actor: { primary: ['unset'], secondary: ['root'] }, + object: { primary: ['cron'], type: ['user-session'] }, + how: ['/usr/sbin/cron'], + }, + }, + host: { + id: ['7c21f5ed03b04d0299569d221fe18bbc'], + name: ['zeek-london'], + ip: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + user: { name: ['root'] }, + process: { + pid: [9592], + executable: ['/usr/sbin/cron'], + }, + }, + }, + { + _id: '25', + data: [ + { field: '@timestamp', value: ['2019-03-19T01:17:01.336Z'] }, + { field: 'event.category', value: ['user-login'] }, + { field: 'host.name', value: ['siem-kibana'] }, + { field: 'user.name', value: ['root'] }, + ], + ecs: { + _id: '25', + timestamp: '2019-03-19T01:17:01.336Z', + event: { + action: ['started-session'], + module: ['auditd'], + category: ['user-login'], + }, + auditd: { + result: ['success'], + session: ['2908'], + data: { acct: ['root'], terminal: ['cron'], op: ['PAM:session_open'] }, + summary: { + actor: { primary: ['root'], secondary: ['root'] }, + object: { primary: ['cron'], type: ['user-session'] }, + how: ['/usr/sbin/cron'], + }, + }, + host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, + user: { name: ['root'] }, + process: { + pid: [725], + executable: ['/usr/sbin/cron'], + }, + }, + }, + { + _id: '26', + data: [ + { field: '@timestamp', value: ['2019-03-13T03:34:08.890Z'] }, + { field: 'event.category', value: ['user-login'] }, + { field: 'host.name', value: ['suricata-bangalore'] }, + { field: 'user.name', value: ['alice'] }, + ], + ecs: { + _id: '26', + timestamp: '2019-03-13T03:34:08.890Z', + event: { + action: ['was-authorized'], + module: ['auditd'], + category: ['user-login'], + }, + auditd: { + result: ['success'], + session: ['338'], + data: { terminal: ['/dev/pts/0'] }, + summary: { + actor: { primary: ['root'], secondary: ['alice'] }, + object: { primary: ['/dev/pts/0'], type: ['user-session'] }, + how: ['/sbin/pam_tally2'], + }, + }, + host: { + id: ['0a63559c1acf4c419d979c4b4d8b83ff'], + name: ['suricata-bangalore'], + ip: ['139.59.11.147', '10.47.0.5', 'fe80::ec0b:1bff:fe29:80bd'], + }, + user: { name: ['alice'] }, + process: { + pid: [21170], + executable: ['/sbin/pam_tally2'], + }, + }, + }, + { + _id: '27', + data: [ + { field: '@timestamp', value: ['2019-03-22T19:13:11.026Z'] }, + { field: 'event.action', value: ['connected-to'] }, + { field: 'event.category', value: ['audit-rule'] }, + { field: 'host.name', value: ['zeek-london'] }, + { field: 'destination.ip', value: ['192.168.216.34'] }, + { field: 'user.name', value: ['alice'] }, + ], + ecs: { + _id: '27', + timestamp: '2019-03-22T19:13:11.026Z', + event: { + action: ['connected-to'], + module: ['auditd'], + category: ['audit-rule'], + }, + auditd: { + result: ['success'], + session: ['246'], + summary: { + actor: { primary: ['alice'], secondary: ['alice'] }, + object: { primary: ['192.168.216.34'], secondary: ['80'], type: ['socket'] }, + how: ['/usr/bin/wget'], + }, + }, + host: { + id: ['7c21f5ed03b04d0299569d221fe18bbc'], + name: ['zeek-london'], + ip: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + destination: { ip: ['192.168.216.34'], port: [80] }, + user: { name: ['alice'] }, + process: { + pid: [1490], + name: ['wget'], + ppid: [1476], + executable: ['/usr/bin/wget'], + title: ['wget www.example.com'], + }, + }, + }, + { + _id: '28', + data: [ + { field: '@timestamp', value: ['2019-03-26T22:12:18.609Z'] }, + { field: 'event.action', value: ['opened-file'] }, + { field: 'event.category', value: ['audit-rule'] }, + { field: 'host.name', value: ['zeek-london'] }, + { field: 'user.name', value: ['root'] }, + ], + ecs: { + _id: '28', + timestamp: '2019-03-26T22:12:18.609Z', + event: { + action: ['opened-file'], + module: ['auditd'], + category: ['audit-rule'], + }, + auditd: { + result: ['success'], + session: ['242'], + summary: { + actor: { primary: ['unset'], secondary: ['root'] }, + object: { primary: ['/proc/15990/attr/current'], type: ['file'] }, + how: ['/lib/systemd/systemd-journald'], + }, + }, + file: { + path: ['/proc/15990/attr/current'], + device: ['00:00'], + inode: ['27672309'], + uid: ['0'], + owner: ['root'], + gid: ['0'], + group: ['root'], + mode: ['0666'], + }, + host: { + id: ['7c21f5ed03b04d0299569d221fe18bbc'], + name: ['zeek-london'], + ip: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + + user: { name: ['root'] }, + process: { + pid: [27244], + name: ['systemd-journal'], + ppid: [1], + executable: ['/lib/systemd/systemd-journald'], + title: ['/lib/systemd/systemd-journald'], + working_directory: ['/'], + }, + }, + }, + { + _id: '29', + data: [ + { field: '@timestamp', value: ['2019-04-08T21:18:57.000Z'] }, + { field: 'event.action', value: ['user_login'] }, + { field: 'event.category', value: null }, + { field: 'host.name', value: ['zeek-london'] }, + { field: 'user.name', value: ['Braden'] }, + ], + ecs: { + _id: '29', + event: { + action: ['user_login'], + dataset: ['login'], + kind: ['event'], + module: ['system'], + outcome: ['failure'], + }, + host: { + id: ['7c21f5ed03b04d0299569d221fe18bbc'], + name: ['zeek-london'], + ip: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + source: { + ip: ['128.199.212.120'], + }, + user: { + name: ['Braden'], + }, + process: { + pid: [6278], + }, + }, + }, + { + _id: '30', + data: [ + { field: '@timestamp', value: ['2019-04-08T22:27:14.814Z'] }, + { field: 'event.action', value: ['process_started'] }, + { field: 'event.category', value: null }, + { field: 'host.name', value: ['zeek-london'] }, + { field: 'user.name', value: ['Evan'] }, + ], + ecs: { + _id: '30', + event: { + action: ['process_started'], + dataset: ['login'], + kind: ['event'], + module: ['system'], + outcome: ['failure'], + }, + host: { + id: ['7c21f5ed03b04d0299569d221fe18bbc'], + name: ['zeek-london'], + ip: ['46.101.3.136', '10.16.0.5', 'fe80::4066:42ff:fe19:b3b9'], + }, + source: { + ip: ['128.199.212.120'], + }, + user: { + name: ['Evan'], + }, + process: { + pid: [6278], + }, + }, + }, + { + _id: '31', + data: [ + { field: '@timestamp', value: ['2018-11-05T19:03:25.937Z'] }, + { field: 'message', value: ['I am a log file message'] }, + { field: 'event.severity', value: ['3'] }, + { field: 'event.category', value: ['Access'] }, + { field: 'event.action', value: ['Action'] }, + { field: 'host.name', value: ['apache'] }, + { field: 'source.ip', value: ['192.168.0.1'] }, + { field: 'destination.ip', value: ['192.168.0.3'] }, + { field: 'destination.bytes', value: ['123456'] }, + { field: 'user.name', value: ['john.dee'] }, + ], + ecs: { + _id: '1', + timestamp: '2018-11-05T19:03:25.937Z', + host: { name: ['apache'], ip: ['192.168.0.1'] }, + event: { + id: ['1'], + action: ['Action'], + category: ['Access'], + module: ['nginx'], + severity: [3], + }, + message: ['I am a log file message'], + source: { ip: ['192.168.0.1'], port: [80] }, + destination: { ip: ['192.168.0.3'], port: [6343] }, + user: { id: ['1'], name: ['john.dee'] }, + geo: { region_name: ['xx'], country_iso_code: ['xx'] }, + }, + }, + { + _id: '32', + data: [], + ecs: { + _id: 'BuBP4W0BOpWiDweSoYSg', + timestamp: '2019-10-18T23:59:15.091Z', + threat: { + enrichments: [ + { + indicator: { + provider: ['indicator_provider'], + reference: ['https://example.com'], + }, + matched: { + atomic: ['192.168.1.1'], + field: ['source.ip'], + type: ['ip'], + }, + feed: { + name: ['feed_name'], + }, + }, + ], + }, + }, + }, +]; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/global_state.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/global_state.ts new file mode 100644 index 0000000000000..dda9d8b6c6d8d --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/global_state.ts @@ -0,0 +1,57 @@ +/* + * 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 { TableId } from '../common/types'; +import { defaultHeaders } from './header'; + +// FIXME add strong typings +export const mockGlobalState: any = { + app: { + notesById: {}, + errors: [ + { id: 'error-id-1', title: 'title-1', message: ['error-message-1'] }, + { id: 'error-id-2', title: 'title-2', message: ['error-message-2'] }, + ], + }, + dataTable: { + tableById: { + [TableId.test]: { + columns: defaultHeaders, + defaultColumns: defaultHeaders, + dataViewId: 'security-solution-default', + deletedEventIds: [], + expandedDetail: {}, + filters: [], + indexNames: ['.alerts-security.alerts-default'], + isSelectAllChecked: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + loadingEventIds: [], + selectedEventIds: {}, + showCheckboxes: false, + sort: [ + { + columnId: '@timestamp', + columnType: 'date', + esTypes: ['date'], + sortDirection: 'desc', + }, + ], + graphEventId: '', + sessionViewConfig: null, + selectAll: false, + id: TableId.test, + title: '', + initialized: true, + updated: 1663882629000, + isLoading: false, + queryFields: [], + totalCount: 0, + }, + }, + }, +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/header.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/header.ts new file mode 100644 index 0000000000000..ce7ac9a08d031 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/header.ts @@ -0,0 +1,144 @@ +/* + * 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 { defaultColumnHeaderType } from '@kbn/timelines-plugin/common/types'; +import type { ColumnHeaderOptions } from '../common/types'; +import { + DEFAULT_COLUMN_MIN_WIDTH, + DEFAULT_DATE_COLUMN_MIN_WIDTH, +} from '../components/data_table/constants'; + +export const defaultHeaders: 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: '@timestamp', + type: 'date', + esTypes: ['date'], + aggregatable: true, + initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH, + }, + { + category: 'event', + columnHeaderType: defaultColumnHeaderType, + description: + "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.", + example: '7', + id: 'event.severity', + type: 'number', + esTypes: ['long'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'event', + columnHeaderType: defaultColumnHeaderType, + description: + 'Event category.\nThis contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', + example: 'user-management', + id: 'event.category', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'event', + columnHeaderType: defaultColumnHeaderType, + description: + 'The action captured by the event.\nThis describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer.', + example: 'user-password-change', + id: 'event.action', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'host', + columnHeaderType: defaultColumnHeaderType, + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + example: '', + id: 'host.name', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'source', + columnHeaderType: defaultColumnHeaderType, + description: 'IP address of the source.\nCan be one or multiple IPv4 or IPv6 addresses.', + example: '', + id: 'source.ip', + type: 'ip', + esTypes: ['ip'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'destination', + columnHeaderType: defaultColumnHeaderType, + description: 'IP address of the destination.\nCan be one or multiple IPv4 or IPv6 addresses.', + example: '', + id: 'destination.ip', + type: 'ip', + esTypes: ['ip'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + aggregatable: true, + category: 'destination', + columnHeaderType: defaultColumnHeaderType, + description: 'Bytes sent from the source to the destination', + example: '123', + format: 'bytes', + id: 'destination.bytes', + type: 'number', + esTypes: ['long'], + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'user', + columnHeaderType: defaultColumnHeaderType, + description: 'Short name or login of the user.', + example: 'albert', + id: 'user.name', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'base', + columnHeaderType: defaultColumnHeaderType, + description: 'Each document has an _id that uniquely identifies it', + example: 'Y-6TfmcB0WOhS6qyMv3s', + id: '_id', + type: 'string', + esTypes: [], // empty for _id + aggregatable: false, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + { + category: 'base', + columnHeaderType: defaultColumnHeaderType, + description: + 'For log events the message field contains the log message.\nIn other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + example: 'Hello World', + id: 'message', + type: 'string', + esTypes: ['text'], + aggregatable: false, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, +]; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/mock_local_storage.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/mock_local_storage.ts new file mode 100644 index 0000000000000..00036d2030838 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/mock_local_storage.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 type { IStorage } from '@kbn/kibana-utils-plugin/public'; + +export const localStorageMock = (): IStorage => { + let store: Record = {}; + + return { + getItem: (key: string) => { + return store[key] || null; + }, + setItem: (key: string, value: unknown) => { + store[key] = value; + }, + clear() { + store = {}; + }, + removeItem(key: string) { + delete store[key]; + }, + }; +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/mock_source.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/mock_source.ts new file mode 100644 index 0000000000000..0cc791f288410 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/mock_source.ts @@ -0,0 +1,515 @@ +/* + * 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 { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { BrowserFields } from '@kbn/timelines-plugin/common'; +import { DEFAULT_INDEX_PATTERN } from '../common/constants'; + +export const mockBrowserFields: BrowserFields = { + agent: { + fields: { + 'agent.ephemeral_id': { + aggregatable: true, + category: 'agent', + description: + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', + example: '8a4f500f', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.ephemeral_id', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'agent.hostname': { + aggregatable: true, + category: 'agent', + description: null, + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.hostname', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'agent.id': { + aggregatable: true, + category: 'agent', + description: + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', + example: '8a4f500d', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.id', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'agent.name': { + aggregatable: true, + category: 'agent', + description: + 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', + example: 'foo', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.name', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + }, + }, + auditd: { + fields: { + 'auditd.data.a0': { + aggregatable: true, + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a0', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'auditd.data.a1': { + aggregatable: true, + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a1', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'auditd.data.a2': { + aggregatable: true, + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a2', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + }, + }, + base: { + fields: { + '@timestamp': { + aggregatable: true, + category: 'base', + description: + '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', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: '@timestamp', + searchable: true, + type: 'date', + esTypes: ['date'], + readFromDocValues: true, + }, + _id: { + category: 'base', + description: 'Each document has an _id that uniquely identifies it', + example: 'Y-6TfmcB0WOhS6qyMv3s', + name: '_id', + type: 'string', + esTypes: [], + searchable: true, + aggregatable: false, + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + }, + message: { + category: 'base', + description: + 'For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.', + example: 'Hello World', + name: 'message', + type: 'string', + esTypes: ['text'], + searchable: true, + aggregatable: false, + format: 'string', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + }, + }, + }, + client: { + fields: { + 'client.address': { + aggregatable: true, + category: 'client', + description: + 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.address', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'client.bytes': { + aggregatable: true, + category: 'client', + description: 'Bytes sent from the client to the server.', + example: '184', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.bytes', + searchable: true, + type: 'number', + esTypes: ['long'], + }, + 'client.domain': { + aggregatable: true, + category: 'client', + description: 'Client domain.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.domain', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'client.geo.country_iso_code': { + aggregatable: true, + category: 'client', + description: 'Country ISO code.', + example: 'CA', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.geo.country_iso_code', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + }, + }, + cloud: { + fields: { + 'cloud.account.id': { + aggregatable: true, + category: 'cloud', + description: + 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', + example: '666777888999', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'cloud.account.id', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'cloud.availability_zone': { + aggregatable: true, + category: 'cloud', + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'cloud.availability_zone', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + }, + }, + container: { + fields: { + 'container.id': { + aggregatable: true, + category: 'container', + description: 'Unique container id.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.id', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'container.image.name': { + aggregatable: true, + category: 'container', + description: 'Name of the image the container was built on.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.image.name', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'container.image.tag': { + aggregatable: true, + category: 'container', + description: 'Container image tag.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.image.tag', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + }, + }, + destination: { + fields: { + 'destination.address': { + aggregatable: true, + category: 'destination', + description: + 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.address', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'destination.bytes': { + aggregatable: true, + category: 'destination', + description: 'Bytes sent from the destination to the source.', + example: '184', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.bytes', + searchable: true, + type: 'number', + esTypes: ['long'], + }, + 'destination.domain': { + aggregatable: true, + category: 'destination', + description: 'Destination domain.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.domain', + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + 'destination.ip': { + aggregatable: true, + category: 'destination', + description: + 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.ip', + searchable: true, + type: 'ip', + esTypes: ['ip'], + }, + 'destination.port': { + aggregatable: true, + category: 'destination', + description: 'Port of the destination.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.port', + searchable: true, + type: 'number', + esTypes: ['long'], + }, + }, + }, + event: { + fields: { + 'event.end': { + category: 'event', + description: + 'event.end contains the date when the event ended or when the activity was last observed.', + example: null, + format: '', + indexes: DEFAULT_INDEX_PATTERN, + name: 'event.end', + searchable: true, + type: 'date', + esTypes: ['date'], + aggregatable: true, + }, + 'event.action': { + category: 'event', + description: + 'The action captured by the event. This describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer.', + example: 'user-password-change', + name: 'event.action', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: true, + format: 'string', + indexes: DEFAULT_INDEX_PATTERN, + }, + 'event.category': { + category: 'event', + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. `event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory. This field is an array. This will allow proper categorization of some events that fall in multiple categories.', + example: 'authentication', + name: 'event.category', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: true, + format: 'string', + indexes: DEFAULT_INDEX_PATTERN, + }, + 'event.severity': { + category: 'event', + description: + "The numeric severity of the event according to your event source. What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. The Syslog severity belongs in `log.syslog.severity.code`. `event.severity` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the `log.syslog.severity.code` to `event.severity`.", + example: 7, + name: 'event.severity', + type: 'number', + esTypes: ['long'], + format: 'number', + searchable: true, + aggregatable: true, + indexes: DEFAULT_INDEX_PATTERN, + }, + }, + }, + host: { + fields: { + 'host.name': { + category: 'host', + description: + 'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + name: 'host.name', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: true, + format: 'string', + indexes: DEFAULT_INDEX_PATTERN, + }, + }, + }, + source: { + fields: { + 'source.ip': { + aggregatable: true, + category: 'source', + description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'source.ip', + searchable: true, + type: 'ip', + esTypes: ['ip'], + }, + 'source.port': { + aggregatable: true, + category: 'source', + description: 'Port of the source.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'source.port', + searchable: true, + type: 'number', + esTypes: ['long'], + }, + }, + }, + user: { + fields: { + 'user.name': { + category: 'user', + description: 'Short name or login of the user.', + example: 'albert', + name: 'user.name', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: true, + format: 'string', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + }, + }, + }, + nestedField: { + fields: { + 'nestedField.firstAttributes': { + aggregatable: false, + category: 'nestedField', + description: '', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'nestedField.firstAttributes', + searchable: true, + type: 'string', + subType: { + nested: { + path: 'nestedField', + }, + }, + }, + 'nestedField.secondAttributes': { + aggregatable: false, + category: 'nestedField', + description: '', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'nestedField.secondAttributes', + searchable: true, + type: 'string', + subType: { + nested: { + path: 'nestedField', + }, + }, + }, + 'nestedField.thirdAttributes': { + aggregatable: false, + category: 'nestedField', + description: '', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'nestedField.thirdAttributes', + searchable: true, + type: 'date', + subType: { + nested: { + path: 'nestedField', + }, + }, + }, + }, + }, +}; + +export const mockRuntimeMappings: MappingRuntimeFields = { + '@a.runtime.field': { + script: { + source: 'emit("Radical dude: " + doc[\'host.name\'].value)', + }, + type: 'keyword', + }, +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/mock_timeline_data.ts b/x-pack/packages/kbn-securitysolution-data-table/mock/mock_timeline_data.ts new file mode 100644 index 0000000000000..e4e5e916136b8 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/mock_timeline_data.ts @@ -0,0 +1,74 @@ +/* + * 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 { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; + +export { demoTimelineData as mockTimelineData } from './demo_data/timeline'; +export { demoEndpointRegistryModificationEvent as mockEndpointRegistryModificationEvent } from './demo_data/endpoint/registry_modification_event'; +export { demoEndpointLibraryLoadEvent as mockEndpointLibraryLoadEvent } from './demo_data/endpoint/library_load_event'; +export { demoEndpointProcessExecutionMalwarePreventionAlert as mockEndpointProcessExecutionMalwarePreventionAlert } from './demo_data/endpoint/process_execution_malware_prevention_alert'; + +export const mockDnsEvent: Ecs = { + _id: 'VUTUqm0BgJt5sZM7nd5g', + destination: { + domain: ['ten.one.one.one'], + port: [53], + bytes: [137], + ip: ['10.1.1.1'], + geo: { + continent_name: ['Oceania'], + location: { + lat: [-33.494], + lon: [143.2104], + }, + country_iso_code: ['AU'], + country_name: ['Australia'], + city_name: [''], + }, + }, + host: { + architecture: ['armv7l'], + id: ['host-id'], + os: { + family: ['debian'], + platform: ['raspbian'], + version: ['9 (stretch)'], + name: ['Raspbian GNU/Linux'], + kernel: ['4.19.57-v7+'], + }, + name: ['iot.example.com'], + }, + dns: { + question: { + name: ['lookup.example.com'], + type: ['A'], + }, + response_code: ['NOERROR'], + resolved_ip: ['10.1.2.3'], + }, + timestamp: '2019-10-08T10:05:23.241Z', + network: { + community_id: ['1:network-community_id'], + direction: ['outbound'], + bytes: [177], + transport: ['udp'], + protocol: ['dns'], + }, + event: { + duration: [6937500], + category: ['network_traffic'], + dataset: ['dns'], + kind: ['event'], + end: ['2019-10-08T10:05:23.248Z'], + start: ['2019-10-08T10:05:23.241Z'], + }, + source: { + port: [58732], + bytes: [40], + ip: ['10.9.9.9'], + }, +}; diff --git a/x-pack/packages/kbn-securitysolution-data-table/mock/test_providers.tsx b/x-pack/packages/kbn-securitysolution-data-table/mock/test_providers.tsx new file mode 100644 index 0000000000000..33c86527f7505 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/mock/test_providers.tsx @@ -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. + */ + +/* eslint-disable @kbn/eslint/module_migration */ + +import { euiDarkVars } from '@kbn/ui-theme'; +import { I18nProvider } from '@kbn/i18n-react'; + +import React from 'react'; +import type { DropResult, ResponderProvided } from 'react-beautiful-dnd'; +import { DragDropContext } from 'react-beautiful-dnd'; +import { Provider as ReduxStoreProvider } from 'react-redux'; +import type { Store } from 'redux'; +import { ThemeProvider } from 'styled-components'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { createStore as createReduxStore } from 'redux'; + +import type { Action } from '@kbn/ui-actions-plugin/public'; +import { CellActionsProvider } from '@kbn/cell-actions'; +import { mockGlobalState } from './global_state'; +import { localStorageMock } from './mock_local_storage'; + +interface Props { + children?: React.ReactNode; + store?: Store; + onDragEnd?: (result: DropResult, provided: ResponderProvided) => void; + cellActions?: Action[]; +} + +Object.defineProperty(window, 'localStorage', { + value: localStorageMock(), +}); +window.scrollTo = jest.fn(); + +const createStore = (state: any) => createReduxStore(() => {}, state); + +/** A utility for wrapping children in the providers required to run most tests */ +export const TestProvidersComponent: React.FC = ({ + children, + store = createStore(mockGlobalState), + onDragEnd = jest.fn(), + cellActions = [], +}) => { + const queryClient = new QueryClient(); + return ( + + + ({ eui: euiDarkVars, darkMode: true })}> + + Promise.resolve(cellActions)}> + {children} + + + + + + ); +}; + +/** + * A utility for wrapping children in the providers required to run most tests + * WITH user privileges provider. + */ +const TestProvidersWithPrivilegesComponent: React.FC = ({ + children, + store = createStore(mockGlobalState), + onDragEnd = jest.fn(), + cellActions = [], +}) => ( + + + ({ eui: euiDarkVars, darkMode: true })}> + Promise.resolve(cellActions)}> + {children} + + + + +); + +export const TestProviders = React.memo(TestProvidersComponent); +export const TestProvidersWithPrivileges = React.memo(TestProvidersWithPrivilegesComponent); diff --git a/x-pack/packages/kbn-securitysolution-data-table/package.json b/x-pack/packages/kbn-securitysolution-data-table/package.json new file mode 100644 index 0000000000000..53c640c3d9c6a --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/securitysolution-data-table", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/actions.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/actions.ts similarity index 92% rename from x-pack/plugins/security_solution/public/common/store/data_table/actions.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/actions.ts index 6ed2b5252e309..5d5e0fb31dddd 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/actions.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/actions.ts @@ -6,10 +6,14 @@ */ import actionCreatorFactory from 'typescript-fsa'; -import type { SessionViewConfig } from '../../../../common/types/session_view'; -import type { ExpandedDetailType } from '../../../../common/types/detail_panel'; -import type { TimelineNonEcsData } from '../../../../common/search_strategy'; -import type { ColumnHeaderOptions, SortColumnTable, ViewSelection } from '../../../../common/types'; +import { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; +import type { ExpandedDetailType } from '../../common/types/detail_panel'; +import type { + ColumnHeaderOptions, + SessionViewConfig, + SortColumnTable, + ViewSelection, +} from '../../common/types'; import type { InitialyzeDataTableSettings, DataTablePersistInput } from './types'; const actionCreator = actionCreatorFactory('x-pack/security_solution/data-table'); diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/defaults.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts similarity index 94% rename from x-pack/plugins/security_solution/public/common/store/data_table/defaults.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts index 7b7a07f6ba3c6..29e28af5da28c 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/defaults.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/defaults.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { ColumnHeaderOptions, ColumnHeaderType } from '../../../../common/types'; + +import { ColumnHeaderOptions, ColumnHeaderType, VIEW_SELECTION } from '../../common/types'; import { DEFAULT_TABLE_COLUMN_MIN_WIDTH, DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH, @@ -14,7 +15,6 @@ import * as i18n from './translations'; export const defaultColumnHeaderType: ColumnHeaderType = 'not-filtered'; -import { VIEW_SELECTION } from '../../../../common/constants'; export const defaultHeaders: ColumnHeaderOptions[] = [ { columnHeaderType: defaultColumnHeaderType, diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/helpers.test.tsx b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/helpers.test.tsx similarity index 94% rename from x-pack/plugins/security_solution/public/common/store/data_table/helpers.test.tsx rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/helpers.test.tsx index 733aa5a3990e3..d414003743c66 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/helpers.test.tsx +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/helpers.test.tsx @@ -12,8 +12,7 @@ import { updateDataTableColumnWidth, } from './helpers'; import { mockGlobalState } from '../../mock/global_state'; -import type { SortColumnTable } from '../../../../common/types'; -import { TableId } from '../../../../common/types'; +import { SortColumnTable, TableId } from '../../common/types'; import type { DataTableModelSettings } from './model'; const id = 'foo'; @@ -69,7 +68,7 @@ describe('setInitializeDataTableSettings', () => { describe('updateDataTableColumnOrder', () => { test('it returns the columns in the new expected order', () => { - const originalIdOrder = defaultTableById[TableId.test].columns.map((x) => x.id); // ['@timestamp', 'event.severity', 'event.category', '...'] + const originalIdOrder = defaultTableById[TableId.test].columns.map((x: any) => x.id); // ['@timestamp', 'event.severity', 'event.category', '...'] // the new order swaps the positions of the first and second columns: const newIdOrder = [originalIdOrder[1], originalIdOrder[0], ...originalIdOrder.slice(2)]; // ['event.severity', '@timestamp', 'event.category', '...'] @@ -94,7 +93,7 @@ describe('updateDataTableColumnOrder', () => { }); test('it omits unknown column IDs when re-ordering columns', () => { - const originalIdOrder = defaultTableById[TableId.test].columns.map((x) => x.id); // ['@timestamp', 'event.severity', 'event.category', '...'] + const originalIdOrder = defaultTableById[TableId.test].columns.map((x: any) => x.id); // ['@timestamp', 'event.severity', 'event.category', '...'] const unknownColumId = 'does.not.exist'; const newIdOrder = [originalIdOrder[0], unknownColumId, ...originalIdOrder.slice(1)]; // ['@timestamp', 'does.not.exist', 'event.severity', 'event.category', '...'] diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/helpers.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/helpers.ts similarity index 97% rename from x-pack/plugins/security_solution/public/common/store/data_table/helpers.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/helpers.ts index 323352bcbbec4..035c865f1fc82 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/helpers.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/helpers.ts @@ -9,9 +9,8 @@ import { omit, union } from 'lodash/fp'; import { isEmpty } from 'lodash'; import type { EuiDataGridColumn } from '@elastic/eui'; -import type { SessionViewConfig } from '../../../../common/types/session_view'; -import type { ExpandedDetail, ExpandedDetailType } from '../../../../common/types/detail_panel'; -import type { ColumnHeaderOptions, SortColumnTable } from '../../../../common/types'; +import type { ExpandedDetail, ExpandedDetailType } from '../../common/types/detail_panel'; +import type { ColumnHeaderOptions, SessionViewConfig, SortColumnTable } from '../../common/types'; import type { TableToggleDetailPanel } from './actions'; import type { DataTablePersistInput, TableById } from './types'; import type { DataTableModelSettings } from './model'; diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/index.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/index.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/store/data_table/index.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/index.ts diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/inputs.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/inputs.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/store/data_table/inputs.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/inputs.ts diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/model.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts similarity index 90% rename from x-pack/plugins/security_solution/public/common/store/data_table/model.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts index d9f1f6f30a159..b4aad728ff8ce 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/model.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/model.ts @@ -7,10 +7,10 @@ import type { EuiDataGridColumn } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; -import type { ExpandedDetail } from '../../../../common/types/detail_panel'; -import type { SessionViewConfig } from '../../../../common/types/session_view'; -import type { TimelineNonEcsData } from '../../../../common/search_strategy'; -import type { ColumnHeaderOptions, SortColumnTable, ViewSelection } from '../../../../common/types'; +import { TimelineNonEcsData } from '@kbn/timelines-plugin/common'; +import { ExpandedDetail } from '../../common/types/detail_panel'; +import { ColumnHeaderOptions, SessionViewConfig, SortColumnTable } from '../../common/types'; +import { ViewSelection } from '../../common/types'; export interface DataTableModelSettings { defaultColumns: Array< diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/reducer.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/reducer.ts similarity index 99% rename from x-pack/plugins/security_solution/public/common/store/data_table/reducer.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/reducer.ts index 9ed7bbe38a726..f65292a2919e4 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/reducer.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/reducer.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { reducerWithInitialState } from 'typescript-fsa-reducers'; import { diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/selectors.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/selectors.ts similarity index 99% rename from x-pack/plugins/security_solution/public/common/store/data_table/selectors.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/selectors.ts index db7470dae945d..665c1bae1a07f 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/selectors.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/selectors.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { getOr } from 'lodash/fp'; import { createSelector } from 'reselect'; import { tableDefaults, getDataTableManageDefaults } from './defaults'; diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/translations.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/translations.ts similarity index 80% rename from x-pack/plugins/security_solution/public/common/store/data_table/translations.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/translations.ts index e17e2a464b41a..17c735bac5a23 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/translations.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/translations.ts @@ -8,14 +8,14 @@ import { i18n } from '@kbn/i18n'; export const LOADING_EVENTS = i18n.translate( - 'xpack.securitySolution.dataTable.loadingEventsDataLabel', + 'securitySolutionDataTable.dataTable.loadingEventsDataLabel', { defaultMessage: 'Loading Events', } ); export const UNIT = (totalCount: number) => - i18n.translate('xpack.securitySolution.dataTable.unit', { + i18n.translate('securitySolutionDataTable.dataTable.unit', { values: { totalCount }, defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`, }); diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/types.ts b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts similarity index 98% rename from x-pack/plugins/security_solution/public/common/store/data_table/types.ts rename to x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts index 53dd7093b5912..4f1dc514f48f9 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/types.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/store/data_table/types.ts @@ -6,7 +6,7 @@ */ import type { EuiDataGridColumn } from '@elastic/eui'; -import type { ColumnHeaderOptions, SortColumnTable } from '../../../../common/types'; +import type { ColumnHeaderOptions, SortColumnTable } from '../../common/types'; import type { DataTableModel, DataTableModelSettings } from './model'; /** The state of all timelines is stored here */ diff --git a/x-pack/packages/kbn-securitysolution-data-table/tsconfig.json b/x-pack/packages/kbn-securitysolution-data-table/tsconfig.json new file mode 100644 index 0000000000000..64d26b9c80257 --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": ["jest", "node", "react"] + }, + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["target/**/*"], + "kbn_references": [ + "@kbn/cell-actions", + "@kbn/timelines-plugin", + "@kbn/es-query", + "@kbn/triggers-actions-ui-plugin", + "@kbn/securitysolution-ecs", + "@kbn/rule-registry-plugin", + "@kbn/safer-lodash-set", + "@kbn/i18n", + "@kbn/ui-theme", + "@kbn/kibana-react-plugin", + "@kbn/kibana-utils-plugin", + "@kbn/i18n-react", + "@kbn/ui-actions-plugin" + ] +} diff --git a/x-pack/packages/kbn-securitysolution-data-table/utils/use_mount_appended.ts b/x-pack/packages/kbn-securitysolution-data-table/utils/use_mount_appended.ts new file mode 100644 index 0000000000000..d43b0455f47da --- /dev/null +++ b/x-pack/packages/kbn-securitysolution-data-table/utils/use_mount_appended.ts @@ -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. + */ + +// eslint-disable-next-line import/no-extraneous-dependencies +import { mount } from 'enzyme'; + +type WrapperOf any> = (...args: Parameters) => ReturnType; +export type MountAppended = WrapperOf; + +export const useMountAppended = () => { + let root: HTMLElement; + + beforeEach(() => { + root = document.createElement('div'); + root.id = 'root'; + document.body.appendChild(root); + }); + + afterEach(() => { + document.body.removeChild(root); + }); + + const mountAppended: MountAppended = (node, options) => + mount(node, { ...options, attachTo: root }); + + return mountAppended; +}; diff --git a/x-pack/packages/ml/agg_utils/tsconfig.json b/x-pack/packages/ml/agg_utils/tsconfig.json index 53328adab80f2..fc8993c828529 100644 --- a/x-pack/packages/ml/agg_utils/tsconfig.json +++ b/x-pack/packages/ml/agg_utils/tsconfig.json @@ -17,7 +17,7 @@ "@kbn/ml-is-populated-object", "@kbn/ml-string-hash", "@kbn/data-views-plugin", - "@kbn/ml-random-sampler-utils" + "@kbn/ml-random-sampler-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.test.ts b/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.test.ts index 58ab666b967ee..2a6a01b23cb8d 100644 --- a/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.test.ts +++ b/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.test.ts @@ -14,22 +14,28 @@ describe('createRandomSamplerWrapper', () => { }, }; - const wrappedTestAggs = { + const getWrappedTestAggs = (probability: number) => ({ sample: { random_sampler: { - probability: 0.01, + probability, }, aggs: testAggs, }, - }; + }); it('returns the un-sampled aggregation as-is for a probability of 1', () => { expect(createRandomSamplerWrapper({ probability: 1 }).wrap(testAggs)).toEqual(testAggs); }); + it('returns wrapped random sampler aggregation for probability of 0.5', () => { + expect(createRandomSamplerWrapper({ probability: 0.5 }).wrap(testAggs)).toEqual( + getWrappedTestAggs(0.5) + ); + }); + it('returns wrapped random sampler aggregation for probability of 0.01', () => { expect(createRandomSamplerWrapper({ probability: 0.01 }).wrap(testAggs)).toEqual( - wrappedTestAggs + getWrappedTestAggs(0.01) ); }); @@ -39,9 +45,9 @@ describe('createRandomSamplerWrapper', () => { expect(randomSamplerWrapper.wrap(testAggs)).toEqual(testAggs); }); - it('returns probability of 0.01 and does not wrap when used for 5000000 docs', () => { + it('returns probability of 0.01 and does wrap when used for 5000000 docs', () => { const randomSamplerWrapper = createRandomSamplerWrapper({ totalNumDocs: 5000000 }); expect(randomSamplerWrapper.probability).toBe(0.01); - expect(randomSamplerWrapper.wrap(testAggs)).toEqual(wrappedTestAggs); + expect(randomSamplerWrapper.wrap(testAggs)).toEqual(getWrappedTestAggs(0.01)); }); }); diff --git a/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts b/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts index b6f51a901ea36..8912dc5470521 100644 --- a/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts +++ b/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts @@ -40,7 +40,7 @@ type RandomSamplerOptions = RandomSamplerOptionProbability | RandomSamplerOption * @returns {boolean} */ export function isValidProbability(p: unknown): p is number { - return typeof p === 'number' && p > 0 && p < 0.5; + return typeof p === 'number' && p > 0 && p <= 0.5; } /** diff --git a/x-pack/packages/ml/url_state/src/url_state.test.tsx b/x-pack/packages/ml/url_state/src/url_state.test.tsx index 734c730dd91ba..033ecd77fadf4 100644 --- a/x-pack/packages/ml/url_state/src/url_state.test.tsx +++ b/x-pack/packages/ml/url_state/src/url_state.test.tsx @@ -5,29 +5,18 @@ * 2.0. */ -import React, { FC } from 'react'; +import React, { useEffect, type FC } from 'react'; import { render, act } from '@testing-library/react'; -import { parseUrlState, useUrlState, UrlStateProvider } from './url_state'; +import { MemoryRouter } from 'react-router-dom'; -const mockHistoryPush = jest.fn(); +import { parseUrlState, useUrlState, UrlStateProvider } from './url_state'; -jest.mock('react-router-dom', () => ({ - useHistory: () => ({ - push: mockHistoryPush, - }), - useLocation: () => ({ - search: - "?_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:(query_string:(analyze_wildcard:!t,query:'*')))&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d", - }), -})); +const mockHistoryInitialState = + "?_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:(query_string:(analyze_wildcard:!t,query:'*')))&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d"; describe('getUrlState', () => { test('properly decode url with _g and _a', () => { - expect( - parseUrlState( - "?_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:(query_string:(analyze_wildcard:!t,query:'*')))&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!t,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d" - ) - ).toEqual({ + expect(parseUrlState(mockHistoryInitialState)).toEqual({ _a: { mlExplorerFilter: {}, mlExplorerSwimlane: { @@ -46,7 +35,7 @@ describe('getUrlState', () => { }, refreshInterval: { display: 'Off', - pause: true, + pause: false, value: 0, }, time: { @@ -61,29 +50,96 @@ describe('getUrlState', () => { }); describe('useUrlState', () => { - beforeEach(() => { - mockHistoryPush.mockClear(); + it('pushes a properly encoded search string to history', () => { + const TestComponent: FC = () => { + const [appState, setAppState] = useUrlState('_a'); + + useEffect(() => { + setAppState(parseUrlState(mockHistoryInitialState)._a); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + <> + +
{JSON.stringify(appState?.query)}
+ + ); + }; + + const { getByText, getByTestId } = render( + + + + + + ); + + expect(getByTestId('appState').innerHTML).toBe( + '{"query_string":{"analyze_wildcard":true,"query":"*"}}' + ); + + act(() => { + getByText('ButtonText').click(); + }); + + expect(getByTestId('appState').innerHTML).toBe('"my-query"'); }); - test('pushes a properly encoded search string to history', () => { + it('updates both _g and _a state successfully', () => { const TestComponent: FC = () => { - const [, setUrlState] = useUrlState('_a'); - return ; + const [globalState, setGlobalState] = useUrlState('_g'); + const [appState, setAppState] = useUrlState('_a'); + + useEffect(() => { + setGlobalState({ time: 'initial time' }); + setAppState({ query: 'initial query' }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + <> + + + +
{globalState?.time}
+
{appState?.query}
+ + ); }; - const { getByText } = render( - - - + const { getByText, getByTestId } = render( + + + + + ); + expect(getByTestId('globalState').innerHTML).toBe('initial time'); + expect(getByTestId('appState').innerHTML).toBe('initial query'); + act(() => { - getByText('ButtonText').click(); + getByText('GlobalStateButton1').click(); }); - expect(mockHistoryPush).toHaveBeenCalledWith({ - search: - '_a=%28mlExplorerFilter%3A%28%29%2CmlExplorerSwimlane%3A%28viewByFieldName%3Aaction%29%2Cquery%3A%28%29%29&_g=%28ml%3A%28jobIds%3A%21%28dec-2%29%29%2CrefreshInterval%3A%28display%3AOff%2Cpause%3A%21f%2Cvalue%3A0%29%2Ctime%3A%28from%3A%272019-01-01T00%3A03%3A40.000Z%27%2Cmode%3Aabsolute%2Cto%3A%272019-08-30T11%3A55%3A07.000Z%27%29%29&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d', + expect(getByTestId('globalState').innerHTML).toBe('now-15m'); + expect(getByTestId('appState').innerHTML).toBe('initial query'); + + act(() => { + getByText('AppStateButton').click(); + }); + + expect(getByTestId('globalState').innerHTML).toBe('now-15m'); + expect(getByTestId('appState').innerHTML).toBe('the updated query'); + + act(() => { + getByText('GlobalStateButton2').click(); }); + + expect(getByTestId('globalState').innerHTML).toBe('now-5y'); + expect(getByTestId('appState').innerHTML).toBe('the updated query'); }); }); diff --git a/x-pack/packages/ml/url_state/src/url_state.tsx b/x-pack/packages/ml/url_state/src/url_state.tsx index d643a22bde6e4..bd62e1f61029a 100644 --- a/x-pack/packages/ml/url_state/src/url_state.tsx +++ b/x-pack/packages/ml/url_state/src/url_state.tsx @@ -94,6 +94,12 @@ export const UrlStateProvider: FC = ({ children }) => { const history = useHistory(); const { search: searchString } = useLocation(); + const searchStringRef = useRef(searchString); + + useEffect(() => { + searchStringRef.current = searchString; + }, [searchString]); + const setUrlState: SetUrlState = useCallback( ( accessor: Accessor, @@ -101,7 +107,8 @@ export const UrlStateProvider: FC = ({ children }) => { value?: any, replaceState?: boolean ) => { - const prevSearchString = searchString; + const prevSearchString = searchStringRef.current; + const urlState = parseUrlState(prevSearchString); const parsedQueryString = parse(prevSearchString, { sort: false }); @@ -142,6 +149,10 @@ export const UrlStateProvider: FC = ({ children }) => { if (oldLocationSearchString !== newLocationSearchString) { const newSearchString = stringify(parsedQueryString, { sort: false }); + // Another `setUrlState` call could happen before the updated + // `searchString` gets propagated via `useLocation` therefore + // we update the ref right away too. + searchStringRef.current = newSearchString; if (replaceState) { history.replace({ search: newSearchString }); } else { @@ -154,7 +165,7 @@ export const UrlStateProvider: FC = ({ children }) => { } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [searchString] + [] ); return {children}; diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index de31b3cff7f7c..73911db9cece4 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -6,6 +6,7 @@ */ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { schema } from '@kbn/config-schema'; import { ActionTypeRegistry, ActionTypeRegistryOpts } from './action_type_registry'; import { ActionType, ExecutorType } from './types'; import { ActionExecutor, ILicenseState, TaskRunnerFactory } from './lib'; @@ -56,11 +57,16 @@ describe('actionTypeRegistry', () => { describe('register()', () => { test('able to register action types', () => { const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); - actionTypeRegistry.register({ + actionTypeRegistry.register<{}, {}, {}, void>({ id: 'my-action-type', name: 'My action type', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); expect(actionTypeRegistry.has('my-action-type')).toEqual(true); @@ -88,6 +94,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }; const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); @@ -103,6 +114,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); expect(() => @@ -111,6 +127,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }) ).toThrowErrorMatchingInlineSnapshot( @@ -126,6 +147,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: [], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }) ).toThrowErrorMatchingInlineSnapshot( @@ -141,6 +167,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['foo'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }) ).toThrowErrorMatchingInlineSnapshot( @@ -155,6 +186,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); expect(actionTypeRegistryParams.licensing.featureUsage.register).toHaveBeenCalledWith( @@ -170,6 +206,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); expect(actionTypeRegistryParams.licensing.featureUsage.register).not.toHaveBeenCalled(); @@ -184,10 +225,16 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); - const actionType = actionTypeRegistry.get('my-action-type'); - expect(actionType).toMatchInlineSnapshot(` + const { validate, ...rest } = actionTypeRegistry.get('my-action-type'); + expect(validate).toBeDefined(); + expect(rest).toMatchInlineSnapshot(` Object { "executor": [Function], "id": "my-action-type", @@ -217,6 +264,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); const actionTypes = actionTypeRegistry.list(); @@ -243,6 +295,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); actionTypeRegistry.register({ @@ -250,6 +307,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['cases'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); const actionTypes = actionTypeRegistry.list('alerting'); @@ -282,6 +344,11 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); expect(actionTypeRegistry.has('my-action-type')); @@ -295,6 +362,11 @@ describe('actionTypeRegistry', () => { name: 'Foo', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: async (options) => { return { status: 'ok', actionId: options.actionId }; }, @@ -363,6 +435,11 @@ describe('actionTypeRegistry', () => { name: 'Foo', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: async (options) => { return { status: 'ok', actionId: options.actionId }; }, @@ -409,6 +486,11 @@ describe('actionTypeRegistry', () => { name: 'Foo', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: async (options) => { return { status: 'ok', actionId: options.actionId }; }, @@ -451,6 +533,11 @@ describe('actionTypeRegistry', () => { name: 'Foo', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: async (options) => { return { status: 'ok', actionId: options.actionId }; }, diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index d093c6298b7b1..3c4b1a876c522 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -180,6 +180,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); @@ -213,6 +218,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); @@ -254,6 +264,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); @@ -293,6 +308,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); @@ -346,6 +366,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); @@ -391,6 +416,8 @@ describe('create()', () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, config: { schema: schema.object({ param1: schema.string(), @@ -414,11 +441,8 @@ describe('create()', () => { }); test('validates connector: config and secrets', async () => { - const connectorValidator = ({}, secrets: { param1: '1' }) => { - if (secrets.param1 == null) { - return '[param1] is required'; - } - return null; + const connectorValidator = () => { + return '[param1] is required'; }; actionTypeRegistry.register({ id: 'my-action-type', @@ -426,6 +450,9 @@ describe('create()', () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({ param1: schema.string() }) }, + params: { schema: schema.object({}) }, connector: connectorValidator, }, executor, @@ -436,7 +463,7 @@ describe('create()', () => { name: 'my name', actionTypeId: 'my-action-type', config: {}, - secrets: {}, + secrets: { param1: '1' }, }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -465,6 +492,13 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { + schema: schema.object({ a: schema.boolean(), b: schema.boolean(), c: schema.boolean() }), + }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -546,6 +580,7 @@ describe('create()', () => { verificationMode: 'full', proxyVerificationMode: 'full', }, + enableFooterInEmail: true, }); const localActionTypeRegistryParams = { @@ -594,6 +629,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); @@ -629,6 +669,11 @@ describe('create()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); mockedLicenseState.ensureLicenseForActionType.mockImplementation(() => { @@ -1725,6 +1770,11 @@ describe('update()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -1829,6 +1879,11 @@ describe('update()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -1902,6 +1957,11 @@ describe('update()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -1969,6 +2029,8 @@ describe('update()', () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, config: { schema: schema.object({ param1: schema.string(), @@ -2006,6 +2068,9 @@ describe('update()', () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, connector: () => { return '[param1] is required'; }, @@ -2040,6 +2105,13 @@ describe('update()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { + schema: schema.object({ a: schema.boolean(), b: schema.boolean(), c: schema.boolean() }), + }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -2121,6 +2193,11 @@ describe('update()', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); mockedLicenseState.ensureLicenseForActionType.mockImplementation(() => { @@ -2511,6 +2588,11 @@ describe('isActionTypeEnabled()', () => { name: 'Foo', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: jest.fn(), }; beforeEach(() => { diff --git a/x-pack/plugins/actions/server/actions_config.mock.ts b/x-pack/plugins/actions/server/actions_config.mock.ts index 26a626cd88bf4..95c6ec1c0cc11 100644 --- a/x-pack/plugins/actions/server/actions_config.mock.ts +++ b/x-pack/plugins/actions/server/actions_config.mock.ts @@ -27,6 +27,7 @@ const createActionsConfigMock = () => { getMicrosoftGraphApiUrl: jest.fn().mockReturnValue(undefined), validateEmailAddresses: jest.fn().mockReturnValue(undefined), getMaxAttempts: jest.fn().mockReturnValue(3), + enableFooterInEmail: jest.fn().mockReturnValue(true), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/actions_config.test.ts b/x-pack/plugins/actions/server/actions_config.test.ts index 2c987ae7d48bd..e1b761ee30001 100644 --- a/x-pack/plugins/actions/server/actions_config.test.ts +++ b/x-pack/plugins/actions/server/actions_config.test.ts @@ -33,6 +33,7 @@ const defaultActionsConfig: ActionsConfig = { proxyVerificationMode: 'full', verificationMode: 'full', }, + enableFooterInEmail: true, }; describe('ensureUriAllowed', () => { diff --git a/x-pack/plugins/actions/server/actions_config.ts b/x-pack/plugins/actions/server/actions_config.ts index 43dd35ba38021..78e62684ad898 100644 --- a/x-pack/plugins/actions/server/actions_config.ts +++ b/x-pack/plugins/actions/server/actions_config.ts @@ -53,6 +53,7 @@ export interface ActionsConfigurationUtilities { addresses: string[], options?: ValidateEmailAddressesOptions ): string | undefined; + enableFooterInEmail: () => boolean; } function allowListErrorMessage(field: AllowListingField, value: string) { @@ -215,5 +216,6 @@ export function getActionsConfigurationUtilities( DEFAULT_MAX_ATTEMPTS ); }, + enableFooterInEmail: () => config.enableFooterInEmail, }; } diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts index a1e6e96dcc69b..f5971a8650681 100644 --- a/x-pack/plugins/actions/server/config.test.ts +++ b/x-pack/plugins/actions/server/config.test.ts @@ -23,6 +23,7 @@ describe('config validation', () => { "allowedHosts": Array [ "*", ], + "enableFooterInEmail": true, "enabledActionTypes": Array [ "*", ], @@ -57,6 +58,7 @@ describe('config validation', () => { "allowedHosts": Array [ "*", ], + "enableFooterInEmail": true, "enabledActionTypes": Array [ "*", ], @@ -198,6 +200,7 @@ describe('config validation', () => { "allowedHosts": Array [ "*", ], + "enableFooterInEmail": true, "enabledActionTypes": Array [ "*", ], diff --git a/x-pack/plugins/actions/server/config.ts b/x-pack/plugins/actions/server/config.ts index bbac076783179..9a620d1452f23 100644 --- a/x-pack/plugins/actions/server/config.ts +++ b/x-pack/plugins/actions/server/config.ts @@ -125,6 +125,7 @@ export const configSchema = schema.object({ connectorTypeOverrides: schema.maybe(schema.arrayOf(connectorTypeSchema)), }) ), + enableFooterInEmail: schema.boolean({ defaultValue: true }), }); export type ActionsConfig = TypeOf; diff --git a/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts b/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts index ddd128422f25b..04b8b412e3647 100644 --- a/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts +++ b/x-pack/plugins/actions/server/integration_tests/axios_utils_connection.test.ts @@ -580,6 +580,7 @@ const BaseActionsConfig: ActionsConfig = { maxResponseContentLength: ByteSizeValue.parse('1mb'), responseTimeout: momentDuration(1000 * 30), customHostSettings: undefined, + enableFooterInEmail: true, }; function getACUfromConfig(config: Partial = {}): ActionsConfigurationUtilities { diff --git a/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts b/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts index 2be4e04aaba5b..d56818f2c6d3e 100644 --- a/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts +++ b/x-pack/plugins/actions/server/integration_tests/axios_utils_proxy.test.ts @@ -592,6 +592,7 @@ const BaseActionsConfig: ActionsConfig = { maxResponseContentLength: ByteSizeValue.parse('1mb'), responseTimeout: momentDuration(1000 * 30), customHostSettings: undefined, + enableFooterInEmail: true, }; function getACUfromConfig(config: Partial = {}): ActionsConfigurationUtilities { diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index a6acfc25a923a..b2d869e61ceea 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -77,6 +77,11 @@ test('successfully executes', async () => { name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.boolean() }) }, + secrets: { schema: schema.object({ baz: schema.boolean() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -210,6 +215,11 @@ test('successfully executes when http_request source is specified', async () => name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.boolean() }) }, + secrets: { schema: schema.object({ baz: schema.boolean() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -348,6 +358,11 @@ test('successfully executes when saved_object source is specified', async () => name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.boolean() }) }, + secrets: { schema: schema.object({ baz: schema.boolean() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -489,6 +504,11 @@ test('successfully executes with preconfigured connector', async () => { name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.string() }) }, + secrets: { schema: schema.object({ apiKey: schema.string() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; @@ -605,6 +625,11 @@ test('successfully executes as a task', async () => { name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -649,6 +674,11 @@ test('provides empty config when config and / or secrets is empty', async () => name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -666,7 +696,7 @@ test('provides empty config when config and / or secrets is empty', async () => expect(actionType.executor).toHaveBeenCalledTimes(1); const executorCall = actionType.executor.mock.calls[0][0]; - expect(executorCall.config).toMatchInlineSnapshot(`undefined`); + expect(executorCall.config).toMatchInlineSnapshot(`Object {}`); }); test('throws an error when config is invalid', async () => { @@ -676,6 +706,8 @@ test('throws an error when config is invalid', async () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, config: { schema: schema.object({ param1: schema.string(), @@ -712,6 +744,9 @@ test('throws an error when connector is invalid', async () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + secrets: { schema: schema.object({}) }, + config: { schema: schema.object({}) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, connector: () => { return 'error'; }, @@ -746,6 +781,8 @@ test('throws an error when params is invalid', async () => { minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, params: { schema: schema.object({ param1: schema.string(), @@ -790,6 +827,11 @@ test('throws an error if actionType is not enabled', async () => { name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -819,6 +861,11 @@ test('should not throws an error if actionType is preconfigured', async () => { name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.boolean() }) }, + secrets: { schema: schema.object({ baz: schema.boolean() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; const actionSavedObject = { @@ -907,6 +954,11 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.string() }) }, + secrets: { schema: schema.object({ apiKey: schema.string() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; @@ -1233,6 +1285,11 @@ function setupActionExecutorMock() { name: 'Test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.boolean() }) }, + secrets: { schema: schema.object({ baz: schema.boolean() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, executor: jest.fn(), }; const actionSavedObject = { diff --git a/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts b/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts index 4bb0825a24ab0..f5cd9c268bd7e 100644 --- a/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts +++ b/x-pack/plugins/actions/server/lib/custom_host_settings.test.ts @@ -73,6 +73,7 @@ describe('custom_host_settings', () => { rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), + enableFooterInEmail: true, }; test('ensure it copies over the config parts that it does not touch', () => { diff --git a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts index 0aeb0a8db3541..06e5380c04f97 100644 --- a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts +++ b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import { ActionType } from '../types'; import { ensureSufficientLicense } from './ensure_sufficient_license'; @@ -13,6 +14,11 @@ const sampleActionType: ActionType = { name: 'test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, async executor({ actionId }) { return { status: 'ok', actionId }; }, diff --git a/x-pack/plugins/actions/server/lib/license_state.test.ts b/x-pack/plugins/actions/server/lib/license_state.test.ts index 4445a4e048e8d..7c2ed39f1fa49 100644 --- a/x-pack/plugins/actions/server/lib/license_state.test.ts +++ b/x-pack/plugins/actions/server/lib/license_state.test.ts @@ -7,6 +7,7 @@ import { ActionType } from '../types'; import { Subject } from 'rxjs'; +import { schema } from '@kbn/config-schema'; import { LicenseState, ILicenseState } from './license_state'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { ILicense } from '@kbn/licensing-plugin/server'; @@ -62,6 +63,11 @@ describe('isLicenseValidForActionType', () => { name: 'Foo', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: async (options) => { return { status: 'ok', actionId: options.actionId }; }, @@ -158,6 +164,11 @@ describe('ensureLicenseForActionType()', () => { name: 'Foo', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: async (options) => { return { status: 'ok', actionId: options.actionId }; }, diff --git a/x-pack/plugins/actions/server/lib/validate_with_schema.test.ts b/x-pack/plugins/actions/server/lib/validate_with_schema.test.ts index 24eb9a57a32f3..01f48769bd273 100644 --- a/x-pack/plugins/actions/server/lib/validate_with_schema.test.ts +++ b/x-pack/plugins/actions/server/lib/validate_with_schema.test.ts @@ -35,6 +35,11 @@ test('should validate when there are no validators', () => { name: 'bar', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ any: schema.arrayOf(schema.string()) }) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }; const testValue = { any: ['old', 'thing'] }; @@ -43,32 +48,6 @@ test('should validate when there are no validators', () => { expect(result).toEqual(testValue); }); -test('should validate when there are no individual validators', () => { - const actionType: ActionType = { - id: 'foo', - name: 'bar', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - executor, - validate: {}, - }; - - let result; - const testValue = { any: ['old', 'thing'] }; - - result = validateParams(actionType, testValue, { configurationUtilities }); - expect(result).toEqual(testValue); - - result = validateConfig(actionType, testValue, { configurationUtilities }); - expect(result).toEqual(testValue); - - result = validateSecrets(actionType, testValue, { configurationUtilities }); - expect(result).toEqual(testValue); - - result = validateConnector(actionType, { config: testValue }); - expect(result).toBeNull(); -}); - test('should validate when validators return incoming value', () => { const selfValidator = { validate: (value: Record) => value }; const actionType: ActionType = { diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 954a2b73d55cb..0136410920c66 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import { ByteSizeValue } from '@kbn/config-schema'; +import { schema, ByteSizeValue } from '@kbn/config-schema'; import { PluginInitializerContext, RequestHandlerContext } from '@kbn/core/server'; import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks'; @@ -46,6 +46,7 @@ describe('Actions Plugin', () => { rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), + enableFooterInEmail: true, }); plugin = new ActionsPlugin(context); coreSetup = coreMock.createSetup(); @@ -144,6 +145,11 @@ describe('Actions Plugin', () => { name: 'test', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, async executor(options) { return { status: 'ok', actionId: options.actionId }; }, @@ -213,6 +219,7 @@ describe('Actions Plugin', () => { rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration('60s'), + enableFooterInEmail: true, ...overrides, }; } @@ -268,6 +275,7 @@ describe('Actions Plugin', () => { rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration(60000), + enableFooterInEmail: true, }); plugin = new ActionsPlugin(context); coreSetup = coreMock.createSetup(); @@ -336,6 +344,7 @@ describe('Actions Plugin', () => { rejectUnauthorized: true, maxResponseContentLength: new ByteSizeValue(1000000), responseTimeout: moment.duration('60s'), + enableFooterInEmail: true, ...overrides, }; } @@ -371,6 +380,11 @@ describe('Actions Plugin', () => { name: 'Server log', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); @@ -390,6 +404,11 @@ describe('Actions Plugin', () => { name: 'ES Index', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor, }); @@ -432,6 +451,11 @@ describe('Actions Plugin', () => { name: 'My action type', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: jest.fn(), }; @@ -455,6 +479,11 @@ describe('Actions Plugin', () => { name: 'My action type', minimumLicenseRequired: 'gold', supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, executor: jest.fn(), }; diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts index 27850b6945a21..a110320586750 100644 --- a/x-pack/plugins/actions/server/saved_objects/index.ts +++ b/x-pack/plugins/actions/server/saved_objects/index.ts @@ -9,11 +9,10 @@ import type { SavedObject, SavedObjectsExportTransformContext, SavedObjectsServiceSetup, - SavedObjectsTypeMappingDefinition, } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { getOldestIdleActionTask } from '@kbn/task-manager-plugin/server'; -import mappings from './mappings.json'; +import { actionMappings, actionTaskParamsMappings, connectorTokenMappings } from './mappings'; import { getActionsMigrations } from './actions_migrations'; import { getActionTaskParamsMigrations } from './action_task_params_migrations'; import { PreConfiguredAction, RawAction } from '../types'; @@ -38,7 +37,7 @@ export function setupSavedObjects( hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', - mappings: mappings.action as SavedObjectsTypeMappingDefinition, + mappings: actionMappings, migrations: getActionsMigrations(encryptedSavedObjects), management: { displayName: 'connector', @@ -76,7 +75,7 @@ export function setupSavedObjects( hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', - mappings: mappings.action_task_params as SavedObjectsTypeMappingDefinition, + mappings: actionTaskParamsMappings, migrations: getActionTaskParamsMigrations(encryptedSavedObjects, preconfiguredActions), excludeOnUpgrade: async ({ readonlyEsClient }) => { const oldestIdleActionTask = await getOldestIdleActionTask( @@ -102,7 +101,7 @@ export function setupSavedObjects( name: CONNECTOR_TOKEN_SAVED_OBJECT_TYPE, hidden: true, namespaceType: 'agnostic', - mappings: mappings.connector_token as SavedObjectsTypeMappingDefinition, + mappings: connectorTokenMappings, management: { importableAndExportable: false, }, diff --git a/x-pack/plugins/actions/server/saved_objects/mappings.json b/x-pack/plugins/actions/server/saved_objects/mappings.json deleted file mode 100644 index be0a691f852b2..0000000000000 --- a/x-pack/plugins/actions/server/saved_objects/mappings.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "action": { - "properties": { - "name": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword" - } - } - }, - "actionTypeId": { - "type": "keyword" - }, - "isMissingSecrets": { - "type": "boolean" - }, - "config": { - "enabled": false, - "type": "object" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "dynamic": false, - "properties": { - "actionId": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - }, - "apiKey": { - "type": "binary" - }, - "executionId": { - "type": "keyword" - }, - "relatedSavedObjects": { - "enabled": false, - "type": "object" - } - } - }, - "connector_token": { - "properties": { - "connectorId": { - "type": "keyword" - }, - "tokenType": { - "type": "keyword" - }, - "token": { - "type": "binary" - }, - "expiresAt": { - "type": "date" - }, - "createdAt": { - "type": "date" - }, - "updatedAt": { - "type": "date" - } - } - } -} diff --git a/x-pack/plugins/actions/server/saved_objects/mappings.ts b/x-pack/plugins/actions/server/saved_objects/mappings.ts new file mode 100644 index 0000000000000..70e3e7447a6a0 --- /dev/null +++ b/x-pack/plugins/actions/server/saved_objects/mappings.ts @@ -0,0 +1,93 @@ +/* + * 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 { SavedObjectsTypeMappingDefinition } from '@kbn/core/server'; + +export const actionMappings: SavedObjectsTypeMappingDefinition = { + dynamic: false, + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + actionTypeId: { + type: 'keyword', + }, + // NO NEED TO BE INDEXED + // isMissingSecrets: { + // type: 'boolean', + // }, + // NO NEED TO BE INDEXED + // config: { + // enabled: false, + // type: 'object', + // }, + // NEED TO CHECK WITH KIBANA SECURITY + // secrets: { + // type: 'binary', + // }, + }, +}; + +export const actionTaskParamsMappings: SavedObjectsTypeMappingDefinition = { + dynamic: false, + properties: { + // NO NEED TO BE INDEXED + // actionId: { + // type: 'keyword', + // }, + // consumer: { + // type: 'keyword', + // }, + // params: { + // enabled: false, + // type: 'object', + // }, + // apiKey: { + // type: 'binary', + // }, + // executionId: { + // type: 'keyword', + // }, + // relatedSavedObjects: { + // enabled: false, + // type: 'object', + // }, + // source: { + // type: 'keyword' + // } + }, +}; + +export const connectorTokenMappings: SavedObjectsTypeMappingDefinition = { + dynamic: false, + properties: { + connectorId: { + type: 'keyword', + }, + tokenType: { + type: 'keyword', + }, + // NO NEED TO BE INDEXED + // token: { + // type: 'binary', + // }, + // expiresAt: { + // type: 'date', + // }, + // createdAt: { + // type: 'date', + // }, + // updatedAt: { + // type: 'date', + // }, + }, +}; diff --git a/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts b/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts index bbe8009566e94..6fd19bad45897 100644 --- a/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/transform_connectors_for_export.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import { transformConnectorsForExport } from './transform_connectors_for_export'; import { actionTypeRegistryMock } from '../action_type_registry.mock'; import { ActionType, ActionTypeRegistryContract, ActionTypeSecrets } from '../types'; @@ -17,6 +18,8 @@ describe('transform connector for export', () => { supportedFeatureIds: ['alerting'], executor: jest.fn(), validate: { + config: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, secrets: { schema: { validate: (value: unknown) => value as ActionTypeSecrets, @@ -245,6 +248,8 @@ describe('transform connector for export', () => { actionTypeRegistry.get.mockReturnValue({ ...connectorType, validate: { + config: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, secrets: { schema: { validate: (value: unknown) => { diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 72e7eeddce1c1..5bbd3598f0b5f 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -127,10 +127,10 @@ export interface ActionType< maxAttempts?: number; minimumLicenseRequired: LicenseType; supportedFeatureIds: string[]; - validate?: { - params?: ValidatorType; - config?: ValidatorType; - secrets?: ValidatorType; + validate: { + params: ValidatorType; + config: ValidatorType; + secrets: ValidatorType; connector?: (config: Config, secrets: Secrets) => string | null; }; diff --git a/x-pack/plugins/aiops/common/index.ts b/x-pack/plugins/aiops/common/index.ts index b4ec6c287b22d..e4ef3c5a10af0 100755 --- a/x-pack/plugins/aiops/common/index.ts +++ b/x-pack/plugins/aiops/common/index.ts @@ -21,4 +21,8 @@ export const PLUGIN_NAME = 'AIOps'; */ export const AIOPS_ENABLED = true; +/** + * This is an internal hard coded feature flag so we can easily turn on/off the + * "Change Point Detection UI" during development until the first release. + */ export const CHANGE_POINT_DETECTION_ENABLED = true; diff --git a/x-pack/plugins/aiops/kibana.jsonc b/x-pack/plugins/aiops/kibana.jsonc index a343af521ed80..019c77cc155fa 100644 --- a/x-pack/plugins/aiops/kibana.jsonc +++ b/x-pack/plugins/aiops/kibana.jsonc @@ -11,7 +11,8 @@ "charts", "data", "lens", - "licensing" + "licensing", + "unifiedFieldList", ], "requiredBundles": [ "fieldFormats", diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx index f1bcdb18fbaec..7ddef239cf2f4 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import React, { useEffect, useMemo, useState, type FC } from 'react'; +import React, { useEffect, useMemo, useState, FC } from 'react'; import { isEqual, uniq } from 'lodash'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiButton, @@ -25,7 +26,6 @@ import { useFetchStream } from '@kbn/aiops-utils'; import type { WindowParameters } from '@kbn/aiops-utils'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { Query } from '@kbn/es-query'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { initialState, streamReducer } from '../../../common/api/stream_reducer'; @@ -67,7 +67,7 @@ interface ExplainLogRateSpikesAnalysisProps { /** Window parameters for the analysis */ windowParameters: WindowParameters; /** The search query to be applied to the analysis as a filter */ - searchQuery: Query['query']; + searchQuery: estypes.QueryDslQueryContainer; /** Sample probability to be applied to random sampler aggregations */ sampleProbability: number; } @@ -215,6 +215,7 @@ export const ExplainLogRateSpikesAnalysis: FC return p + c.groupItemsSortedByUniqueness.length; }, 0); const foundGroups = groupTableItems.length > 0 && groupItemCount > 0; + const timeRangeMs = { from: earliest, to: latest }; // Disable the grouping switch toggle only if no groups were found, // the toggle wasn't enabled already and no fields were selected to be skipped. @@ -326,14 +327,18 @@ export const ExplainLogRateSpikesAnalysis: FC significantTerms={data.significantTerms} groupTableItems={groupTableItems} loading={isRunning} - dataViewId={dataView.id} + dataView={dataView} + timeRangeMs={timeRangeMs} + searchQuery={searchQuery} /> ) : null} {showSpikeAnalysisTable && !groupResults ? ( ) : null}
diff --git a/x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_content.tsx b/x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_content.tsx new file mode 100644 index 0000000000000..e3ea0d5bb01c0 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_content.tsx @@ -0,0 +1,191 @@ +/* + * 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, { FC, useMemo, useState, useCallback } from 'react'; +import { + FieldStats, + FieldStatsProps, + FieldStatsServices, + FieldStatsState, + FieldTopValuesBucketParams, + FieldTopValuesBucket, +} from '@kbn/unified-field-list-plugin/public'; +import { isDefined } from '@kbn/ml-is-defined'; +import type { DataView, DataViewField } from '@kbn/data-plugin/common'; +import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiHorizontalRule, euiPaletteColorBlind, EuiSpacer, EuiText } from '@elastic/eui'; + +const DEFAULT_COLOR = euiPaletteColorBlind()[0]; +const HIGHLIGHTED_FIELD_PROPS = { + color: 'accent', + textProps: { + color: 'accent', + }, +}; + +function getPercentValue( + currentValue: number, + totalCount: number, + digitsRequired: boolean +): number { + const percentageString = + totalCount > 0 + ? `${(Math.round((currentValue / totalCount) * 1000) / 10).toFixed(digitsRequired ? 1 : 0)}` + : ''; + return Number(percentageString); +} + +interface FieldStatsContentProps { + dataView: DataView; + field: DataViewField; + fieldName: string; + fieldValue: string | number; + fieldStatsServices: FieldStatsServices; + timeRangeMs?: TimeRangeMs; + dslQuery?: FieldStatsProps['dslQuery']; +} + +export const FieldStatsContent: FC = ({ + dataView: currentDataView, + field, + fieldName, + fieldValue, + fieldStatsServices, + timeRangeMs, + dslQuery, +}) => { + const [fieldStatsState, setFieldStatsState] = useState(); + + // Format timestamp to ISO formatted date strings + const timeRange = useMemo(() => { + // Use the provided timeRange if available + if (timeRangeMs) { + return { + from: moment(timeRangeMs.from).toISOString(), + to: moment(timeRangeMs.to).toISOString(), + }; + } + + const now = moment(); + return { from: now.toISOString(), to: now.toISOString() }; + }, [timeRangeMs]); + + const onStateChange = useCallback((nextState: FieldStatsState) => { + setFieldStatsState(nextState); + }, []); + + const individualStatForDisplay = useMemo((): { + needToDisplayIndividualStat: boolean; + percentage: string; + } => { + const defaultIndividualStatForDisplay = { + needToDisplayIndividualStat: false, + percentage: '< 1%', + }; + if (fieldStatsState === undefined) return defaultIndividualStatForDisplay; + + const { topValues: currentTopValues, sampledValues } = fieldStatsState; + + const idxToHighlight = + currentTopValues?.buckets && Array.isArray(currentTopValues.buckets) + ? currentTopValues.buckets.findIndex((value) => value.key === fieldValue) + : null; + + const needToDisplayIndividualStat = + idxToHighlight === -1 && fieldName !== undefined && fieldValue !== undefined; + + if (needToDisplayIndividualStat) { + defaultIndividualStatForDisplay.needToDisplayIndividualStat = true; + + const buckets = + currentTopValues?.buckets && Array.isArray(currentTopValues.buckets) + ? currentTopValues.buckets + : []; + let lowestPercentage: number | undefined; + + // Taken from the unifiedFieldList plugin + const digitsRequired = buckets.some( + (bucket) => !Number.isInteger(bucket.count / (sampledValues ?? 5000)) + ); + + buckets.forEach((bucket) => { + const currentPercentage = getPercentValue( + bucket.count, + sampledValues ?? 5000, + digitsRequired + ); + + if (lowestPercentage === undefined || currentPercentage < lowestPercentage) { + lowestPercentage = currentPercentage; + } + }); + + defaultIndividualStatForDisplay.percentage = `< ${lowestPercentage ?? 1}%`; + } + + return defaultIndividualStatForDisplay; + }, [fieldStatsState, fieldName, fieldValue]); + + const overrideFieldTopValueBar = useCallback( + (fieldTopValuesBucketParams: FieldTopValuesBucketParams) => { + if (fieldTopValuesBucketParams.type === 'other') { + return { color: 'primary' }; + } + return fieldValue === fieldTopValuesBucketParams.fieldValue ? HIGHLIGHTED_FIELD_PROPS : {}; + }, + [fieldValue] + ); + + const showFieldStats = timeRange && isDefined(currentDataView) && field; + + if (!showFieldStats) return null; + const formatter = currentDataView.getFormatterForField(field); + + return ( + <> + + {individualStatForDisplay.needToDisplayIndividualStat ? ( + <> + + + + + + + + ) : null} + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_popover.tsx b/x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_popover.tsx new file mode 100644 index 0000000000000..ba0d1c29e8965 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/field_stats_popover/field_stats_popover.tsx @@ -0,0 +1,89 @@ +/* + * 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, EuiToolTip } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { isDefined } from '@kbn/ml-is-defined'; +import { + FieldPopover, + FieldPopoverHeader, + FieldStatsServices, + FieldStatsProps, +} from '@kbn/unified-field-list-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import { useEuiTheme } from '../../hooks/use_eui_theme'; +import { FieldStatsContent } from './field_stats_content'; + +export function FieldStatsPopover({ + dataView, + dslQuery, + fieldName, + fieldValue, + fieldStatsServices, + timeRangeMs, +}: { + dataView: DataView; + dslQuery?: FieldStatsProps['dslQuery']; + fieldName: string; + fieldValue: string | number; + fieldStatsServices: FieldStatsServices; + timeRangeMs?: TimeRangeMs; +}) { + const [infoIsOpen, setInfoOpen] = useState(false); + const euiTheme = useEuiTheme(); + + const closePopover = useCallback(() => setInfoOpen(false), []); + + const fieldForStats = useMemo( + () => (isDefined(fieldName) ? dataView.getFieldByName(fieldName) : undefined), + [fieldName, dataView] + ); + + const trigger = ( + + ) => { + setInfoOpen(!infoIsOpen); + }} + aria-label={i18n.translate('xpack.aiops.fieldContextPopover.topFieldValuesAriaLabel', { + defaultMessage: 'Show top field values', + })} + data-test-subj={'aiopsContextPopoverButton'} + style={{ marginLeft: euiTheme.euiSizeXS }} + /> + + ); + + if (!fieldForStats) return null; + + return ( + } + renderContent={() => ( + + )} + /> + ); +} diff --git a/x-pack/plugins/aiops/public/components/field_stats_popover/index.ts b/x-pack/plugins/aiops/public/components/field_stats_popover/index.ts new file mode 100644 index 0000000000000..c982a7dd397ea --- /dev/null +++ b/x-pack/plugins/aiops/public/components/field_stats_popover/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 { FieldStatsPopover } from './field_stats_popover'; diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx index afbe7b693bfda..1a5b7460b62fe 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -7,6 +7,7 @@ import React, { FC, useCallback, useMemo, useState } from 'react'; import { sortBy } from 'lodash'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { useEuiBackgroundColor, @@ -19,16 +20,22 @@ import { EuiToolTip, } from '@elastic/eui'; +import { FieldStatsServices } from '@kbn/unified-field-list-plugin/public'; + +import type { DataView } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { SignificantTerm } from '@kbn/ml-agg-utils'; +import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; import { useEuiTheme } from '../../hooks/use_eui_theme'; import { MiniHistogram } from '../mini_histogram'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label'; import { useSpikeAnalysisTableRowContext } from './spike_analysis_table_row_provider'; +import { FieldStatsPopover } from '../field_stats_popover'; import { useCopyToClipboardAction } from './use_copy_to_clipboard_action'; import { useViewInDiscoverAction } from './use_view_in_discover_action'; @@ -43,19 +50,24 @@ const DEFAULT_SORT_DIRECTION = 'asc'; interface SpikeAnalysisTableProps { significantTerms: SignificantTerm[]; - dataViewId?: string; + dataView: DataView; loading: boolean; isExpandedRow?: boolean; + searchQuery: estypes.QueryDslQueryContainer; + timeRangeMs: TimeRangeMs; } export const SpikeAnalysisTable: FC = ({ significantTerms, - dataViewId, + dataView, loading, isExpandedRow, + searchQuery, + timeRangeMs, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); + const dataViewId = dataView.id; const { pinnedSignificantTerm, @@ -69,6 +81,18 @@ export const SpikeAnalysisTable: FC = ({ const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION); + const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext(); + + const fieldStatsServices: FieldStatsServices = useMemo(() => { + return { + uiSettings, + dataViews: data.dataViews, + data, + fieldFormats, + charts, + }; + }, [uiSettings, data, fieldFormats, charts]); + const copyToClipBoardAction = useCopyToClipboardAction(); const viewInDiscoverAction = useViewInDiscoverAction(dataViewId); @@ -79,8 +103,21 @@ export const SpikeAnalysisTable: FC = ({ name: i18n.translate('xpack.aiops.explainLogRateSpikes.spikeAnalysisTable.fieldNameLabel', { defaultMessage: 'Field name', }), + render: (_, { fieldName, fieldValue }) => ( + <> + + {fieldName} + + ), sortable: true, - valign: 'top', + valign: 'middle', }, { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnFieldValue', @@ -91,7 +128,7 @@ export const SpikeAnalysisTable: FC = ({ render: (_, { fieldValue }) => String(fieldValue), sortable: true, textOnly: true, - valign: 'top', + valign: 'middle', }, { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnLogRate', @@ -125,7 +162,7 @@ export const SpikeAnalysisTable: FC = ({ /> ), sortable: false, - valign: 'top', + valign: 'middle', }, { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnDocCount', @@ -135,7 +172,7 @@ export const SpikeAnalysisTable: FC = ({ defaultMessage: 'Doc count', }), sortable: true, - valign: 'top', + valign: 'middle', }, { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnPValue', @@ -163,7 +200,7 @@ export const SpikeAnalysisTable: FC = ({ ), render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE, sortable: true, - valign: 'top', + valign: 'middle', }, { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnImpact', @@ -194,7 +231,7 @@ export const SpikeAnalysisTable: FC = ({ return label ? {label.impact} : null; }, sortable: true, - valign: 'top', + valign: 'middle', }, { 'data-test-subj': 'aiOpsSpikeAnalysisTableColumnAction', @@ -203,7 +240,7 @@ export const SpikeAnalysisTable: FC = ({ }), actions: [viewInDiscoverAction, copyToClipBoardAction], width: ACTIONS_COLUMN_WIDTH, - valign: 'top', + valign: 'middle', }, ]; @@ -231,7 +268,7 @@ export const SpikeAnalysisTable: FC = ({ return ''; }, sortable: false, - valign: 'top', + valign: 'middle', }); } diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx index 22c6f717c3a0a..b319db0088d4d 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx @@ -25,9 +25,12 @@ import { euiPaletteColorBlind, } from '@elastic/eui'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { SignificantTerm } from '@kbn/ml-agg-utils'; +import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import type { DataView } from '@kbn/data-views-plugin/public'; import { MiniHistogram } from '../mini_histogram'; @@ -51,15 +54,19 @@ const DEFAULT_SORT_DIRECTION = 'asc'; interface SpikeAnalysisTableProps { significantTerms: SignificantTerm[]; groupTableItems: GroupTableItem[]; - dataViewId?: string; loading: boolean; + searchQuery: estypes.QueryDslQueryContainer; + timeRangeMs: TimeRangeMs; + dataView: DataView; } export const SpikeAnalysisGroupsTable: FC = ({ significantTerms, groupTableItems, - dataViewId, loading, + dataView, + timeRangeMs, + searchQuery, }) => { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); @@ -75,6 +82,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ const { pinnedGroup, selectedGroup, setPinnedGroup, setSelectedGroup } = useSpikeAnalysisTableRowContext(); + const dataViewId = dataView.id; const toggleDetails = (item: GroupTableItem) => { const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; @@ -101,8 +109,10 @@ export const SpikeAnalysisGroupsTable: FC = ({ [] )} loading={loading} - dataViewId={dataViewId} isExpandedRow + dataView={dataView} + timeRangeMs={timeRangeMs} + searchQuery={searchQuery} /> ); } diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts index 3546cead3edab..82db675a94c4f 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data.ts +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -45,9 +45,6 @@ export const useData = ( } = useAiopsAppContext(); const [lastRefresh, setLastRefresh] = useState(0); - const [fieldStatsRequest, setFieldStatsRequest] = useState< - DocumentStatsSearchStrategyParams | undefined - >(); /** Prepare required params to pass to search strategy **/ const { searchQueryLanguage, searchString, searchQuery } = useMemo(() => { @@ -91,12 +88,30 @@ export const useData = ( ]); const _timeBuckets = useTimeBuckets(); - const timefilter = useTimefilter({ timeRangeSelector: selectedDataView?.timeFieldName !== undefined, autoRefreshSelector: true, }); + const fieldStatsRequest: DocumentStatsSearchStrategyParams | undefined = useMemo(() => { + const timefilterActiveBounds = timefilter.getActiveBounds(); + if (timefilterActiveBounds !== undefined) { + _timeBuckets.setInterval('auto'); + _timeBuckets.setBounds(timefilterActiveBounds); + _timeBuckets.setBarTarget(barTarget); + return { + earliest: timefilterActiveBounds.min?.valueOf(), + latest: timefilterActiveBounds.max?.valueOf(), + intervalMs: _timeBuckets.getInterval()?.asMilliseconds(), + index: selectedDataView.getIndexPattern(), + searchQuery, + timeFieldName: selectedDataView.timeFieldName, + runtimeFieldMap: selectedDataView.getRuntimeMappings(), + }; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [lastRefresh, searchQuery]); + const overallStatsRequest = useMemo(() => { return fieldStatsRequest ? { @@ -125,25 +140,6 @@ export const useData = ( lastRefresh ); - function updateFieldStatsRequest() { - const timefilterActiveBounds = timefilter.getActiveBounds(); - if (timefilterActiveBounds !== undefined) { - _timeBuckets.setInterval('auto'); - _timeBuckets.setBounds(timefilterActiveBounds); - _timeBuckets.setBarTarget(barTarget); - setFieldStatsRequest({ - earliest: timefilterActiveBounds.min?.valueOf(), - latest: timefilterActiveBounds.max?.valueOf(), - intervalMs: _timeBuckets.getInterval()?.asMilliseconds(), - index: selectedDataView.getIndexPattern(), - searchQuery, - timeFieldName: selectedDataView.timeFieldName, - runtimeFieldMap: selectedDataView.getRuntimeMappings(), - }); - setLastRefresh(Date.now()); - } - } - useEffect(() => { const timefilterUpdateSubscription = merge( timefilter.getAutoRefreshFetch$(), @@ -156,13 +152,13 @@ export const useData = ( refreshInterval: timefilter.getRefreshInterval(), }); } - updateFieldStatsRequest(); + setLastRefresh(Date.now()); }); // This listens just for an initial update of the timefilter to be switched on. const timefilterEnabledSubscription = timefilter.getEnabledUpdated$().subscribe(() => { if (fieldStatsRequest === undefined) { - updateFieldStatsRequest(); + setLastRefresh(Date.now()); } }); @@ -173,12 +169,6 @@ export const useData = ( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // Ensure request is updated when search changes - useEffect(() => { - updateFieldStatsRequest(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchString, JSON.stringify(searchQuery)]); - return { documentStats, timefilter, diff --git a/x-pack/plugins/aiops/public/index.ts b/x-pack/plugins/aiops/public/index.ts index 74a70f0acfd27..04b640c362878 100755 --- a/x-pack/plugins/aiops/public/index.ts +++ b/x-pack/plugins/aiops/public/index.ts @@ -13,6 +13,11 @@ export function plugin() { return new AiopsPlugin(); } +export type { AiopsAppDependencies } from './hooks/use_aiops_app_context'; +export type { ExplainLogRateSpikesAppStateProps } from './components/explain_log_rate_spikes'; +export type { LogCategorizationAppStateProps } from './components/log_categorization'; +export type { ChangePointDetectionAppStateProps } from './components/change_point_detection'; + export { ExplainLogRateSpikes, LogCategorization, diff --git a/x-pack/plugins/aiops/public/shared_lazy_components.tsx b/x-pack/plugins/aiops/public/shared_lazy_components.tsx index a5ad80bd7587e..4f9ade98d4dc0 100644 --- a/x-pack/plugins/aiops/public/shared_lazy_components.tsx +++ b/x-pack/plugins/aiops/public/shared_lazy_components.tsx @@ -10,6 +10,7 @@ import React, { FC, Suspense } from 'react'; import { EuiErrorBoundary, EuiLoadingContent } from '@elastic/eui'; import type { ExplainLogRateSpikesAppStateProps } from './components/explain_log_rate_spikes'; import type { LogCategorizationAppStateProps } from './components/log_categorization'; +import type { ChangePointDetectionAppStateProps } from './components/change_point_detection'; const ExplainLogRateSpikesAppStateLazy = React.lazy( () => import('./components/explain_log_rate_spikes') @@ -45,10 +46,10 @@ export const LogCategorization: FC = (props) => const ChangePointDetectionLazy = React.lazy(() => import('./components/change_point_detection')); /** - * Lazy-wrapped LogCategorizationAppStateProps React component - * @param {LogCategorizationAppStateProps} props - properties specifying the data on which to run the analysis. + * Lazy-wrapped ChangePointDetectionAppStateProps React component + * @param {ChangePointDetectionAppStateProps} props - properties specifying the data on which to run the analysis. */ -export const ChangePointDetection: FC = (props) => ( +export const ChangePointDetection: FC = (props) => ( diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index 9398c0c189e04..b9a6ff5408eda 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -49,6 +49,7 @@ "@kbn/ml-query-utils", "@kbn/ml-is-defined", "@kbn/ml-route-utils", + "@kbn/unified-field-list-plugin", "@kbn/ml-random-sampler-utils", ], "exclude": [ diff --git a/x-pack/plugins/alerting/server/rules_client/lib/index.ts b/x-pack/plugins/alerting/server/rules_client/lib/index.ts index 9f48184b94df6..0dcaa31fe51a1 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/index.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/index.ts @@ -15,5 +15,7 @@ export { checkAuthorizationAndGetTotal } from './check_authorization_and_get_tot export { scheduleTask } from './schedule_task'; export { createNewAPIKeySet } from './create_new_api_key_set'; export { recoverRuleAlerts } from './recover_rule_alerts'; +export { migrateLegacyActions } from './siem_legacy_actions/migrate_legacy_actions'; +export { formatLegacyActions } from './siem_legacy_actions/format_legacy_actions'; export { addGeneratedActionValues } from './add_generated_action_values'; export { incrementRevision } from './increment_revision'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_get_bulk_rule_actions_saved_object.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts similarity index 66% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_get_bulk_rule_actions_saved_object.test.ts rename to x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts index 6caca1b085be7..072a1e98cc3de 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_get_bulk_rule_actions_saved_object.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts @@ -5,23 +5,23 @@ * 2.0. */ -import type { SavedObjectsFindOptions, SavedObjectsFindResult } from '@kbn/core/server'; +import type { SavedObjectsFindResult, SavedObjectAttribute } from '@kbn/core/server'; import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; -// eslint-disable-next-line no-restricted-imports -import { legacyGetBulkRuleActionsSavedObject } from './legacy_get_bulk_rule_actions_saved_object'; -// eslint-disable-next-line no-restricted-imports -import type { LegacyRulesActionsSavedObject } from './legacy_get_rule_actions_saved_object'; -// eslint-disable-next-line no-restricted-imports -import { legacyRuleActionsSavedObjectType } from './legacy_saved_object_mappings'; -// eslint-disable-next-line no-restricted-imports -import type { LegacyIRuleActionsAttributesSavedObjectAttributes } from './legacy_types'; +import { Rule } from '../../../types'; -describe('legacy_get_bulk_rule_actions_saved_object', () => { +import { + legacyGetBulkRuleActionsSavedObject, + LegacyActionsObj, + formatLegacyActions, +} from './format_legacy_actions'; +import { legacyRuleActionsSavedObjectType } from './types'; + +describe('legacyGetBulkRuleActionsSavedObject', () => { let savedObjectsClient: ReturnType; let logger: ReturnType; - type FuncReturn = Record; + type FuncReturn = Record; beforeEach(() => { logger = loggingSystemMock.createLogger(); @@ -34,10 +34,9 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); }); - test('calls "savedObjectsClient.find" with the expected "hasReferences"', () => { - legacyGetBulkRuleActionsSavedObject({ alertIds: ['123'], savedObjectsClient, logger }); - const [[arg1]] = savedObjectsClient.find.mock.calls; - expect(arg1).toEqual({ + test('calls "savedObjectsClient.find" with the expected "hasReferences"', async () => { + await legacyGetBulkRuleActionsSavedObject({ alertIds: ['123'], savedObjectsClient, logger }); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ hasReference: [{ id: '123', type: 'alert' }], perPage: 10000, type: legacyRuleActionsSavedObjectType, @@ -45,9 +44,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns nothing transformed through the find if it does not return any matches against the alert id', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = []; + const savedObjects: Array> = []; savedObjectsClient.find.mockResolvedValue({ total: 0, per_page: 0, @@ -64,9 +61,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns 1 action transformed through the find if 1 was found for 1 single alert id', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = [ + const savedObjects: Array> = [ { score: 0, id: '123', @@ -111,12 +106,15 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); expect(returnValue).toEqual({ 'alert-123': { - id: '123', - alertThrottle: '1d', ruleThrottle: '1d', - actions: [ + legacyRuleActions: [ { - action_type_id: 'action_type_1', + actionTypeId: 'action_type_1', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_1', id: 'action-123', params: {}, @@ -127,9 +125,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns 1 action transformed through the find for 2 alerts with 1 action each', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = [ + const savedObjects: Array> = [ { score: 0, id: '123', @@ -203,12 +199,16 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); expect(returnValue).toEqual({ 'alert-123': { - id: '123', - alertThrottle: '1d', ruleThrottle: '1d', - actions: [ + + legacyRuleActions: [ { - action_type_id: 'action_type_1', + actionTypeId: 'action_type_1', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_1', id: 'action-123', params: {}, @@ -216,12 +216,15 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { ], }, 'alert-456': { - id: '456', - alertThrottle: '1d', ruleThrottle: '1d', - actions: [ + legacyRuleActions: [ { - action_type_id: 'action_type_2', + actionTypeId: 'action_type_2', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_2', id: 'action-456', params: {}, @@ -232,9 +235,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns 2 actions transformed through the find if they were found for 1 single alert id', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = [ + const savedObjects: Array> = [ { score: 0, id: '123', @@ -290,18 +291,26 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); expect(returnValue).toEqual({ 'alert-123': { - id: '123', - alertThrottle: '1d', ruleThrottle: '1d', - actions: [ + legacyRuleActions: [ { - action_type_id: 'action_type_1', + actionTypeId: 'action_type_1', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_1', id: 'action-123', params: {}, }, { - action_type_id: 'action_type_2', + actionTypeId: 'action_type_2', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_2', id: 'action-456', params: {}, @@ -312,9 +321,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns only 1 action if for some unusual reason the actions reference is missing an item for 1 single alert id', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = [ + const savedObjects: Array> = [ { score: 0, id: '123', @@ -366,12 +373,15 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); expect(returnValue).toEqual({ 'alert-123': { - id: '123', - alertThrottle: '1d', ruleThrottle: '1d', - actions: [ + legacyRuleActions: [ { - action_type_id: 'action_type_1', + actionTypeId: 'action_type_1', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_1', id: 'action-123', params: {}, @@ -382,9 +392,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns only 1 action if for some unusual reason the action is missing from the attributes', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = [ + const savedObjects: Array> = [ { score: 0, id: '123', @@ -435,12 +443,15 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); expect(returnValue).toEqual({ 'alert-123': { - id: '123', - alertThrottle: '1d', ruleThrottle: '1d', - actions: [ + legacyRuleActions: [ { - action_type_id: 'action_type_1', + actionTypeId: 'action_type_1', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, group: 'group_1', id: 'action-123', params: {}, @@ -451,9 +462,7 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { }); test('returns nothing if the alert id is missing within the references array', async () => { - const savedObjects: Array< - SavedObjectsFindResult - > = [ + const savedObjects: Array> = [ { score: 0, id: '123', @@ -495,3 +504,112 @@ describe('legacy_get_bulk_rule_actions_saved_object', () => { expect(returnValue).toEqual({}); }); }); + +describe('formatLegacyActions', () => { + let savedObjectsClient: ReturnType; + let logger: ReturnType; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + savedObjectsClient = savedObjectsClientMock.create(); + }); + + it('should return not modified rule when error is thrown within method', async () => { + savedObjectsClient.find.mockRejectedValueOnce(new Error('test failure')); + const mockRules = [{ id: 'mock-id0' }, { id: 'mock-id1' }] as Rule[]; + expect( + await formatLegacyActions(mockRules, { + logger, + savedObjectsClient, + }) + ).toEqual(mockRules); + + expect(logger.error).toHaveBeenCalledWith( + `formatLegacyActions(): Failed to read legacy actions for SIEM rules mock-id0, mock-id1: test failure` + ); + }); + + it('should format rule correctly', async () => { + const savedObjects: Array> = [ + { + score: 0, + id: '123', + type: legacyRuleActionsSavedObjectType, + references: [ + { + name: 'alert_0', + id: 'alert-123', + type: 'alert', + }, + { + name: 'action_0', + id: 'action-123', + type: 'action', + }, + ], + attributes: { + actions: [ + { + group: 'group_1', + params: {}, + action_type_id: 'action_type_1', + actionRef: 'action_0', + }, + ], + ruleThrottle: '1d', + alertThrottle: '1d', + }, + }, + ]; + savedObjectsClient.find.mockResolvedValue({ + total: 0, + per_page: 0, + page: 1, + saved_objects: savedObjects, + }); + + const mockRules = [ + { + id: 'alert-123', + actions: [ + { + actionTypeId: 'action_type_2', + group: 'group_1', + id: 'action-456', + params: {}, + }, + ], + }, + ] as Rule[]; + const migratedRules = await formatLegacyActions(mockRules, { + logger, + savedObjectsClient, + }); + + expect(migratedRules).toEqual([ + { + // actions have been merged + actions: [ + { + actionTypeId: 'action_type_2', + group: 'group_1', + id: 'action-456', + params: {}, + }, + { + actionTypeId: 'action_type_1', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1d' }, + group: 'group_1', + id: 'action-123', + params: {}, + }, + ], + id: 'alert-123', + // muteAll set to false + muteAll: false, + notifyWhen: 'onThrottleInterval', + throttle: '1d', + }, + ]); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts new file mode 100644 index 0000000000000..8689113ca7907 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { chunk } from 'lodash'; +import type { SavedObjectsFindOptionsReference, Logger } from '@kbn/core/server'; +import pMap from 'p-map'; +import { RuleAction, Rule } from '../../../types'; +import type { RuleExecutorServices } from '../../..'; +import { injectReferencesIntoActions } from '../../common'; +import { transformToNotifyWhen } from './transform_to_notify_when'; +import { transformFromLegacyActions } from './transform_legacy_actions'; +import { LegacyIRuleActionsAttributes, legacyRuleActionsSavedObjectType } from './types'; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +interface LegacyGetBulkRuleActionsSavedObject { + alertIds: string[]; + savedObjectsClient: RuleExecutorServices['savedObjectsClient']; + logger: Logger; +} + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export interface LegacyActionsObj { + ruleThrottle: string | null; + legacyRuleActions: RuleAction[]; +} + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + * this function finds all legacy actions associated with rules in bulk + * it's useful for such methods as find, so we do not request legacy actions in a separate request per rule + * @params params.alertIds - list of rule ids to look for legacy actions for + * @params params.savedObjectsClient - savedObjectsClient + * @params params.logger - logger + * @returns map of legacy actions objects per rule with legacy actions + */ +export const legacyGetBulkRuleActionsSavedObject = async ({ + alertIds, + savedObjectsClient, + logger, +}: LegacyGetBulkRuleActionsSavedObject): Promise> => { + const references = alertIds.map((alertId) => ({ + id: alertId, + type: 'alert', + })); + const errors: unknown[] = []; + const results = await pMap( + chunk(references, 1000), + async (referencesChunk) => { + try { + return savedObjectsClient.find({ + // here we looking legacyRuleActionsSavedObjectType, as not all of rules create `siem.notifications` + // more information on that can be found in https://github.com/elastic/kibana/pull/130511 PR + type: legacyRuleActionsSavedObjectType, + perPage: 10000, + hasReference: referencesChunk, + }); + } catch (error) { + logger.error( + `Error fetching rule actions: ${error instanceof Error ? error.message : String(error)}` + ); + errors.push(error); + return []; + } + }, + { concurrency: 1 } + ); + const actionSavedObjects = results.flat().flatMap((r) => r.saved_objects); + + if (errors.length) { + throw new AggregateError(errors, 'Error fetching rule actions'); + } + + return actionSavedObjects.reduce((acc: { [key: string]: LegacyActionsObj }, savedObject) => { + const ruleAlertId = savedObject.references.find((reference) => { + // Find the first rule alert and assume that is the one we want since we should only ever have 1. + return reference.type === 'alert'; + }); + // We check to ensure we have found a "ruleAlertId" and hopefully we have. + const ruleAlertIdKey = ruleAlertId != null ? ruleAlertId.id : undefined; + if (ruleAlertIdKey != null) { + const legacyRawActions = transformFromLegacyActions( + savedObject.attributes, + savedObject.references + ); + acc[ruleAlertIdKey] = { + ruleThrottle: savedObject.attributes.ruleThrottle, + legacyRuleActions: injectReferencesIntoActions( + ruleAlertIdKey, + legacyRawActions, + savedObject.references + ) // remove uuid from action, as this uuid is not persistent + .map(({ uuid, ...action }) => action), + }; + } else { + logger.error( + `Security Solution notification (Legacy) Was expecting to find a reference of type "alert" within ${savedObject.references} but did not. Skipping this notification.` + ); + } + return acc; + }, {}); +}; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + * formats rules with associated SIEM legacy actions, if any legacy actions present + * @param rules - list of rules to format + * @param params - logger, savedObjectsClient + * @returns + */ +export const formatLegacyActions = async ( + rules: T[], + { logger, savedObjectsClient }: Omit +): Promise => { + try { + const res = await legacyGetBulkRuleActionsSavedObject({ + alertIds: rules.map((rule) => rule.id), + savedObjectsClient, + logger, + }); + + return rules.map((rule) => { + const legacyRuleActionsMatch = res[rule.id]; + if (!legacyRuleActionsMatch) { + return rule; + } + + const { legacyRuleActions, ruleThrottle } = legacyRuleActionsMatch; + return { + ...rule, + actions: [...rule.actions, ...legacyRuleActions], + throttle: (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions', + notifyWhen: transformToNotifyWhen(ruleThrottle), + // muteAll property is disregarded in further rule processing in Security Solution when legacy actions are present. + // So it should be safe to set it as false, so it won't be displayed to user as w/o actions see transformFromAlertThrottle method + muteAll: legacyRuleActions.length ? false : rule.muteAll, + }; + }); + } catch (e) { + const ruleIds = rules.map((rule) => rule.id).join(', '); + logger.error( + `formatLegacyActions(): Failed to read legacy actions for SIEM rules ${ruleIds}: ${e.message}` + ); + return rules; + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts new file mode 100644 index 0000000000000..bf67bb5a97191 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts @@ -0,0 +1,357 @@ +/* + * 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 { AlertConsumers } from '@kbn/rule-data-utils'; + +import type { SavedObjectReference } from '@kbn/core/server'; + +import { migrateLegacyActions } from './migrate_legacy_actions'; +import { retrieveMigratedLegacyActions } from './retrieve_migrated_legacy_actions'; +import { injectReferencesIntoActions } from '../../common'; + +import { validateActions } from '../validate_actions'; +import { RulesClientContext } from '../..'; +import { RawRuleAction, RawRule } from '../../../types'; + +import { UntypedNormalizedRuleType } from '../../../rule_type_registry'; +import { RecoveredActionGroup } from '../../../../common'; + +jest.mock('./retrieve_migrated_legacy_actions', () => ({ + retrieveMigratedLegacyActions: jest.fn(), +})); + +jest.mock('../validate_actions', () => ({ + validateActions: jest.fn(), +})); + +jest.mock('../../common', () => ({ + injectReferencesIntoActions: jest.fn(), +})); + +const ruleType: jest.Mocked = { + id: 'test', + name: 'My test rule', + actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + executor: jest.fn(), + producer: 'alerts', + cancelAlertsOnRuleTimeout: true, + ruleTaskTimeout: '5m', + getSummarizedAlerts: jest.fn(), +}; + +const context = { + ruleTypeRegistry: { + get: () => ruleType, + }, + logger: { + error: jest.fn(), + }, +} as unknown as RulesClientContext; + +const ruleId = 'rule_id_1'; + +const attributes = { + alertTypeId: 'siem.query', + consumer: AlertConsumers.SIEM, +} as unknown as RawRule; + +(retrieveMigratedLegacyActions as jest.Mock).mockResolvedValue({ + legacyActions: [], + legacyActionsReferences: [], +}); + +const legacyActionsMock: RawRuleAction[] = [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + uuid: '11403909-ca9b-49ba-9d7a-7e5320e68d05', + actionRef: 'action_0', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, + }, +]; + +const legacyReferencesMock: SavedObjectReference[] = [ + { + id: 'cc85da20-d480-11ed-8e69-1df522116c28', + name: 'action_0', + type: 'action', + }, +]; + +const existingActionsMock: RawRuleAction[] = [ + { + group: 'default', + params: { + body: { + test_web_hook: 'alert.id - {{alert.id}}', + }, + }, + actionTypeId: '.webhook', + uuid: '6e253775-693c-4dcb-a4f5-ad37d9524ecf', + actionRef: 'action_0', + }, +]; + +const referencesMock: SavedObjectReference[] = [ + { + id: 'b2fd3f90-cd81-11ed-9f6d-a746729ca213', + name: 'action_0', + type: 'action', + }, +]; + +describe('migrateLegacyActions', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return empty migratedActions when error is thrown within method', async () => { + (retrieveMigratedLegacyActions as jest.Mock).mockRejectedValueOnce(new Error('test failure')); + const migratedActions = await migrateLegacyActions(context, { + ruleId, + attributes, + }); + + expect(migratedActions).toEqual({ + resultedActions: [], + hasLegacyActions: false, + resultedReferences: [], + }); + expect(context.logger.error).toHaveBeenCalledWith( + `migrateLegacyActions(): Failed to migrate legacy actions for SIEM rule ${ruleId}: test failure` + ); + }); + + it('should return earley empty migratedActions when consumer is not SIEM', async () => { + (retrieveMigratedLegacyActions as jest.Mock).mockResolvedValue({ + legacyActions: [], + legacyActionsReferences: [], + }); + const migratedActions = await migrateLegacyActions(context, { + ruleId, + attributes: { ...attributes, consumer: 'mine' }, + }); + + expect(migratedActions).toEqual({ + resultedActions: [], + hasLegacyActions: false, + resultedReferences: [], + }); + expect(retrieveMigratedLegacyActions).not.toHaveBeenCalled(); + expect(validateActions).not.toHaveBeenCalled(); + expect(injectReferencesIntoActions).not.toHaveBeenCalled(); + }); + + it('should call retrieveMigratedLegacyActions with correct rule id', async () => { + (retrieveMigratedLegacyActions as jest.Mock).mockResolvedValue({ + legacyActions: [], + legacyActionsReferences: [], + }); + await migrateLegacyActions(context, { ruleId, attributes }); + + expect(retrieveMigratedLegacyActions).toHaveBeenCalledWith(context, { ruleId }); + }); + + it('should not call validateActions and injectReferencesIntoActions if skipActionsValidation=true', async () => { + await migrateLegacyActions(context, { ruleId, attributes, skipActionsValidation: true }); + + expect(validateActions).not.toHaveBeenCalled(); + expect(injectReferencesIntoActions).not.toHaveBeenCalled(); + }); + + it('should call validateActions and injectReferencesIntoActions if attributes provided', async () => { + (retrieveMigratedLegacyActions as jest.Mock).mockResolvedValueOnce({ + legacyActions: legacyActionsMock, + legacyActionsReferences: legacyReferencesMock, + }); + + (injectReferencesIntoActions as jest.Mock).mockReturnValue('actions-with-references'); + await migrateLegacyActions(context, { ruleId, attributes }); + + expect(validateActions).toHaveBeenCalledWith(context, ruleType, { + ...attributes, + actions: 'actions-with-references', + }); + + expect(injectReferencesIntoActions).toHaveBeenCalledWith( + 'rule_id_1', + [ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: '11403909-ca9b-49ba-9d7a-7e5320e68d05', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1d', + }, + }, + ], + [{ id: 'cc85da20-d480-11ed-8e69-1df522116c28', name: 'action_0', type: 'action' }] + ); + }); + + it('should set frequency props from rule level to existing actions', async () => { + const result = await migrateLegacyActions(context, { + ruleId, + actions: existingActionsMock, + references: referencesMock, + attributes: { ...attributes, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }); + + expect(result).toHaveProperty('hasLegacyActions', false); + expect(result).toHaveProperty('resultedReferences', referencesMock); + expect(result).toHaveProperty('resultedActions', [ + { + actionRef: 'action_0', + actionTypeId: '.webhook', + group: 'default', + params: { body: { test_web_hook: 'alert.id - {{alert.id}}' } }, + uuid: '6e253775-693c-4dcb-a4f5-ad37d9524ecf', + frequency: { + notifyWhen: 'onThrottleInterval', + summary: true, + throttle: '1h', + }, + }, + ]); + }); + + it('should return correct response when legacy actions empty and existing empty', async () => { + const result = await migrateLegacyActions(context, { + ruleId, + actions: existingActionsMock, + references: referencesMock, + attributes, + }); + + expect(result).toHaveProperty('hasLegacyActions', false); + expect(result).toHaveProperty('resultedReferences', referencesMock); + expect(result).toHaveProperty('resultedActions', [ + { + actionRef: 'action_0', + actionTypeId: '.webhook', + group: 'default', + params: { body: { test_web_hook: 'alert.id - {{alert.id}}' } }, + uuid: '6e253775-693c-4dcb-a4f5-ad37d9524ecf', + frequency: { + notifyWhen: 'onActiveAlert', + summary: true, + throttle: null, + }, + }, + ]); + }); + + it('should return correct response when legacy actions empty and existing actions empty', async () => { + const result = await migrateLegacyActions(context, { + ruleId, + attributes, + }); + + expect(result).toHaveProperty('hasLegacyActions', false); + expect(result).toHaveProperty('resultedReferences', []); + expect(result).toHaveProperty('resultedActions', []); + }); + it('should return correct response when existing actions empty and legacy present', async () => { + (retrieveMigratedLegacyActions as jest.Mock).mockResolvedValueOnce({ + legacyActions: legacyActionsMock, + legacyActionsReferences: legacyReferencesMock, + }); + + const result = await migrateLegacyActions(context, { + ruleId, + attributes, + }); + + expect(result).toHaveProperty('hasLegacyActions', true); + expect(result).toHaveProperty('resultedReferences', legacyReferencesMock); + expect(result).toHaveProperty('resultedActions', legacyActionsMock); + }); + it('should merge actions and references correctly when existing and legacy actions both present', async () => { + (retrieveMigratedLegacyActions as jest.Mock).mockResolvedValueOnce({ + legacyActions: legacyActionsMock, + legacyActionsReferences: legacyReferencesMock, + }); + + const result = await migrateLegacyActions(context, { + ruleId, + actions: existingActionsMock, + references: referencesMock, + attributes, + }); + + expect(result.resultedReferences[0].name).toBe('action_0'); + expect(result.resultedReferences[1].name).toBe('action_1'); + + expect(result).toHaveProperty('hasLegacyActions', true); + + // ensure references are correct + expect(result.resultedReferences[0].name).toBe('action_0'); + expect(result.resultedReferences[1].name).toBe('action_1'); + expect(result).toHaveProperty('resultedReferences', [ + { + id: 'b2fd3f90-cd81-11ed-9f6d-a746729ca213', + name: 'action_0', + type: 'action', + }, + { + id: 'cc85da20-d480-11ed-8e69-1df522116c28', + name: 'action_1', + type: 'action', + }, + ]); + + // ensure actionsRefs are correct + expect(result.resultedActions[0].actionRef).toBe('action_0'); + expect(result.resultedActions[1].actionRef).toBe('action_1'); + expect(result).toHaveProperty('resultedActions', [ + { + actionRef: 'action_0', + actionTypeId: '.webhook', + group: 'default', + params: { body: { test_web_hook: 'alert.id - {{alert.id}}' } }, + uuid: '6e253775-693c-4dcb-a4f5-ad37d9524ecf', + frequency: { + notifyWhen: 'onActiveAlert', + summary: true, + throttle: null, + }, + }, + { + actionRef: 'action_1', + actionTypeId: '.email', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1d' }, + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: '11403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ]); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts new file mode 100644 index 0000000000000..c092e3fed982b --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.ts @@ -0,0 +1,113 @@ +/* + * 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 { AlertConsumers } from '@kbn/rule-data-utils'; + +import type { SavedObjectReference } from '@kbn/core/server'; +import type { RulesClientContext } from '../..'; +import { RawRuleAction, RawRule } from '../../../types'; +import { validateActions } from '../validate_actions'; +import { injectReferencesIntoActions } from '../../common'; +import { retrieveMigratedLegacyActions } from './retrieve_migrated_legacy_actions'; + +type MigrateLegacyActions = ( + context: RulesClientContext, + params: { + ruleId: string; + references?: SavedObjectReference[]; + actions?: RawRuleAction[]; + attributes: RawRule; + skipActionsValidation?: boolean; + } +) => Promise<{ + resultedActions: RawRuleAction[]; + resultedReferences: SavedObjectReference[]; + hasLegacyActions: boolean; +}>; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + * migrates SIEM legacy actions and merges rule actions and references + * @param context RulesClient context + * @param params + * @returns + */ +export const migrateLegacyActions: MigrateLegacyActions = async ( + context: RulesClientContext, + { ruleId, actions = [], references = [], attributes, skipActionsValidation } +) => { + try { + if (attributes.consumer !== AlertConsumers.SIEM) { + return { + resultedActions: [], + hasLegacyActions: false, + resultedReferences: [], + }; + } + + const { legacyActions, legacyActionsReferences } = await retrieveMigratedLegacyActions( + context, + { + ruleId, + } + ); + + // sometimes we don't need to validate legacy actions. For example, when delete rules or update rule from payload + if (skipActionsValidation !== true) { + const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId); + await validateActions(context, ruleType, { + ...attributes, + // set to undefined to avoid both per-actin and rule level values clashing + throttle: undefined, + notifyWhen: undefined, + actions: injectReferencesIntoActions(ruleId, legacyActions, legacyActionsReferences), + }); + } + + // fix references for a case when actions present in a rule + if (actions.length) { + legacyActions.forEach((legacyAction, i) => { + const oldReference = legacyAction.actionRef; + const legacyReference = legacyActionsReferences.find(({ name }) => name === oldReference); + const newReference = `action_${actions.length + i}`; + legacyAction.actionRef = newReference; + + if (legacyReference) { + legacyReference.name = newReference; + } + }); + } + + // put frequencies into existing actions + // the situation when both action in rule nad legacy exist should be rare one + // but if it happens, we put frequency in existing actions per-action level + const existingActionsWithFrequencies: RawRuleAction[] = actions.map((action) => ({ + ...action, + frequency: { + summary: true, + notifyWhen: attributes.notifyWhen ?? 'onActiveAlert', + throttle: attributes.throttle ?? null, + }, + })); + + return { + resultedActions: [...existingActionsWithFrequencies, ...legacyActions], + hasLegacyActions: legacyActions.length > 0, + resultedReferences: [...references, ...legacyActionsReferences], + }; + } catch (e) { + context.logger.error( + `migrateLegacyActions(): Failed to migrate legacy actions for SIEM rule ${ruleId}: ${e.message}` + ); + + return { + resultedActions: [], + hasLegacyActions: false, + resultedReferences: [], + }; + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts new file mode 100644 index 0000000000000..d45c86c3f05a5 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts @@ -0,0 +1,368 @@ +/* + * 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 { + SavedObjectsFindResponse, + SavedObjectsFindResult, + SavedObjectAttribute, +} from '@kbn/core/server'; + +import type { LegacyRuleNotificationAlertType } from './types'; + +export const migrateLegacyActionsMock = { + legacyActions: [], + legacyActionsReferences: [], +}; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetHourlyNotificationResult = ( + id = '456', + ruleId = '123' +): LegacyRuleNotificationAlertType => ({ + id, + name: 'Notification for Rule Test', + tags: [], + alertTypeId: 'siem.notifications', + consumer: 'siem', + params: { + ruleAlertId: `${ruleId}`, + }, + schedule: { + interval: '1h', + }, + enabled: true, + actions: [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + apiKey: null, + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: new Date('2020-03-21T11:15:13.530Z'), + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', + updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + revision: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetWeeklyNotificationResult = ( + id = '456', + ruleId = '123' +): LegacyRuleNotificationAlertType => ({ + id, + name: 'Notification for Rule Test', + tags: [], + alertTypeId: 'siem.notifications', + consumer: 'siem', + params: { + ruleAlertId: `${ruleId}`, + }, + schedule: { + interval: '7d', + }, + enabled: true, + actions: [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + apiKey: null, + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: new Date('2020-03-21T11:15:13.530Z'), + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', + updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + revision: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetDailyNotificationResult = ( + id = '456', + ruleId = '123' +): LegacyRuleNotificationAlertType => ({ + id, + name: 'Notification for Rule Test', + tags: [], + alertTypeId: 'siem.notifications', + consumer: 'siem', + params: { + ruleAlertId: `${ruleId}`, + }, + schedule: { + interval: '1d', + }, + enabled: true, + actions: [ + { + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + actionTypeId: '.email', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ], + throttle: null, + notifyWhen: 'onActiveAlert', + apiKey: null, + apiKeyOwner: 'elastic', + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: new Date('2020-03-21T11:15:13.530Z'), + muteAll: false, + mutedInstanceIds: [], + scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', + updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + revision: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleNoActionsSOResult = ( + ruleId = '123' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_NO_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [], + ruleThrottle: 'no_actions', + alertThrottle: null, + }, + references: [{ id: ruleId, type: 'alert', name: 'alert_0' }], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleEveryRunSOResult = ( + ruleId = '123' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_RULE_RUN_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: 'rule', + alertThrottle: null, + }, + references: [{ id: ruleId, type: 'alert', name: 'alert_0' }], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleHourlyActionsSOResult = ( + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_HOURLY_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: '1h', + alertThrottle: '1h', + }, + references: [ + { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: connectorId, type: 'action', name: 'action_0' }, + ], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleDailyActionsSOResult = ( + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_DAILY_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: '1d', + alertThrottle: '1d', + }, + references: [ + { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: connectorId, type: 'action', name: 'action_0' }, + ], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleWeeklyActionsSOResult = ( + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResult => ({ + type: 'siem-detection-engine-rule-actions', + id: 'ID_OF_LEGACY_SIDECAR_WEEKLY_ACTIONS', + namespaces: ['default'], + attributes: { + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + to: ['test@test.com'], + subject: 'Test Actions', + }, + action_type_id: '.email', + }, + ], + ruleThrottle: '7d', + alertThrottle: '7d', + }, + references: [ + { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: connectorId, type: 'action', name: 'action_0' }, + ], + migrationVersion: { + 'siem-detection-engine-rule-actions': '7.11.2', + }, + coreMigrationVersion: '7.15.2', + updated_at: '2022-03-31T19:06:40.473Z', + version: 'WzIzNywxXQ==', + score: 0, +}); + +const getLegacyActionSOs = (ruleId = '123', connectorId = '456') => ({ + none: () => legacyGetSiemNotificationRuleNoActionsSOResult(ruleId), + rule: () => legacyGetSiemNotificationRuleEveryRunSOResult(ruleId), + hourly: () => legacyGetSiemNotificationRuleHourlyActionsSOResult(ruleId, connectorId), + daily: () => legacyGetSiemNotificationRuleDailyActionsSOResult(ruleId, connectorId), + weekly: () => legacyGetSiemNotificationRuleWeeklyActionsSOResult(ruleId, connectorId), +}); + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyGetSiemNotificationRuleActionsSOResultWithSingleHit = ( + actionTypes: Array<'none' | 'rule' | 'daily' | 'hourly' | 'weekly'>, + ruleId = '123', + connectorId = '456' +): SavedObjectsFindResponse => { + const actions = getLegacyActionSOs(ruleId, connectorId); + + return { + page: 1, + per_page: 1, + total: 1, + saved_objects: actionTypes.map((type) => actions[type]()), + }; +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts new file mode 100644 index 0000000000000..b49497d086efe --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.test.ts @@ -0,0 +1,267 @@ +/* + * 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 { RulesClientContext } from '../..'; +import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; + +import { + legacyGetDailyNotificationResult, + legacyGetHourlyNotificationResult, + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit, + legacyGetWeeklyNotificationResult, +} from './retrieve_migrated_legacy_actions.mock'; + +import { retrieveMigratedLegacyActions } from './retrieve_migrated_legacy_actions'; + +import { find } from '../../methods/find'; +import { deleteRule } from '../../methods/delete'; + +jest.mock('../../methods/find', () => { + return { + find: jest.fn(), + }; +}); + +jest.mock('../../methods/delete', () => { + return { + deleteRule: jest.fn(), + }; +}); + +const findMock = find as jest.Mock; +const deleteRuleMock = deleteRule as jest.Mock; + +const getEmptyFindResult = () => ({ + page: 0, + per_page: 0, + total: 0, + data: [], +}); + +describe('Legacy rule action migration logic', () => { + describe('legacyMigrate', () => { + let savedObjectsClient: ReturnType; + let logger: ReturnType; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + savedObjectsClient = savedObjectsClientMock.create(); + }); + + const ruleId = '123'; + const connectorId = '456'; + + test('it does no cleanup or migration if no legacy remnants found', async () => { + findMock.mockResolvedValueOnce(getEmptyFindResult()); + savedObjectsClient.find.mockResolvedValueOnce({ + total: 0, + per_page: 0, + page: 1, + saved_objects: [], + }); + + const migratedProperties = await retrieveMigratedLegacyActions( + { + unsecuredSavedObjectsClient: savedObjectsClient, + logger, + } as unknown as RulesClientContext, + { ruleId } + ); + + expect(deleteRuleMock).not.toHaveBeenCalled(); + expect(savedObjectsClient.delete).not.toHaveBeenCalled(); + expect(migratedProperties).toEqual({ legacyActions: [], legacyActionsReferences: [] }); + }); + + // Even if a rule is created with no actions pre 7.16, a + // siem-detection-engine-rule-actions SO is still created + test('it migrates a rule with no actions', async () => { + // siem.notifications is not created for a rule with no actions + findMock.mockResolvedValueOnce(getEmptyFindResult()); + // siem-detection-engine-rule-actions SO is still created + savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['none'], ruleId, connectorId) + ); + + const migratedProperties = await retrieveMigratedLegacyActions( + { + unsecuredSavedObjectsClient: savedObjectsClient, + logger, + } as unknown as RulesClientContext, + { ruleId } + ); + + expect(deleteRuleMock).not.toHaveBeenCalled(); + expect(savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_NO_ACTIONS' + ); + expect(migratedProperties).toEqual({ legacyActions: [], legacyActionsReferences: [] }); + }); + + test('it migrates a rule with every rule run action', async () => { + // siem.notifications is not created for a rule with actions run every rule run + findMock.mockResolvedValueOnce(getEmptyFindResult()); + // siem-detection-engine-rule-actions SO is still created + savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['rule'], ruleId, connectorId) + ); + + const migratedProperties = await retrieveMigratedLegacyActions( + { + unsecuredSavedObjectsClient: savedObjectsClient, + logger, + } as unknown as RulesClientContext, + { ruleId } + ); + + expect(deleteRuleMock).not.toHaveBeenCalled(); + expect(savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_RULE_RUN_ACTIONS' + ); + + expect(migratedProperties).toEqual({ legacyActions: [], legacyActionsReferences: [] }); + }); + + test('it migrates a rule with daily legacy actions', async () => { + // siem.notifications is not created for a rule with no actions + findMock.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetDailyNotificationResult(connectorId, ruleId)], + }); + // siem-detection-engine-rule-actions SO is still created + savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['daily'], ruleId, connectorId) + ); + + const migratedProperties = await retrieveMigratedLegacyActions( + { + unsecuredSavedObjectsClient: savedObjectsClient, + logger, + } as unknown as RulesClientContext, + { ruleId } + ); + + expect(deleteRuleMock).toHaveBeenCalledWith(expect.any(Object), { id: '456' }); + expect(savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_DAILY_ACTIONS' + ); + + expect(migratedProperties).toEqual({ + legacyActions: [ + { + actionRef: 'action_0', + actionTypeId: '.email', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1d' }, + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: expect.any(String), + }, + ], + legacyActionsReferences: [{ id: '456', name: 'action_0', type: 'action' }], + }); + }); + + test('it migrates a rule with hourly legacy actions', async () => { + // siem.notifications is not created for a rule with no actions + findMock.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetHourlyNotificationResult(connectorId, ruleId)], + }); + // siem-detection-engine-rule-actions SO is still created + savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['hourly'], ruleId, connectorId) + ); + + const migratedProperties = await retrieveMigratedLegacyActions( + { + unsecuredSavedObjectsClient: savedObjectsClient, + logger, + } as unknown as RulesClientContext, + { ruleId } + ); + + expect(deleteRuleMock).toHaveBeenCalledWith(expect.any(Object), { id: '456' }); + expect(savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_HOURLY_ACTIONS' + ); + expect(migratedProperties).toEqual({ + legacyActions: [ + { + actionRef: 'action_0', + actionTypeId: '.email', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1h' }, + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: expect.any(String), + }, + ], + legacyActionsReferences: [{ id: '456', name: 'action_0', type: 'action' }], + }); + }); + + test('it migrates a rule with weekly legacy actions', async () => { + // siem.notifications is not created for a rule with no actions + findMock.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 1, + data: [legacyGetWeeklyNotificationResult(connectorId, ruleId)], + }); + // siem-detection-engine-rule-actions SO is still created + savedObjectsClient.find.mockResolvedValueOnce( + legacyGetSiemNotificationRuleActionsSOResultWithSingleHit(['weekly'], ruleId, connectorId) + ); + + const migratedProperties = await retrieveMigratedLegacyActions( + { + unsecuredSavedObjectsClient: savedObjectsClient, + logger, + } as unknown as RulesClientContext, + { ruleId } + ); + + expect(deleteRuleMock).toHaveBeenCalledWith(expect.any(Object), { id: '456' }); + expect(savedObjectsClient.delete).toHaveBeenCalledWith( + 'siem-detection-engine-rule-actions', + 'ID_OF_LEGACY_SIDECAR_WEEKLY_ACTIONS' + ); + expect(migratedProperties).toEqual({ + legacyActions: [ + { + actionRef: 'action_0', + actionTypeId: '.email', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '7d' }, + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: expect.any(String), + }, + ], + legacyActionsReferences: [{ id: '456', name: 'action_0', type: 'action' }], + }); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts new file mode 100644 index 0000000000000..83cd315aaa5a5 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts @@ -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 type { SavedObjectReference } from '@kbn/core/server'; +import type { RulesClientContext } from '../..'; +import { RawRuleAction } from '../../../types'; +import { find } from '../../methods/find'; +import { deleteRule } from '../../methods/delete'; +import { LegacyIRuleActionsAttributes, legacyRuleActionsSavedObjectType } from './types'; +import { transformFromLegacyActions } from './transform_legacy_actions'; + +type RetrieveMigratedLegacyActions = ( + context: RulesClientContext, + { ruleId }: { ruleId: string } +) => Promise<{ legacyActions: RawRuleAction[]; legacyActionsReferences: SavedObjectReference[] }>; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + * retrieves legacy actions for SIEM rule and deletes associated sidecar SO + * @param context RulesClient context + * @param params.ruleId - id of rule to be migrated + * @returns + */ +export const retrieveMigratedLegacyActions: RetrieveMigratedLegacyActions = async ( + context, + { ruleId } +) => { + const { unsecuredSavedObjectsClient } = context; + try { + if (ruleId == null) { + return { legacyActions: [], legacyActionsReferences: [] }; + } + + /** + * On update / patch I'm going to take the actions as they are, better off taking rules client.find (siem.notification) result + * and putting that into the actions array of the rule, then set the rules onThrottle property, notifyWhen and throttle from null -> actual value (1hr etc..) + * Then use the rules client to delete the siem.notification + * Then with the legacy Rule Actions saved object type, just delete it. + */ + // find it using the references array, not params.ruleAlertId + const [siemNotification, legacyRuleActionsSO] = await Promise.all([ + find(context, { + options: { + filter: 'alert.attributes.alertTypeId:(siem.notifications)', + hasReference: { + type: 'alert', + id: ruleId, + }, + }, + }), + unsecuredSavedObjectsClient.find({ + type: legacyRuleActionsSavedObjectType, + hasReference: { + type: 'alert', + id: ruleId, + }, + }), + ]); + + const siemNotificationsExist = siemNotification != null && siemNotification.data.length > 0; + const legacyRuleNotificationSOsExist = + legacyRuleActionsSO != null && legacyRuleActionsSO.saved_objects.length > 0; + + // Assumption: if no legacy sidecar SO or notification rule types exist + // that reference the rule in question, assume rule actions are not legacy + if (!siemNotificationsExist && !legacyRuleNotificationSOsExist) { + return { legacyActions: [], legacyActionsReferences: [] }; + } + + await Promise.all([ + // If the legacy notification rule type ("siem.notification") exist, + // migration and cleanup are needed + siemNotificationsExist && deleteRule(context, { id: siemNotification.data[0].id }), + // Delete the legacy sidecar SO if it exists + legacyRuleNotificationSOsExist && + unsecuredSavedObjectsClient.delete( + legacyRuleActionsSavedObjectType, + legacyRuleActionsSO.saved_objects[0].id + ), + ]); + + // If legacy notification sidecar ("siem-detection-engine-rule-actions") + // exist, migration is needed + if (legacyRuleNotificationSOsExist) { + // If "siem-detection-engine-rule-actions" notes that `ruleThrottle` is + // "no_actions" or "rule", rule has no actions or rule is set to run + // action on every rule run. In these cases, sidecar deletion is the only + // cleanup needed and updates to the "throttle" and "notifyWhen". "siem.notification" are + // not created for these action types + if ( + legacyRuleActionsSO.saved_objects[0].attributes.ruleThrottle === 'no_actions' || + legacyRuleActionsSO.saved_objects[0].attributes.ruleThrottle === 'rule' + ) { + return { legacyActions: [], legacyActionsReferences: [] }; + } + + return { + legacyActions: transformFromLegacyActions( + legacyRuleActionsSO.saved_objects[0].attributes, + legacyRuleActionsSO.saved_objects[0].references + ), + legacyActionsReferences: + // only action references need to be saved + legacyRuleActionsSO.saved_objects[0].references.filter(({ type }) => type === 'action') ?? + [], + }; + } + } catch (e) { + context.logger.debug(`Migration has failed for rule ${ruleId}: ${e.message}`); + } + + return { legacyActions: [], legacyActionsReferences: [] }; +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts new file mode 100644 index 0000000000000..b23798294b300 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts @@ -0,0 +1,89 @@ +/* + * 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 { SavedObjectReference } from '@kbn/core/server'; + +import { transformFromLegacyActions } from './transform_legacy_actions'; +import { transformToNotifyWhen } from './transform_to_notify_when'; +import { LegacyIRuleActionsAttributes } from './types'; + +jest.mock('./transform_to_notify_when', () => ({ + transformToNotifyWhen: jest.fn(), +})); + +const legacyActionsAttr: LegacyIRuleActionsAttributes = { + actions: [ + { + group: 'group_1', + params: {}, + action_type_id: 'action_type_1', + actionRef: 'action_0', + }, + ], + ruleThrottle: '1d', + alertThrottle: '1d', +}; + +const references: SavedObjectReference[] = [ + { + name: 'action_0', + id: 'action-123', + type: 'action', + }, +]; + +describe('transformFromLegacyActions', () => { + it('should throw error if if references are empty', () => { + const executor = () => { + return transformFromLegacyActions(legacyActionsAttr, []); + }; + expect(executor).toThrow('Connector reference id not found.'); + }); + it('should return empty list of actions if legacy actions do not have correct references', () => { + const actions = transformFromLegacyActions(legacyActionsAttr, [ + { + name: 'alert_0', + id: 'alert-1', + type: 'alert', + }, + ]); + + expect(actions).toHaveLength(0); + }); + + it('should return notifyWhen as result of transformToNotifyWhen if it is not null', () => { + (transformToNotifyWhen as jest.Mock).mockReturnValueOnce('onActiveAlert'); + const actions = transformFromLegacyActions(legacyActionsAttr, references); + + expect(transformToNotifyWhen).toHaveBeenCalledWith('1d'); + expect(actions[0].frequency?.notifyWhen).toBe('onActiveAlert'); + }); + + it('should return notifyWhen as onThrottleInterval when transformToNotifyWhen returns null', () => { + (transformToNotifyWhen as jest.Mock).mockReturnValueOnce(null); + const actions = transformFromLegacyActions(legacyActionsAttr, references); + + expect(actions[0].frequency?.notifyWhen).toBe('onThrottleInterval'); + }); + + it('should return transformed legacy actions', () => { + (transformToNotifyWhen as jest.Mock).mockReturnValue('onThrottleInterval'); + + const actions = transformFromLegacyActions(legacyActionsAttr, references); + + expect(actions).toEqual([ + { + actionRef: 'action_0', + actionTypeId: 'action_type_1', + frequency: { notifyWhen: 'onThrottleInterval', summary: true, throttle: '1d' }, + group: 'group_1', + params: {}, + uuid: expect.any(String), + }, + ]); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts new file mode 100644 index 0000000000000..485a32f781695 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts @@ -0,0 +1,59 @@ +/* + * 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 { v4 } from 'uuid'; +import { isEmpty } from 'lodash/fp'; +import type { SavedObjectReference } from '@kbn/core/server'; +import { RawRuleAction } from '../../../types'; +import { transformToNotifyWhen } from './transform_to_notify_when'; +import { LegacyIRuleActionsAttributes } from './types'; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + * transforms siem legacy actions {@link LegacyIRuleActionsAttributes} objects into {@link RawRuleAction} + * @param legacyActionsAttr + * @param references + * @returns array of RawRuleAction + */ +export const transformFromLegacyActions = ( + legacyActionsAttr: LegacyIRuleActionsAttributes, + references: SavedObjectReference[] +): RawRuleAction[] => { + const actionReference = references.reduce>( + (acc, reference) => { + acc[reference.name] = reference; + return acc; + }, + {} + ); + + if (isEmpty(actionReference)) { + throw new Error(`Connector reference id not found.`); + } + + return legacyActionsAttr.actions.reduce((acc, action) => { + const { actionRef, action_type_id: actionTypeId, group, params } = action; + if (!actionReference[actionRef]) { + return acc; + } + return [ + ...acc, + { + group, + params, + uuid: v4(), + actionRef, + actionTypeId, + frequency: { + summary: true, + notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onThrottleInterval', + throttle: legacyActionsAttr.ruleThrottle, + }, + }, + ]; + }, []); +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.test.ts new file mode 100644 index 0000000000000..9d3fae5d3cb2c --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.test.ts @@ -0,0 +1,23 @@ +/* + * 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 { transformToNotifyWhen } from './transform_to_notify_when'; + +describe('transformToNotifyWhen', () => { + it('should return null when throttle is null OR no_actions', () => { + expect(transformToNotifyWhen(null)).toBeNull(); + expect(transformToNotifyWhen('no_actions')).toBeNull(); + }); + it('should return onActiveAlert when throttle is rule', () => { + expect(transformToNotifyWhen('rule')).toBe('onActiveAlert'); + }); + it('should return onThrottleInterval for other throttle values', () => { + expect(transformToNotifyWhen('1h')).toBe('onThrottleInterval'); + expect(transformToNotifyWhen('1m')).toBe('onThrottleInterval'); + expect(transformToNotifyWhen('1d')).toBe('onThrottleInterval'); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.ts new file mode 100644 index 0000000000000..76c43617adc22 --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_notify_when.ts @@ -0,0 +1,25 @@ +/* + * 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 { RuleNotifyWhenType } from '../../../../common'; + +/** + * Given a throttle from a "security_solution" rule this will transform it into an "alerting" notifyWhen + * on their saved object. + * @params throttle The throttle from a "security_solution" rule + * @returns The correct "NotifyWhen" for a Kibana alerting. + */ +export const transformToNotifyWhen = ( + throttle: string | null | undefined +): RuleNotifyWhenType | null => { + if (throttle == null || throttle === 'no_actions') { + return null; // Although I return null, this does not change the value of the "notifyWhen" and it keeps the current value of "notifyWhen" + } else if (throttle === 'rule') { + return 'onActiveAlert'; + } else { + return 'onThrottleInterval'; + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/types.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/types.ts new file mode 100644 index 0000000000000..487f18807d35b --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/types.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 { RuleActionParams } from '../../../types'; +import type { RuleTypeParams } from '../../..'; +import type { Rule } from '../../../../common'; + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export const legacyRuleActionsSavedObjectType = 'siem-detection-engine-rule-actions'; + +/** + * This is how it is stored on disk in its "raw format" for 7.16+ + * @deprecated Remove this once the legacy notification/side car is gone + */ +export interface LegacyRuleAlertSavedObjectAction { + group: string; + params: RuleActionParams; + action_type_id: string; + actionRef: string; +} + +/** + * We keep this around to migrate and update data for the old deprecated rule actions saved object mapping but we + * do not use it anymore within the code base. Once we feel comfortable that users are upgrade far enough and this is no longer + * needed then it will be safe to remove this saved object and all its migrations. + * @deprecated Remove this once the legacy notification/side car is gone + */ +export interface LegacyIRuleActionsAttributes extends Record { + actions: LegacyRuleAlertSavedObjectAction[]; + ruleThrottle: string; + alertThrottle: string | null; +} + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +interface LegacyRuleNotificationAlertTypeParams extends RuleTypeParams { + ruleAlertId: string; +} + +/** + * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function + */ +export type LegacyRuleNotificationAlertType = Rule; diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_delete.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_delete.ts index 0142fbe9e4ebb..03ce78952e9db 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_delete.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_delete.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import pMap from 'p-map'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { SavedObjectsBulkUpdateObject } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; @@ -13,7 +13,13 @@ import { convertRuleIdsToKueryNode } from '../../lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { tryToRemoveTasks } from '../common'; -import { getAuthorizationFilter, checkAuthorizationAndGetTotal, getAlertFromRaw } from '../lib'; +import { API_KEY_GENERATE_CONCURRENCY } from '../common/constants'; +import { + getAuthorizationFilter, + checkAuthorizationAndGetTotal, + getAlertFromRaw, + migrateLegacyActions, +} from '../lib'; import { retryIfBulkOperationConflicts, buildKueryNodeFilter, @@ -166,6 +172,20 @@ const bulkDeleteWithOCC = async ( }); const rules = rulesToDelete.filter((rule) => deletedRuleIds.includes(rule.id)); + // migrate legacy actions only for SIEM rules + await pMap( + rules, + async (rule) => { + await migrateLegacyActions(context, { + ruleId: rule.id, + attributes: rule.attributes as RawRule, + skipActionsValidation: true, + }); + }, + // max concurrency for bulk edit operations, that is limited by api key generations, should be sufficient for bulk migrations + { concurrency: API_KEY_GENERATE_CONCURRENCY } + ); + return { errors, rules, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_disable.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_disable.ts index 06efa425eccef..5ec0ae99ca99c 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_disable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_disable.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { SavedObjectsBulkUpdateObject } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; @@ -25,6 +24,7 @@ import { getAlertFromRaw, recoverRuleAlerts, updateMeta, + migrateLegacyActions, } from '../lib'; import { BulkOptions, BulkOperationError, RulesClientContext } from '../types'; import { tryToRemoveTasks } from '../common'; @@ -119,8 +119,22 @@ const bulkDisableRulesWithOCC = async ( ruleNameToRuleIdMapping[rule.id] = rule.attributes.name; } + const migratedActions = await migrateLegacyActions(context, { + ruleId: rule.id, + actions: rule.attributes.actions, + references: rule.references, + attributes: rule.attributes, + }); + const updatedAttributes = updateMeta(context, { ...rule.attributes, + ...(migratedActions.hasLegacyActions + ? { + actions: migratedActions.resultedActions, + throttle: undefined, + notifyWhen: undefined, + } + : {}), enabled: false, scheduledTaskId: rule.attributes.scheduledTaskId === rule.id @@ -135,6 +149,9 @@ const bulkDisableRulesWithOCC = async ( attributes: { ...updatedAttributes, }, + ...(migratedActions.hasLegacyActions + ? { references: migratedActions.resultedReferences } + : {}), }); context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts index a3d845af2c0af..dfe4ecc952bf6 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts @@ -69,6 +69,8 @@ import { NormalizedAlertActionWithGeneratedValues, } from '../types'; +import { migrateLegacyActions } from '../lib'; + export type BulkEditFields = keyof Pick< Rule, 'actions' | 'tags' | 'schedule' | 'throttle' | 'notifyWhen' | 'snoozeSchedule' | 'apiKey' @@ -449,6 +451,19 @@ async function updateRuleAttributesAndParamsInMemory action?.frequency + )?.frequency; if (rule.attributes.consumer === AlertConsumers.SIEM && firstFrequency) { ruleActions.actions = ruleActions.actions.map((action) => omit(action, 'frequency')); if (!attributes.notifyWhen) { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts index 7273855379a1c..570ad3d5abc56 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import pMap from 'p-map'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { SavedObjectsBulkUpdateObject } from '@kbn/core/server'; @@ -26,6 +25,7 @@ import { scheduleTask, updateMeta, createNewAPIKeySet, + migrateLegacyActions, } from '../lib'; import { RulesClientContext, BulkOperationError, BulkOptions } from '../types'; @@ -143,6 +143,13 @@ const bulkEnableRulesWithOCC = async ( ruleNameToRuleIdMapping[rule.id] = rule.attributes.name; } + const migratedActions = await migrateLegacyActions(context, { + ruleId: rule.id, + actions: rule.attributes.actions, + references: rule.references, + attributes: rule.attributes, + }); + const updatedAttributes = updateMeta(context, { ...rule.attributes, ...(!rule.attributes.apiKey && @@ -152,6 +159,13 @@ const bulkEnableRulesWithOCC = async ( username, shouldUpdateApiKey: true, }))), + ...(migratedActions.hasLegacyActions + ? { + actions: migratedActions.resultedActions, + throttle: undefined, + notifyWhen: undefined, + } + : {}), enabled: true, updatedBy: username, updatedAt: new Date().toISOString(), @@ -187,6 +201,9 @@ const bulkEnableRulesWithOCC = async ( ...updatedAttributes, ...(scheduledTaskId ? { scheduledTaskId } : undefined), }, + ...(migratedActions.hasLegacyActions + ? { references: migratedActions.resultedReferences } + : {}), }); context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/create.ts b/x-pack/plugins/alerting/server/rules_client/methods/create.ts index 4d2e586dcaade..4bf83043f99af 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/create.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/create.ts @@ -113,7 +113,7 @@ export async function create( // TODO https://github.com/elastic/kibana/issues/148414 // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions[0]?.frequency; + const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; if (data.consumer === AlertConsumers.SIEM && firstFrequency) { data.actions = data.actions.map((action) => omit(action, 'frequency')); if (!data.notifyWhen) { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/delete.ts b/x-pack/plugins/alerting/server/rules_client/methods/delete.ts index ca6feefe3ff9b..605753cfcdfc8 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/delete.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/delete.ts @@ -5,12 +5,14 @@ * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RawRule } from '../../types'; import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; import { retryIfConflicts } from '../../lib/retry_if_conflicts'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; +import { migrateLegacyActions } from '../lib'; export async function deleteRule(context: RulesClientContext, { id }: { id: string }) { return await retryIfConflicts( @@ -64,6 +66,11 @@ async function deleteWithOCC(context: RulesClientContext, { id }: { id: string } throw error; } + // migrate legacy actions only for SIEM rules + if (attributes.consumer === AlertConsumers.SIEM) { + await migrateLegacyActions(context, { ruleId: id, attributes, skipActionsValidation: true }); + } + context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.DELETE, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/disable.ts b/x-pack/plugins/alerting/server/rules_client/methods/disable.ts index bbdb1ade167eb..1382b7d14b111 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/disable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/disable.ts @@ -4,13 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { SavedObjectReference } from '@kbn/core/server'; import { RawRule } from '../../types'; import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; import { retryIfConflicts } from '../../lib/retry_if_conflicts'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; -import { recoverRuleAlerts, updateMeta } from '../lib'; +import { recoverRuleAlerts, updateMeta, migrateLegacyActions } from '../lib'; export async function disable(context: RulesClientContext, { id }: { id: string }): Promise { return await retryIfConflicts( @@ -23,6 +24,7 @@ export async function disable(context: RulesClientContext, { id }: { id: string async function disableWithOCC(context: RulesClientContext, { id }: { id: string }) { let attributes: RawRule; let version: string | undefined; + let references: SavedObjectReference[]; try { const decryptedAlert = @@ -31,12 +33,14 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string }); attributes = decryptedAlert.attributes; version = decryptedAlert.version; + references = decryptedAlert.references; } catch (e) { context.logger.error(`disable(): Failed to load API key of alert ${id}: ${e.message}`); // Still attempt to load the attributes and version using SOC const alert = await context.unsecuredSavedObjectsClient.get('alert', id); attributes = alert.attributes; version = alert.version; + references = alert.references; } await recoverRuleAlerts(context, id, attributes); @@ -70,6 +74,13 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string context.ruleTypeRegistry.ensureRuleTypeEnabled(attributes.alertTypeId); if (attributes.enabled === true) { + const migratedActions = await migrateLegacyActions(context, { + ruleId: id, + actions: attributes.actions, + references, + attributes, + }); + await context.unsecuredSavedObjectsClient.update( 'alert', id, @@ -80,8 +91,16 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string updatedBy: await context.getUserName(), updatedAt: new Date().toISOString(), nextRun: null, + ...(migratedActions.hasLegacyActions + ? { actions: migratedActions.resultedActions, throttle: undefined, notifyWhen: undefined } + : {}), }), - { version } + { + version, + ...(migratedActions.hasLegacyActions + ? { references: migratedActions.resultedReferences } + : {}), + } ); // If the scheduledTaskId does not match the rule id, we should diff --git a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts index a70b71b7b5116..0cd42f282bcbc 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { SavedObjectReference } from '@kbn/core/server'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { RawRule, IntervalSchedule } from '../../types'; import { resetMonitoringLastRun, getNextRun } from '../../lib'; @@ -12,7 +12,7 @@ import { WriteOperations, AlertingAuthorizationEntity } from '../../authorizatio import { retryIfConflicts } from '../../lib/retry_if_conflicts'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; -import { updateMeta, createNewAPIKeySet, scheduleTask } from '../lib'; +import { updateMeta, createNewAPIKeySet, scheduleTask, migrateLegacyActions } from '../lib'; export async function enable(context: RulesClientContext, { id }: { id: string }): Promise { return await retryIfConflicts( @@ -26,6 +26,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } let existingApiKey: string | null = null; let attributes: RawRule; let version: string | undefined; + let references: SavedObjectReference[]; try { const decryptedAlert = @@ -35,12 +36,14 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } existingApiKey = decryptedAlert.attributes.apiKey; attributes = decryptedAlert.attributes; version = decryptedAlert.version; + references = decryptedAlert.references; } catch (e) { context.logger.error(`enable(): Failed to load API key of alert ${id}: ${e.message}`); // Still attempt to load the attributes and version using SOC const alert = await context.unsecuredSavedObjectsClient.get('alert', id); attributes = alert.attributes; version = alert.version; + references = alert.references; } try { @@ -76,6 +79,13 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } context.ruleTypeRegistry.ensureRuleTypeEnabled(attributes.alertTypeId); if (attributes.enabled === false) { + const migratedActions = await migrateLegacyActions(context, { + ruleId: id, + actions: attributes.actions, + references, + attributes, + }); + const username = await context.getUserName(); const now = new Date(); @@ -107,7 +117,29 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } }); try { - await context.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { version }); + // to mitigate AAD issues(actions property is not used for encrypting API key in partial SO update) + // we call create with overwrite=true + if (migratedActions.hasLegacyActions) { + await context.unsecuredSavedObjectsClient.create( + 'alert', + { + ...updateAttributes, + actions: migratedActions.resultedActions, + throttle: undefined, + notifyWhen: undefined, + }, + { + id, + overwrite: true, + version, + references: migratedActions.resultedReferences, + } + ); + } else { + await context.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { + version, + }); + } } catch (e) { throw e; } diff --git a/x-pack/plugins/alerting/server/rules_client/methods/find.ts b/x-pack/plugins/alerting/server/rules_client/methods/find.ts index 080c72c624cb3..6493174a61b6a 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/find.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/find.ts @@ -9,7 +9,8 @@ import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { pick } from 'lodash'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; -import { RawRule, RuleTypeParams, SanitizedRule } from '../../types'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { RawRule, RuleTypeParams, SanitizedRule, Rule } from '../../types'; import { AlertingAuthorizationEntity } from '../../authorization'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { @@ -27,6 +28,7 @@ import { import { alertingAuthorizationFilterOpts } from '../common/constants'; import { getAlertFromRaw } from '../lib/get_alert_from_raw'; import type { IndexType, RulesClientContext } from '../types'; +import { formatLegacyActions } from '../lib'; export interface FindParams { options?: FindOptions; @@ -132,6 +134,8 @@ export async function find( type: 'alert', }); + const siemRules: Rule[] = []; + const authorizedData = data.map(({ id, attributes, references }) => { try { ensureRuleTypeIsAuthorized( @@ -149,7 +153,8 @@ export async function find( ); throw error; } - return getAlertFromRaw( + + const rule = getAlertFromRaw( context, id, attributes.alertTypeId, @@ -159,6 +164,13 @@ export async function find( excludeFromPublicApi, includeSnoozeData ); + + // collect SIEM rule for further formatting legacy actions + if (attributes.consumer === AlertConsumers.SIEM) { + siemRules.push(rule); + } + + return rule; }); authorizedData.forEach(({ id }) => @@ -170,6 +182,27 @@ export async function find( ) ); + // format legacy actions for SIEM rules, if there any + if (siemRules.length) { + const formattedRules = await formatLegacyActions(siemRules, { + savedObjectsClient: context.unsecuredSavedObjectsClient, + logger: context.logger, + }); + + const formattedRulesMap = formattedRules.reduce>((acc, rule) => { + acc[rule.id] = rule; + return acc; + }, {}); + + return { + page, + perPage, + total, + // replace siem formatted rules + data: authorizedData.map((rule) => formattedRulesMap[rule.id] ?? rule), + }; + } + return { page, perPage, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/get.ts b/x-pack/plugins/alerting/server/rules_client/methods/get.ts index 932772f06d209..8b25f990d3cf1 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/get.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/get.ts @@ -4,12 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RawRule, SanitizedRule, RuleTypeParams, SanitizedRuleWithLegacyId } from '../../types'; import { ReadOperations, AlertingAuthorizationEntity } from '../../authorization'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { getAlertFromRaw } from '../lib/get_alert_from_raw'; import { RulesClientContext } from '../types'; +import { formatLegacyActions } from '../lib'; export interface GetParams { id: string; @@ -51,7 +53,7 @@ export async function get( savedObject: { type: 'alert', id }, }) ); - return getAlertFromRaw( + const rule = getAlertFromRaw( context, result.id, result.attributes.alertTypeId, @@ -61,4 +63,16 @@ export async function get( excludeFromPublicApi, includeSnoozeData ); + + // format legacy actions for SIEM rules + if (result.attributes.consumer === AlertConsumers.SIEM) { + const [migratedRule] = await formatLegacyActions([rule], { + savedObjectsClient: context.unsecuredSavedObjectsClient, + logger: context.logger, + }); + + return migratedRule; + } + + return rule; } diff --git a/x-pack/plugins/alerting/server/rules_client/methods/resolve.ts b/x-pack/plugins/alerting/server/rules_client/methods/resolve.ts index 539e4c089d36d..a663b9aac56d1 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/resolve.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/resolve.ts @@ -5,11 +5,14 @@ * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; + import { RawRule, RuleTypeParams, ResolvedSanitizedRule } from '../../types'; import { ReadOperations, AlertingAuthorizationEntity } from '../../authorization'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { getAlertFromRaw } from '../lib/get_alert_from_raw'; import { RulesClientContext } from '../types'; +import { formatLegacyActions } from '../lib'; export interface ResolveParams { id: string; @@ -58,6 +61,19 @@ export async function resolve( includeSnoozeData ); + // format legacy actions for SIEM rules + if (result.attributes.consumer === AlertConsumers.SIEM) { + const [migratedRule] = await formatLegacyActions([rule], { + savedObjectsClient: context.unsecuredSavedObjectsClient, + logger: context.logger, + }); + + return { + ...migratedRule, + ...resolveResponse, + }; + } + return { ...rule, ...resolveResponse, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update.ts b/x-pack/plugins/alerting/server/rules_client/methods/update.ts index d2b57d44fc528..4703cd5b92b28 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update.ts @@ -33,6 +33,7 @@ import { addGeneratedActionValues, incrementRevision, createNewAPIKeySet, + migrateLegacyActions, } from '../lib'; export interface UpdateOptions { @@ -115,10 +116,24 @@ async function updateWithOCC( context.ruleTypeRegistry.ensureRuleTypeEnabled(alertSavedObject.attributes.alertTypeId); + const migratedActions = await migrateLegacyActions(context, { + ruleId: id, + attributes: alertSavedObject.attributes, + }); + const updateResult = await updateAlert( context, { id, data, allowMissingConnectorSecrets, shouldIncrementRevision }, - alertSavedObject + migratedActions.hasLegacyActions + ? { + ...alertSavedObject, + attributes: { + ...alertSavedObject.attributes, + notifyWhen: undefined, + throttle: undefined, + }, + } + : alertSavedObject ); await Promise.all([ @@ -173,7 +188,7 @@ async function updateAlert( // TODO https://github.com/elastic/kibana/issues/148414 // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions[0]?.frequency; + const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; if (attributes.consumer === AlertConsumers.SIEM && firstFrequency) { data.actions = data.actions.map((action) => omit(action, 'frequency')); if (!attributes.notifyWhen) { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts index 8e9f1a79b8de0..74808613f869d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts @@ -25,7 +25,20 @@ import { enabledRule2, returnedRule1, returnedRule2, + siemRule1, } from './test_helpers'; +import { migrateLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); +(migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -453,6 +466,46 @@ describe('bulkDelete', () => { }); }); + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest + .fn() + .mockResolvedValueOnce({ + close: jest.fn(), + find: function* asyncGenerator() { + yield { saved_objects: [enabledRule1, enabledRule2, siemRule1] }; + }, + }); + + unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ + statuses: [ + { id: enabledRule1.id, type: 'alert', success: true }, + { id: enabledRule2.id, type: 'alert', success: true }, + { id: siemRule1.id, type: 'alert', success: true }, + ], + }); + + await rulesClient.bulkDeleteRules({ filter: 'fake_filter' }); + + expect(migrateLegacyActions).toHaveBeenCalledTimes(3); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + ruleId: enabledRule1.id, + skipActionsValidation: true, + attributes: enabledRule1.attributes, + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + ruleId: enabledRule2.id, + skipActionsValidation: true, + attributes: enabledRule2.attributes, + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + ruleId: siemRule1.id, + skipActionsValidation: true, + attributes: siemRule1.attributes, + }); + }); + }); + describe('auditLogger', () => { jest.spyOn(auditLogger, 'log').mockImplementation(); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_disable.test.ts index 03c637adaae00..18cc4492be8c9 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_disable.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; @@ -29,7 +29,16 @@ import { savedObjectWith500Error, returnedDisabledRule1, returnedDisabledRule2, + siemRule1, + siemRule2, } from './test_helpers'; +import { migrateLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -126,6 +135,11 @@ describe('bulkDisableRules', () => { }); mockCreatePointInTimeFinderAsInternalUser(); mockUnsecuredSavedObjectFind(2); + (migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], + }); }); test('should disable two rule', async () => { @@ -598,4 +612,49 @@ describe('bulkDisableRules', () => { ); }); }); + + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest + .fn() + .mockResolvedValueOnce({ + close: jest.fn(), + find: function* asyncGenerator() { + yield { saved_objects: [enabledRule1, enabledRule2, siemRule1, siemRule2] }; + }, + }); + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [enabledRule1, enabledRule2, siemRule1, siemRule2], + }); + + await rulesClient.bulkDisableRules({ filter: 'fake_filter' }); + + expect(migrateLegacyActions).toHaveBeenCalledTimes(4); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: enabledRule1.attributes, + ruleId: enabledRule1.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: enabledRule2.attributes, + ruleId: enabledRule2.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + ruleId: siemRule1.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + ruleId: siemRule2.id, + actions: [], + references: [], + }); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index b91bb1cae5e3c..c9e9302fb0146 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -7,6 +7,7 @@ import { schema } from '@kbn/config-schema'; import { v4 as uuidv4 } from 'uuid'; +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; @@ -21,6 +22,20 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { NormalizedAlertAction } from '../types'; +import { enabledRule1, enabledRule2, siemRule1, siemRule2 } from './test_helpers'; +import { migrateLegacyActions } from '../lib'; +import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); +(migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -196,6 +211,8 @@ describe('bulkEdit()', () => { }, producer: 'alerts', }); + + (migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock); }); describe('tags operations', () => { @@ -2491,4 +2508,53 @@ describe('bulkEdit()', () => { expect(taskManager.bulkUpdateSchedules).not.toHaveBeenCalled(); }); }); + + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest + .fn() + .mockResolvedValueOnce({ + close: jest.fn(), + find: function* asyncGenerator() { + yield { saved_objects: [enabledRule1, enabledRule2, siemRule1, siemRule2] }; + }, + }); + + await rulesClient.bulkEdit({ + operations: [ + { + field: 'tags', + operation: 'set', + value: ['test-tag'], + }, + ], + }); + + expect(migrateLegacyActions).toHaveBeenCalledTimes(4); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: enabledRule1.attributes, + ruleId: enabledRule1.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: enabledRule2.attributes, + ruleId: enabledRule2.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + ruleId: siemRule1.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + ruleId: siemRule2.id, + actions: [], + references: [], + }); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts index b82001e83886c..03f9c06109059 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; @@ -29,8 +29,17 @@ import { savedObjectWith500Error, returnedRule1, returnedRule2, + siemRule1, + siemRule2, } from './test_helpers'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; +import { migrateLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -78,6 +87,11 @@ beforeEach(() => { } as unknown as BulkUpdateTaskResult) ); (auditLogger.log as jest.Mock).mockClear(); + (migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], + }); }); setGlobalDate(); @@ -779,4 +793,43 @@ describe('bulkEnableRules', () => { expect(auditLogger.log.mock.calls[0][0]?.event?.outcome).toEqual('failure'); }); }); + + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest + .fn() + .mockResolvedValueOnce({ + close: jest.fn(), + find: function* asyncGenerator() { + yield { saved_objects: [disabledRule1, siemRule1, siemRule2] }; + }, + }); + + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [disabledRule1, siemRule1, siemRule2], + }); + + await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); + + expect(migrateLegacyActions).toHaveBeenCalledTimes(3); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: disabledRule1.attributes, + ruleId: disabledRule1.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + ruleId: siemRule1.id, + actions: [], + references: [], + }); + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + ruleId: siemRule2.id, + actions: [], + references: [], + }); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts index edf03a2d54e42..a8f13dc25869d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; + import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; @@ -17,6 +19,18 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; +import { migrateLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); +(migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -215,6 +229,27 @@ describe('delete()', () => { ); }); + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + const existingDecryptedSiemAlert = { + ...existingDecryptedAlert, + attributes: { ...existingDecryptedAlert.attributes, consumer: AlertConsumers.SIEM }, + }; + + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce( + existingDecryptedSiemAlert + ); + + await rulesClient.delete({ id: '1' }); + + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + ruleId: '1', + skipActionsValidation: true, + attributes: existingDecryptedSiemAlert.attributes, + }); + }); + }); + describe('authorization', () => { test('ensures user is authorised to delete this type of alert under the consumer', async () => { await rulesClient.delete({ id: '1' }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index 09dd8f54d01fd..c09b491dbbdcc 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; @@ -18,6 +19,14 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; +import { migrateLegacyActions } from '../lib'; +import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -122,6 +131,11 @@ describe('disable()', () => { rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValue(existingRule); encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedRule); + (migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], + }); }); describe('authorization', () => { @@ -573,4 +587,35 @@ describe('disable()', () => { ); expect(taskManager.bulkDisable).not.toHaveBeenCalled(); }); + + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + const existingDecryptedSiemRule = { + ...existingDecryptedRule, + attributes: { ...existingDecryptedRule.attributes, consumer: AlertConsumers.SIEM }, + }; + + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedSiemRule); + (migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock); + + await rulesClient.disable({ id: '1' }); + + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + actions: [ + { + actionRef: '1', + actionTypeId: '1', + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + references: [], + ruleId: '1', + }); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts index d15e96f573a84..d7175e9a47b89 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; @@ -17,6 +18,14 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { migrateLegacyActions } from '../lib'; +import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -122,6 +131,11 @@ describe('enable()', () => { apiKeysEnabled: false, }); taskManager.get.mockResolvedValue(mockTask); + (migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], + }); }); describe('authorization', () => { @@ -658,4 +672,58 @@ describe('enable()', () => { scheduledTaskId: '1', }); }); + + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + (migrateLegacyActions as jest.Mock).mockResolvedValueOnce({ + hasLegacyActions: true, + resultedActions: ['fake-action-1'], + resultedReferences: ['fake-ref-1'], + }); + + const existingDecryptedSiemRule = { + ...existingRule, + attributes: { ...existingRule.attributes, consumer: AlertConsumers.SIEM }, + }; + + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedSiemRule); + (migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock); + + await rulesClient.enable({ id: '1' }); + + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + attributes: expect.objectContaining({ consumer: AlertConsumers.SIEM }), + actions: [ + { + actionRef: '1', + actionTypeId: '1', + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + references: [], + ruleId: '1', + }); + // to mitigate AAD issues, we call create with overwrite=true and actions related props + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + expect.objectContaining({ + ...existingDecryptedSiemRule.attributes, + actions: ['fake-action-1'], + throttle: undefined, + notifyWhen: undefined, + enabled: true, + }), + { + id: existingDecryptedSiemRule.id, + overwrite: true, + references: ['fake-ref-1'], + version: existingDecryptedSiemRule.version, + } + ); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts index b8eb4d4d0c478..3573508d891cd 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts @@ -19,6 +19,14 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { RegistryRuleType } from '../../rule_type_registry'; +import { enabledRule1, enabledRule2, siemRule1, siemRule2 } from './test_helpers'; +import { formatLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { + return { + formatLegacyActions: jest.fn(), + }; +}); const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -806,4 +814,39 @@ describe('find()', () => { ); }); }); + + describe('legacy actions migration for SIEM', () => { + test('should call migrateLegacyActions', async () => { + const rulesClient = new RulesClient(rulesClientParams); + + (formatLegacyActions as jest.Mock).mockResolvedValueOnce([ + { ...siemRule1, migrated: true }, + { ...siemRule2, migrated: true }, + ]); + + unsecuredSavedObjectsClient.find.mockReset(); + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [enabledRule1, enabledRule2, siemRule1, siemRule2].map((r) => ({ + ...r, + score: 1, + })), + }); + + const result = await rulesClient.find({ options: {} }); + + expect(formatLegacyActions).toHaveBeenCalledTimes(1); + expect(formatLegacyActions).toHaveBeenCalledWith( + [ + expect.objectContaining({ id: siemRule1.id }), + expect.objectContaining({ id: siemRule2.id }), + ], + expect.any(Object) + ); + expect(result.data[2]).toEqual(expect.objectContaining({ id: siemRule1.id, migrated: true })); + expect(result.data[3]).toEqual(expect.objectContaining({ id: siemRule2.id, migrated: true })); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts index 30abf4c661d19..1ca6e664fc359 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; @@ -17,6 +18,13 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; +import { formatLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { + return { + formatLegacyActions: jest.fn(), + }; +}); const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -509,4 +517,72 @@ describe('get()', () => { ); }); }); + + describe('legacy actions migration for SIEM', () => { + const rule = { + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + notifyWhen: 'onActiveAlert', + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }; + + test('should call formatLegacyActions if consumer is SIEM', async () => { + const rulesClient = new RulesClient(rulesClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + ...rule, + attributes: { + ...rule.attributes, + consumer: AlertConsumers.SIEM, + }, + }); + (formatLegacyActions as jest.Mock).mockResolvedValue([ + { + id: 'migrated_rule_mock', + }, + ]); + + const result = await rulesClient.get({ id: '1' }); + + expect(formatLegacyActions).toHaveBeenCalledWith( + [expect.objectContaining({ id: '1' })], + expect.any(Object) + ); + + expect(result).toEqual({ + id: 'migrated_rule_mock', + }); + }); + + test('should not call formatLegacyActions if consumer is not SIEM', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(rule); + const rulesClient = new RulesClient(rulesClientParams); + await rulesClient.get({ id: '1' }); + + expect(formatLegacyActions).not.toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts index 6f52da62023e9..bb22b5f6d0d53 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; @@ -17,6 +18,13 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; +import { formatLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { + return { + formatLegacyActions: jest.fn(), + }; +}); const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -583,4 +591,82 @@ describe('resolve()', () => { ); }); }); + + describe('legacy actions migration for SIEM', () => { + const rule = { + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + notifyWhen: 'onActiveAlert', + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }; + + test('should call formatLegacyActions if consumer is SIEM', async () => { + const rulesClient = new RulesClient(rulesClientParams); + unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ + saved_object: { + ...rule, + attributes: { + ...rule.attributes, + consumer: AlertConsumers.SIEM, + }, + }, + outcome: 'aliasMatch', + alias_target_id: '2', + }); + (formatLegacyActions as jest.Mock).mockResolvedValue([ + { + id: 'migrated_rule_mock', + }, + ]); + + const result = await rulesClient.resolve({ id: '1' }); + + expect(formatLegacyActions).toHaveBeenCalledWith( + [expect.objectContaining({ id: '1' })], + expect.any(Object) + ); + + expect(result).toEqual({ + id: 'migrated_rule_mock', + outcome: 'aliasMatch', + alias_target_id: '2', + }); + }); + + test('should not call formatLegacyActions if consumer is not SIEM', async () => { + unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ + saved_object: rule, + outcome: 'aliasMatch', + alias_target_id: '2', + }); + const rulesClient = new RulesClient(rulesClientParams); + await rulesClient.resolve({ id: '1' }); + + expect(formatLegacyActions).not.toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts b/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts index 08c037e9410e7..fd4e534838940 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { AlertConsumers } from '@kbn/rule-data-utils'; import type { SavedObject } from '@kbn/core-saved-objects-server'; @@ -41,6 +42,20 @@ export const defaultRule = { version: '1', }; +export const siemRule1 = { + ...defaultRule, + attributes: { + ...defaultRule.attributes, + consumer: AlertConsumers.SIEM, + }, + id: 'siem-id1', +}; + +export const siemRule2 = { + ...siemRule1, + id: 'siem-id2', +}; + export const enabledRule1 = { ...defaultRule, attributes: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 6fcc0cb915a36..b76d0607f22a8 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -7,6 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; import { schema } from '@kbn/config-schema'; +import { AlertConsumers } from '@kbn/rule-data-utils'; import { RulesClient, ConstructorOptions } from '../rules_client'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; @@ -22,6 +23,13 @@ import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; +import { migrateLegacyActions } from '../lib'; + +jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { + return { + migrateLegacyActions: jest.fn(), + }; +}); jest.mock('@kbn/core-saved-objects-utils-server', () => { const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); @@ -164,6 +172,11 @@ describe('update()', () => { }, producer: 'alerts', }); + (migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], + }); }); test('updates given parameters', async () => { @@ -2734,6 +2747,64 @@ describe('update()', () => { ); }); + describe('legacy actions migration for SIEM', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: true, + schedule: { interval: '1m' }, + params: { + bar: true, + }, + actions: [], + notifyWhen: 'onActiveAlert', + scheduledTaskId: 'task-123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + references: [], + }); + }); + + test('should call migrateLegacyActions', async () => { + const existingDecryptedSiemAlert = { + ...existingDecryptedAlert, + attributes: { ...existingDecryptedAlert, consumer: AlertConsumers.SIEM }, + }; + + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce( + existingDecryptedSiemAlert + ); + + actionsClient.getBulk.mockReset(); + actionsClient.isPreconfigured.mockReset(); + + await rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1m' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + risk_score: 40, + severity: 'low', + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [], + }, + }); + + expect(migrateLegacyActions).toHaveBeenCalledWith(expect.any(Object), { + ruleId: '1', + attributes: existingDecryptedSiemAlert.attributes, + }); + }); + }); + it('calls the authentication API key function if the user is authenticated using an api key', async () => { rulesClientParams.isAuthenticationTypeAPIKey.mockReturnValueOnce(true); rulesClientParams.getAuthenticationAPIKey.mockReturnValueOnce({ diff --git a/x-pack/plugins/alerting/server/saved_objects/mappings.ts b/x-pack/plugins/alerting/server/saved_objects/mappings.ts index 9ee204bb53896..2cec5bdd449e6 100644 --- a/x-pack/plugins/alerting/server/saved_objects/mappings.ts +++ b/x-pack/plugins/alerting/server/saved_objects/mappings.ts @@ -6,7 +6,6 @@ */ import { SavedObjectsTypeMappingDefinition } from '@kbn/core/server'; -import { rRuleMappingsField } from './rrule_mappings_field'; export const alertMappings: SavedObjectsTypeMappingDefinition = { dynamic: false, @@ -55,26 +54,27 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = { actionTypeId: { type: 'keyword', }, - params: { - dynamic: false, - properties: {}, - }, - frequency: { - properties: { - summary: { - index: false, - type: 'boolean', - }, - notifyWhen: { - index: false, - type: 'keyword', - }, - throttle: { - index: false, - type: 'keyword', - }, - }, - }, + // NO NEED TO BE INDEXED + // params: { + // dynamic: false, + // properties: {}, + // }, + // frequency: { + // properties: { + // summary: { + // index: false, + // type: 'boolean', + // }, + // notifyWhen: { + // index: false, + // type: 'keyword', + // }, + // throttle: { + // index: false, + // type: 'keyword', + // }, + // }, + // }, }, }, params: { @@ -106,12 +106,14 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = { updatedAt: { type: 'date', }, - apiKey: { - type: 'binary', - }, - apiKeyOwner: { - type: 'keyword', - }, + // NEED TO CHECK WITH KIBANA SECURITY + // apiKey: { + // type: 'binary', + // }, + // NO NEED TO BE INDEXED + // apiKeyOwner: { + // type: 'keyword', + // }, throttle: { type: 'keyword', }, @@ -124,33 +126,34 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = { mutedInstanceIds: { type: 'keyword', }, - meta: { - properties: { - versionApiKeyLastmodified: { - type: 'keyword', - }, - }, - }, + // NO NEED TO BE INDEXED + // meta: { + // properties: { + // versionApiKeyLastmodified: { + // type: 'keyword', + // }, + // }, + // }, monitoring: { properties: { run: { properties: { - history: { - properties: { - duration: { - type: 'long', - }, - success: { - type: 'boolean', - }, - timestamp: { - type: 'date', - }, - outcome: { - type: 'keyword', - }, - }, - }, + // history: { + // properties: { + // duration: { + // type: 'long', + // }, + // success: { + // type: 'boolean', + // }, + // timestamp: { + // type: 'date', + // }, + // outcome: { + // type: 'keyword', + // }, + // }, + // }, calculated_metrics: { properties: { p50: { @@ -200,8 +203,8 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = { }, }, }, + // There is need to query for a rule by a specific revision revision: { - index: true, // Explicitly setting to `true` as there is need to query for a rule by a specific revision type: 'long', }, snoozeSchedule: { @@ -217,12 +220,14 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = { type: 'date', format: 'strict_date_time', }, - rRule: rRuleMappingsField, + // NO NEED TO BE INDEXED + // rRule: rRuleMappingsField, }, }, - nextRun: { - type: 'date', - }, + // NO NEED TO BE INDEXED + // nextRun: { + // type: 'date', + // }, // Deprecated, if you need to add new property please do it in `last_run` executionStatus: { properties: { @@ -268,12 +273,13 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = { outcomeOrder: { type: 'float', }, - warning: { - type: 'text', - }, - outcomeMsg: { - type: 'text', - }, + // NO NEED TO BE INDEXED + // warning: { + // type: 'text', + // }, + // outcomeMsg: { + // type: 'text', + // }, alertsCount: { properties: { active: { diff --git a/x-pack/plugins/alerting/server/saved_objects/rules_settings_mappings.ts b/x-pack/plugins/alerting/server/saved_objects/rules_settings_mappings.ts index d20567edc2832..3f363a39e35fa 100644 --- a/x-pack/plugins/alerting/server/saved_objects/rules_settings_mappings.ts +++ b/x-pack/plugins/alerting/server/saved_objects/rules_settings_mappings.ts @@ -8,37 +8,39 @@ import { SavedObjectsTypeMappingDefinition } from '@kbn/core/server'; export const rulesSettingsMappings: SavedObjectsTypeMappingDefinition = { + dynamic: false, properties: { flapping: { properties: { - enabled: { - type: 'boolean', - index: false, - }, - lookBackWindow: { - type: 'long', - index: false, - }, - statusChangeThreshold: { - type: 'long', - index: false, - }, - createdBy: { - type: 'keyword', - index: false, - }, - updatedBy: { - type: 'keyword', - index: false, - }, - createdAt: { - type: 'date', - index: false, - }, - updatedAt: { - type: 'date', - index: false, - }, + // NO NEED TO BE INDEXED + // enabled: { + // type: 'boolean', + // index: false, + // }, + // lookBackWindow: { + // type: 'long', + // index: false, + // }, + // statusChangeThreshold: { + // type: 'long', + // index: false, + // }, + // createdBy: { + // type: 'keyword', + // index: false, + // }, + // updatedBy: { + // type: 'keyword', + // index: false, + // }, + // createdAt: { + // type: 'date', + // index: false, + // }, + // updatedAt: { + // type: 'date', + // index: false, + // }, }, }, }, diff --git a/x-pack/plugins/apm/kibana.jsonc b/x-pack/plugins/apm/kibana.jsonc index 02ca85e30f0ad..f8038cad8fff7 100644 --- a/x-pack/plugins/apm/kibana.jsonc +++ b/x-pack/plugins/apm/kibana.jsonc @@ -7,10 +7,7 @@ "id": "apm", "server": true, "browser": true, - "configPath": [ - "xpack", - "apm" - ], + "configPath": ["xpack", "apm"], "requiredPlugins": [ "data", "embeddable", @@ -19,6 +16,7 @@ "inspector", "licensing", "observability", + "observabilityShared", "exploratoryView", "ruleRegistry", "triggersActionsUi", diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx index 5711b5a1f41c8..9bad342c7669e 100644 --- a/x-pack/plugins/apm/public/application/index.tsx +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -49,6 +49,7 @@ export const renderApp = ({ data: pluginsStart.data, inspector: pluginsStart.inspector, observability: pluginsStart.observability, + observabilityShared: pluginsStart.observabilityShared, observabilityRuleTypeRegistry, dataViews: pluginsStart.dataViews, unifiedSearch: pluginsStart.unifiedSearch, diff --git a/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx b/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx index 26b641a25896d..3b7849ddacf30 100644 --- a/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx +++ b/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx @@ -45,9 +45,9 @@ const pageHeader = { function ErrorWithTemplate({ error }: { error: Error }) { const { services } = useKibana(); - const { observability } = services; + const { observabilityShared } = services; - const ObservabilityPageTemplate = observability.navigation.PageTemplate; + const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate; if (error instanceof NotFoundRouteException) { return ( diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx index c4bcc4e5fc612..5704fd18058bd 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx @@ -7,7 +7,7 @@ import { EuiPageHeaderProps } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ObservabilityPageTemplateProps } from '@kbn/observability-plugin/public/components/shared/page_template/page_template'; +import { ObservabilityPageTemplateProps } from '@kbn/observability-shared-plugin/public'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; import React from 'react'; import { useLocation } from 'react-router-dom'; @@ -54,10 +54,10 @@ export function ApmMainTemplate({ const location = useLocation(); const { services } = useKibana(); - const { http, docLinks, observability, application } = services; + const { http, docLinks, observabilityShared, application } = services; const basePath = http?.basePath.get(); - const ObservabilityPageTemplate = observability.navigation.PageTemplate; + const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate; const { data, status } = useFetcher((callApmApi) => { return callApmApi('GET /internal/apm/has_data'); diff --git a/x-pack/plugins/apm/public/components/routing/templates/settings_template.stories.tsx b/x-pack/plugins/apm/public/components/routing/templates/settings_template.stories.tsx index ec3188f4ff197..b214a79b7a1f8 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/settings_template.stories.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/settings_template.stories.tsx @@ -16,7 +16,7 @@ import { SettingsTemplate } from './settings_template'; type Args = ComponentProps; const coreMock = { - observability: { + observabilityShared: { navigation: { PageTemplate: () => { return <>hello world; diff --git a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx index ffca2a1b3cc4a..6924277f229c0 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx @@ -94,6 +94,7 @@ const mockCorePlugins = { inspector: {}, maps: {}, observability: {}, + observabilityShared: {}, data: {}, }; diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index ba7b455c05bb6..ac1d7294a27be 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -38,6 +38,10 @@ import type { LicensingPluginSetup } from '@kbn/licensing-plugin/public'; import type { MapsStartApi } from '@kbn/maps-plugin/public'; import type { MlPluginSetup, MlPluginStart } from '@kbn/ml-plugin/public'; import type { SharePluginSetup } from '@kbn/share-plugin/public'; +import type { + ObservabilitySharedPluginSetup, + ObservabilitySharedPluginStart, +} from '@kbn/observability-shared-plugin/public'; import { FetchDataParams, METRIC_TYPE, @@ -80,6 +84,7 @@ export interface ApmPluginSetupDeps { licensing: LicensingPluginSetup; ml?: MlPluginSetup; observability: ObservabilityPublicSetup; + observabilityShared: ObservabilitySharedPluginSetup; triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; share: SharePluginSetup; } @@ -96,6 +101,7 @@ export interface ApmPluginStartDeps { ml?: MlPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; observability: ObservabilityPublicStart; + observabilityShared: ObservabilitySharedPluginStart; fleet?: FleetStart; fieldFormats?: FieldFormatsStart; security?: SecurityPluginStart; @@ -162,7 +168,7 @@ export class ApmPlugin implements Plugin { } // register observability nav if user has access to plugin - plugins.observability.navigation.registerSections( + plugins.observabilityShared.navigation.registerSections( from(core.getStartServices()).pipe( map(([coreStart, pluginsStart]) => { if (coreStart.application.capabilities.apm.show) { diff --git a/x-pack/plugins/apm/readme.md b/x-pack/plugins/apm/readme.md index fb7cf20732bf3..51164460ce8be 100644 --- a/x-pack/plugins/apm/readme.md +++ b/x-pack/plugins/apm/readme.md @@ -1,21 +1,25 @@ -# Documentation for APM UI developers +# APM -## Getting started +This plugin provides access to App Monitoring features provided by Elastic. It allows you to monitor your software services and applications in real-time; visualize detailed performance information on your services, identify and analyze errors, and monitor host-level and APM agent-specific metrics like JVM and Go runtime metrics. + +## Documentation for APM UI developers + +### Getting started - [Local setup](./dev_docs/local_setup.md) - [Testing (unit, api, e2e, storybook)](./dev_docs/testing.md) - [Linting (typescript, eslint, prettier)](./dev_docs/linting.md) -## APM concepts +### APM concepts - [Queries and data model](./dev_docs/apm_queries.md) - [Telemetry](./dev_docs/telemetry.md) - [Routing and Linking](./dev_docs/routing_and_linking.md) -## Tooling +### Tooling - [VSCode setup instructions](./dev_docs/vscode_setup.md) - [Github PR commands](./dev_docs/github_commands.md) - [Synthtrace (data generation)](https://github.com/elastic/kibana/blob/main/packages/kbn-apm-synthtrace/README.md) - [Query debugging in development and production](./dev_docs/query_debugging_in_development_and_production.md) -## Other resources +### Other resources - [Official APM UI settings docs](https://www.elastic.co/guide/en/kibana/current/apm-settings-in-kibana.html) - [Reading Material](./dev_docs/learning_material.md) diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index 5419244c7cb15..e049bed128afa 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -83,6 +83,7 @@ "@kbn/alerts-as-data-utils", "@kbn/exploratory-view-plugin", "@kbn/logging-mocks", + "@kbn/observability-shared-plugin", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cases/common/api/cases/comment/files.ts b/x-pack/plugins/cases/common/api/cases/comment/files.ts index 66555b1a584d9..af42a7a779e55 100644 --- a/x-pack/plugins/cases/common/api/cases/comment/files.ts +++ b/x-pack/plugins/cases/common/api/cases/comment/files.ts @@ -9,15 +9,15 @@ import * as rt from 'io-ts'; import { MAX_DELETE_FILES } from '../../../constants'; import { limitedArraySchema, NonEmptyString } from '../../../schema'; +export const SingleFileAttachmentMetadataRt = rt.type({ + name: rt.string, + extension: rt.string, + mimeType: rt.string, + created: rt.string, +}); + export const FileAttachmentMetadataRt = rt.type({ - files: rt.array( - rt.type({ - name: rt.string, - extension: rt.string, - mimeType: rt.string, - createdAt: rt.string, - }) - ), + files: rt.array(SingleFileAttachmentMetadataRt), }); export type FileAttachmentMetadata = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/constants/mime_types.ts b/x-pack/plugins/cases/common/constants/mime_types.ts index 9f1f455513dab..c35e5ef674c81 100644 --- a/x-pack/plugins/cases/common/constants/mime_types.ts +++ b/x-pack/plugins/cases/common/constants/mime_types.ts @@ -8,7 +8,7 @@ /** * These were retrieved from https://www.iana.org/assignments/media-types/media-types.xhtml#image */ -const imageMimeTypes = [ +export const imageMimeTypes = [ 'image/aces', 'image/apng', 'image/avci', @@ -87,9 +87,9 @@ const imageMimeTypes = [ 'image/wmf', ]; -const textMimeTypes = ['text/plain', 'text/csv', 'text/json', 'application/json']; +export const textMimeTypes = ['text/plain', 'text/csv', 'text/json', 'application/json']; -const compressionMimeTypes = [ +export const compressionMimeTypes = [ 'application/zip', 'application/gzip', 'application/x-bzip', @@ -98,7 +98,7 @@ const compressionMimeTypes = [ 'application/x-tar', ]; -const pdfMimeTypes = ['application/pdf']; +export const pdfMimeTypes = ['application/pdf']; export const ALLOWED_MIME_TYPES = [ ...imageMimeTypes, diff --git a/x-pack/plugins/cases/common/types.ts b/x-pack/plugins/cases/common/types.ts index 3ff14b0905110..32d6b34b11c16 100644 --- a/x-pack/plugins/cases/common/types.ts +++ b/x-pack/plugins/cases/common/types.ts @@ -24,4 +24,5 @@ export type SnakeToCamelCase = T extends Record export enum CASE_VIEW_PAGE_TABS { ALERTS = 'alerts', ACTIVITY = 'activity', + FILES = 'files', } diff --git a/x-pack/plugins/cases/public/application.tsx b/x-pack/plugins/cases/public/application.tsx index bac423a9f8292..742f254472160 100644 --- a/x-pack/plugins/cases/public/application.tsx +++ b/x-pack/plugins/cases/public/application.tsx @@ -9,19 +9,21 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Router } from 'react-router-dom'; -import { I18nProvider } from '@kbn/i18n-react'; import { EuiErrorBoundary } from '@elastic/eui'; - +import { I18nProvider } from '@kbn/i18n-react'; +import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common'; import { KibanaContextProvider, KibanaThemeProvider, useUiSetting$, } from '@kbn/kibana-react-plugin/public'; -import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common'; -import type { RenderAppProps } from './types'; -import { CasesApp } from './components/app'; + +import type { ScopedFilesClient } from '@kbn/files-plugin/public'; import type { ExternalReferenceAttachmentTypeRegistry } from './client/attachment_framework/external_reference_registry'; import type { PersistableStateAttachmentTypeRegistry } from './client/attachment_framework/persistable_state_registry'; +import type { RenderAppProps } from './types'; + +import { CasesApp } from './components/app'; export const renderApp = (deps: RenderAppProps) => { const { mountParams } = deps; @@ -37,10 +39,15 @@ export const renderApp = (deps: RenderAppProps) => { interface CasesAppWithContextProps { externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry; persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; + getFilesClient: (scope: string) => ScopedFilesClient; } const CasesAppWithContext: React.FC = React.memo( - ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry }) => { + ({ + externalReferenceAttachmentTypeRegistry, + persistableStateAttachmentTypeRegistry, + getFilesClient, + }) => { const [darkMode] = useUiSetting$('theme:darkMode'); return ( @@ -48,6 +55,7 @@ const CasesAppWithContext: React.FC = React.memo( ); @@ -78,6 +86,7 @@ export const App: React.FC<{ deps: RenderAppProps }> = ({ deps }) => { deps.externalReferenceAttachmentTypeRegistry } persistableStateAttachmentTypeRegistry={deps.persistableStateAttachmentTypeRegistry} + getFilesClient={pluginsStart.files.filesClientFactory.asScoped} /> diff --git a/x-pack/plugins/cases/public/client/attachment_framework/types.ts b/x-pack/plugins/cases/public/client/attachment_framework/types.ts index 414b8a0086654..95a453b9d0a12 100644 --- a/x-pack/plugins/cases/public/client/attachment_framework/types.ts +++ b/x-pack/plugins/cases/public/client/attachment_framework/types.ts @@ -13,19 +13,38 @@ import type { } from '../../../common/api'; import type { Case } from '../../containers/types'; -export interface AttachmentAction { +export enum AttachmentActionType { + BUTTON = 'button', + CUSTOM = 'custom', +} + +interface BaseAttachmentAction { + type: AttachmentActionType; + label: string; + isPrimary?: boolean; + disabled?: boolean; +} + +interface ButtonAttachmentAction extends BaseAttachmentAction { + type: AttachmentActionType.BUTTON; onClick: () => void; iconType: string; - label: string; color?: EuiButtonProps['color']; - isPrimary?: boolean; } +interface CustomAttachmentAction extends BaseAttachmentAction { + type: AttachmentActionType.CUSTOM; + render: () => JSX.Element; +} + +export type AttachmentAction = ButtonAttachmentAction | CustomAttachmentAction; + export interface AttachmentViewObject { timelineAvatar?: EuiCommentProps['timelineAvatar']; getActions?: (props: Props) => AttachmentAction[]; event?: EuiCommentProps['event']; children?: React.LazyExoticComponent>; + hideDefaultActions?: boolean; } export interface CommonAttachmentViewProps { @@ -46,8 +65,9 @@ export interface AttachmentType { id: string; icon: IconType; displayName: string; - getAttachmentViewObject: () => AttachmentViewObject; + getAttachmentViewObject: (props: Props) => AttachmentViewObject; getAttachmentRemovalObject?: (props: Props) => Pick, 'event'>; + hideDefaultActions?: boolean; } export type ExternalReferenceAttachmentType = AttachmentType; diff --git a/x-pack/plugins/cases/public/client/ui/get_all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/client/ui/get_all_cases_selector_modal.tsx index b0807b0509135..fc85e84639baa 100644 --- a/x-pack/plugins/cases/public/client/ui/get_all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/client/ui/get_all_cases_selector_modal.tsx @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context'; type GetAllCasesSelectorModalPropsInternal = AllCasesSelectorModalProps & CasesContextProps; export type GetAllCasesSelectorModalProps = Omit< GetAllCasesSelectorModalPropsInternal, - 'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry' + | 'externalReferenceAttachmentTypeRegistry' + | 'persistableStateAttachmentTypeRegistry' + | 'getFilesClient' >; const AllCasesSelectorModalLazy: React.FC = lazy( @@ -23,6 +25,7 @@ const AllCasesSelectorModalLazy: React.FC = lazy( export const getAllCasesSelectorModalLazy = ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, hiddenStatuses, @@ -33,6 +36,7 @@ export const getAllCasesSelectorModalLazy = ({ value={{ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, }} diff --git a/x-pack/plugins/cases/public/client/ui/get_cases.tsx b/x-pack/plugins/cases/public/client/ui/get_cases.tsx index 45c9f30b984d2..36556523fc3a3 100644 --- a/x-pack/plugins/cases/public/client/ui/get_cases.tsx +++ b/x-pack/plugins/cases/public/client/ui/get_cases.tsx @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context'; type GetCasesPropsInternal = CasesProps & CasesContextProps; export type GetCasesProps = Omit< GetCasesPropsInternal, - 'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry' + | 'externalReferenceAttachmentTypeRegistry' + | 'persistableStateAttachmentTypeRegistry' + | 'getFilesClient' >; const CasesRoutesLazy: React.FC = lazy(() => import('../../components/app/routes')); @@ -22,6 +24,7 @@ const CasesRoutesLazy: React.FC = lazy(() => import('../../component export const getCasesLazy = ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, basePath, @@ -39,6 +42,7 @@ export const getCasesLazy = ({ value={{ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, basePath, diff --git a/x-pack/plugins/cases/public/client/ui/get_cases_context.tsx b/x-pack/plugins/cases/public/client/ui/get_cases_context.tsx index 77e6ca3c87e24..9db49ef9776ba 100644 --- a/x-pack/plugins/cases/public/client/ui/get_cases_context.tsx +++ b/x-pack/plugins/cases/public/client/ui/get_cases_context.tsx @@ -13,7 +13,9 @@ import type { CasesContextProps } from '../../components/cases_context'; export type GetCasesContextPropsInternal = CasesContextProps; export type GetCasesContextProps = Omit< CasesContextProps, - 'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry' + | 'externalReferenceAttachmentTypeRegistry' + | 'persistableStateAttachmentTypeRegistry' + | 'getFilesClient' >; const CasesProviderLazy: React.FC<{ value: GetCasesContextPropsInternal }> = lazy( @@ -28,6 +30,7 @@ const CasesProviderLazyWrapper = ({ features, children, releasePhase, + getFilesClient, }: GetCasesContextPropsInternal & { children: ReactNode }) => { return ( }> @@ -39,6 +42,7 @@ const CasesProviderLazyWrapper = ({ permissions, features, releasePhase, + getFilesClient, }} > {children} @@ -52,9 +56,12 @@ CasesProviderLazyWrapper.displayName = 'CasesProviderLazyWrapper'; export const getCasesContextLazy = ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, }: Pick< GetCasesContextPropsInternal, - 'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry' + | 'externalReferenceAttachmentTypeRegistry' + | 'persistableStateAttachmentTypeRegistry' + | 'getFilesClient' >): (() => React.FC) => { const CasesProviderLazyWrapperWithRegistry: React.FC = ({ children, @@ -64,6 +71,7 @@ export const getCasesContextLazy = ({ {...props} externalReferenceAttachmentTypeRegistry={externalReferenceAttachmentTypeRegistry} persistableStateAttachmentTypeRegistry={persistableStateAttachmentTypeRegistry} + getFilesClient={getFilesClient} > {children} diff --git a/x-pack/plugins/cases/public/client/ui/get_create_case_flyout.tsx b/x-pack/plugins/cases/public/client/ui/get_create_case_flyout.tsx index af932b53e1dde..e52a14033a614 100644 --- a/x-pack/plugins/cases/public/client/ui/get_create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/client/ui/get_create_case_flyout.tsx @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context'; type GetCreateCaseFlyoutPropsInternal = CreateCaseFlyoutProps & CasesContextProps; export type GetCreateCaseFlyoutProps = Omit< GetCreateCaseFlyoutPropsInternal, - 'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry' + | 'externalReferenceAttachmentTypeRegistry' + | 'persistableStateAttachmentTypeRegistry' + | 'getFilesClient' >; export const CreateCaseFlyoutLazy: React.FC = lazy( @@ -23,6 +25,7 @@ export const CreateCaseFlyoutLazy: React.FC = lazy( export const getCreateCaseFlyoutLazy = ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, features, @@ -35,6 +38,7 @@ export const getCreateCaseFlyoutLazy = ({ value={{ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, features, diff --git a/x-pack/plugins/cases/public/client/ui/get_recent_cases.tsx b/x-pack/plugins/cases/public/client/ui/get_recent_cases.tsx index a047c106246da..7c41cc3842bf7 100644 --- a/x-pack/plugins/cases/public/client/ui/get_recent_cases.tsx +++ b/x-pack/plugins/cases/public/client/ui/get_recent_cases.tsx @@ -14,7 +14,9 @@ import type { RecentCasesProps } from '../../components/recent_cases'; type GetRecentCasesPropsInternal = RecentCasesProps & CasesContextProps; export type GetRecentCasesProps = Omit< GetRecentCasesPropsInternal, - 'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry' + | 'externalReferenceAttachmentTypeRegistry' + | 'persistableStateAttachmentTypeRegistry' + | 'getFilesClient' >; const RecentCasesLazy: React.FC = lazy( @@ -23,6 +25,7 @@ const RecentCasesLazy: React.FC = lazy( export const getRecentCasesLazy = ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, maxCasesToShow, @@ -31,6 +34,7 @@ export const getRecentCasesLazy = ({ value={{ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner, permissions, }} diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index 2a5a75bf7a789..f0b2e71231bb1 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -9,22 +9,30 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { ThemeProvider } from 'styled-components'; + +import type { RenderOptions, RenderResult } from '@testing-library/react'; +import type { ILicense } from '@kbn/licensing-plugin/public'; +import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import type { ScopedFilesClient } from '@kbn/files-plugin/public'; + import { euiDarkVars } from '@kbn/ui-theme'; import { I18nProvider } from '@kbn/i18n-react'; -import { ThemeProvider } from 'styled-components'; +import { createMockFilesClient } from '@kbn/shared-ux-file-mocks'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import type { RenderOptions, RenderResult } from '@testing-library/react'; import { render as reactRender } from '@testing-library/react'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import type { ILicense } from '@kbn/licensing-plugin/public'; -import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; +import { FilesContext } from '@kbn/shared-ux-file-context'; + +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import type { CasesFeatures, CasesPermissions } from '../../../common/ui/types'; -import { CasesProvider } from '../../components/cases_context'; -import { createStartServicesMock } from '../lib/kibana/kibana_react.mock'; import type { StartServices } from '../../types'; import type { ReleasePhase } from '../../components/types'; + +import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; +import { CasesProvider } from '../../components/cases_context'; +import { createStartServicesMock } from '../lib/kibana/kibana_react.mock'; import { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry'; import { PersistableStateAttachmentTypeRegistry } from '../../client/attachment_framework/persistable_state_registry'; import { allCasesPermissions } from './permissions'; @@ -43,17 +51,35 @@ type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResul window.scrollTo = jest.fn(); +const mockGetFilesClient = () => { + const mockedFilesClient = createMockFilesClient() as unknown as DeeplyMockedKeys< + ScopedFilesClient + >; + + mockedFilesClient.getFileKind.mockImplementation(() => ({ + id: 'test', + maxSizeBytes: 10000, + http: {}, + })); + + return () => mockedFilesClient; +}; + +export const mockedTestProvidersOwner = [SECURITY_SOLUTION_OWNER]; + /** A utility for wrapping children in the providers required to run most tests */ const TestProvidersComponent: React.FC = ({ children, features, - owner = [SECURITY_SOLUTION_OWNER], + owner = mockedTestProvidersOwner, permissions = allCasesPermissions(), releasePhase = 'ga', externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(), persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry(), license, }) => { + const services = createStartServicesMock({ license }); + const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -67,7 +93,7 @@ const TestProvidersComponent: React.FC = ({ }, }); - const services = createStartServicesMock({ license }); + const getFilesClient = mockGetFilesClient(); return ( @@ -82,9 +108,10 @@ const TestProvidersComponent: React.FC = ({ features, owner, permissions, + getFilesClient, }} > - {children} + {children} @@ -104,6 +131,7 @@ export interface AppMockRenderer { coreStart: StartServices; queryClient: QueryClient; AppWrapper: React.FC<{ children: React.ReactElement }>; + getFilesClient: () => ScopedFilesClient; } export const testQueryClient = new QueryClient({ @@ -125,7 +153,7 @@ export const testQueryClient = new QueryClient({ export const createAppMockRenderer = ({ features, - owner = [SECURITY_SOLUTION_OWNER], + owner = mockedTestProvidersOwner, permissions = allCasesPermissions(), releasePhase = 'ga', externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(), @@ -147,6 +175,8 @@ export const createAppMockRenderer = ({ }, }); + const getFilesClient = mockGetFilesClient(); + const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( @@ -161,6 +191,7 @@ export const createAppMockRenderer = ({ owner, permissions, releasePhase, + getFilesClient, }} > {children} @@ -188,6 +219,7 @@ export const createAppMockRenderer = ({ AppWrapper, externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, }; }; diff --git a/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx b/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx index 6c33c86d29d51..d6597e31362e7 100644 --- a/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx +++ b/x-pack/plugins/cases/public/common/use_cases_toast.test.tsx @@ -25,6 +25,7 @@ const useKibanaMock = useKibana as jest.Mocked; describe('Use cases toast hook', () => { const successMock = jest.fn(); const errorMock = jest.fn(); + const dangerMock = jest.fn(); const getUrlForApp = jest.fn().mockReturnValue(`/app/cases/${mockCase.id}`); const navigateToUrl = jest.fn(); @@ -54,6 +55,7 @@ describe('Use cases toast hook', () => { return { addSuccess: successMock, addError: errorMock, + addDanger: dangerMock, }; }); @@ -352,4 +354,22 @@ describe('Use cases toast hook', () => { }); }); }); + + describe('showDangerToast', () => { + it('should show a danger toast', () => { + const { result } = renderHook( + () => { + return useCasesToast(); + }, + { wrapper: TestProviders } + ); + + result.current.showDangerToast('my danger toast'); + + expect(dangerMock).toHaveBeenCalledWith({ + className: 'eui-textBreakWord', + title: 'my danger toast', + }); + }); + }); }); diff --git a/x-pack/plugins/cases/public/common/use_cases_toast.tsx b/x-pack/plugins/cases/public/common/use_cases_toast.tsx index fd143345e2deb..26027905f8f0e 100644 --- a/x-pack/plugins/cases/public/common/use_cases_toast.tsx +++ b/x-pack/plugins/cases/public/common/use_cases_toast.tsx @@ -169,6 +169,9 @@ export const useCasesToast = () => { showSuccessToast: (title: string) => { toasts.addSuccess({ title, className: 'eui-textBreakWord' }); }, + showDangerToast: (title: string) => { + toasts.addDanger({ title, className: 'eui-textBreakWord' }); + }, showInfoToast: (title: string, text?: string) => { toasts.addInfo({ title, diff --git a/x-pack/plugins/cases/public/components/app/index.tsx b/x-pack/plugins/cases/public/components/app/index.tsx index 42ef9b658fea7..f53e7edf9356a 100644 --- a/x-pack/plugins/cases/public/components/app/index.tsx +++ b/x-pack/plugins/cases/public/components/app/index.tsx @@ -6,12 +6,15 @@ */ import React from 'react'; -import { APP_OWNER } from '../../../common/constants'; + +import type { ScopedFilesClient } from '@kbn/files-plugin/public'; + import type { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry'; import type { PersistableStateAttachmentTypeRegistry } from '../../client/attachment_framework/persistable_state_registry'; + +import { APP_OWNER } from '../../../common/constants'; import { getCasesLazy } from '../../client/ui/get_cases'; import { useApplicationCapabilities } from '../../common/lib/kibana'; - import { Wrapper } from '../wrappers'; import type { CasesRoutesProps } from './types'; @@ -20,11 +23,13 @@ export type CasesProps = CasesRoutesProps; interface CasesAppProps { externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry; persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; + getFilesClient: (scope: string) => ScopedFilesClient; } const CasesAppComponent: React.FC = ({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, }) => { const userCapabilities = useApplicationCapabilities(); @@ -33,6 +38,7 @@ const CasesAppComponent: React.FC = ({ {getCasesLazy({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry, + getFilesClient, owner: [APP_OWNER], useFetchAlertData: () => [false, {}], permissions: userCapabilities.generalCases, diff --git a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx index 87cd1fc732a30..b80cd5c2dbe74 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx @@ -16,6 +16,7 @@ import type { Case } from '../../../common/ui/types'; import { useAllCasesNavigation } from '../../common/navigation'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesToast } from '../../common/use_cases_toast'; +import { AttachmentActionType } from '../../client/attachment_framework/types'; interface CaseViewActions { caseData: Case; @@ -40,6 +41,7 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal const propertyActions = useMemo( () => [ { + type: AttachmentActionType.BUTTON as const, iconType: 'copyClipboard', label: i18n.COPY_ID_ACTION_LABEL, onClick: () => { @@ -50,6 +52,7 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal ...(currentExternalIncident != null && !isEmpty(currentExternalIncident?.externalUrl) ? [ { + type: AttachmentActionType.BUTTON as const, iconType: 'popout', label: i18n.VIEW_INCIDENT(currentExternalIncident?.externalTitle ?? ''), onClick: () => window.open(currentExternalIncident?.externalUrl, '_blank'), @@ -59,6 +62,7 @@ const ActionsComponent: React.FC = ({ caseData, currentExternal ...(permissions.delete ? [ { + type: AttachmentActionType.BUTTON as const, iconType: 'trash', label: i18n.DELETE_CASE(), color: 'danger' as const, diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index bf348124e4616..f247945c7c700 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -493,8 +493,9 @@ describe('CaseViewPage', () => { it('renders tabs correctly', async () => { const result = appMockRenderer.render(); await act(async () => { - expect(result.getByTestId('case-view-tab-title-alerts')).toBeTruthy(); expect(result.getByTestId('case-view-tab-title-activity')).toBeTruthy(); + expect(result.getByTestId('case-view-tab-title-alerts')).toBeTruthy(); + expect(result.getByTestId('case-view-tab-title-files')).toBeTruthy(); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx index a26793e501897..55245de4b22b2 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx @@ -18,6 +18,7 @@ import { useCasesTitleBreadcrumbs } from '../use_breadcrumbs'; import { WhitePageWrapperNoBorder } from '../wrappers'; import { CaseViewActivity } from './components/case_view_activity'; import { CaseViewAlerts } from './components/case_view_alerts'; +import { CaseViewFiles } from './components/case_view_files'; import { CaseViewMetrics } from './metrics'; import type { CaseViewPageProps } from './types'; import { useRefreshCaseViewPage } from './use_on_refresh_case_view_page'; @@ -140,6 +141,7 @@ export const CaseViewPage = React.memo( {activeTabId === CASE_VIEW_PAGE_TABS.ALERTS && features.alerts.enabled && ( )} + {activeTabId === CASE_VIEW_PAGE_TABS.FILES && } {timelineUi?.renderTimelineDetailsPanel ? timelineUi.renderTimelineDetailsPanel() : null} diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_tabs.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_tabs.test.tsx index a3da7d90267cf..bd532d95ba58b 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_tabs.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_tabs.test.tsx @@ -8,23 +8,28 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + import type { AppMockRenderer } from '../../common/mock'; -import { createAppMockRenderer } from '../../common/mock'; +import type { UseGetCase } from '../../containers/use_get_case'; +import type { CaseViewTabsProps } from './case_view_tabs'; + +import { CASE_VIEW_PAGE_TABS } from '../../../common/types'; import '../../common/mock/match_media'; +import { createAppMockRenderer } from '../../common/mock'; import { useCaseViewNavigation } from '../../common/navigation/hooks'; -import type { UseGetCase } from '../../containers/use_get_case'; import { useGetCase } from '../../containers/use_get_case'; import { CaseViewTabs } from './case_view_tabs'; import { caseData, defaultGetCase } from './mocks'; -import type { CaseViewTabsProps } from './case_view_tabs'; -import { CASE_VIEW_PAGE_TABS } from '../../../common/types'; +import { useGetCaseFileStats } from '../../containers/use_get_case_file_stats'; jest.mock('../../containers/use_get_case'); jest.mock('../../common/navigation/hooks'); jest.mock('../../common/hooks'); +jest.mock('../../containers/use_get_case_file_stats'); const useFetchCaseMock = useGetCase as jest.Mock; const useCaseViewNavigationMock = useCaseViewNavigation as jest.Mock; +const useGetCaseFileStatsMock = useGetCaseFileStats as jest.Mock; const mockGetCase = (props: Partial = {}) => { const data = { @@ -45,8 +50,10 @@ export const caseProps: CaseViewTabsProps = { describe('CaseViewTabs', () => { let appMockRenderer: AppMockRenderer; + const data = { total: 3 }; beforeEach(() => { + useGetCaseFileStatsMock.mockReturnValue({ data }); mockGetCase(); appMockRenderer = createAppMockRenderer(); @@ -62,6 +69,7 @@ describe('CaseViewTabs', () => { expect(await screen.findByTestId('case-view-tab-title-activity')).toBeInTheDocument(); expect(await screen.findByTestId('case-view-tab-title-alerts')).toBeInTheDocument(); + expect(await screen.findByTestId('case-view-tab-title-files')).toBeInTheDocument(); }); it('renders the activity tab by default', async () => { @@ -82,6 +90,40 @@ describe('CaseViewTabs', () => { ); }); + it('shows the files tab as active', async () => { + appMockRenderer.render(); + + expect(await screen.findByTestId('case-view-tab-title-files')).toHaveAttribute( + 'aria-selected', + 'true' + ); + }); + + it('shows the files tab with the correct count and colour', async () => { + appMockRenderer.render(); + + const badge = await screen.findByTestId('case-view-files-stats-badge'); + + expect(badge.getAttribute('class')).toMatch(/accent/); + expect(badge).toHaveTextContent('3'); + }); + + it('do not show count on the files tab if the call isLoading', async () => { + useGetCaseFileStatsMock.mockReturnValue({ isLoading: true, data }); + + appMockRenderer.render(); + + expect(screen.queryByTestId('case-view-files-stats-badge')).not.toBeInTheDocument(); + }); + + it('the files tab count has a different colour if the tab is not active', async () => { + appMockRenderer.render(); + + expect( + (await screen.findByTestId('case-view-files-stats-badge')).getAttribute('class') + ).not.toMatch(/accent/); + }); + it('navigates to the activity tab when the activity tab is clicked', async () => { const navigateToCaseViewMock = useCaseViewNavigationMock().navigateToCaseView; appMockRenderer.render(); @@ -109,4 +151,18 @@ describe('CaseViewTabs', () => { }); }); }); + + it('navigates to the files tab when the files tab is clicked', async () => { + const navigateToCaseViewMock = useCaseViewNavigationMock().navigateToCaseView; + appMockRenderer.render(); + + userEvent.click(await screen.findByTestId('case-view-tab-title-files')); + + await waitFor(() => { + expect(navigateToCaseViewMock).toHaveBeenCalledWith({ + detailName: caseData.id, + tabId: CASE_VIEW_PAGE_TABS.FILES, + }); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_tabs.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_tabs.tsx index 746311051f147..630248bf79d52 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_tabs.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_tabs.tsx @@ -5,20 +5,49 @@ * 2.0. */ -import { EuiBetaBadge, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; +import { EuiBetaBadge, EuiNotificationBadge, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { CASE_VIEW_PAGE_TABS } from '../../../common/types'; import { useCaseViewNavigation } from '../../common/navigation'; import { useCasesContext } from '../cases_context/use_cases_context'; import { EXPERIMENTAL_DESC, EXPERIMENTAL_LABEL } from '../header_page/translations'; -import { ACTIVITY_TAB, ALERTS_TAB } from './translations'; +import { ACTIVITY_TAB, ALERTS_TAB, FILES_TAB } from './translations'; import type { Case } from '../../../common'; +import { useGetCaseFileStats } from '../../containers/use_get_case_file_stats'; const ExperimentalBadge = styled(EuiBetaBadge)` margin-left: 5px; `; +const StyledNotificationBadge = styled(EuiNotificationBadge)` + margin-left: 5px; +`; + +const FilesTab = ({ + activeTab, + fileStatsData, + isLoading, +}: { + activeTab: string; + fileStatsData: { total: number } | undefined; + isLoading: boolean; +}) => ( + <> + {FILES_TAB} + {!isLoading && fileStatsData && ( + + {fileStatsData.total > 0 ? fileStatsData.total : 0} + + )} + +); + +FilesTab.displayName = 'FilesTab'; + export interface CaseViewTabsProps { caseData: Case; activeTab: CASE_VIEW_PAGE_TABS; @@ -27,6 +56,7 @@ export interface CaseViewTabsProps { export const CaseViewTabs = React.memo(({ caseData, activeTab }) => { const { features } = useCasesContext(); const { navigateToCaseView } = useCaseViewNavigation(); + const { data: fileStatsData, isLoading } = useGetCaseFileStats({ caseId: caseData.id }); const tabs = useMemo( () => [ @@ -56,8 +86,14 @@ export const CaseViewTabs = React.memo(({ caseData, activeTab }, ] : []), + { + id: CASE_VIEW_PAGE_TABS.FILES, + name: ( + + ), + }, ], - [features.alerts.enabled, features.alerts.isExperimental] + [activeTab, features.alerts.enabled, features.alerts.isExperimental, fileStatsData, isLoading] ); const renderTabs = useCallback(() => { diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_files.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_files.test.tsx new file mode 100644 index 0000000000000..dc5b937bd8781 --- /dev/null +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_files.test.tsx @@ -0,0 +1,114 @@ +/* + * 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 { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { Case } from '../../../../common'; +import type { AppMockRenderer } from '../../../common/mock'; + +import { createAppMockRenderer } from '../../../common/mock'; +import { alertCommentWithIndices, basicCase } from '../../../containers/mock'; +import { useGetCaseFiles } from '../../../containers/use_get_case_files'; +import { CaseViewFiles, DEFAULT_CASE_FILES_FILTERING_OPTIONS } from './case_view_files'; + +jest.mock('../../../containers/use_get_case_files'); + +const useGetCaseFilesMock = useGetCaseFiles as jest.Mock; + +const caseData: Case = { + ...basicCase, + comments: [...basicCase.comments, alertCommentWithIndices], +}; + +describe('Case View Page files tab', () => { + let appMockRender: AppMockRenderer; + + useGetCaseFilesMock.mockReturnValue({ + data: { files: [], total: 11 }, + isLoading: false, + }); + + beforeEach(() => { + appMockRender = createAppMockRenderer(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should render the utility bar for the files table', async () => { + appMockRender.render(); + + expect((await screen.findAllByTestId('cases-files-add')).length).toBe(2); + expect(await screen.findByTestId('cases-files-search')).toBeInTheDocument(); + }); + + it('should render the files table', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table')).toBeInTheDocument(); + }); + + it('clicking table pagination triggers calls to useGetCaseFiles', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('pagination-button-next')); + + await waitFor(() => + expect(useGetCaseFilesMock).toHaveBeenCalledWith({ + caseId: basicCase.id, + page: DEFAULT_CASE_FILES_FILTERING_OPTIONS.page + 1, + perPage: DEFAULT_CASE_FILES_FILTERING_OPTIONS.perPage, + }) + ); + }); + + it('changing perPage value triggers calls to useGetCaseFiles', async () => { + const targetPagination = 50; + + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('tablePaginationPopoverButton')); + + const pageSizeOption = screen.getByTestId('tablePagination-50-rows'); + + pageSizeOption.style.pointerEvents = 'all'; + + userEvent.click(pageSizeOption); + + await waitFor(() => + expect(useGetCaseFilesMock).toHaveBeenCalledWith({ + caseId: basicCase.id, + page: DEFAULT_CASE_FILES_FILTERING_OPTIONS.page, + perPage: targetPagination, + }) + ); + }); + + it('search by word triggers calls to useGetCaseFiles', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table')).toBeInTheDocument(); + + await userEvent.type(screen.getByTestId('cases-files-search'), 'Foobar{enter}'); + + await waitFor(() => + expect(useGetCaseFilesMock).toHaveBeenCalledWith({ + caseId: basicCase.id, + page: DEFAULT_CASE_FILES_FILTERING_OPTIONS.page, + perPage: DEFAULT_CASE_FILES_FILTERING_OPTIONS.perPage, + searchTerm: 'Foobar', + }) + ); + }); +}); diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_files.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_files.tsx new file mode 100644 index 0000000000000..54693acfa2390 --- /dev/null +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_files.tsx @@ -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 { isEqual } from 'lodash/fp'; +import React, { useCallback, useMemo, useState } from 'react'; + +import type { Criteria } from '@elastic/eui'; +import type { FileJSON } from '@kbn/shared-ux-file-types'; + +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; + +import type { Case } from '../../../../common/ui/types'; +import type { CaseFilesFilteringOptions } from '../../../containers/use_get_case_files'; + +import { CASE_VIEW_PAGE_TABS } from '../../../../common/types'; +import { useGetCaseFiles } from '../../../containers/use_get_case_files'; +import { FilesTable } from '../../files/files_table'; +import { CaseViewTabs } from '../case_view_tabs'; +import { FilesUtilityBar } from '../../files/files_utility_bar'; + +interface CaseViewFilesProps { + caseData: Case; +} + +export const DEFAULT_CASE_FILES_FILTERING_OPTIONS = { + page: 0, + perPage: 10, +}; + +export const CaseViewFiles = ({ caseData }: CaseViewFilesProps) => { + const [filteringOptions, setFilteringOptions] = useState( + DEFAULT_CASE_FILES_FILTERING_OPTIONS + ); + const { + data: caseFiles, + isLoading, + isPreviousData, + } = useGetCaseFiles({ + ...filteringOptions, + caseId: caseData.id, + }); + + const onTableChange = useCallback( + ({ page }: Criteria) => { + if (page && !isPreviousData) { + setFilteringOptions({ + ...filteringOptions, + page: page.index, + perPage: page.size, + }); + } + }, + [filteringOptions, isPreviousData] + ); + + const onSearchChange = useCallback( + (newSearch) => { + const trimSearch = newSearch.trim(); + if (!isEqual(trimSearch, filteringOptions.searchTerm)) { + setFilteringOptions({ + ...filteringOptions, + searchTerm: trimSearch, + }); + } + }, + [filteringOptions] + ); + + const pagination = useMemo( + () => ({ + pageIndex: filteringOptions.page, + pageSize: filteringOptions.perPage, + totalItemCount: caseFiles?.total ?? 0, + pageSizeOptions: [10, 25, 50], + showPerPageOptions: true, + }), + [filteringOptions.page, filteringOptions.perPage, caseFiles?.total] + ); + + return ( + + + + + + + + + + + + ); +}; + +CaseViewFiles.displayName = 'CaseViewFiles'; diff --git a/x-pack/plugins/cases/public/components/case_view/translations.ts b/x-pack/plugins/cases/public/components/case_view/translations.ts index d71c56fc97fca..8fc80c1a0aba3 100644 --- a/x-pack/plugins/cases/public/components/case_view/translations.ts +++ b/x-pack/plugins/cases/public/components/case_view/translations.ts @@ -165,6 +165,10 @@ export const ALERTS_TAB = i18n.translate('xpack.cases.caseView.tabs.alerts', { defaultMessage: 'Alerts', }); +export const FILES_TAB = i18n.translate('xpack.cases.caseView.tabs.files', { + defaultMessage: 'Files', +}); + export const ALERTS_EMPTY_DESCRIPTION = i18n.translate( 'xpack.cases.caseView.tabs.alerts.emptyDescription', { diff --git a/x-pack/plugins/cases/public/components/cases_context/index.tsx b/x-pack/plugins/cases/public/components/cases_context/index.tsx index 4e31fffdd7701..dc7eac6381b4d 100644 --- a/x-pack/plugins/cases/public/components/cases_context/index.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/index.tsx @@ -5,25 +5,34 @@ * 2.0. */ -import type { Dispatch } from 'react'; -import React, { useState, useEffect, useReducer } from 'react'; +import type { Dispatch, ReactNode } from 'react'; + import { merge } from 'lodash'; +import React, { useCallback, useEffect, useState, useReducer } from 'react'; import useDeepCompareEffect from 'react-use/lib/useDeepCompareEffect'; -import { DEFAULT_FEATURES } from '../../../common/constants'; -import { DEFAULT_BASE_PATH } from '../../common/navigation'; -import { useApplication } from './use_application'; + +import type { ScopedFilesClient } from '@kbn/files-plugin/public'; + +import { FilesContext } from '@kbn/shared-ux-file-context'; + import type { CasesContextStoreAction } from './cases_context_reducer'; -import { casesContextReducer, getInitialCasesContextState } from './cases_context_reducer'; import type { CasesFeaturesAllRequired, CasesFeatures, CasesPermissions, } from '../../containers/types'; -import { CasesGlobalComponents } from './cases_global_components'; import type { ReleasePhase } from '../types'; import type { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry'; import type { PersistableStateAttachmentTypeRegistry } from '../../client/attachment_framework/persistable_state_registry'; +import { CasesGlobalComponents } from './cases_global_components'; +import { DEFAULT_FEATURES } from '../../../common/constants'; +import { constructFileKindIdByOwner } from '../../../common/files'; +import { DEFAULT_BASE_PATH } from '../../common/navigation'; +import { useApplication } from './use_application'; +import { casesContextReducer, getInitialCasesContextState } from './cases_context_reducer'; +import { isRegisteredOwner } from '../../files'; + export type CasesContextValueDispatch = Dispatch; export interface CasesContextValue { @@ -50,6 +59,7 @@ export interface CasesContextProps basePath?: string; features?: CasesFeatures; releasePhase?: ReleasePhase; + getFilesClient: (scope: string) => ScopedFilesClient; } export const CasesContext = React.createContext(undefined); @@ -69,6 +79,7 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({ basePath = DEFAULT_BASE_PATH, features = {}, releasePhase = 'ga', + getFilesClient, }, }) => { const { appId, appTitle } = useApplication(); @@ -114,10 +125,35 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({ } }, [appTitle, appId]); + const applyFilesContext = useCallback( + (contextChildren: ReactNode) => { + if (owner.length === 0) { + return contextChildren; + } + + if (isRegisteredOwner(owner[0])) { + return ( + + {contextChildren} + + ); + } else { + throw new Error( + 'Invalid owner provided to cases context. See https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/README.md#casescontext-setup' + ); + } + }, + [getFilesClient, owner] + ); + return isCasesContextValue(value) ? ( - - {children} + {applyFilesContext( + <> + + {children} + + )} ) : null; }; diff --git a/x-pack/plugins/cases/public/components/files/add_file.test.tsx b/x-pack/plugins/cases/public/components/files/add_file.test.tsx new file mode 100644 index 0000000000000..911f8a4df538d --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/add_file.test.tsx @@ -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 React from 'react'; + +import type { FileUploadProps } from '@kbn/shared-ux-file-upload'; + +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AppMockRenderer } from '../../common/mock'; + +import * as api from '../../containers/api'; +import { + buildCasesPermissions, + createAppMockRenderer, + mockedTestProvidersOwner, +} from '../../common/mock'; +import { AddFile } from './add_file'; +import { useToasts } from '../../common/lib/kibana'; + +import { useCreateAttachments } from '../../containers/use_create_attachments'; +import { basicCaseId, basicFileMock } from '../../containers/mock'; + +jest.mock('../../containers/api'); +jest.mock('../../containers/use_create_attachments'); +jest.mock('../../common/lib/kibana'); + +const useToastsMock = useToasts as jest.Mock; +const useCreateAttachmentsMock = useCreateAttachments as jest.Mock; + +const mockedExternalReferenceId = 'externalReferenceId'; +const validateMetadata = jest.fn(); +const mockFileUpload = jest + .fn() + .mockImplementation( + ({ + kind, + onDone, + onError, + meta, + }: Required>) => ( + <> + + + + + ) + ); + +jest.mock('@kbn/shared-ux-file-upload', () => { + const original = jest.requireActual('@kbn/shared-ux-file-upload'); + return { + ...original, + FileUpload: (props: unknown) => mockFileUpload(props), + }; +}); + +describe('AddFile', () => { + let appMockRender: AppMockRenderer; + + const successMock = jest.fn(); + const errorMock = jest.fn(); + + useToastsMock.mockImplementation(() => { + return { + addSuccess: successMock, + addError: errorMock, + }; + }); + + const createAttachmentsMock = jest.fn(); + + useCreateAttachmentsMock.mockReturnValue({ + isLoading: false, + createAttachments: createAttachmentsMock, + }); + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders correctly', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-add')).toBeInTheDocument(); + }); + + it('AddFile is not rendered if user has no create permission', async () => { + appMockRender = createAppMockRenderer({ + permissions: buildCasesPermissions({ create: false }), + }); + + appMockRender.render(); + + expect(screen.queryByTestId('cases-files-add')).not.toBeInTheDocument(); + }); + + it('AddFile is not rendered if user has no update permission', async () => { + appMockRender = createAppMockRenderer({ + permissions: buildCasesPermissions({ update: false }), + }); + + appMockRender.render(); + + expect(screen.queryByTestId('cases-files-add')).not.toBeInTheDocument(); + }); + + it('clicking button renders modal', async () => { + appMockRender.render(); + + userEvent.click(await screen.findByTestId('cases-files-add')); + + expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument(); + }); + + it('createAttachments called with right parameters', async () => { + appMockRender.render(); + + userEvent.click(await screen.findByTestId('cases-files-add')); + + expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('testOnDone')); + + await waitFor(() => + expect(createAttachmentsMock).toBeCalledWith({ + caseId: 'foobar', + caseOwner: mockedTestProvidersOwner[0], + data: [ + { + externalReferenceAttachmentTypeId: '.files', + externalReferenceId: mockedExternalReferenceId, + externalReferenceMetadata: { + files: [ + { + created: '2020-02-19T23:06:33.798Z', + extension: 'png', + mimeType: 'image/png', + name: 'my-super-cool-screenshot', + }, + ], + }, + externalReferenceStorage: { soType: 'file', type: 'savedObject' }, + type: 'externalReference', + }, + ], + throwOnError: true, + updateCase: expect.any(Function), + }) + ); + + await waitFor(() => + expect(successMock).toHaveBeenCalledWith({ + className: 'eui-textBreakWord', + title: `File ${basicFileMock.name} uploaded successfully`, + }) + ); + }); + + it('failed upload displays error toast', async () => { + appMockRender.render(); + + userEvent.click(await screen.findByTestId('cases-files-add')); + + expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('testOnError')); + + expect(errorMock).toHaveBeenCalledWith( + { name: 'upload error name', message: 'upload error message' }, + { + title: 'Failed to upload file', + } + ); + }); + + it('correct metadata is passed to FileUpload component', async () => { + const caseId = 'foobar'; + + appMockRender.render(); + + userEvent.click(await screen.findByTestId('cases-files-add')); + + expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('testMetadata')); + + await waitFor(() => + expect(validateMetadata).toHaveBeenCalledWith({ + caseIds: [caseId], + owner: [mockedTestProvidersOwner[0]], + }) + ); + }); + + it('deleteFileAttachments is called correctly if createAttachments fails', async () => { + const spyOnDeleteFileAttachments = jest.spyOn(api, 'deleteFileAttachments'); + + createAttachmentsMock.mockImplementation(() => { + throw new Error(); + }); + + appMockRender.render(); + + userEvent.click(await screen.findByTestId('cases-files-add')); + + expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('testOnDone')); + + expect(spyOnDeleteFileAttachments).toHaveBeenCalledWith({ + caseId: basicCaseId, + fileIds: [mockedExternalReferenceId], + signal: expect.any(AbortSignal), + }); + + createAttachmentsMock.mockRestore(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/add_file.tsx b/x-pack/plugins/cases/public/components/files/add_file.tsx new file mode 100644 index 0000000000000..a3c9fba1188ea --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/add_file.tsx @@ -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 { + EuiButton, + EuiFlexItem, + EuiModal, + EuiModalBody, + EuiModalHeader, + EuiModalHeaderTitle, +} from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; + +import type { UploadedFile } from '@kbn/shared-ux-file-upload/src/file_upload'; + +import { FILE_SO_TYPE } from '@kbn/files-plugin/common'; +import { FileUpload } from '@kbn/shared-ux-file-upload'; + +import { constructFileKindIdByOwner } from '../../../common/files'; +import type { Owner } from '../../../common/constants/types'; + +import { CommentType, ExternalReferenceStorageType } from '../../../common'; +import { FILE_ATTACHMENT_TYPE } from '../../../common/api'; +import { useCasesToast } from '../../common/use_cases_toast'; +import { useCreateAttachments } from '../../containers/use_create_attachments'; +import { useCasesContext } from '../cases_context/use_cases_context'; +import * as i18n from './translations'; +import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page'; +import { deleteFileAttachments } from '../../containers/api'; + +interface AddFileProps { + caseId: string; +} + +const AddFileComponent: React.FC = ({ caseId }) => { + const { owner, permissions } = useCasesContext(); + const { showDangerToast, showErrorToast, showSuccessToast } = useCasesToast(); + const { isLoading, createAttachments } = useCreateAttachments(); + const refreshAttachmentsTable = useRefreshCaseViewPage(); + const [isModalVisible, setIsModalVisible] = useState(false); + + const closeModal = () => setIsModalVisible(false); + const showModal = () => setIsModalVisible(true); + + const onError = useCallback( + (error) => { + showErrorToast(error, { + title: i18n.FAILED_UPLOAD, + }); + }, + [showErrorToast] + ); + + const onUploadDone = useCallback( + async (chosenFiles: UploadedFile[]) => { + if (chosenFiles.length === 0) { + showDangerToast(i18n.FAILED_UPLOAD); + return; + } + + const file = chosenFiles[0]; + + try { + await createAttachments({ + caseId, + caseOwner: owner[0], + data: [ + { + type: CommentType.externalReference, + externalReferenceId: file.id, + externalReferenceStorage: { + type: ExternalReferenceStorageType.savedObject, + soType: FILE_SO_TYPE, + }, + externalReferenceAttachmentTypeId: FILE_ATTACHMENT_TYPE, + externalReferenceMetadata: { + files: [ + { + name: file.fileJSON.name, + extension: file.fileJSON.extension ?? '', + mimeType: file.fileJSON.mimeType ?? '', + created: file.fileJSON.created, + }, + ], + }, + }, + ], + updateCase: refreshAttachmentsTable, + throwOnError: true, + }); + + showSuccessToast(i18n.SUCCESSFUL_UPLOAD_FILE_NAME(file.fileJSON.name)); + } catch (error) { + // error toast is handled inside createAttachments + + // we need to delete the file if attachment creation failed + const abortCtrlRef = new AbortController(); + return deleteFileAttachments({ caseId, fileIds: [file.id], signal: abortCtrlRef.signal }); + } + + closeModal(); + }, + [caseId, createAttachments, owner, refreshAttachmentsTable, showDangerToast, showSuccessToast] + ); + + return permissions.create && permissions.update ? ( + + + {i18n.ADD_FILE} + + {isModalVisible && ( + + + {i18n.ADD_FILE} + + + + + + )} + + ) : null; +}; +AddFileComponent.displayName = 'AddFile'; + +export const AddFile = React.memo(AddFileComponent); diff --git a/x-pack/plugins/cases/public/components/files/file_delete_button.test.tsx b/x-pack/plugins/cases/public/components/files/file_delete_button.test.tsx new file mode 100644 index 0000000000000..38ed8a20eab40 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_delete_button.test.tsx @@ -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 React from 'react'; + +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AppMockRenderer } from '../../common/mock'; + +import { buildCasesPermissions, createAppMockRenderer } from '../../common/mock'; +import { basicCaseId, basicFileMock } from '../../containers/mock'; +import { useDeleteFileAttachment } from '../../containers/use_delete_file_attachment'; +import { FileDeleteButton } from './file_delete_button'; + +jest.mock('../../containers/use_delete_file_attachment'); + +const useDeleteFileAttachmentMock = useDeleteFileAttachment as jest.Mock; + +describe('FileDeleteButton', () => { + let appMockRender: AppMockRenderer; + const mutate = jest.fn(); + + useDeleteFileAttachmentMock.mockReturnValue({ isLoading: false, mutate }); + + describe('isIcon', () => { + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders delete button correctly', async () => { + appMockRender.render( + + ); + + expect(await screen.findByTestId('cases-files-delete-button')).toBeInTheDocument(); + + expect(useDeleteFileAttachmentMock).toBeCalledTimes(1); + }); + + it('clicking delete button opens the confirmation modal', async () => { + appMockRender.render( + + ); + + const deleteButton = await screen.findByTestId('cases-files-delete-button'); + + expect(deleteButton).toBeInTheDocument(); + + userEvent.click(deleteButton); + + expect(await screen.findByTestId('property-actions-confirm-modal')).toBeInTheDocument(); + }); + + it('clicking delete button in the confirmation modal calls deleteFileAttachment with proper params', async () => { + appMockRender.render( + + ); + + const deleteButton = await screen.findByTestId('cases-files-delete-button'); + + expect(deleteButton).toBeInTheDocument(); + + userEvent.click(deleteButton); + + expect(await screen.findByTestId('property-actions-confirm-modal')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('confirmModalConfirmButton')); + + await waitFor(() => { + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith({ + caseId: basicCaseId, + fileId: basicFileMock.id, + }); + }); + }); + + it('delete button is not rendered if user has no delete permission', async () => { + appMockRender = createAppMockRenderer({ + permissions: buildCasesPermissions({ delete: false }), + }); + + appMockRender.render( + + ); + + expect(screen.queryByTestId('cases-files-delete-button')).not.toBeInTheDocument(); + }); + }); + + describe('not isIcon', () => { + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders delete button correctly', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-delete-button')).toBeInTheDocument(); + + expect(useDeleteFileAttachmentMock).toBeCalledTimes(1); + }); + + it('clicking delete button opens the confirmation modal', async () => { + appMockRender.render(); + + const deleteButton = await screen.findByTestId('cases-files-delete-button'); + + expect(deleteButton).toBeInTheDocument(); + + userEvent.click(deleteButton); + + expect(await screen.findByTestId('property-actions-confirm-modal')).toBeInTheDocument(); + }); + + it('clicking delete button in the confirmation modal calls deleteFileAttachment with proper params', async () => { + appMockRender.render(); + + const deleteButton = await screen.findByTestId('cases-files-delete-button'); + + expect(deleteButton).toBeInTheDocument(); + + userEvent.click(deleteButton); + + expect(await screen.findByTestId('property-actions-confirm-modal')).toBeInTheDocument(); + + userEvent.click(await screen.findByTestId('confirmModalConfirmButton')); + + await waitFor(() => { + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith({ + caseId: basicCaseId, + fileId: basicFileMock.id, + }); + }); + }); + + it('delete button is not rendered if user has no delete permission', async () => { + appMockRender = createAppMockRenderer({ + permissions: buildCasesPermissions({ delete: false }), + }); + + appMockRender.render(); + + expect(screen.queryByTestId('cases-files-delete-button')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/file_delete_button.tsx b/x-pack/plugins/cases/public/components/files/file_delete_button.tsx new file mode 100644 index 0000000000000..f344b942aa2c2 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_delete_button.tsx @@ -0,0 +1,62 @@ +/* + * 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 { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; +import * as i18n from './translations'; +import { useDeleteFileAttachment } from '../../containers/use_delete_file_attachment'; +import { useDeletePropertyAction } from '../user_actions/property_actions/use_delete_property_action'; +import { DeleteAttachmentConfirmationModal } from '../user_actions/delete_attachment_confirmation_modal'; +import { useCasesContext } from '../cases_context/use_cases_context'; + +interface FileDeleteButtonProps { + caseId: string; + fileId: string; + isIcon?: boolean; +} + +const FileDeleteButtonComponent: React.FC = ({ caseId, fileId, isIcon }) => { + const { permissions } = useCasesContext(); + const { isLoading, mutate: deleteFileAttachment } = useDeleteFileAttachment(); + + const { showDeletionModal, onModalOpen, onConfirm, onCancel } = useDeletePropertyAction({ + onDelete: () => deleteFileAttachment({ caseId, fileId }), + }); + + const buttonProps = { + iconType: 'trash', + 'aria-label': i18n.DELETE_FILE, + color: 'danger' as const, + isDisabled: isLoading, + onClick: onModalOpen, + 'data-test-subj': 'cases-files-delete-button', + }; + + return permissions.delete ? ( + <> + {isIcon ? ( + + ) : ( + {i18n.DELETE_FILE} + )} + {showDeletionModal ? ( + + ) : null} + + ) : ( + <> + ); +}; +FileDeleteButtonComponent.displayName = 'FileDeleteButton'; + +export const FileDeleteButton = React.memo(FileDeleteButtonComponent); diff --git a/x-pack/plugins/cases/public/components/files/file_download_button.test.tsx b/x-pack/plugins/cases/public/components/files/file_download_button.test.tsx new file mode 100644 index 0000000000000..0c729900a9ea6 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_download_button.test.tsx @@ -0,0 +1,57 @@ +/* + * 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 { screen } from '@testing-library/react'; + +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer, mockedTestProvidersOwner } from '../../common/mock'; +import { FileDownloadButton } from './file_download_button'; +import { basicFileMock } from '../../containers/mock'; +import { constructFileKindIdByOwner } from '../../../common/files'; + +describe('FileDownloadButton', () => { + let appMockRender: AppMockRenderer; + + describe('isIcon', () => { + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders download button with correct href', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-download-button')).toBeInTheDocument(); + + expect(appMockRender.getFilesClient().getDownloadHref).toBeCalledTimes(1); + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + id: basicFileMock.id, + }); + }); + }); + + describe('not isIcon', () => { + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders download button with correct href', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-download-button')).toBeInTheDocument(); + + expect(appMockRender.getFilesClient().getDownloadHref).toBeCalledTimes(1); + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + id: basicFileMock.id, + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/file_download_button.tsx b/x-pack/plugins/cases/public/components/files/file_download_button.tsx new file mode 100644 index 0000000000000..856c7000ba9d5 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_download_button.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; +import { useFilesContext } from '@kbn/shared-ux-file-context'; + +import type { Owner } from '../../../common/constants/types'; + +import { constructFileKindIdByOwner } from '../../../common/files'; +import { useCasesContext } from '../cases_context/use_cases_context'; +import * as i18n from './translations'; + +interface FileDownloadButtonProps { + fileId: string; + isIcon?: boolean; +} + +const FileDownloadButtonComponent: React.FC = ({ fileId, isIcon }) => { + const { owner } = useCasesContext(); + const { client: filesClient } = useFilesContext(); + + const buttonProps = { + iconType: 'download', + 'aria-label': i18n.DOWNLOAD_FILE, + href: filesClient.getDownloadHref({ + fileKind: constructFileKindIdByOwner(owner[0] as Owner), + id: fileId, + }), + 'data-test-subj': 'cases-files-download-button', + }; + + return isIcon ? ( + + ) : ( + {i18n.DOWNLOAD_FILE} + ); +}; +FileDownloadButtonComponent.displayName = 'FileDownloadButton'; + +export const FileDownloadButton = React.memo(FileDownloadButtonComponent); diff --git a/x-pack/plugins/cases/public/components/files/file_name_link.test.tsx b/x-pack/plugins/cases/public/components/files/file_name_link.test.tsx new file mode 100644 index 0000000000000..39c62322dedeb --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_name_link.test.tsx @@ -0,0 +1,58 @@ +/* + * 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 { screen } from '@testing-library/react'; + +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import userEvent from '@testing-library/user-event'; +import { FileNameLink } from './file_name_link'; +import { basicFileMock } from '../../containers/mock'; + +describe('FileNameLink', () => { + let appMockRender: AppMockRenderer; + + const defaultProps = { + file: basicFileMock, + showPreview: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders clickable name if file is image', async () => { + appMockRender.render(); + + const nameLink = await screen.findByTestId('cases-files-name-link'); + + expect(nameLink).toBeInTheDocument(); + + userEvent.click(nameLink); + + expect(defaultProps.showPreview).toHaveBeenCalled(); + }); + + it('renders simple text name if file is not image', async () => { + appMockRender.render( + + ); + + const nameLink = await screen.findByTestId('cases-files-name-text'); + + expect(nameLink).toBeInTheDocument(); + + userEvent.click(nameLink); + + expect(defaultProps.showPreview).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/file_name_link.tsx b/x-pack/plugins/cases/public/components/files/file_name_link.tsx new file mode 100644 index 0000000000000..4c9aedc3ad85b --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_name_link.tsx @@ -0,0 +1,44 @@ +/* + * 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 { EuiLink } from '@elastic/eui'; + +import type { FileJSON } from '@kbn/shared-ux-file-types'; +import * as i18n from './translations'; +import { isImage } from './utils'; + +interface FileNameLinkProps { + file: Pick; + showPreview: () => void; +} + +const FileNameLinkComponent: React.FC = ({ file, showPreview }) => { + let fileName = file.name; + + if (typeof file.extension !== 'undefined') { + fileName += `.${file.extension}`; + } + + if (isImage(file)) { + return ( + + {fileName} + + ); + } else { + return ( + + {fileName} + + ); + } +}; +FileNameLinkComponent.displayName = 'FileNameLink'; + +export const FileNameLink = React.memo(FileNameLinkComponent); diff --git a/x-pack/plugins/cases/public/components/files/file_preview.test.tsx b/x-pack/plugins/cases/public/components/files/file_preview.test.tsx new file mode 100644 index 0000000000000..b02df3a82228f --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_preview.test.tsx @@ -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 React from 'react'; + +import { screen, waitFor } from '@testing-library/react'; + +import type { AppMockRenderer } from '../../common/mock'; + +import { constructFileKindIdByOwner } from '../../../common/files'; +import { createAppMockRenderer, mockedTestProvidersOwner } from '../../common/mock'; +import { basicFileMock } from '../../containers/mock'; +import { FilePreview } from './file_preview'; + +describe('FilePreview', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('FilePreview rendered correctly', async () => { + appMockRender.render(); + + await waitFor(() => + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + id: basicFileMock.id, + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + }) + ); + + expect(await screen.findByTestId('cases-files-image-preview')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/file_preview.tsx b/x-pack/plugins/cases/public/components/files/file_preview.tsx new file mode 100644 index 0000000000000..1bb91c5b53ff7 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_preview.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 from 'react'; +import styled from 'styled-components'; + +import type { FileJSON } from '@kbn/shared-ux-file-types'; + +import { EuiOverlayMask, EuiFocusTrap, EuiImage } from '@elastic/eui'; +import { useFilesContext } from '@kbn/shared-ux-file-context'; + +import type { Owner } from '../../../common/constants/types'; + +import { constructFileKindIdByOwner } from '../../../common/files'; +import { useCasesContext } from '../cases_context/use_cases_context'; + +interface FilePreviewProps { + closePreview: () => void; + selectedFile: Pick; +} + +const StyledOverlayMask = styled(EuiOverlayMask)` + padding-block-end: 0vh !important; + + img { + max-height: 85vh; + max-width: 85vw; + object-fit: contain; + } +`; + +export const FilePreview = ({ closePreview, selectedFile }: FilePreviewProps) => { + const { client: filesClient } = useFilesContext(); + const { owner } = useCasesContext(); + + return ( + + + + + + ); +}; + +FilePreview.displayName = 'FilePreview'; diff --git a/x-pack/plugins/cases/public/components/files/file_type.test.tsx b/x-pack/plugins/cases/public/components/files/file_type.test.tsx new file mode 100644 index 0000000000000..8d4fd4c0eabde --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_type.test.tsx @@ -0,0 +1,187 @@ +/* + * 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 { JsonValue } from '@kbn/utility-types'; + +import { screen } from '@testing-library/react'; + +import type { ExternalReferenceAttachmentViewProps } from '../../client/attachment_framework/types'; +import type { AppMockRenderer } from '../../common/mock'; + +import { AttachmentActionType } from '../../client/attachment_framework/types'; +import { FILE_ATTACHMENT_TYPE } from '../../../common/api'; +import { createAppMockRenderer } from '../../common/mock'; +import { basicCase, basicFileMock } from '../../containers/mock'; +import { getFileType } from './file_type'; +import userEvent from '@testing-library/user-event'; + +describe('getFileType', () => { + const fileType = getFileType(); + + it('invalid props return blank FileAttachmentViewObject', () => { + expect(fileType).toStrictEqual({ + id: FILE_ATTACHMENT_TYPE, + icon: 'document', + displayName: 'File Attachment Type', + getAttachmentViewObject: expect.any(Function), + }); + }); + + describe('getFileAttachmentViewObject', () => { + let appMockRender: AppMockRenderer; + + const attachmentViewProps = { + externalReferenceId: basicFileMock.id, + externalReferenceMetadata: { files: [basicFileMock] }, + caseData: { title: basicCase.title, id: basicCase.id }, + } as unknown as ExternalReferenceAttachmentViewProps; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('event renders a clickable name if the file is an image', async () => { + appMockRender = createAppMockRenderer(); + + // @ts-ignore + appMockRender.render(fileType.getAttachmentViewObject({ ...attachmentViewProps }).event); + + expect(await screen.findByText('my-super-cool-screenshot.png')).toBeInTheDocument(); + expect(screen.queryByTestId('cases-files-image-preview')).not.toBeInTheDocument(); + }); + + it('clicking the name rendered in event opens the file preview', async () => { + appMockRender = createAppMockRenderer(); + + // @ts-ignore + appMockRender.render(fileType.getAttachmentViewObject({ ...attachmentViewProps }).event); + + userEvent.click(await screen.findByText('my-super-cool-screenshot.png')); + expect(await screen.findByTestId('cases-files-image-preview')).toBeInTheDocument(); + }); + + it('getActions renders a download button', async () => { + appMockRender = createAppMockRenderer(); + + const attachmentViewObject = fileType.getAttachmentViewObject({ ...attachmentViewProps }); + + expect(attachmentViewObject).not.toBeUndefined(); + + // @ts-ignore + const actions = attachmentViewObject.getActions(); + + expect(actions.length).toBe(2); + expect(actions[0]).toStrictEqual({ + type: AttachmentActionType.CUSTOM, + isPrimary: false, + label: 'Download File', + render: expect.any(Function), + }); + + // @ts-ignore + appMockRender.render(actions[0].render()); + + expect(await screen.findByTestId('cases-files-download-button')).toBeInTheDocument(); + }); + + it('getActions renders a delete button', async () => { + appMockRender = createAppMockRenderer(); + + const attachmentViewObject = fileType.getAttachmentViewObject({ ...attachmentViewProps }); + + expect(attachmentViewObject).not.toBeUndefined(); + + // @ts-ignore + const actions = attachmentViewObject.getActions(); + + expect(actions.length).toBe(2); + expect(actions[1]).toStrictEqual({ + type: AttachmentActionType.CUSTOM, + isPrimary: false, + label: 'Delete File', + render: expect.any(Function), + }); + + // @ts-ignore + appMockRender.render(actions[1].render()); + + expect(await screen.findByTestId('cases-files-delete-button')).toBeInTheDocument(); + }); + + it('clicking the delete button in actions opens deletion modal', async () => { + appMockRender = createAppMockRenderer(); + + const attachmentViewObject = fileType.getAttachmentViewObject({ ...attachmentViewProps }); + + expect(attachmentViewObject).not.toBeUndefined(); + + // @ts-ignore + const actions = attachmentViewObject.getActions(); + + expect(actions.length).toBe(2); + expect(actions[1]).toStrictEqual({ + type: AttachmentActionType.CUSTOM, + isPrimary: false, + label: 'Delete File', + render: expect.any(Function), + }); + + // @ts-ignore + appMockRender.render(actions[1].render()); + + const deleteButton = await screen.findByTestId('cases-files-delete-button'); + + expect(deleteButton).toBeInTheDocument(); + + userEvent.click(deleteButton); + + expect(await screen.findByTestId('property-actions-confirm-modal')).toBeInTheDocument(); + }); + + it('empty externalReferenceMetadata returns blank FileAttachmentViewObject', () => { + expect( + fileType.getAttachmentViewObject({ ...attachmentViewProps, externalReferenceMetadata: {} }) + ).toEqual({ + event: 'added an unknown file', + hideDefaultActions: true, + timelineAvatar: 'document', + type: 'regular', + getActions: expect.any(Function), + }); + }); + + it('timelineAvatar is image if file is an image', () => { + expect(fileType.getAttachmentViewObject(attachmentViewProps)).toEqual( + expect.objectContaining({ + timelineAvatar: 'image', + }) + ); + }); + + it('timelineAvatar is document if file is not an image', () => { + expect( + fileType.getAttachmentViewObject({ + ...attachmentViewProps, + externalReferenceMetadata: { + files: [{ ...basicFileMock, mimeType: 'text/csv' } as JsonValue], + }, + }) + ).toEqual( + expect.objectContaining({ + timelineAvatar: 'document', + }) + ); + }); + + it('default actions should be hidden', () => { + expect(fileType.getAttachmentViewObject(attachmentViewProps)).toEqual( + expect.objectContaining({ + hideDefaultActions: true, + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/file_type.tsx b/x-pack/plugins/cases/public/components/files/file_type.tsx new file mode 100644 index 0000000000000..271bf3008e70e --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/file_type.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import type { + ExternalReferenceAttachmentType, + ExternalReferenceAttachmentViewProps, +} from '../../client/attachment_framework/types'; +import type { DownloadableFile } from './types'; + +import { AttachmentActionType } from '../../client/attachment_framework/types'; +import { FILE_ATTACHMENT_TYPE } from '../../../common/api'; +import { FileDownloadButton } from './file_download_button'; +import { FileNameLink } from './file_name_link'; +import { FilePreview } from './file_preview'; +import * as i18n from './translations'; +import { isImage, isValidFileExternalReferenceMetadata } from './utils'; +import { useFilePreview } from './use_file_preview'; +import { FileDeleteButton } from './file_delete_button'; + +interface FileAttachmentEventProps { + file: DownloadableFile; +} + +const FileAttachmentEvent = ({ file }: FileAttachmentEventProps) => { + const { isPreviewVisible, showPreview, closePreview } = useFilePreview(); + + return ( + <> + {i18n.ADDED} + + {isPreviewVisible && } + + ); +}; + +FileAttachmentEvent.displayName = 'FileAttachmentEvent'; + +function getFileDownloadButton(fileId: string) { + return ; +} + +function getFileDeleteButton(caseId: string, fileId: string) { + return ; +} + +const getFileAttachmentActions = ({ caseId, fileId }: { caseId: string; fileId: string }) => [ + { + type: AttachmentActionType.CUSTOM as const, + render: () => getFileDownloadButton(fileId), + label: i18n.DOWNLOAD_FILE, + isPrimary: false, + }, + { + type: AttachmentActionType.CUSTOM as const, + render: () => getFileDeleteButton(caseId, fileId), + label: i18n.DELETE_FILE, + isPrimary: false, + }, +]; + +const getFileAttachmentViewObject = (props: ExternalReferenceAttachmentViewProps) => { + const caseId = props.caseData.id; + const fileId = props.externalReferenceId; + + if (!isValidFileExternalReferenceMetadata(props.externalReferenceMetadata)) { + return { + type: 'regular', + event: i18n.ADDED_UNKNOWN_FILE, + timelineAvatar: 'document', + getActions: () => [ + { + type: AttachmentActionType.CUSTOM as const, + render: () => getFileDeleteButton(caseId, fileId), + label: i18n.DELETE_FILE, + isPrimary: false, + }, + ], + hideDefaultActions: true, + }; + } + + const fileMetadata = props.externalReferenceMetadata.files[0]; + const file = { + id: fileId, + ...fileMetadata, + }; + + return { + event: , + timelineAvatar: isImage(file) ? 'image' : 'document', + getActions: () => getFileAttachmentActions({ caseId, fileId }), + hideDefaultActions: true, + }; +}; + +export const getFileType = (): ExternalReferenceAttachmentType => ({ + id: FILE_ATTACHMENT_TYPE, + icon: 'document', + displayName: 'File Attachment Type', + getAttachmentViewObject: getFileAttachmentViewObject, +}); diff --git a/x-pack/plugins/cases/public/components/files/files_table.test.tsx b/x-pack/plugins/cases/public/components/files/files_table.test.tsx new file mode 100644 index 0000000000000..651f86e76b462 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/files_table.test.tsx @@ -0,0 +1,233 @@ +/* + * 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 { screen, waitFor, within } from '@testing-library/react'; + +import { basicFileMock } from '../../containers/mock'; +import type { AppMockRenderer } from '../../common/mock'; + +import { constructFileKindIdByOwner } from '../../../common/files'; +import { createAppMockRenderer, mockedTestProvidersOwner } from '../../common/mock'; +import { FilesTable } from './files_table'; +import userEvent from '@testing-library/user-event'; + +describe('FilesTable', () => { + const onTableChange = jest.fn(); + const defaultProps = { + caseId: 'foobar', + items: [basicFileMock], + pagination: { pageIndex: 0, pageSize: 10, totalItemCount: 1 }, + isLoading: false, + onChange: onTableChange, + }; + + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders correctly', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table-results-count')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-files-table-filename')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-files-table-filetype')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-files-table-date-added')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-files-download-button')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-files-delete-button')).toBeInTheDocument(); + }); + + it('renders loading state', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table-loading')).toBeInTheDocument(); + }); + + it('renders empty table', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table-empty')).toBeInTheDocument(); + }); + + it('FileAdd in empty table is clickable', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table-empty')).toBeInTheDocument(); + + const addFileButton = await screen.findByTestId('cases-files-add'); + + expect(addFileButton).toBeInTheDocument(); + + userEvent.click(addFileButton); + + expect(await screen.findByTestId('cases-files-add-modal')).toBeInTheDocument(); + }); + + it('renders single result count properly', async () => { + const mockPagination = { pageIndex: 0, pageSize: 10, totalItemCount: 1 }; + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-table-results-count')).toHaveTextContent( + `Showing ${defaultProps.items.length} file` + ); + }); + + it('non image rows dont open file preview', async () => { + const nonImageFileMock = { ...basicFileMock, mimeType: 'something/else' }; + + appMockRender.render(); + + userEvent.click( + await within(await screen.findByTestId('cases-files-table-filename')).findByTitle( + 'No preview available' + ) + ); + + expect(await screen.queryByTestId('cases-files-image-preview')).not.toBeInTheDocument(); + }); + + it('image rows open file preview', async () => { + appMockRender.render(); + + userEvent.click( + await screen.findByRole('button', { + name: `${basicFileMock.name}.${basicFileMock.extension}`, + }) + ); + + expect(await screen.findByTestId('cases-files-image-preview')).toBeInTheDocument(); + }); + + it('different mimeTypes are displayed correctly', async () => { + const mockPagination = { pageIndex: 0, pageSize: 10, totalItemCount: 7 }; + appMockRender.render( + + ); + + expect((await screen.findAllByText('Unknown')).length).toBe(4); + expect(await screen.findByText('Compressed')).toBeInTheDocument(); + expect(await screen.findByText('Text')).toBeInTheDocument(); + expect(await screen.findByText('Image')).toBeInTheDocument(); + }); + + it('download button renders correctly', async () => { + appMockRender.render(); + + expect(appMockRender.getFilesClient().getDownloadHref).toBeCalledTimes(1); + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + id: basicFileMock.id, + }); + + expect(await screen.findByTestId('cases-files-download-button')).toBeInTheDocument(); + }); + + it('delete button renders correctly', async () => { + appMockRender.render(); + + expect(appMockRender.getFilesClient().getDownloadHref).toBeCalledTimes(1); + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + id: basicFileMock.id, + }); + + expect(await screen.findByTestId('cases-files-delete-button')).toBeInTheDocument(); + }); + + it('clicking delete button opens deletion modal', async () => { + appMockRender.render(); + + expect(appMockRender.getFilesClient().getDownloadHref).toBeCalledTimes(1); + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + id: basicFileMock.id, + }); + + const deleteButton = await screen.findByTestId('cases-files-delete-button'); + + expect(deleteButton).toBeInTheDocument(); + + userEvent.click(deleteButton); + + expect(await screen.findByTestId('property-actions-confirm-modal')).toBeInTheDocument(); + }); + + it('go to next page calls onTableChange with correct values', async () => { + const mockPagination = { pageIndex: 0, pageSize: 1, totalItemCount: 2 }; + + appMockRender.render( + + ); + + userEvent.click(await screen.findByTestId('pagination-button-next')); + + await waitFor(() => + expect(onTableChange).toHaveBeenCalledWith({ + page: { index: mockPagination.pageIndex + 1, size: mockPagination.pageSize }, + }) + ); + }); + + it('go to previous page calls onTableChange with correct values', async () => { + const mockPagination = { pageIndex: 1, pageSize: 1, totalItemCount: 2 }; + + appMockRender.render( + + ); + + userEvent.click(await screen.findByTestId('pagination-button-previous')); + + await waitFor(() => + expect(onTableChange).toHaveBeenCalledWith({ + page: { index: mockPagination.pageIndex - 1, size: mockPagination.pageSize }, + }) + ); + }); + + it('changing perPage calls onTableChange with correct values', async () => { + appMockRender.render( + + ); + + userEvent.click(screen.getByTestId('tablePaginationPopoverButton')); + + const pageSizeOption = screen.getByTestId('tablePagination-50-rows'); + + pageSizeOption.style.pointerEvents = 'all'; + + userEvent.click(pageSizeOption); + + await waitFor(() => + expect(onTableChange).toHaveBeenCalledWith({ + page: { index: 0, size: 50 }, + }) + ); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/files_table.tsx b/x-pack/plugins/cases/public/components/files/files_table.tsx new file mode 100644 index 0000000000000..6433d90a91d44 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/files_table.tsx @@ -0,0 +1,83 @@ +/* + * 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 type { Pagination, EuiBasicTableProps } from '@elastic/eui'; +import type { FileJSON } from '@kbn/shared-ux-file-types'; + +import { EuiBasicTable, EuiLoadingContent, EuiSpacer, EuiText, EuiEmptyPrompt } from '@elastic/eui'; + +import * as i18n from './translations'; +import { useFilesTableColumns } from './use_files_table_columns'; +import { FilePreview } from './file_preview'; +import { AddFile } from './add_file'; +import { useFilePreview } from './use_file_preview'; + +const EmptyFilesTable = ({ caseId }: { caseId: string }) => ( + {i18n.NO_FILES}} + data-test-subj="cases-files-table-empty" + titleSize="xs" + actions={} + /> +); + +EmptyFilesTable.displayName = 'EmptyFilesTable'; + +interface FilesTableProps { + caseId: string; + isLoading: boolean; + items: FileJSON[]; + onChange: EuiBasicTableProps['onChange']; + pagination: Pagination; +} + +export const FilesTable = ({ caseId, items, pagination, onChange, isLoading }: FilesTableProps) => { + const { isPreviewVisible, showPreview, closePreview } = useFilePreview(); + + const [selectedFile, setSelectedFile] = useState(); + + const displayPreview = (file: FileJSON) => { + setSelectedFile(file); + showPreview(); + }; + + const columns = useFilesTableColumns({ caseId, showPreview: displayPreview }); + + return isLoading ? ( + <> + + + + ) : ( + <> + {pagination.totalItemCount > 0 && ( + <> + + + {i18n.SHOWING_FILES(items.length)} + + + )} + + } + /> + {isPreviewVisible && selectedFile !== undefined && ( + + )} + + ); +}; + +FilesTable.displayName = 'FilesTable'; diff --git a/x-pack/plugins/cases/public/components/files/files_utility_bar.test.tsx b/x-pack/plugins/cases/public/components/files/files_utility_bar.test.tsx new file mode 100644 index 0000000000000..bfac1998a857a --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/files_utility_bar.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { screen } from '@testing-library/react'; + +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import userEvent from '@testing-library/user-event'; +import { FilesUtilityBar } from './files_utility_bar'; + +const defaultProps = { + caseId: 'foobar', + onSearch: jest.fn(), +}; + +describe('FilesUtilityBar', () => { + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders correctly', async () => { + appMockRender.render(); + + expect(await screen.findByTestId('cases-files-add')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-files-search')).toBeInTheDocument(); + }); + + it('search text passed correctly to callback', async () => { + appMockRender.render(); + + await userEvent.type(screen.getByTestId('cases-files-search'), 'My search{enter}'); + expect(defaultProps.onSearch).toBeCalledWith('My search'); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/files_utility_bar.tsx b/x-pack/plugins/cases/public/components/files/files_utility_bar.tsx new file mode 100644 index 0000000000000..71b1ef503fc63 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/files_utility_bar.tsx @@ -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 React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch } from '@elastic/eui'; +import { AddFile } from './add_file'; + +import * as i18n from './translations'; + +interface FilesUtilityBarProps { + caseId: string; + onSearch: (newSearch: string) => void; +} + +export const FilesUtilityBar = ({ caseId, onSearch }: FilesUtilityBarProps) => { + return ( + + + + + + + ); +}; + +FilesUtilityBar.displayName = 'FilesUtilityBar'; diff --git a/x-pack/plugins/cases/public/components/files/translations.tsx b/x-pack/plugins/cases/public/components/files/translations.tsx new file mode 100644 index 0000000000000..4023c5b18cea8 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/translations.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 { i18n } from '@kbn/i18n'; + +export const ACTIONS = i18n.translate('xpack.cases.caseView.files.actions', { + defaultMessage: 'Actions', +}); + +export const ADD_FILE = i18n.translate('xpack.cases.caseView.files.addFile', { + defaultMessage: 'Add File', +}); + +export const CLOSE_MODAL = i18n.translate('xpack.cases.caseView.files.closeModal', { + defaultMessage: 'Close', +}); + +export const DATE_ADDED = i18n.translate('xpack.cases.caseView.files.dateAdded', { + defaultMessage: 'Date Added', +}); + +export const DELETE_FILE = i18n.translate('xpack.cases.caseView.files.deleteFile', { + defaultMessage: 'Delete File', +}); + +export const DOWNLOAD_FILE = i18n.translate('xpack.cases.caseView.files.downloadFile', { + defaultMessage: 'Download File', +}); + +export const FILES_TABLE = i18n.translate('xpack.cases.caseView.files.filesTable', { + defaultMessage: 'Files table', +}); + +export const NAME = i18n.translate('xpack.cases.caseView.files.name', { + defaultMessage: 'Name', +}); + +export const NO_FILES = i18n.translate('xpack.cases.caseView.files.noFilesAvailable', { + defaultMessage: 'No files available', +}); + +export const NO_PREVIEW = i18n.translate('xpack.cases.caseView.files.noPreviewAvailable', { + defaultMessage: 'No preview available', +}); + +export const RESULTS_COUNT = i18n.translate('xpack.cases.caseView.files.resultsCount', { + defaultMessage: 'Showing', +}); + +export const TYPE = i18n.translate('xpack.cases.caseView.files.type', { + defaultMessage: 'Type', +}); + +export const SEARCH_PLACEHOLDER = i18n.translate('xpack.cases.caseView.files.searchPlaceholder', { + defaultMessage: 'Search files', +}); + +export const FAILED_UPLOAD = i18n.translate('xpack.cases.caseView.files.failedUpload', { + defaultMessage: 'Failed to upload file', +}); + +export const UNKNOWN_MIME_TYPE = i18n.translate('xpack.cases.caseView.files.unknownMimeType', { + defaultMessage: 'Unknown', +}); + +export const IMAGE_MIME_TYPE = i18n.translate('xpack.cases.caseView.files.imageMimeType', { + defaultMessage: 'Image', +}); + +export const TEXT_MIME_TYPE = i18n.translate('xpack.cases.caseView.files.textMimeType', { + defaultMessage: 'Text', +}); + +export const COMPRESSED_MIME_TYPE = i18n.translate( + 'xpack.cases.caseView.files.compressedMimeType', + { + defaultMessage: 'Compressed', + } +); + +export const PDF_MIME_TYPE = i18n.translate('xpack.cases.caseView.files.pdfMimeType', { + defaultMessage: 'PDF', +}); + +export const SUCCESSFUL_UPLOAD_FILE_NAME = (fileName: string) => + i18n.translate('xpack.cases.caseView.files.successfulUploadFileName', { + defaultMessage: 'File {fileName} uploaded successfully', + values: { fileName }, + }); + +export const SHOWING_FILES = (totalFiles: number) => + i18n.translate('xpack.cases.caseView.files.showingFilesTitle', { + values: { totalFiles }, + defaultMessage: 'Showing {totalFiles} {totalFiles, plural, =1 {file} other {files}}', + }); + +export const ADDED = i18n.translate('xpack.cases.caseView.files.added', { + defaultMessage: 'added ', +}); + +export const ADDED_UNKNOWN_FILE = i18n.translate('xpack.cases.caseView.files.addedUnknownFile', { + defaultMessage: 'added an unknown file', +}); + +export const DELETE = i18n.translate('xpack.cases.caseView.files.delete', { + defaultMessage: 'Delete', +}); + +export const DELETE_FILE_TITLE = i18n.translate('xpack.cases.caseView.files.deleteThisFile', { + defaultMessage: 'Delete this file?', +}); diff --git a/x-pack/plugins/cases/public/components/files/types.ts b/x-pack/plugins/cases/public/components/files/types.ts new file mode 100644 index 0000000000000..a211b5ac4053d --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/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 type * as rt from 'io-ts'; + +import type { SingleFileAttachmentMetadataRt } from '../../../common/api'; + +export type DownloadableFile = rt.TypeOf & { id: string }; diff --git a/x-pack/plugins/cases/public/components/files/use_file_preview.test.tsx b/x-pack/plugins/cases/public/components/files/use_file_preview.test.tsx new file mode 100644 index 0000000000000..49e18fb818cd9 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/use_file_preview.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 { act, renderHook } from '@testing-library/react-hooks'; + +import { useFilePreview } from './use_file_preview'; + +describe('useFilePreview', () => { + it('isPreviewVisible is false by default', () => { + const { result } = renderHook(() => { + return useFilePreview(); + }); + + expect(result.current.isPreviewVisible).toBeFalsy(); + }); + + it('showPreview sets isPreviewVisible to true', () => { + const { result } = renderHook(() => { + return useFilePreview(); + }); + + expect(result.current.isPreviewVisible).toBeFalsy(); + + act(() => { + result.current.showPreview(); + }); + + expect(result.current.isPreviewVisible).toBeTruthy(); + }); + + it('closePreview sets isPreviewVisible to false', () => { + const { result } = renderHook(() => { + return useFilePreview(); + }); + + expect(result.current.isPreviewVisible).toBeFalsy(); + + act(() => { + result.current.showPreview(); + }); + + expect(result.current.isPreviewVisible).toBeTruthy(); + + act(() => { + result.current.closePreview(); + }); + + expect(result.current.isPreviewVisible).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/use_file_preview.tsx b/x-pack/plugins/cases/public/components/files/use_file_preview.tsx new file mode 100644 index 0000000000000..c802aa38fc688 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/use_file_preview.tsx @@ -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 { useState } from 'react'; + +export const useFilePreview = () => { + const [isPreviewVisible, setIsPreviewVisible] = useState(false); + + const closePreview = () => setIsPreviewVisible(false); + const showPreview = () => setIsPreviewVisible(true); + + return { isPreviewVisible, showPreview, closePreview }; +}; diff --git a/x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx b/x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx new file mode 100644 index 0000000000000..77070da0dbc57 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/use_files_table_columns.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 type { FilesTableColumnsProps } from './use_files_table_columns'; +import { useFilesTableColumns } from './use_files_table_columns'; +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import { renderHook } from '@testing-library/react-hooks'; +import { basicCase } from '../../containers/mock'; + +describe('useFilesTableColumns', () => { + let appMockRender: AppMockRenderer; + + const useFilesTableColumnsProps: FilesTableColumnsProps = { + caseId: basicCase.id, + showPreview: () => {}, + }; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('return all files table columns correctly', async () => { + const { result } = renderHook(() => useFilesTableColumns(useFilesTableColumnsProps), { + wrapper: appMockRender.AppWrapper, + }); + + expect(result.current).toMatchInlineSnapshot(` + Array [ + Object { + "data-test-subj": "cases-files-table-filename", + "name": "Name", + "render": [Function], + "width": "60%", + }, + Object { + "data-test-subj": "cases-files-table-filetype", + "name": "Type", + "render": [Function], + }, + Object { + "data-test-subj": "cases-files-table-date-added", + "dataType": "date", + "field": "created", + "name": "Date Added", + }, + Object { + "actions": Array [ + Object { + "description": "Download File", + "isPrimary": true, + "name": "Download", + "render": [Function], + }, + Object { + "description": "Delete File", + "isPrimary": true, + "name": "Delete", + "render": [Function], + }, + ], + "name": "Actions", + "width": "120px", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/use_files_table_columns.tsx b/x-pack/plugins/cases/public/components/files/use_files_table_columns.tsx new file mode 100644 index 0000000000000..80568189afb58 --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/use_files_table_columns.tsx @@ -0,0 +1,71 @@ +/* + * 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 type { EuiBasicTableColumn } from '@elastic/eui'; +import type { FileJSON } from '@kbn/shared-ux-file-types'; + +import * as i18n from './translations'; +import { parseMimeType } from './utils'; +import { FileNameLink } from './file_name_link'; +import { FileDownloadButton } from './file_download_button'; +import { FileDeleteButton } from './file_delete_button'; + +export interface FilesTableColumnsProps { + caseId: string; + showPreview: (file: FileJSON) => void; +} + +export const useFilesTableColumns = ({ + caseId, + showPreview, +}: FilesTableColumnsProps): Array> => { + return [ + { + name: i18n.NAME, + 'data-test-subj': 'cases-files-table-filename', + render: (file: FileJSON) => ( + showPreview(file)} /> + ), + width: '60%', + }, + { + name: i18n.TYPE, + 'data-test-subj': 'cases-files-table-filetype', + render: (attachment: FileJSON) => { + return {parseMimeType(attachment.mimeType)}; + }, + }, + { + name: i18n.DATE_ADDED, + field: 'created', + 'data-test-subj': 'cases-files-table-date-added', + dataType: 'date', + }, + { + name: i18n.ACTIONS, + width: '120px', + actions: [ + { + name: 'Download', + isPrimary: true, + description: i18n.DOWNLOAD_FILE, + render: (file: FileJSON) => , + }, + { + name: 'Delete', + isPrimary: true, + description: i18n.DELETE_FILE, + render: (file: FileJSON) => ( + + ), + }, + ], + }, + ]; +}; diff --git a/x-pack/plugins/cases/public/components/files/utils.test.tsx b/x-pack/plugins/cases/public/components/files/utils.test.tsx new file mode 100644 index 0000000000000..411492d1a2bab --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/utils.test.tsx @@ -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 type { JsonValue } from '@kbn/utility-types'; + +import { + compressionMimeTypes, + imageMimeTypes, + pdfMimeTypes, + textMimeTypes, +} from '../../../common/constants/mime_types'; +import { basicFileMock } from '../../containers/mock'; +import { isImage, isValidFileExternalReferenceMetadata, parseMimeType } from './utils'; + +describe('isImage', () => { + it.each(imageMimeTypes)('should return true for image mime type: %s', (mimeType) => { + expect(isImage({ mimeType })).toBeTruthy(); + }); + + it.each(textMimeTypes)('should return false for text mime type: %s', (mimeType) => { + expect(isImage({ mimeType })).toBeFalsy(); + }); +}); + +describe('parseMimeType', () => { + it('should return Unknown for empty strings', () => { + expect(parseMimeType('')).toBe('Unknown'); + }); + + it('should return Unknown for undefined', () => { + expect(parseMimeType(undefined)).toBe('Unknown'); + }); + + it('should return Unknown for strings starting with forward slash', () => { + expect(parseMimeType('/start')).toBe('Unknown'); + }); + + it('should return Unknown for strings with no forward slash', () => { + expect(parseMimeType('no-slash')).toBe('Unknown'); + }); + + it('should return capitalize first letter for valid strings', () => { + expect(parseMimeType('foo/bar')).toBe('Foo'); + }); + + it.each(imageMimeTypes)('should return "Image" for image mime type: %s', (mimeType) => { + expect(parseMimeType(mimeType)).toBe('Image'); + }); + + it.each(textMimeTypes)('should return "Text" for text mime type: %s', (mimeType) => { + expect(parseMimeType(mimeType)).toBe('Text'); + }); + + it.each(compressionMimeTypes)( + 'should return "Compressed" for image mime type: %s', + (mimeType) => { + expect(parseMimeType(mimeType)).toBe('Compressed'); + } + ); + + it.each(pdfMimeTypes)('should return "Pdf" for text mime type: %s', (mimeType) => { + expect(parseMimeType(mimeType)).toBe('PDF'); + }); +}); + +describe('isValidFileExternalReferenceMetadata', () => { + it('should return false for empty objects', () => { + expect(isValidFileExternalReferenceMetadata({})).toBeFalsy(); + }); + + it('should return false if the files property is missing', () => { + expect(isValidFileExternalReferenceMetadata({ foo: 'bar' })).toBeFalsy(); + }); + + it('should return false if the files property is not an array', () => { + expect(isValidFileExternalReferenceMetadata({ files: 'bar' })).toBeFalsy(); + }); + + it('should return false if files is not an array of file metadata', () => { + expect(isValidFileExternalReferenceMetadata({ files: [3] })).toBeFalsy(); + }); + + it('should return false if files is not an array of file metadata 2', () => { + expect( + isValidFileExternalReferenceMetadata({ files: [{ name: 'foo', mimeType: 'bar' }] }) + ).toBeFalsy(); + }); + + it('should return true if the metadata is as expected', () => { + expect( + isValidFileExternalReferenceMetadata({ files: [basicFileMock as unknown as JsonValue] }) + ).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/files/utils.tsx b/x-pack/plugins/cases/public/components/files/utils.tsx new file mode 100644 index 0000000000000..b870c733eb10e --- /dev/null +++ b/x-pack/plugins/cases/public/components/files/utils.tsx @@ -0,0 +1,61 @@ +/* + * 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 { + CommentRequestExternalReferenceType, + FileAttachmentMetadata, +} from '../../../common/api'; + +import { + compressionMimeTypes, + imageMimeTypes, + textMimeTypes, + pdfMimeTypes, +} from '../../../common/constants/mime_types'; +import { FileAttachmentMetadataRt } from '../../../common/api'; +import * as i18n from './translations'; + +export const isImage = (file: { mimeType?: string }) => file.mimeType?.startsWith('image/'); + +export const parseMimeType = (mimeType: string | undefined) => { + if (typeof mimeType === 'undefined') { + return i18n.UNKNOWN_MIME_TYPE; + } + + if (imageMimeTypes.includes(mimeType)) { + return i18n.IMAGE_MIME_TYPE; + } + + if (textMimeTypes.includes(mimeType)) { + return i18n.TEXT_MIME_TYPE; + } + + if (compressionMimeTypes.includes(mimeType)) { + return i18n.COMPRESSED_MIME_TYPE; + } + + if (pdfMimeTypes.includes(mimeType)) { + return i18n.PDF_MIME_TYPE; + } + + const result = mimeType.split('/'); + + if (result.length <= 1 || result[0] === '') { + return i18n.UNKNOWN_MIME_TYPE; + } + + return result[0].charAt(0).toUpperCase() + result[0].slice(1); +}; + +export const isValidFileExternalReferenceMetadata = ( + externalReferenceMetadata: CommentRequestExternalReferenceType['externalReferenceMetadata'] +): externalReferenceMetadata is FileAttachmentMetadata => { + return ( + FileAttachmentMetadataRt.is(externalReferenceMetadata) && + externalReferenceMetadata?.files?.length >= 1 + ); +}; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_open_visualization.tsx b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_open_visualization.tsx index 1d6a5ec8ca11a..84dddd64ba61b 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_open_visualization.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/plugins/lens/use_lens_open_visualization.tsx @@ -9,6 +9,8 @@ import { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; + +import { AttachmentActionType } from '../../../../client/attachment_framework/types'; import { useKibana } from '../../../../common/lib/kibana'; import { parseCommentString, @@ -42,6 +44,7 @@ export const useLensOpenVisualization = ({ comment }: { comment: string }) => { actionConfig: !lensVisualization.length ? null : { + type: AttachmentActionType.BUTTON as const, iconType: 'lensApp', label: i18n.translate( 'xpack.cases.markdownEditor.plugins.lens.openVisualizationButtonLabel', diff --git a/x-pack/plugins/cases/public/components/property_actions/index.tsx b/x-pack/plugins/cases/public/components/property_actions/index.tsx index 4de52d551bf2f..833ace8333d2b 100644 --- a/x-pack/plugins/cases/public/components/property_actions/index.tsx +++ b/x-pack/plugins/cases/public/components/property_actions/index.tsx @@ -9,6 +9,9 @@ import React, { useCallback, useState } from 'react'; import type { EuiButtonProps } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiPopover, EuiButtonIcon, EuiButtonEmpty } from '@elastic/eui'; +import type { AttachmentAction } from '../../client/attachment_framework/types'; + +import { AttachmentActionType } from '../../client/attachment_framework/types'; import * as i18n from './translations'; export interface PropertyActionButtonProps { @@ -45,7 +48,7 @@ const PropertyActionButton = React.memo( PropertyActionButton.displayName = 'PropertyActionButton'; export interface PropertyActionsProps { - propertyActions: PropertyActionButtonProps[]; + propertyActions: AttachmentAction[]; customDataTestSubj?: string; } @@ -93,14 +96,17 @@ export const PropertyActions = React.memo( {propertyActions.map((action, key) => ( - onClosePopover(action.onClick)} - customDataTestSubj={customDataTestSubj} - /> + {(action.type === AttachmentActionType.BUTTON && ( + onClosePopover(action.onClick)} + customDataTestSubj={customDataTestSubj} + /> + )) || + (action.type === AttachmentActionType.CUSTOM && action.render())} ))} diff --git a/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx b/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx index db21c2f2100c6..4cf6c9844e948 100644 --- a/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/comment/comment.test.tsx @@ -36,6 +36,7 @@ import { useCaseViewNavigation, useCaseViewParams } from '../../../common/naviga import { ExternalReferenceAttachmentTypeRegistry } from '../../../client/attachment_framework/external_reference_registry'; import { PersistableStateAttachmentTypeRegistry } from '../../../client/attachment_framework/persistable_state_registry'; import { userProfiles } from '../../../containers/user_profiles/api.mock'; +import { AttachmentActionType } from '../../../client/attachment_framework/types'; jest.mock('../../../common/lib/kibana'); jest.mock('../../../common/navigation/hooks'); @@ -849,9 +850,27 @@ describe('createCommentUserActionBuilder', () => { const attachment = getExternalReferenceAttachment({ getActions: () => [ - { label: 'My primary button', isPrimary: true, iconType: 'danger', onClick }, - { label: 'My primary 2 button', isPrimary: true, iconType: 'danger', onClick }, - { label: 'My primary 3 button', isPrimary: true, iconType: 'danger', onClick }, + { + type: AttachmentActionType.BUTTON as const, + label: 'My primary button', + isPrimary: true, + iconType: 'danger', + onClick, + }, + { + type: AttachmentActionType.BUTTON as const, + label: 'My primary 2 button', + isPrimary: true, + iconType: 'danger', + onClick, + }, + { + type: AttachmentActionType.BUTTON as const, + label: 'My primary 3 button', + isPrimary: true, + iconType: 'danger', + onClick, + }, ], }); @@ -888,14 +907,75 @@ describe('createCommentUserActionBuilder', () => { expect(onClick).toHaveBeenCalledTimes(2); }); + it('shows correctly a custom action', async () => { + const onClick = jest.fn(); + + const attachment = getExternalReferenceAttachment({ + getActions: () => [ + { + type: AttachmentActionType.CUSTOM as const, + isPrimary: true, + label: 'Test button', + render: () => ( +
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 %n3^!<+crA1)p7rPDKS z98BoLQO{3^e8ndUdbrE>a2w1AtVG{qJs)z-S9xjjS$8c8Q{7ZuyHUtvmXMDS!+8iN!TgR0+{z}l}l8}r3`;0~-@cgIHZ%T8o z9`%m;j-6blmsLnp^yf;%FvK719oQ{(z{=(38H-yQ)*2p{HtrSqim+Q}I+jH&eOX}$ z;fm4!l8P1#&k+31RPD>}NR0lzpiAq%Xq0Au&}rA*ybnx0EWyF`u6Aci2W2L_9+#z! zP6v6lx%=tmwE72rX&brgipZ#3W?Z)NP=_=t8JH2 zSB6L0mFN+D@A1Yj`Jq$hex=m|8h~=g?QY}#;r#QfTo%$49D>qcQNGS;sf=b8J|wz5 zINYE53D6@ao?|J>wrr@QpAvn}i9{u41Cjawc+0w1&*tzuDMy!a{{)yfSsJX3Pk*yB z=RXTrr!0RiHkf4$E*7>CP^bP!f4zK|ql7S>4)b~RYaRZVSDJXSSl>JYzWA5SI&B76 zpWG$0>;Fq60l)|9?#7qoh(E3W=n9loJ7zZ|`LAa9bpYRMjf$EU|AB!1To(!2!L-7C z{%Yo$HDGp69zg1D{O@(|@PT%SANLFW)y)5+`w~eUCGL1QUQ+2dj5Lw320-+5tkgj; zMVFZzhuvx#8zqQ;x`Km0v`=dd%qL_^8!nA+XNe1DW}c^aAiSyr`=ws)?fN~uZ+F`^ zT;z?5Ryv_j0`#Mkb!^pY%Q#BKY22jED4ELN{w?5wFIFNZmL-2E4g-m@JP;X_Y5 z8IxsPt{S8v3XluJeqZ7RuH$rJ>$0JiJT4S~%`uoc^!C?9xPL8}?^0_feP92HE)%-Yk(_&08Kd^_rim44#+j<+g=YVXiOBEDMN zLJz7aqzkH|2*JGVzY+Aq)R=M`^vH7s8R%so8i;6@ptU^w#$yk4VcLaQ{oQHqiC?%( zR7W=#oq463hN(N=q+jh0t@D&bc&UiSv0yszYk2_1?_cQK(_j7*;}4WN(6mlF4t5@U zKp&T2jXY;ojMb!n@5z7z!-#^4pDF|xPBl=7wPt3TQ}gWBqF?1F0 z_SaY53I1HMcXn-0;3?j6j2tfX5;NaT^K=T%OVetFiTjKoEFqB6fVee3FS|r0M z`-3MRDTVdPWccqO=#)M1he{dD^4QuHB_D;pboFGwdMncs8u6#E+H!gwbDK3&zEv_s zs9STrbqacNjL_0oDSfm0c6zDF@6F}{GeMcpIGO|JNvNMAo4>UlD|E;mRy3c!`gu}V z;6y&ZqNQcJ3dYa5DtPK;`KuE-7qJc$o$vr=YGi;YID8c+#$4Az~g9+0aBGP zXvb1-(Gv0gh}&~zS5muT_Myn|O493G+)vgEu08dM1KWpiGzLc1${Y`O*&0a}L}d5o zn{#QCL$a~6Wx=a2q*)7qnGn-$OXAK}6u}8m`irb&Kg-R8WG`$#Rq1nW&X(5c+dqW! zNkO;%3LH^PkEuUit=2Rk*;mQO)QH!}Fj{G}=cM)ONd9GfOe8qjPzojI;F@+m_qBcx z2g~(kMf6Arv*K?;WCZ>)K|3vg36g+_;QumxJbQr&3eZd8{Q2|$YC5qpK)kt=lwfu2 z_eZ~MGb`>#OVncLUnc7)FR-|sWb^#LdTR#c3N_)x+&|jWk8VDc*u80N`5ZRXeA0(~ P8+cj3kY;73p0WQ2C*N$d diff --git a/examples/content_management_examples/kibana.jsonc b/examples/content_management_examples/kibana.jsonc index 24759d7abdfb6..ae7cd95e4190a 100644 --- a/examples/content_management_examples/kibana.jsonc +++ b/examples/content_management_examples/kibana.jsonc @@ -9,7 +9,9 @@ "browser": true, "requiredPlugins": [ "contentManagement", - "developerExamples" + "developerExamples", + "kibanaReact", + "savedObjectsTaggingOss" ] } } diff --git a/examples/content_management_examples/public/examples/index.tsx b/examples/content_management_examples/public/examples/index.tsx index 715f3a6809581..611bcde59d445 100644 --- a/examples/content_management_examples/public/examples/index.tsx +++ b/examples/content_management_examples/public/examples/index.tsx @@ -8,22 +8,67 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { EuiPageTemplate } from '@elastic/eui'; +// eslint-disable-next-line no-restricted-imports +import { Router, Switch, Route, Redirect } from 'react-router-dom'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { EuiPageTemplate, EuiSideNav } from '@elastic/eui'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { StartDeps } from '../types'; import { TodoApp } from './todos'; +import { MSearchApp } from './msearch'; export const renderApp = ( - { notifications }: CoreStart, - { contentManagement }: StartDeps, - { element }: AppMountParameters + core: CoreStart, + { contentManagement, savedObjectsTaggingOss }: StartDeps, + { element, history }: AppMountParameters ) => { ReactDOM.render( - - - - - , + + + + + + + + + + + + + + + + + + + + + , element ); diff --git a/examples/content_management_examples/public/examples/msearch/index.tsx b/examples/content_management_examples/public/examples/msearch/index.tsx new file mode 100644 index 0000000000000..7caf5d200748a --- /dev/null +++ b/examples/content_management_examples/public/examples/msearch/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 { MSearchApp } from './msearch_app'; diff --git a/examples/content_management_examples/public/examples/msearch/msearch_app.tsx b/examples/content_management_examples/public/examples/msearch/msearch_app.tsx new file mode 100644 index 0000000000000..aa2bc6c1a8b7b --- /dev/null +++ b/examples/content_management_examples/public/examples/msearch/msearch_app.tsx @@ -0,0 +1,42 @@ +/* + * 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 { ContentClientProvider, type ContentClient } from '@kbn/content-management-plugin/public'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import type { CoreStart } from '@kbn/core/public'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; +import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { MSearchTable } from './msearch_table'; + +export const MSearchApp = (props: { + contentClient: ContentClient; + core: CoreStart; + savedObjectsTagging: SavedObjectTaggingOssPluginStart; +}) => { + return ( + + + + + + + + ); +}; diff --git a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx new file mode 100644 index 0000000000000..47eaab4ba602b --- /dev/null +++ b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx @@ -0,0 +1,62 @@ +/* + * 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 { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { useContentClient } from '@kbn/content-management-plugin/public'; +import React from 'react'; +import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; + +const LISTING_LIMIT = 1000; + +export const MSearchTable = () => { + const contentClient = useContentClient(); + + const findItems = async ( + searchQuery: string, + refs?: { + references?: SavedObjectsFindOptionsReference[]; + referencesToExclude?: SavedObjectsFindOptionsReference[]; + } + ) => { + const { hits, pagination } = await contentClient.mSearch({ + query: { + text: searchQuery, + limit: LISTING_LIMIT, + cursor: '1', + tags: { + included: refs?.references?.map((ref) => ref.id), + excluded: refs?.referencesToExclude?.map((ref) => ref.id), + }, + }, + contentTypes: [{ contentTypeId: 'map' }], // TODO: improve types to not require objects here? + }); + + // TODO: needs to have logic of extracting common schema from an unknown mSearch hit: hits.map(hit => cm.convertToCommonSchema(hit)) + // for now we just assume that mSearch hit satisfies UserContentCommonSchema + + return { hits, total: pagination.total }; + }; + + return ( + No data found. Try to install some sample data first.} + onClickTitle={(item) => { + alert(`Clicked item ${item.attributes.title} (${item.id})`); + }} + /> + ); +}; diff --git a/examples/content_management_examples/public/types.ts b/examples/content_management_examples/public/types.ts index 83e65c5455afd..747bfd961c434 100644 --- a/examples/content_management_examples/public/types.ts +++ b/examples/content_management_examples/public/types.ts @@ -11,6 +11,7 @@ import { ContentManagementPublicStart, } from '@kbn/content-management-plugin/public'; import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; +import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; export interface SetupDeps { contentManagement: ContentManagementPublicSetup; @@ -19,4 +20,5 @@ export interface SetupDeps { export interface StartDeps { contentManagement: ContentManagementPublicStart; + savedObjectsTaggingOss: SavedObjectTaggingOssPluginStart; } diff --git a/examples/content_management_examples/tsconfig.json b/examples/content_management_examples/tsconfig.json index f383cd9d9b33e..7f07213ce82b6 100644 --- a/examples/content_management_examples/tsconfig.json +++ b/examples/content_management_examples/tsconfig.json @@ -19,5 +19,11 @@ "@kbn/developer-examples-plugin", "@kbn/content-management-plugin", "@kbn/core-application-browser", + "@kbn/shared-ux-link-redirect-app", + "@kbn/content-management-table-list", + "@kbn/kibana-react-plugin", + "@kbn/i18n-react", + "@kbn/saved-objects-tagging-oss-plugin", + "@kbn/core-saved-objects-api-browser", ] } diff --git a/examples/controls_example/public/add_button_example.tsx b/examples/controls_example/public/add_button_example.tsx index a1603bf66606e..e9931c9b6e8ad 100644 --- a/examples/controls_example/public/add_button_example.tsx +++ b/examples/controls_example/public/add_button_example.tsx @@ -9,11 +9,8 @@ import React from 'react'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; -import { LazyControlGroupRenderer } from '@kbn/controls-plugin/public'; - -const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); +import { ControlGroupRenderer } from '@kbn/controls-plugin/public'; export const AddButtonExample = ({ dataViewId }: { dataViewId: string }) => { return ( diff --git a/examples/controls_example/public/basic_redux_example.tsx b/examples/controls_example/public/basic_redux_example.tsx index 4500aa9aaf4bf..ede251d7ae3f1 100644 --- a/examples/controls_example/public/basic_redux_example.tsx +++ b/examples/controls_example/public/basic_redux_example.tsx @@ -6,36 +6,18 @@ * Side Public License, v 1. */ -import React, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; -import { - LazyControlGroupRenderer, - ControlGroupContainer, - useControlGroupContainerContext, - ControlStyle, -} from '@kbn/controls-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; -import { EuiButtonGroup, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; - -const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); +import { EuiButtonGroup, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { ControlGroupRenderer, ControlStyle, ControlGroupAPI } from '@kbn/controls-plugin/public'; +import { AwaitingControlGroupAPI } from '@kbn/controls-plugin/public/control_group'; export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => { - const [controlGroup, setControlGroup] = useState(); - - const ControlGroupReduxWrapper = useMemo(() => { - if (controlGroup) return controlGroup.getReduxEmbeddableTools().Wrapper; - }, [controlGroup]); - - const ButtonControls = () => { - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setControlStyle }, - } = useControlGroupContainerContext(); - const dispatch = useEmbeddableDispatch(); - const controlStyle = select((state) => state.explicitInput.controlStyle); + const [controlGroupAPI, setControlGroupApi] = useState(null); + const Buttons = ({ api }: { api: ControlGroupAPI }) => { + const controlStyle = api.select((state) => state.explicitInput.controlStyle); return ( { }, ]} idSelected={controlStyle} - onChange={(id, value) => { - dispatch(setControlStyle(value)); - }} + onChange={(id, value) => api.dispatch.setControlStyle(value)} type="single" /> ); @@ -70,16 +50,10 @@ export const BasicReduxExample = ({ dataViewId }: { dataViewId: string }) => { - {ControlGroupReduxWrapper && ( - - - - )} + {controlGroupAPI && } { - setControlGroup(newControlGroup); - }} + ref={setControlGroupApi} getCreationOptions={async (initialInput, builder) => { await builder.addDataControlFromField(initialInput, { dataViewId, diff --git a/examples/controls_example/public/edit_example.tsx b/examples/controls_example/public/edit_example.tsx index cf5430cad48f1..148867337fedd 100644 --- a/examples/controls_example/public/edit_example.tsx +++ b/examples/controls_example/public/edit_example.tsx @@ -21,22 +21,24 @@ import { EuiTitle, } from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '@kbn/controls-plugin/common'; import { - LazyControlGroupRenderer, - ControlGroupContainer, - ControlGroupInput, + type ControlGroupInput, + ControlGroupRenderer, + AwaitingControlGroupAPI, + ACTION_EDIT_CONTROL, + ACTION_DELETE_CONTROL, } from '@kbn/controls-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; -import { ACTION_EDIT_CONTROL, ACTION_DELETE_CONTROL } from '@kbn/controls-plugin/public'; - -const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); +import { ControlInputTransform } from '@kbn/controls-plugin/common/types'; const INPUT_KEY = 'kbnControls:saveExample:input'; +const WITH_CUSTOM_PLACEHOLDER = 'Custom Placeholder'; + export const EditExample = () => { const [isSaving, setIsSaving] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [controlGroup, setControlGroup] = useState(); + const [controlGroupAPI, setControlGroupAPI] = useState(null); const [toggleIconIdToSelectedMapIcon, setToggleIconIdToSelectedMapIcon] = useState<{ [id: string]: boolean; }>({}); @@ -49,20 +51,21 @@ export const EditExample = () => { }, }; - if (controlGroup) { + if (controlGroupAPI) { const disabledActions: string[] = Object.keys( pickBy(newToggleIconIdToSelectedMapIcon, (value) => value) ); - controlGroup.updateInput({ disabledActions }); + controlGroupAPI.updateInput({ disabledActions }); } setToggleIconIdToSelectedMapIcon(newToggleIconIdToSelectedMapIcon); } async function onSave() { - setIsSaving(true); + if (!controlGroupAPI) return; - localStorage.setItem(INPUT_KEY, JSON.stringify(controlGroup!.getInput())); + setIsSaving(true); + localStorage.setItem(INPUT_KEY, JSON.stringify(controlGroupAPI.getInput())); // simulated async save await await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -85,6 +88,7 @@ export const EditExample = () => { setToggleIconIdToSelectedMapIcon({ [ACTION_EDIT_CONTROL]: disabledActions.includes(ACTION_EDIT_CONTROL), [ACTION_DELETE_CONTROL]: disabledActions.includes(ACTION_DELETE_CONTROL), + [WITH_CUSTOM_PLACEHOLDER]: false, }); } catch (e) { // ignore parse errors @@ -94,6 +98,24 @@ export const EditExample = () => { return input; } + const controlInputTransform: ControlInputTransform = (newState, type) => { + if (type === OPTIONS_LIST_CONTROL && toggleIconIdToSelectedMapIcon[WITH_CUSTOM_PLACEHOLDER]) { + return { + ...newState, + placeholder: 'Custom Placeholder', + }; + } + + if (type === RANGE_SLIDER_CONTROL) { + return { + ...newState, + value: ['0', '4'], + }; + } + + return newState; + }; + return ( <> @@ -109,9 +131,9 @@ export const EditExample = () => { { - controlGroup!.openAddDataControlFlyout(); + controlGroupAPI!.openAddDataControlFlyout({ controlInputTransform }); }} > Add control @@ -132,6 +154,11 @@ export const EditExample = () => { label: 'Disable delete action', value: ACTION_DELETE_CONTROL, }, + { + id: WITH_CUSTOM_PLACEHOLDER, + label: WITH_CUSTOM_PLACEHOLDER, + value: WITH_CUSTOM_PLACEHOLDER, + }, ]} idToSelectedMap={toggleIconIdToSelectedMapIcon} type="multi" @@ -142,7 +169,7 @@ export const EditExample = () => { @@ -157,6 +184,7 @@ export const EditExample = () => { ) : null} { const persistedInput = await onLoad(); return { @@ -167,9 +195,6 @@ export const EditExample = () => { }, }; }} - onLoadComplete={async (newControlGroup) => { - setControlGroup(newControlGroup); - }} /> diff --git a/examples/controls_example/public/search_example.tsx b/examples/controls_example/public/search_example.tsx index f9ca9d8e24eca..85c37a1e21628 100644 --- a/examples/controls_example/public/search_example.tsx +++ b/examples/controls_example/public/search_example.tsx @@ -22,12 +22,9 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { LazyControlGroupRenderer, ControlGroupContainer } from '@kbn/controls-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; +import { AwaitingControlGroupAPI, ControlGroupRenderer } from '@kbn/controls-plugin/public'; import { PLUGIN_ID } from './constants'; -const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); - interface Props { data: DataPublicPluginStart; dataView: DataView; @@ -36,7 +33,7 @@ interface Props { export const SearchExample = ({ data, dataView, navigation }: Props) => { const [controlFilters, setControlFilters] = useState([]); - const [controlGroup, setControlGroup] = useState(); + const [controlGroupAPI, setControlGroupAPI] = useState(); const [hits, setHits] = useState(0); const [filters, setFilters] = useState([]); const [isSearching, setIsSearching] = useState(false); @@ -47,16 +44,16 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => { const [timeRange, setTimeRange] = useState({ from: 'now-7d', to: 'now' }); useEffect(() => { - if (!controlGroup) { + if (!controlGroupAPI) { return; } - const subscription = controlGroup.onFiltersPublished$.subscribe((newFilters) => { + const subscription = controlGroupAPI.onFiltersPublished$.subscribe((newFilters) => { setControlFilters([...newFilters]); }); return () => { subscription.unsubscribe(); }; - }, [controlGroup]); + }, [controlGroupAPI]); useEffect(() => { const abortController = new AbortController(); @@ -155,10 +152,8 @@ export const SearchExample = ({ data, dataView, navigation }: Props) => { }, }; }} - onLoadComplete={async (newControlGroup) => { - setControlGroup(newControlGroup); - }} query={query} + ref={setControlGroupAPI} timeRange={timeRange} /> diff --git a/examples/controls_example/tsconfig.json b/examples/controls_example/tsconfig.json index ae0426cf8b3f5..508a61e7020e0 100644 --- a/examples/controls_example/tsconfig.json +++ b/examples/controls_example/tsconfig.json @@ -18,10 +18,9 @@ "@kbn/data-plugin", "@kbn/controls-plugin", "@kbn/navigation-plugin", - "@kbn/presentation-util-plugin", "@kbn/shared-ux-page-kibana-template", "@kbn/embeddable-plugin", "@kbn/data-views-plugin", - "@kbn/es-query" + "@kbn/es-query", ] } diff --git a/examples/guided_onboarding_example/public/components/app.tsx b/examples/guided_onboarding_example/public/components/app.tsx index 41b9f62ddc075..1a1083a10f1f3 100755 --- a/examples/guided_onboarding_example/public/components/app.tsx +++ b/examples/guided_onboarding_example/public/components/app.tsx @@ -25,6 +25,7 @@ import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/publi import { StepTwo } from './step_two'; import { StepOne } from './step_one'; import { StepThree } from './step_three'; +import { StepFour } from './step_four'; import { Main } from './main'; interface GuidedOnboardingExampleAppDeps { @@ -65,6 +66,13 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps + p + ( + + )} + /> diff --git a/examples/guided_onboarding_example/public/components/main.tsx b/examples/guided_onboarding_example/public/components/main.tsx index 4e7c3843f5f34..d5f1febccfbce 100644 --- a/examples/guided_onboarding_example/public/components/main.tsx +++ b/examples/guided_onboarding_example/public/components/main.tsx @@ -345,6 +345,14 @@ export const Main = (props: MainProps) => { /> + + history.push('stepFour')}> + + + diff --git a/examples/guided_onboarding_example/public/components/step_four.tsx b/examples/guided_onboarding_example/public/components/step_four.tsx new file mode 100644 index 0000000000000..57df11d422519 --- /dev/null +++ b/examples/guided_onboarding_example/public/components/step_four.tsx @@ -0,0 +1,83 @@ +/* + * 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, { useEffect, useState } from 'react'; + +import { EuiButton, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; + +import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public/types'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiPageContentHeader_Deprecated as EuiPageContentHeader, + EuiPageContentBody_Deprecated as EuiPageContentBody, + EuiCode, +} from '@elastic/eui'; +import { RouteComponentProps } from 'react-router-dom'; + +interface StepFourProps { + guidedOnboarding: GuidedOnboardingPluginStart; +} + +export const StepFour = (props: StepFourProps & RouteComponentProps<{ indexName: string }>) => { + const { + guidedOnboarding: { guidedOnboardingApi }, + match: { + params: { indexName }, + }, + } = props; + + const [, setIsTourStepOpen] = useState(false); + + useEffect(() => { + const subscription = guidedOnboardingApi + ?.isGuideStepActive$('testGuide', 'step4') + .subscribe((isStepActive) => { + setIsTourStepOpen(isStepActive); + }); + return () => subscription?.unsubscribe(); + }, [guidedOnboardingApi]); + + return ( + <> + + +

+ +

+
+
+ + +

+ {indexName: {indexName}} + ), + }} + /> +

+
+ + + { + await guidedOnboardingApi?.completeGuideStep('testGuide', 'step4'); + }} + > + Complete step 4 + +
+ + ); +}; diff --git a/examples/guided_onboarding_example/public/components/step_one.tsx b/examples/guided_onboarding_example/public/components/step_one.tsx index fd5cb132b6b91..632b902e14a3e 100644 --- a/examples/guided_onboarding_example/public/components/step_one.tsx +++ b/examples/guided_onboarding_example/public/components/step_one.tsx @@ -16,6 +16,11 @@ import { EuiPageContentHeader_Deprecated as EuiPageContentHeader, EuiPageContentBody_Deprecated as EuiPageContentBody, EuiSpacer, + EuiCode, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; @@ -30,6 +35,7 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) => const { guidedOnboardingApi } = guidedOnboarding; const [isTourStepOpen, setIsTourStepOpen] = useState(false); + const [indexName, setIndexName] = useState('test1234'); const isTourActive = useObservable( guidedOnboardingApi!.isGuideStepActive$('testGuide', 'step1'), @@ -59,30 +65,59 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) => Test guide, step 1, a EUI tour will be displayed, pointing to the button below." />

+

+ indexName, + }} + /> +

- -

Click this button to complete step 1.

- - } - isStepOpen={isTourStepOpen} - minWidth={300} - onFinish={() => setIsTourStepOpen(false)} - step={1} - stepsTotal={1} - title="Step 1" - anchorPosition="rightUp" - > - { - await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1'); - }} - > - Complete step 1 - -
+ + + + } + > + setIndexName(e.target.value)} /> + + + + + +

Click this button to complete step 1.

+ + } + isStepOpen={isTourStepOpen} + minWidth={300} + onFinish={() => setIsTourStepOpen(false)} + step={1} + stepsTotal={1} + title="Step 1" + anchorPosition="rightUp" + > + { + await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1', { + indexName, + }); + }} + > + Complete step 1 + +
+
+
+
); diff --git a/examples/portable_dashboards_example/kibana.jsonc b/examples/portable_dashboards_example/kibana.jsonc index 22699c136a96e..e4016f2b1c01e 100644 --- a/examples/portable_dashboards_example/kibana.jsonc +++ b/examples/portable_dashboards_example/kibana.jsonc @@ -20,7 +20,6 @@ "unifiedSearch", "developerExamples", "embeddableExamples" - ], - "requiredBundles": ["presentationUtil"] + ] } } diff --git a/examples/portable_dashboards_example/public/dashboard_with_controls_example.tsx b/examples/portable_dashboards_example/public/dashboard_with_controls_example.tsx index 562d5392e8270..b0ad160b0829c 100644 --- a/examples/portable_dashboards_example/public/dashboard_with_controls_example.tsx +++ b/examples/portable_dashboards_example/public/dashboard_with_controls_example.tsx @@ -6,19 +6,37 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; -import { withSuspense } from '@kbn/shared-ux-utility'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { controlGroupInputBuilder } from '@kbn/controls-plugin/public'; import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { controlGroupInputBuilder } from '@kbn/controls-plugin/public'; +import { getDefaultControlGroupInput } from '@kbn/controls-plugin/common'; import { FILTER_DEBUGGER_EMBEDDABLE } from '@kbn/embeddable-examples-plugin/public'; -import { LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public'; - -const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer); +import { AwaitingDashboardAPI, DashboardRenderer } from '@kbn/dashboard-plugin/public'; export const DashboardWithControlsExample = ({ dataView }: { dataView: DataView }) => { + const [dashboard, setDashboard] = useState(); + + // add a filter debugger panel as soon as the dashboard becomes available + useEffect(() => { + if (!dashboard) return; + (async () => { + const embeddable = await dashboard.addNewEmbeddable(FILTER_DEBUGGER_EMBEDDABLE, {}); + const prevPanelState = dashboard.getExplicitInput().panels[embeddable.id]; + // resize the new panel so that it fills up the entire width of the dashboard + dashboard.updateInput({ + panels: { + [embeddable.id]: { + ...prevPanelState, + gridData: { i: embeddable.id, x: 0, y: 0, w: 48, h: 12 }, + }, + }, + }); + })(); + }, [dashboard]); + return ( <> @@ -29,10 +47,10 @@ export const DashboardWithControlsExample = ({ dataView }: { dataView: DataView - { const builder = controlGroupInputBuilder; - const controlGroupInput = {}; + const controlGroupInput = getDefaultControlGroupInput(); await builder.addDataControlFromField(controlGroupInput, { dataViewId: dataView.id ?? '', title: 'Destintion country', @@ -57,22 +75,7 @@ export const DashboardWithControlsExample = ({ dataView }: { dataView: DataView }, }; }} - onDashboardContainerLoaded={(container) => { - const addFilterEmbeddable = async () => { - const embeddable = await container.addNewEmbeddable(FILTER_DEBUGGER_EMBEDDABLE, {}); - const prevPanelState = container.getExplicitInput().panels[embeddable.id]; - // resize the new panel so that it fills up the entire width of the dashboard - container.updateInput({ - panels: { - [embeddable.id]: { - ...prevPanelState, - gridData: { i: embeddable.id, x: 0, y: 0, w: 48, h: 12 }, - }, - }, - }); - }; - addFilterEmbeddable(); - }} + ref={setDashboard} /> diff --git a/examples/portable_dashboards_example/public/dual_redux_example.tsx b/examples/portable_dashboards_example/public/dual_redux_example.tsx index 0154ca2f2272e..0385cc0c8eb4b 100644 --- a/examples/portable_dashboards_example/public/dual_redux_example.tsx +++ b/examples/portable_dashboards_example/public/dual_redux_example.tsx @@ -6,9 +6,8 @@ * Side Public License, v 1. */ -import React, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; -import { DashboardContainer, LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public'; import { EuiButtonGroup, EuiFlexGroup, @@ -18,35 +17,19 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; +import { + AwaitingDashboardAPI, + DashboardAPI, + DashboardRenderer, +} from '@kbn/dashboard-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; -import { useDashboardContainerContext } from '@kbn/dashboard-plugin/public'; - -const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer); export const DualReduxExample = () => { - const [firstDashboardContainer, setFirstDashboardContainer] = useState< - DashboardContainer | undefined - >(); - const [secondDashboardContainer, setSecondDashboardContainer] = useState< - DashboardContainer | undefined - >(); - - const FirstDashboardReduxWrapper = useMemo(() => { - if (firstDashboardContainer) return firstDashboardContainer.getReduxEmbeddableTools().Wrapper; - }, [firstDashboardContainer]); - const SecondDashboardReduxWrapper = useMemo(() => { - if (secondDashboardContainer) return secondDashboardContainer.getReduxEmbeddableTools().Wrapper; - }, [secondDashboardContainer]); + const [firstDashboardContainer, setFirstDashboardContainer] = useState(); + const [secondDashboardContainer, setSecondDashboardContainer] = useState(); - const ButtonControls = () => { - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setViewMode }, - } = useDashboardContainerContext(); - const dispatch = useEmbeddableDispatch(); - const viewMode = select((state) => state.explicitInput.viewMode); + const ButtonControls = ({ dashboard }: { dashboard: DashboardAPI }) => { + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); return ( { }, ]} idSelected={viewMode} - onChange={(id, value) => { - dispatch(setViewMode(value)); - }} + onChange={(id, value) => dashboard.dispatch.setViewMode(value)} type="single" /> ); @@ -91,34 +72,18 @@ export const DualReduxExample = () => {

Dashboard #1

- {FirstDashboardReduxWrapper && ( - - - - )} + {firstDashboardContainer && } - { - setFirstDashboardContainer(container); - }} - /> +

Dashboard #2

- {SecondDashboardReduxWrapper && ( - - - - )} + {secondDashboardContainer && } - { - setSecondDashboardContainer(container); - }} - /> +
diff --git a/examples/portable_dashboards_example/public/dynamically_add_panels_example.tsx b/examples/portable_dashboards_example/public/dynamically_add_panels_example.tsx index 20fa99b631134..c1c4f7b850f21 100644 --- a/examples/portable_dashboards_example/public/dynamically_add_panels_example.tsx +++ b/examples/portable_dashboards_example/public/dynamically_add_panels_example.tsx @@ -8,7 +8,7 @@ import React, { useMemo, useState } from 'react'; -import { DashboardContainer, LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public'; +import { AwaitingDashboardAPI, DashboardRenderer } from '@kbn/dashboard-plugin/public'; import { EuiButton, EuiFlexGroup, @@ -23,19 +23,17 @@ import { VisualizeInput, VisualizeOutput, } from '@kbn/visualizations-plugin/public/embeddable/visualize_embeddable'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; const INPUT_KEY = 'portableDashboard:saveExample:input'; -const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer); // make this so we don't have two loading states - loading in the dashboard plugin instead - export const DynamicByReferenceExample = () => { const [isSaving, setIsSaving] = useState(false); - const [dashboardContainer, setDashboardContainer] = useState(); + const [dashboard, setdashboard] = useState(); const onSave = async () => { + if (!dashboard) return; setIsSaving(true); - localStorage.setItem(INPUT_KEY, JSON.stringify(dashboardContainer!.getInput())); + localStorage.setItem(INPUT_KEY, JSON.stringify(dashboard.getInput())); // simulated async save await await new Promise((resolve) => setTimeout(resolve, 1000)); setIsSaving(false); @@ -56,44 +54,37 @@ export const DynamicByReferenceExample = () => { const resetPersistableInput = () => { localStorage.removeItem(INPUT_KEY); - if (dashboardContainer) { - const children = dashboardContainer.getChildIds(); + if (dashboard) { + const children = dashboard.getChildIds(); children.map((childId) => { - dashboardContainer.removeEmbeddable(childId); + dashboard.removeEmbeddable(childId); }); } }; - const addByReference = () => { - if (dashboardContainer) { - dashboardContainer.addFromLibrary(); - } - }; - const addByValue = async () => { - if (dashboardContainer) { - dashboardContainer.addNewEmbeddable( - 'visualization', - { - title: 'Sample Markdown Vis', - savedVis: { - type: 'markdown', - title: '', - data: { aggs: [], searchSource: {} }, - params: { - fontSize: 12, - openLinksInNewTab: false, - markdown: '### By Value Visualization\nThis is a sample by value panel.', - }, + if (!dashboard) return; + dashboard.addNewEmbeddable( + 'visualization', + { + title: 'Sample Markdown Vis', + savedVis: { + type: 'markdown', + title: '', + data: { aggs: [], searchSource: {} }, + params: { + fontSize: 12, + openLinksInNewTab: false, + markdown: '### By Value Visualization\nThis is a sample by value panel.', }, - } - ); - } + }, + } + ); }; const disableButtons = useMemo(() => { - return dashboardContainer === undefined || isSaving; - }, [dashboardContainer, isSaving]); + return !dashboard || isSaving; + }, [dashboard, isSaving]); return ( <> @@ -114,7 +105,7 @@ export const DynamicByReferenceExample = () => { - + dashboard?.addFromLibrary()} isDisabled={disableButtons}> Add visualization from library @@ -141,7 +132,7 @@ export const DynamicByReferenceExample = () => { - { const persistedInput = getPersistableInput(); return { @@ -151,9 +142,7 @@ export const DynamicByReferenceExample = () => { }, }; }} - onDashboardContainerLoaded={(container) => { - setDashboardContainer(container); - }} + ref={setdashboard} /> diff --git a/examples/portable_dashboards_example/public/static_by_reference_example.tsx b/examples/portable_dashboards_example/public/static_by_reference_example.tsx index 8df1bb09c4590..3cd5bcb073a75 100644 --- a/examples/portable_dashboards_example/public/static_by_reference_example.tsx +++ b/examples/portable_dashboards_example/public/static_by_reference_example.tsx @@ -11,15 +11,9 @@ import { css } from '@emotion/react'; import { buildPhraseFilter, Filter } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { - LazyDashboardContainerRenderer, - DashboardCreationOptions, -} from '@kbn/dashboard-plugin/public'; +import { DashboardRenderer, DashboardCreationOptions } from '@kbn/dashboard-plugin/public'; import { EuiCode, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; - -const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer); export const StaticByReferenceExample = ({ dashboardId, @@ -50,7 +44,7 @@ export const StaticByReferenceExample = ({ overflow-y: auto; `} > - { const field = dataView.getFieldByName('machine.os.keyword'); @@ -61,13 +55,10 @@ export const StaticByReferenceExample = ({ if (field) { filter = buildPhraseFilter(field, 'win xp', dataView); filter.meta.negate = true; - creationOptions = { ...creationOptions, overrideInput: { filters: [filter] } }; + creationOptions = { ...creationOptions, initialInput: { filters: [filter] } }; } return creationOptions; // if can't find the field, then just return no special creation options }} - onDashboardContainerLoaded={(container) => { - return; // this example is static, so don't need to do anything with the dashboard container - }} /> diff --git a/examples/portable_dashboards_example/public/static_by_value_example.tsx b/examples/portable_dashboards_example/public/static_by_value_example.tsx index b686c701ced0d..3fbf416fbfbfa 100644 --- a/examples/portable_dashboards_example/public/static_by_value_example.tsx +++ b/examples/portable_dashboards_example/public/static_by_value_example.tsx @@ -11,13 +11,10 @@ import React from 'react'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import type { DashboardPanelMap } from '@kbn/dashboard-plugin/common'; -import { LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; +import { DashboardRenderer } from '@kbn/dashboard-plugin/public'; import panelsJson from './static_by_value_example_panels.json'; -const DashboardContainerRenderer = withSuspense(LazyDashboardContainerRenderer); - export const StaticByValueExample = () => { return ( <> @@ -29,7 +26,7 @@ export const StaticByValueExample = () => { - { return { initialInput: { @@ -39,9 +36,6 @@ export const StaticByValueExample = () => { }, }; }} - onDashboardContainerLoaded={(container) => { - return; // this example is static, so don't need to do anything with the dashboard container - }} /> diff --git a/examples/portable_dashboards_example/tsconfig.json b/examples/portable_dashboards_example/tsconfig.json index 338fd93863aa5..fa93233be3f2a 100644 --- a/examples/portable_dashboards_example/tsconfig.json +++ b/examples/portable_dashboards_example/tsconfig.json @@ -20,11 +20,9 @@ "@kbn/embeddable-plugin", "@kbn/data-views-plugin", "@kbn/visualizations-plugin", - "@kbn/presentation-util-plugin", "@kbn/developer-examples-plugin", "@kbn/embeddable-examples-plugin", "@kbn/shared-ux-page-kibana-template", - "@kbn/shared-ux-utility", "@kbn/controls-plugin", "@kbn/shared-ux-router" ] diff --git a/fleet_packages.json b/fleet_packages.json index e78e1fbe6451f..eef982351e455 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -38,7 +38,13 @@ }, { "name": "fleet_server", - "version": "1.2.0" + "version": "1.3.0" + }, + { + "name": "profiler_symbolizer", + "version": "8.8.0-preview", + "forceAlignStackVersion": true, + "allowSyncToPrerelease": true }, { "name": "synthetics", diff --git a/package.json b/package.json index d36fc1284122b..bd9cc352f6af7 100644 --- a/package.json +++ b/package.json @@ -187,6 +187,7 @@ "@kbn/content-management-examples-plugin": "link:examples/content_management_examples", "@kbn/content-management-plugin": "link:src/plugins/content_management", "@kbn/content-management-table-list": "link:packages/content-management/table_list", + "@kbn/content-management-utils": "link:packages/kbn-content-management-utils", "@kbn/controls-example-plugin": "link:examples/controls_example", "@kbn/controls-plugin": "link:src/plugins/controls", "@kbn/core": "link:src/core", @@ -330,6 +331,9 @@ "@kbn/core-usage-data-base-server-internal": "link:packages/core/usage-data/core-usage-data-base-server-internal", "@kbn/core-usage-data-server": "link:packages/core/usage-data/core-usage-data-server", "@kbn/core-usage-data-server-internal": "link:packages/core/usage-data/core-usage-data-server-internal", + "@kbn/core-user-settings-server": "link:packages/core/user-settings/core-user-settings-server", + "@kbn/core-user-settings-server-internal": "link:packages/core/user-settings/core-user-settings-server-internal", + "@kbn/core-user-settings-server-mocks": "link:packages/core/user-settings/core-user-settings-server-mocks", "@kbn/cross-cluster-replication-plugin": "link:x-pack/plugins/cross_cluster_replication", "@kbn/crypto": "link:packages/kbn-crypto", "@kbn/crypto-browser": "link:packages/kbn-crypto-browser", @@ -470,6 +474,7 @@ "@kbn/maps-plugin": "link:x-pack/plugins/maps", "@kbn/ml-agg-utils": "link:x-pack/packages/ml/agg_utils", "@kbn/ml-date-picker": "link:x-pack/packages/ml/date_picker", + "@kbn/ml-error-utils": "link:x-pack/packages/ml/error_utils", "@kbn/ml-is-defined": "link:x-pack/packages/ml/is_defined", "@kbn/ml-is-populated-object": "link:x-pack/packages/ml/is_populated_object", "@kbn/ml-local-storage": "link:x-pack/packages/ml/local_storage", @@ -492,6 +497,7 @@ "@kbn/object-versioning": "link:packages/kbn-object-versioning", "@kbn/observability-alert-details": "link:x-pack/packages/observability/alert_details", "@kbn/observability-fixtures-plugin": "link:x-pack/test/cases_api_integration/common/plugins/observability", + "@kbn/observability-onboarding-plugin": "link:x-pack/plugins/observability_onboarding", "@kbn/observability-plugin": "link:x-pack/plugins/observability", "@kbn/observability-shared-plugin": "link:x-pack/plugins/observability_shared", "@kbn/oidc-provider-plugin": "link:x-pack/test/security_api_integration/plugins/oidc_provider", @@ -672,6 +678,7 @@ "@kbn/upgrade-assistant-plugin": "link:x-pack/plugins/upgrade_assistant", "@kbn/url-drilldown-plugin": "link:x-pack/plugins/drilldowns/url_drilldown", "@kbn/url-forwarding-plugin": "link:src/plugins/url_forwarding", + "@kbn/url-state": "link:packages/kbn-url-state", "@kbn/usage-collection-plugin": "link:src/plugins/usage_collection", "@kbn/usage-collection-test-plugin": "link:test/plugin_functional/plugins/usage_collection", "@kbn/user-profile-components": "link:packages/kbn-user-profile-components", @@ -728,7 +735,7 @@ "JSONStream": "1.3.5", "abort-controller": "^3.0.0", "adm-zip": "^0.5.9", - "ajv": "^8.11.0", + "ajv": "^8.12.0", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.3.1", "async": "^3.2.3", @@ -850,6 +857,7 @@ "p-limit": "^3.0.1", "p-map": "^4.0.0", "p-retry": "^4.2.0", + "p-settle": "4.1.1", "papaparse": "^5.2.0", "pbf": "3.2.1", "pdfjs-dist": "^2.13.216", @@ -947,7 +955,7 @@ "whatwg-fetch": "^3.0.0", "xml2js": "^0.4.22", "xstate": "^4.35.2", - "xterm": "^5.0.0", + "xterm": "^5.1.0", "yauzl": "^2.10.0", "yazl": "^2.5.1" }, @@ -1426,6 +1434,7 @@ "nyc": "^15.1.0", "oboe": "^2.1.4", "openapi-types": "^10.0.0", + "p-reflect": "2.1.0", "pbf": "3.2.1", "peggy": "^1.2.0", "picomatch": "^2.3.1", diff --git a/packages/content-management/table_list/index.ts b/packages/content-management/table_list/index.ts index f65a63e496054..da29e61111535 100644 --- a/packages/content-management/table_list/index.ts +++ b/packages/content-management/table_list/index.ts @@ -14,5 +14,11 @@ export { TableListViewKibanaProvider, } from './src'; -export type { UserContentCommonSchema, TableListTab, TableListTabParentProps } from './src'; +export type { + UserContentCommonSchema, + TableListTab, + TableListTabParentProps, + RowActions, +} from './src'; + export type { TableListViewKibanaDependencies } from './src/services'; diff --git a/packages/content-management/table_list/src/components/item_details.tsx b/packages/content-management/table_list/src/components/item_details.tsx index 92945a238478a..fa5966b80781f 100644 --- a/packages/content-management/table_list/src/components/item_details.tsx +++ b/packages/content-management/table_list/src/components/item_details.tsx @@ -12,11 +12,11 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import type { Tag } from '../types'; import { useServices } from '../services'; -import type { UserContentCommonSchema, TableListProps as TableListProps } from '../table_list_view'; +import type { UserContentCommonSchema, TableListViewProps } from '../table_list_view'; import { TagBadge } from './tag_badge'; type InheritedProps = Pick< - TableListProps, + TableListViewProps, 'onClickTitle' | 'getDetailViewLink' | 'id' >; interface Props extends InheritedProps { diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index d44baaa9250da..596c09cad34c2 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -18,7 +18,9 @@ import { Direction, Query, Search, + type EuiTableSelectionType, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { useServices } from '../services'; import type { Action } from '../actions'; @@ -27,6 +29,7 @@ import type { TableListProps, UserContentCommonSchema, } from '../table_list_view'; +import type { TableItemsRowActions } from '../types'; import { TableSortSelect } from './table_sort_select'; import { TagFilterPanel } from './tag_filter_panel'; import { useTagFilterPanel } from './use_tag_filter_panel'; @@ -52,6 +55,7 @@ interface Props extends State, TagManageme tableColumns: Array>; hasUpdatedAtMetadata: boolean; deleteItems: TableListProps['deleteItems']; + tableItemsRowActions: TableItemsRowActions; renderCreateButton: () => React.ReactElement | undefined; onSortChange: (column: SortColumnField, direction: Direction) => void; onTableChange: (criteria: CriteriaWithPagination) => void; @@ -72,6 +76,7 @@ export function Table({ entityName, entityNamePlural, tagsToTableItemMap, + tableItemsRowActions, deleteItems, renderCreateButton, tableCaption, @@ -108,13 +113,32 @@ export function Table({ ); }, [deleteItems, dispatch, entityName, entityNamePlural, selectedIds.length]); - const selection = deleteItems - ? { + const selection = useMemo | undefined>(() => { + if (deleteItems) { + return { onSelectionChange: (obj: T[]) => { dispatch({ type: 'onSelectionChange', data: obj }); }, - } - : undefined; + selectable: (obj) => { + const actions = tableItemsRowActions[obj.id]; + return actions?.delete?.enabled !== false; + }, + selectableMessage: (selectable, obj) => { + if (!selectable) { + const actions = tableItemsRowActions[obj.id]; + return ( + actions?.delete?.reason ?? + i18n.translate('contentManagement.tableList.actionsDisabledLabel', { + defaultMessage: 'Actions disabled for this item', + }) + ); + } + return ''; + }, + initialSelected: [], + }; + } + }, [deleteItems, dispatch, tableItemsRowActions]); const { isPopoverOpen, @@ -218,6 +242,7 @@ export function Table({ data-test-subj="itemsInMemTable" rowHeader="attributes.title" tableCaption={tableCaption} + isSelectable /> ); } diff --git a/packages/content-management/table_list/src/index.ts b/packages/content-management/table_list/src/index.ts index 31d887e732a3e..65674db8a5037 100644 --- a/packages/content-management/table_list/src/index.ts +++ b/packages/content-management/table_list/src/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export { TableList, TableListView } from './table_list_view'; +export { TableListView, TableList } from './table_list_view'; export type { TableListViewProps, @@ -21,3 +21,5 @@ export { } from './tabbed_table_list_view'; export { TableListViewProvider, TableListViewKibanaProvider } from './services'; + +export type { RowActions } from './types'; diff --git a/packages/content-management/table_list/src/mocks.tsx b/packages/content-management/table_list/src/mocks.tsx index 3fcf27100e22b..2597e890aff6d 100644 --- a/packages/content-management/table_list/src/mocks.tsx +++ b/packages/content-management/table_list/src/mocks.tsx @@ -82,7 +82,7 @@ export const getStoryServices = (params: Params, action: ActionFn = () => {}) => * consuming component stories. */ export const getStoryArgTypes = () => ({ - tableListTitle: { + title: { control: { type: 'text', }, diff --git a/packages/content-management/table_list/src/services.tsx b/packages/content-management/table_list/src/services.tsx index 5edc16d9a915d..d7752a270c86c 100644 --- a/packages/content-management/table_list/src/services.tsx +++ b/packages/content-management/table_list/src/services.tsx @@ -80,9 +80,7 @@ export interface TableListViewKibanaDependencies { core: { application: { capabilities: { - advancedSettings?: { - save: boolean; - }; + [key: string]: Readonly>>; }; getUrlForApp: (app: string, options: { path: string }) => string; currentAppId$: Observable; diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index 960dd23accbed..b5f24ea0a7778 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -55,6 +55,12 @@ const twoDaysAgoToString = new Date(twoDaysAgo.getTime()).toDateString(); const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); const yesterdayToString = new Date(yesterday.getTime()).toDateString(); +const getActions = (testBed: TestBed) => ({ + openSortSelect() { + testBed.find('tableSortSelectBtn').at(0).simulate('click'); + }, +}); + describe('TableListView', () => { const requiredProps: TableListViewProps = { entityName: 'test', @@ -307,6 +313,36 @@ describe('TableListView', () => { expect(lastRowTitle).toBe('Item 19'); }); + test('should allow changing the number of rows in the table', async () => { + let testBed: TestBed; + + await act(async () => { + testBed = await setup({ + initialPageSize, + findItems: jest.fn().mockResolvedValue({ total: hits.length, hits: [...hits] }), + }); + }); + + const { component, table, find } = testBed!; + component.update(); + + let { tableCellsValues } = table.getMetaData('itemsInMemTable'); + expect(tableCellsValues.length).toBe(requiredProps.initialPageSize); + + // Changing the "Rows per page" also sends the "sort" column information and thus updates the sorting. + // We test that the "sort by" column has not changed before and after changing the number of rows + expect(find('tableSortSelectBtn').at(0).text()).toBe('Recently updated'); + + // Open the "Rows per page" drop down + find('tablePaginationPopoverButton').simulate('click'); + find('tablePagination-10-rows').simulate('click'); + + ({ tableCellsValues } = table.getMetaData('itemsInMemTable')); + expect(tableCellsValues.length).toBe(10); + + expect(find('tableSortSelectBtn').at(0).text()).toBe('Recently updated'); // Still the same + }); + test('should navigate to page 2', async () => { let testBed: TestBed; @@ -351,12 +387,6 @@ describe('TableListView', () => { } ); - const getActions = (testBed: TestBed) => ({ - openSortSelect() { - testBed.find('tableSortSelectBtn').at(0).simulate('click'); - }, - }); - const hits: UserContentCommonSchema[] = [ { id: '123', @@ -1038,6 +1068,111 @@ describe('TableListView', () => { expect(router?.history.location?.search).toBe('?sort=title&sortdir=desc'); }); }); + + describe('row item actions', () => { + const hits: UserContentCommonSchema[] = [ + { + id: '123', + updatedAt: twoDaysAgo.toISOString(), + type: 'dashboard', + attributes: { + title: 'Item 1', + description: 'Item 1 description', + }, + references: [], + }, + { + id: '456', + updatedAt: yesterday.toISOString(), + type: 'dashboard', + attributes: { + title: 'Item 2', + description: 'Item 2 description', + }, + references: [], + }, + ]; + + const setupTest = async (props?: Partial) => { + let testBed: TestBed | undefined; + const deleteItems = jest.fn(); + await act(async () => { + testBed = await setup({ + findItems: jest.fn().mockResolvedValue({ total: hits.length, hits }), + deleteItems, + ...props, + }); + }); + + testBed!.component.update(); + return { testBed: testBed!, deleteItems }; + }; + + test('should allow select items to be deleted', async () => { + const { + testBed: { table, find, exists, component, form }, + deleteItems, + } = await setupTest(); + + const { tableCellsValues } = table.getMetaData('itemsInMemTable'); + + expect(tableCellsValues).toEqual([ + ['', 'Item 2Item 2 description', yesterdayToString], // First empty col is the "checkbox" + ['', 'Item 1Item 1 description', twoDaysAgoToString], + ]); + + const selectedHit = hits[1]; + + expect(exists('deleteSelectedItems')).toBe(false); + act(() => { + // Select the second item + form.selectCheckBox(`checkboxSelectRow-${selectedHit.id}`); + }); + component.update(); + // Delete button is now visible + expect(exists('deleteSelectedItems')).toBe(true); + + // Click delete and validate that confirm modal opens + expect(component.exists('.euiModal--confirmation')).toBe(false); + act(() => { + find('deleteSelectedItems').simulate('click'); + }); + component.update(); + expect(component.exists('.euiModal--confirmation')).toBe(true); + + await act(async () => { + find('confirmModalConfirmButton').simulate('click'); + }); + expect(deleteItems).toHaveBeenCalledWith([selectedHit]); + }); + + test('should allow to disable the "delete" action for a row', async () => { + const reasonMessage = 'This file cannot be deleted.'; + + const { + testBed: { find }, + } = await setupTest({ + rowItemActions: (obj) => { + if (obj.id === hits[1].id) { + return { + delete: { + enabled: false, + reason: reasonMessage, + }, + }; + } + }, + }); + + const firstCheckBox = find(`checkboxSelectRow-${hits[0].id}`); + const secondCheckBox = find(`checkboxSelectRow-${hits[1].id}`); + + expect(firstCheckBox.props().disabled).toBe(false); + expect(secondCheckBox.props().disabled).toBe(true); + // EUI changes the check "title" from "Select this row" to the reason to disable the checkbox + expect(secondCheckBox.props().title).toBe(reasonMessage); + }); + }); }); describe('TableList', () => { diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index f035bee2af59d..0d050774164dd 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -50,6 +50,7 @@ import { getReducer } from './reducer'; import type { SortColumnField } from './components'; import { useTags } from './use_tags'; import { useInRouterContext, useUrlState } from './use_url_state'; +import { RowActions, TableItemsRowActions } from './types'; interface ContentEditorConfig extends Pick { @@ -75,6 +76,11 @@ export interface TableListViewProps RowActions | undefined; children?: ReactNode | undefined; findItems( searchQuery: string, @@ -126,6 +132,7 @@ export type TableListProps({ +const tableColumnMetadata = { + title: { + field: 'attributes.title', + name: 'Name, description, tags', + }, + updatedAt: { + field: 'updatedAt', + name: 'Last updated', + }, +} as const; + +function TableListComp({ tableCaption, entityName, entityNamePlural, @@ -260,6 +278,7 @@ const TableListComp = function TableListComp( urlStateEnabled = true, customTableColumn, emptyPrompt, + rowItemActions, findItems, createItem, editItem, @@ -401,7 +420,6 @@ const TableListComp = function TableListComp( response, }, }); - onInitialFetchReturned(); } } catch (err) { dispatch({ @@ -409,7 +427,7 @@ const TableListComp = function TableListComp( data: err, }); } - }, [searchQueryParser, searchQuery.text, findItems, onInitialFetchReturned]); + }, [searchQueryParser, findItems, searchQuery.text]); useEffect(() => { fetchItems(); @@ -472,7 +490,7 @@ const TableListComp = function TableListComp( const tableColumns = useMemo(() => { const columns: Array> = [ { - field: 'attributes.title', + field: tableColumnMetadata.title.field, name: titleColumnName ?? i18n.translate('contentManagement.tableList.mainColumnName', { @@ -506,7 +524,7 @@ const TableListComp = function TableListComp( if (hasUpdatedAtMetadata) { columns.push({ - field: 'updatedAt', + field: tableColumnMetadata.updatedAt.field, name: i18n.translate('contentManagement.tableList.lastUpdatedColumnTitle', { defaultMessage: 'Last updated', }), @@ -604,6 +622,15 @@ const TableListComp = function TableListComp( return selectedIds.map((selectedId) => itemsById[selectedId]); }, [selectedIds, itemsById]); + const tableItemsRowActions = useMemo(() => { + return items.reduce((acc, item) => { + return { + ...acc, + [item.id]: rowItemActions ? rowItemActions(item) : undefined, + }; + }, {}); + }, [items, rowItemActions]); + // ------------ // Callbacks // ------------ @@ -665,8 +692,17 @@ const TableListComp = function TableListComp( } = {}; if (criteria.sort) { + // We need to serialise the field as the return either (1) the field _name_ (e.g. "Last updated") + // when changing the "Rows per page" select value or (2) the field _value_ (e.g. "updatedAt") when clicking the column title + let fieldSerialized: unknown = criteria.sort.field; + if (fieldSerialized === tableColumnMetadata.title.name) { + fieldSerialized = tableColumnMetadata.title.field; + } else if (fieldSerialized === tableColumnMetadata.updatedAt.name) { + fieldSerialized = tableColumnMetadata.updatedAt.field; + } + data.sort = { - field: criteria.sort.field as SortColumnField, + field: fieldSerialized as SortColumnField, direction: criteria.sort.direction, }; } @@ -869,6 +905,20 @@ const TableListComp = function TableListComp( }; }, []); + const PageTemplate = useMemo(() => { + return withoutPageTemplateWrapper + ? ((({ + children: _children, + 'data-test-subj': dataTestSubj, + }: { + children: React.ReactNode; + ['data-test-subj']?: string; + }) => ( +
{_children}
+ )) as unknown as typeof KibanaPageTemplate) + : KibanaPageTemplate; + }, [withoutPageTemplateWrapper]); + // ------------ // Render // ------------ @@ -876,10 +926,6 @@ const TableListComp = function TableListComp( return null; } - const PageTemplate = withoutPageTemplateWrapper - ? (React.Fragment as unknown as typeof KibanaPageTemplate) - : KibanaPageTemplate; - if (!showFetchError && hasNoItems) { return ( @@ -925,6 +971,7 @@ const TableListComp = function TableListComp( tableColumns={tableColumns} hasUpdatedAtMetadata={hasUpdatedAtMetadata} tableSort={tableSort} + tableItemsRowActions={tableItemsRowActions} pagination={pagination} selectedIds={selectedIds} entityName={entityName} @@ -954,7 +1001,7 @@ const TableListComp = function TableListComp( ); -}; +} export const TableList = React.memo(TableListComp) as typeof TableListComp; @@ -976,6 +1023,7 @@ export const TableListView = ({ deleteItems, getDetailViewLink, onClickTitle, + rowItemActions, id: listingId, contentEditor, children, @@ -1018,6 +1066,7 @@ export const TableListView = ({ createItem={createItem} editItem={editItem} deleteItems={deleteItems} + rowItemActions={rowItemActions} getDetailViewLink={getDetailViewLink} onClickTitle={onClickTitle} id={listingId} diff --git a/packages/content-management/table_list/src/types.ts b/packages/content-management/table_list/src/types.ts index 0e716e6d59cf3..c8e734a289451 100644 --- a/packages/content-management/table_list/src/types.ts +++ b/packages/content-management/table_list/src/types.ts @@ -12,3 +12,16 @@ export interface Tag { description: string; color: string; } + +export type TableRowAction = 'delete'; + +export type RowActions = { + [action in TableRowAction]?: { + enabled: boolean; + reason?: string; + }; +}; + +export interface TableItemsRowActions { + [id: string]: RowActions | undefined; +} diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts index 4b2519c6bc80f..0a0353fbae7eb 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.ts @@ -122,7 +122,9 @@ function validOptions( /** @internal */ interface RouterOptions { /** Whether we are running in development */ - isDev: boolean; + isDev?: boolean; + /** Whether we are running in a serverless */ + isServerless?: boolean; } /** @@ -142,7 +144,7 @@ export class Router, - private readonly options: RouterOptions = { isDev: false } + private readonly options: RouterOptions = { isDev: false, isServerless: false } ) { const buildMethod = (method: Method) => @@ -216,7 +218,11 @@ export class Router = undefined; public get versioned(): VersionedRouter { if (this.versionedRouter === undefined) { - this.versionedRouter = CoreVersionedRouter.from({ router: this, isDev: this.options.isDev }); + this.versionedRouter = CoreVersionedRouter.from({ + router: this, + isDev: this.options.isDev, + defaultHandlerResolutionStrategy: this.options.isServerless ? 'newest' : 'oldest', + }); } return this.versionedRouter; } diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts index 4d73b09563077..e053444d72551 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { schema } from '@kbn/config-schema'; import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts index 2d148ab461a6e..79abccea59dd8 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts @@ -9,7 +9,7 @@ import type { IRouter } from '@kbn/core-http-server'; import type { VersionedRouter, VersionedRoute, VersionedRouteConfig } from '@kbn/core-http-server'; import { CoreVersionedRoute } from './core_versioned_route'; -import { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types'; +import type { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types'; /** @internal */ interface Dependencies { diff --git a/packages/core/http/core-http-server-internal/src/http_service.ts b/packages/core/http/core-http-server-internal/src/http_service.ts index 5c1ec864b2141..2afc4150108df 100644 --- a/packages/core/http/core-http-server-internal/src/http_service.ts +++ b/packages/core/http/core-http-server-internal/src/http_service.ts @@ -129,7 +129,7 @@ export class HttpService path, this.log, prebootServerRequestHandlerContext.createHandler.bind(null, this.coreContext.coreId), - { isDev: this.env.mode.dev } + { isDev: this.env.mode.dev, isServerless: this.env.cliArgs.serverless } ); registerCallback(router); @@ -175,6 +175,7 @@ export class HttpService const enhanceHandler = this.requestHandlerContext!.createHandler.bind(null, pluginId); const router = new Router(path, this.log, enhanceHandler, { isDev: this.env.mode.dev, + isServerless: this.env.cliArgs.serverless, }); registerRouter(router); return router; diff --git a/packages/core/lifecycle/core-lifecycle-server-internal/src/internal_core_setup.ts b/packages/core/lifecycle/core-lifecycle-server-internal/src/internal_core_setup.ts index b72925132b710..ec3408e72e3f4 100644 --- a/packages/core/lifecycle/core-lifecycle-server-internal/src/internal_core_setup.ts +++ b/packages/core/lifecycle/core-lifecycle-server-internal/src/internal_core_setup.ts @@ -25,6 +25,7 @@ import type { InternalStatusServiceSetup } from '@kbn/core-status-server-interna import type { InternalUiSettingsServiceSetup } from '@kbn/core-ui-settings-server-internal'; import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { InternalCustomBrandingSetup } from '@kbn/core-custom-branding-server-internal'; +import type { InternalUserSettingsServiceSetup } from '@kbn/core-user-settings-server-internal'; /** @internal */ export interface InternalCoreSetup { @@ -47,4 +48,5 @@ export interface InternalCoreSetup { deprecations: InternalDeprecationsServiceSetup; coreUsageData: InternalCoreUsageDataSetup; customBranding: InternalCustomBrandingSetup; + userSettings: InternalUserSettingsServiceSetup; } diff --git a/packages/core/lifecycle/core-lifecycle-server-internal/tsconfig.json b/packages/core/lifecycle/core-lifecycle-server-internal/tsconfig.json index df4fa3593fd97..c964174dd41f8 100644 --- a/packages/core/lifecycle/core-lifecycle-server-internal/tsconfig.json +++ b/packages/core/lifecycle/core-lifecycle-server-internal/tsconfig.json @@ -32,7 +32,8 @@ "@kbn/core-usage-data-base-server-internal", "@kbn/core-usage-data-server", "@kbn/core-custom-branding-server-internal", - "@kbn/core-custom-branding-server" + "@kbn/core-custom-branding-server", + "@kbn/core-user-settings-server-internal" ], "exclude": [ "target/**/*", diff --git a/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts b/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts index c584476042bbc..5397bc154f862 100644 --- a/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts +++ b/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts @@ -26,6 +26,7 @@ import { executionContextServiceMock } from '@kbn/core-execution-context-server- import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks'; import { customBrandingServiceMock } from '@kbn/core-custom-branding-server-mocks'; import { createCoreStartMock } from './core_start.mock'; +import { userSettingsServiceMock } from '@kbn/core-user-settings-server-mocks'; type CoreSetupMockType = MockedKeys & { elasticsearch: ReturnType; @@ -53,6 +54,7 @@ export function createCoreSetupMock({ analytics: analyticsServiceMock.createAnalyticsServiceSetup(), capabilities: capabilitiesServiceMock.createSetupContract(), customBranding: customBrandingServiceMock.createSetupContract(), + userSettings: userSettingsServiceMock.createSetupContract(), docLinks: docLinksServiceMock.createSetupContract(), elasticsearch: elasticsearchServiceMock.createSetup(), http: httpMock, diff --git a/packages/core/lifecycle/core-lifecycle-server-mocks/src/internal_core_setup.mock.ts b/packages/core/lifecycle/core-lifecycle-server-mocks/src/internal_core_setup.mock.ts index 26cf1e5588d38..686b0a84718db 100644 --- a/packages/core/lifecycle/core-lifecycle-server-mocks/src/internal_core_setup.mock.ts +++ b/packages/core/lifecycle/core-lifecycle-server-mocks/src/internal_core_setup.mock.ts @@ -25,6 +25,7 @@ import { statusServiceMock } from '@kbn/core-status-server-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks'; import { customBrandingServiceMock } from '@kbn/core-custom-branding-server-mocks'; +import { userSettingsServiceMock } from '@kbn/core-user-settings-server-mocks'; export function createInternalCoreSetupMock() { const setupDeps = { @@ -47,6 +48,7 @@ export function createInternalCoreSetupMock() { executionContext: executionContextServiceMock.createInternalSetupContract(), coreUsageData: coreUsageDataServiceMock.createSetupContract(), customBranding: customBrandingServiceMock.createSetupContract(), + userSettings: userSettingsServiceMock.createSetupContract(), }; return setupDeps; } diff --git a/packages/core/lifecycle/core-lifecycle-server-mocks/tsconfig.json b/packages/core/lifecycle/core-lifecycle-server-mocks/tsconfig.json index 777c25f6dc9fe..99e8f4ed6541d 100644 --- a/packages/core/lifecycle/core-lifecycle-server-mocks/tsconfig.json +++ b/packages/core/lifecycle/core-lifecycle-server-mocks/tsconfig.json @@ -34,6 +34,7 @@ "@kbn/core-http-request-handler-context-server", "@kbn/core-logging-server-mocks", "@kbn/core-custom-branding-server-mocks", + "@kbn/core-user-settings-server-mocks", ], "exclude": [ "target/**/*", diff --git a/packages/core/lifecycle/core-lifecycle-server/src/core_setup.ts b/packages/core/lifecycle/core-lifecycle-server/src/core_setup.ts index 29599472531fb..aa4fd54fa2f99 100644 --- a/packages/core/lifecycle/core-lifecycle-server/src/core_setup.ts +++ b/packages/core/lifecycle/core-lifecycle-server/src/core_setup.ts @@ -23,6 +23,7 @@ import { StatusServiceSetup } from '@kbn/core-status-server'; import { UiSettingsServiceSetup } from '@kbn/core-ui-settings-server'; import { CoreUsageDataSetup } from '@kbn/core-usage-data-server'; import { CustomBrandingSetup } from '@kbn/core-custom-branding-server'; +import { UserSettingsServiceSetup } from '@kbn/core-user-settings-server'; import { CoreStart } from './core_start'; /** @@ -64,6 +65,8 @@ export interface CoreSetup( setSecurityExtension: deps.savedObjects.setSecurityExtension, setSpacesExtension: deps.savedObjects.setSpacesExtension, registerType: deps.savedObjects.registerType, - getKibanaIndex: deps.savedObjects.getKibanaIndex, + getDefaultIndex: deps.savedObjects.getDefaultIndex, + getAllIndices: deps.savedObjects.getAllIndices, }, status: { core$: deps.status.core$, @@ -259,6 +260,9 @@ export function createPluginSetupContext( register: deps.uiSettings.register, registerGlobal: deps.uiSettings.registerGlobal, }, + userSettings: { + setUserProfileSettings: deps.userSettings.setUserProfileSettings, + }, getStartServices: () => plugin.startDependencies, deprecations: deps.deprecations.getRegistry(plugin.name), coreUsageData: { @@ -313,6 +317,10 @@ export function createPluginStartContext( createExporter: deps.savedObjects.createExporter, createImporter: deps.savedObjects.createImporter, getTypeRegistry: deps.savedObjects.getTypeRegistry, + getDefaultIndex: deps.savedObjects.getDefaultIndex, + getIndexForType: deps.savedObjects.getIndexForType, + getIndicesForTypes: deps.savedObjects.getIndicesForTypes, + getAllIndices: deps.savedObjects.getAllIndices, }, metrics: { collectionInterval: deps.metrics.collectionInterval, diff --git a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts index af9de3fbd6ff1..354d3fda659fe 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.test.ts @@ -19,6 +19,7 @@ import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { httpServiceMock, httpServerMock } from '@kbn/core-http-server-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks'; import { bootstrapRendererFactory, BootstrapRenderer } from './bootstrap_renderer'; +import { userSettingsServiceMock } from '@kbn/core-user-settings-server-mocks'; const createPackageInfo = (parts: Partial = {}): PackageInfo => ({ branch: 'master', @@ -41,12 +42,14 @@ describe('bootstrapRenderer', () => { let renderer: BootstrapRenderer; let uiPlugins: UiPlugins; let packageInfo: PackageInfo; + let userSettingsService: ReturnType; beforeEach(() => { auth = httpServiceMock.createAuth(); uiSettingsClient = uiSettingsServiceMock.createClient(); uiPlugins = createUiPlugins(); packageInfo = createPackageInfo(); + userSettingsService = userSettingsServiceMock.createSetupContract(); getThemeTagMock.mockReturnValue('v8light'); getPluginsBundlePathsMock.mockReturnValue(new Map()); @@ -88,7 +91,7 @@ describe('bootstrapRenderer', () => { expect(uiSettingsClient.get).toHaveBeenCalledWith('theme:darkMode'); }); - it('calls getThemeTag with the correct parameters', async () => { + it('calls getThemeTag with the values from the UiSettingsClient when the UserSettingsService is not provided', async () => { uiSettingsClient.get.mockResolvedValue(true); const request = httpServerMock.createKibanaRequest(); @@ -104,6 +107,58 @@ describe('bootstrapRenderer', () => { darkMode: true, }); }); + + it('calls getThemeTag with values from the UserSettingsService when provided', async () => { + userSettingsService.getUserSettingDarkMode.mockReturnValueOnce(true); + + renderer = bootstrapRendererFactory({ + auth, + packageInfo, + uiPlugins, + serverBasePath: '/base-path', + userSettingsService, + }); + + uiSettingsClient.get.mockResolvedValue(false); + const request = httpServerMock.createKibanaRequest(); + + await renderer({ + request, + uiSettingsClient, + }); + + expect(getThemeTagMock).toHaveBeenCalledTimes(1); + expect(getThemeTagMock).toHaveBeenCalledWith({ + themeVersion: 'v8', + darkMode: true, + }); + }); + + it('calls getThemeTag with values from the UiSettingsClient when values from UserSettingsService are `undefined`', async () => { + userSettingsService.getUserSettingDarkMode.mockReturnValueOnce(undefined); + + renderer = bootstrapRendererFactory({ + auth, + packageInfo, + uiPlugins, + serverBasePath: '/base-path', + userSettingsService, + }); + + uiSettingsClient.get.mockResolvedValue(false); + const request = httpServerMock.createKibanaRequest(); + + await renderer({ + request, + uiSettingsClient, + }); + + expect(getThemeTagMock).toHaveBeenCalledTimes(1); + expect(getThemeTagMock).toHaveBeenCalledWith({ + themeVersion: 'v8', + darkMode: false, + }); + }); }); describe('when the auth status is `unknown`', () => { diff --git a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts index 83043a100c857..abda18486657f 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/bootstrap/bootstrap_renderer.ts @@ -12,6 +12,7 @@ import { ThemeVersion } from '@kbn/ui-shared-deps-npm'; import type { KibanaRequest, HttpAuth } from '@kbn/core-http-server'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; +import { InternalUserSettingsServiceSetup } from '@kbn/core-user-settings-server-internal'; import { getPluginsBundlePaths } from './get_plugin_bundle_paths'; import { getJsDependencyPaths } from './get_js_dependency_paths'; import { getThemeTag } from './get_theme_tag'; @@ -25,6 +26,7 @@ interface FactoryOptions { packageInfo: PackageInfo; uiPlugins: UiPlugins; auth: HttpAuth; + userSettingsService?: InternalUserSettingsServiceSetup; } interface RenderedOptions { @@ -43,6 +45,7 @@ export const bootstrapRendererFactory: BootstrapRendererFactory = ({ serverBasePath, uiPlugins, auth, + userSettingsService, }) => { const isAuthenticated = (request: KibanaRequest) => { const { status: authStatus } = auth.get(request); @@ -56,7 +59,16 @@ export const bootstrapRendererFactory: BootstrapRendererFactory = ({ try { const authenticated = isAuthenticated(request); - darkMode = authenticated ? await uiSettingsClient.get('theme:darkMode') : false; + + if (authenticated) { + const userSettingDarkMode = await userSettingsService?.getUserSettingDarkMode(request); + + if (userSettingDarkMode) { + darkMode = userSettingDarkMode; + } else { + darkMode = await uiSettingsClient.get('theme:darkMode'); + } + } } catch (e) { // just use the default values in case of connectivity issues with ES } diff --git a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts index b5ac580ae8d05..c5c19d112cb21 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts @@ -184,6 +184,84 @@ function renderTestCases( }); } +function renderDarkModeTestCases( + getRender: () => Promise< + [ + InternalRenderingServicePreboot['render'] | InternalRenderingServiceSetup['render'], + typeof mockRenderingPrebootDeps | typeof mockRenderingSetupDeps + ] + > +) { + describe('render() Dark Mode tests', () => { + let uiSettings: { + client: ReturnType; + globalClient: ReturnType; + }; + + beforeEach(async () => { + uiSettings = { + client: uiSettingsServiceMock.createClient(), + globalClient: uiSettingsServiceMock.createClient(), + }; + uiSettings.client.getRegistered.mockReturnValue({ + registered: { name: 'title' }, + }); + }); + + describe('Dark Mode', () => { + it('UserSettings value should override the space setting', async () => { + mockRenderingSetupDeps.userSettings.getUserSettingDarkMode.mockReturnValueOnce( + Promise.resolve(true) + ); + + getSettingValueMock.mockImplementation((settingName: string) => { + if (settingName === 'theme:darkMode') { + return false; + } + return settingName; + }); + + const settings = { 'theme:darkMode': { userValue: false } }; + uiSettings.client.getUserProvided.mockResolvedValue(settings); + + const [render] = await getRender(); + await render(createKibanaRequest(), uiSettings); + + expect(getStylesheetPathsMock).toHaveBeenCalledWith({ + darkMode: true, + themeVersion: 'v8', + basePath: '/mock-server-basepath', + buildNum: expect.any(Number), + }); + }); + + it('Space setting value should be used if UsersSettings value is undefined', async () => { + mockRenderingSetupDeps.userSettings.getUserSettingDarkMode.mockReturnValueOnce( + Promise.resolve(undefined) + ); + getSettingValueMock.mockImplementation((settingName: string) => { + if (settingName === 'theme:darkMode') { + return false; + } + return settingName; + }); + + const settings = { 'theme:darkMode': { userValue: false } }; + uiSettings.client.getUserProvided.mockResolvedValue(settings); + const [render] = await getRender(); + await render(createKibanaRequest(), uiSettings); + + expect(getStylesheetPathsMock).toHaveBeenCalledWith({ + darkMode: false, + themeVersion: 'v8', + basePath: '/mock-server-basepath', + buildNum: expect.any(Number), + }); + }); + }); + }); +} + describe('RenderingService', () => { let service: RenderingService; @@ -231,5 +309,9 @@ describe('RenderingService', () => { await service.preboot(mockRenderingPrebootDeps); return [(await service.setup(mockRenderingSetupDeps)).render, mockRenderingSetupDeps]; }); + renderDarkModeTestCases(async () => { + await service.preboot(mockRenderingPrebootDeps); + return [(await service.setup(mockRenderingSetupDeps)).render, mockRenderingSetupDeps]; + }); }); }); diff --git a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx index 063c94077b1bc..d2854059e8508 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx @@ -18,6 +18,7 @@ import type { KibanaRequest, HttpAuth } from '@kbn/core-http-server'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import { CustomBranding } from '@kbn/core-custom-branding-common'; +import { UserProvidedValues } from '@kbn/core-ui-settings-common'; import { Template } from './views'; import { IRenderOptions, @@ -34,7 +35,12 @@ import type { InternalRenderingRequestHandlerContext } from './internal_types'; type RenderOptions = | RenderingSetupDeps - | (RenderingPrebootDeps & { status?: never; elasticsearch?: never; customBranding?: never }); + | (RenderingPrebootDeps & { + status?: never; + elasticsearch?: never; + customBranding?: never; + userSettings?: never; + }); /** @internal */ export class RenderingService { @@ -67,6 +73,7 @@ export class RenderingService { status, uiPlugins, customBranding, + userSettings, }: RenderingSetupDeps): Promise { registerBootstrapRoute({ router: http.createRouter(''), @@ -75,11 +82,19 @@ export class RenderingService { serverBasePath: http.basePath.serverBasePath, packageInfo: this.coreContext.env.packageInfo, auth: http.auth, + userSettingsService: userSettings, }), }); return { - render: this.render.bind(this, { elasticsearch, http, uiPlugins, status, customBranding }), + render: this.render.bind(this, { + elasticsearch, + http, + uiPlugins, + status, + customBranding, + userSettings, + }), }; } @@ -92,7 +107,7 @@ export class RenderingService { }, { isAnonymousPage = false, vars, includeExposedConfigKeys }: IRenderOptions = {} ) { - const { elasticsearch, http, uiPlugins, status, customBranding } = renderOptions; + const { elasticsearch, http, uiPlugins, status, customBranding, userSettings } = renderOptions; const env = { mode: this.coreContext.env.mode, @@ -101,14 +116,29 @@ export class RenderingService { const buildNum = env.packageInfo.buildNum; const basePath = http.basePath.get(request); const { serverBasePath, publicBaseUrl } = http.basePath; + + let settingsUserValues: Record = {}; + let globalSettingsUserValues: Record = {}; + + if (!isAnonymousPage) { + const userValues = await Promise.all([ + uiSettings.client?.getUserProvided(), + uiSettings.globalClient?.getUserProvided(), + ]); + + settingsUserValues = userValues[0]; + globalSettingsUserValues = userValues[1]; + } + const settings = { defaults: uiSettings.client?.getRegistered() ?? {}, - user: isAnonymousPage ? {} : await uiSettings.client?.getUserProvided(), + user: settingsUserValues, }; const globalSettings = { defaults: uiSettings.globalClient?.getRegistered() ?? {}, - user: isAnonymousPage ? {} : await uiSettings.globalClient?.getUserProvided(), + user: globalSettingsUserValues, }; + let clusterInfo = {}; let branding: CustomBranding = {}; try { @@ -129,7 +159,20 @@ export class RenderingService { // swallow error } - const darkMode = getSettingValue('theme:darkMode', settings, Boolean); + let userSettingDarkMode: boolean | undefined; + + if (!isAnonymousPage) { + userSettingDarkMode = await userSettings?.getUserSettingDarkMode(request); + } + + let darkMode: boolean; + + if (userSettingDarkMode) { + darkMode = userSettingDarkMode; + } else { + darkMode = getSettingValue('theme:darkMode', settings, Boolean); + } + const themeVersion: ThemeVersion = 'v8'; const stylesheetPaths = getStylesheetPaths({ diff --git a/packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts b/packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts index d1c726552ae07..42c48d9e0bd21 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/test_helpers/params.ts @@ -11,6 +11,7 @@ import { httpServiceMock } from '@kbn/core-http-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { statusServiceMock } from '@kbn/core-status-server-mocks'; import { customBrandingServiceMock } from '@kbn/core-custom-branding-server-mocks'; +import { userSettingsServiceMock } from '@kbn/core-user-settings-server-mocks'; const context = mockCoreContext.create(); const httpPreboot = httpServiceMock.createInternalPrebootContract(); @@ -18,6 +19,7 @@ const httpSetup = httpServiceMock.createInternalSetupContract(); const status = statusServiceMock.createInternalSetupContract(); const elasticsearch = elasticsearchServiceMock.createInternalSetup(); const customBranding = customBrandingServiceMock.createSetupContract(); +const userSettings = userSettingsServiceMock.createSetupContract(); function createUiPlugins() { return { @@ -38,4 +40,5 @@ export const mockRenderingSetupDeps = { uiPlugins: createUiPlugins(), customBranding, status, + userSettings, }; diff --git a/packages/core/rendering/core-rendering-server-internal/src/types.ts b/packages/core/rendering/core-rendering-server-internal/src/types.ts index a729711869a5d..60dab17a20142 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/types.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/types.ts @@ -20,6 +20,7 @@ import type { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import type { InternalCustomBrandingSetup } from '@kbn/core-custom-branding-server-internal'; import type { CustomBranding } from '@kbn/core-custom-branding-common'; +import type { InternalUserSettingsServiceSetup } from '@kbn/core-user-settings-server-internal'; /** @internal */ export interface RenderingMetadata { @@ -48,6 +49,7 @@ export interface RenderingSetupDeps { status: InternalStatusServiceSetup; uiPlugins: UiPlugins; customBranding: InternalCustomBrandingSetup; + userSettings: InternalUserSettingsServiceSetup; } /** @internal */ diff --git a/packages/core/rendering/core-rendering-server-internal/tsconfig.json b/packages/core/rendering/core-rendering-server-internal/tsconfig.json index e53493b4e2a3e..c689fe370e784 100644 --- a/packages/core/rendering/core-rendering-server-internal/tsconfig.json +++ b/packages/core/rendering/core-rendering-server-internal/tsconfig.json @@ -37,6 +37,8 @@ "@kbn/core-custom-branding-server-internal", "@kbn/core-custom-branding-common", "@kbn/core-custom-branding-server-mocks", + "@kbn/core-user-settings-server-mocks", + "@kbn/core-user-settings-server-internal", ], "exclude": [ "target/**/*", diff --git a/packages/core/root/core-root-server-internal/src/server.test.mocks.ts b/packages/core/root/core-root-server-internal/src/server.test.mocks.ts index 80f2164bd824b..8138433d42360 100644 --- a/packages/core/root/core-root-server-internal/src/server.test.mocks.ts +++ b/packages/core/root/core-root-server-internal/src/server.test.mocks.ts @@ -65,6 +65,11 @@ jest.doMock('@kbn/core-custom-branding-server-internal', () => ({ CustomBrandingService: jest.fn(() => mockCustomBrandingService), })); +export const mockUserSettingsService = userSettingsServiceMock.create(); +jest.doMock('@kbn/core-user-settings-server-internal', () => ({ + UserSettingsService: jest.fn(() => mockUserSettingsService), +})); + export const mockEnsureValidConfiguration = jest.fn(); jest.doMock('@kbn/core-config-server-internal', () => ({ ensureValidConfiguration: mockEnsureValidConfiguration, @@ -134,6 +139,7 @@ jest.doMock('@kbn/core-deprecations-server-internal', () => ({ })); import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; +import { userSettingsServiceMock } from '@kbn/core-user-settings-server-mocks'; export const mockDocLinksService = docLinksServiceMock.create(); jest.doMock('@kbn/core-doc-links-server-internal', () => ({ diff --git a/packages/core/root/core-root-server-internal/src/server.test.ts b/packages/core/root/core-root-server-internal/src/server.test.ts index e7b821f006a76..996eb5057ba46 100644 --- a/packages/core/root/core-root-server-internal/src/server.test.ts +++ b/packages/core/root/core-root-server-internal/src/server.test.ts @@ -26,6 +26,7 @@ import { mockDeprecationService, mockDocLinksService, mockCustomBrandingService, + mockUserSettingsService, } from './server.test.mocks'; import { BehaviorSubject } from 'rxjs'; @@ -113,6 +114,7 @@ test('sets up services on "setup"', async () => { expect(mockDeprecationService.setup).not.toHaveBeenCalled(); expect(mockDocLinksService.setup).not.toHaveBeenCalled(); expect(mockCustomBrandingService.setup).not.toHaveBeenCalled(); + expect(mockUserSettingsService.setup).not.toHaveBeenCalled(); await server.setup(); @@ -130,6 +132,7 @@ test('sets up services on "setup"', async () => { expect(mockDeprecationService.setup).toHaveBeenCalledTimes(1); expect(mockDocLinksService.setup).toHaveBeenCalledTimes(1); expect(mockCustomBrandingService.setup).toHaveBeenCalledTimes(1); + expect(mockUserSettingsService.setup).toHaveBeenCalledTimes(1); }); test('injects legacy dependency to context#setup()', async () => { diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index d05cb89e5efc9..5908a9efee333 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -34,6 +34,7 @@ import { CoreUsageDataService } from '@kbn/core-usage-data-server-internal'; import { StatusService } from '@kbn/core-status-server-internal'; import { UiSettingsService } from '@kbn/core-ui-settings-server-internal'; import { CustomBrandingService } from '@kbn/core-custom-branding-server-internal'; +import { UserSettingsService } from '@kbn/core-user-settings-server-internal'; import { CoreRouteHandlerContext, PrebootCoreRouteHandlerContext, @@ -98,6 +99,7 @@ export class Server { private readonly prebootService: PrebootService; private readonly docLinks: DocLinksService; private readonly customBranding: CustomBrandingService; + private readonly userSettingsService: UserSettingsService; private readonly savedObjectsStartPromise: Promise; private resolveSavedObjectsStartPromise?: (value: SavedObjectsServiceStart) => void; @@ -145,6 +147,7 @@ export class Server { this.prebootService = new PrebootService(core); this.docLinks = new DocLinksService(core); this.customBranding = new CustomBrandingService(core); + this.userSettingsService = new UserSettingsService(core); this.savedObjectsStartPromise = new Promise((resolve) => { this.resolveSavedObjectsStartPromise = resolve; @@ -301,6 +304,7 @@ export class Server { }); const customBrandingSetup = this.customBranding.setup(); + const userSettingsServiceSetup = this.userSettingsService.setup(); const renderingSetup = await this.rendering.setup({ elasticsearch: elasticsearchServiceSetup, @@ -308,6 +312,7 @@ export class Server { status: statusSetup, uiPlugins, customBranding: customBrandingSetup, + userSettings: userSettingsServiceSetup, }); const httpResourcesSetup = this.httpResources.setup({ @@ -337,6 +342,7 @@ export class Server { metrics: metricsSetup, deprecations: deprecationsSetup, coreUsageData: coreUsageDataSetup, + userSettings: userSettingsServiceSetup, }; const pluginsSetup = await this.plugins.setup(coreSetup); diff --git a/packages/core/root/core-root-server-internal/tsconfig.json b/packages/core/root/core-root-server-internal/tsconfig.json index 4eafe9fe15e34..dd2af575ce35b 100644 --- a/packages/core/root/core-root-server-internal/tsconfig.json +++ b/packages/core/root/core-root-server-internal/tsconfig.json @@ -69,6 +69,8 @@ "@kbn/core-custom-branding-server-mocks", "@kbn/repo-packages", "@kbn/core-node-server", + "@kbn/core-user-settings-server-internal", + "@kbn/core-user-settings-server-mocks", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/create.ts b/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/create.ts index a58b4b4a2868d..b9db75e204119 100644 --- a/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/create.ts +++ b/packages/core/saved-objects/core-saved-objects-api-browser/src/apis/create.ts @@ -35,4 +35,12 @@ export interface SavedObjectsCreateOptions { typeMigrationVersion?: string; /** Array of referenced saved objects. */ references?: SavedObjectReference[]; + /** + * Flag indicating if a saved object is managed by Kibana (default=false) + * + * This can be leveraged by applications to e.g. prevent edits to a managed + * saved object. Instead, users can be guided to create a copy first and + * make their edits to the copy. + */ + managed?: boolean; } diff --git a/packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts b/packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts index 12486a32a8c03..2f3cb8dc8fc08 100644 --- a/packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts +++ b/packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts @@ -49,6 +49,14 @@ export interface SimpleSavedObject { * `namespaceType: 'agnostic'`. */ namespaces: SavedObjectType['namespaces']; + /** + * Flag indicating if a saved object is managed by Kibana (default=false) + * + * This can be leveraged by applications to e.g. prevent edits to a managed + * saved object. Instead, users can be guided to create a copy first and + * make their edits to the copy. + */ + managed: SavedObjectType['managed']; /** * Gets an attribute of this object diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.test.ts index 13dacc4f07a64..3ccfd5ca6f2a9 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.test.ts @@ -20,6 +20,7 @@ describe('getRootFields', () => { "migrationVersion", "coreMigrationVersion", "typeMigrationVersion", + "managed", "updated_at", "created_at", "originId", diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.ts index 8ee3f70585452..20e980f4707a1 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/included_fields.ts @@ -18,6 +18,7 @@ const ROOT_FIELDS = [ 'migrationVersion', 'coreMigrationVersion', 'typeMigrationVersion', + 'managed', 'updated_at', 'created_at', 'originId', diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.test.ts index 569142ef8dc5d..cba31cbd02162 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.test.ts @@ -18,6 +18,7 @@ import { normalizeNamespace, rawDocExistsInNamespace, rawDocExistsInNamespaces, + setManaged, } from './internal_utils'; describe('#getBulkOperationError', () => { @@ -99,6 +100,7 @@ describe('#getSavedObjectFromSource', () => { const originId = 'originId'; // eslint-disable-next-line @typescript-eslint/naming-convention const updated_at = 'updatedAt'; + const managed = false; function createRawDoc( type: string, @@ -115,6 +117,7 @@ describe('#getSavedObjectFromSource', () => { migrationVersion, coreMigrationVersion, typeMigrationVersion, + managed, originId, updated_at, ...namespaceAttrs, @@ -131,6 +134,7 @@ describe('#getSavedObjectFromSource', () => { attributes, coreMigrationVersion, typeMigrationVersion, + managed, id, migrationVersion, namespaces: expect.anything(), // see specific test cases below @@ -406,3 +410,26 @@ describe('#getCurrentTime', () => { expect(getCurrentTime()).toEqual('2021-09-10T21:00:00.000Z'); }); }); + +describe('#setManaged', () => { + it('returns false if no arguments are provided', () => { + expect(setManaged({})).toEqual(false); + }); + + it('returns false if only one argument is provided as false', () => { + expect(setManaged({ optionsManaged: false })).toEqual(false); + expect(setManaged({ objectManaged: false })).toEqual(false); + }); + + it('returns true if only one argument is provided as true', () => { + expect(setManaged({ optionsManaged: true })).toEqual(true); + expect(setManaged({ objectManaged: true })).toEqual(true); + }); + + it('overrides objectManaged with optionsManaged', () => { + expect(setManaged({ optionsManaged: false, objectManaged: true })).toEqual(false); + expect(setManaged({ optionsManaged: true, objectManaged: false })).toEqual(true); + expect(setManaged({ optionsManaged: false, objectManaged: false })).toEqual(false); + expect(setManaged({ optionsManaged: true, objectManaged: true })).toEqual(true); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts index 03f3c2c698c3e..afafd67fcab3a 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts @@ -144,6 +144,7 @@ export function getSavedObjectFromSource( created_at: createdAt, coreMigrationVersion, typeMigrationVersion, + managed, migrationVersion = migrationVersionCompatibility === 'compatible' && typeMigrationVersion ? { [type]: typeMigrationVersion } : undefined, @@ -169,6 +170,7 @@ export function getSavedObjectFromSource( version: encodeHitVersion(doc), attributes: doc._source[type], references: doc._source.references || [], + managed, }; } @@ -272,3 +274,30 @@ export function normalizeNamespace(namespace?: string) { export function getCurrentTime() { return new Date(Date.now()).toISOString(); } + +/** + * Returns the managed boolean to apply to a document as it's managed value. + * For use by applications to modify behavior for managed saved objects. + * The behavior is as follows: + * If `optionsManaged` is set, it will override any existing `managed` value in all the documents being created + * If `optionsManaged` is not provided, then the documents are created with whatever may be assigned to their `managed` property + * or default to `false`. + * + * @internal + */ + +export function setManaged({ + optionsManaged, + objectManaged, +}: { + optionsManaged?: boolean; + objectManaged?: boolean; +}): boolean { + if (optionsManaged !== undefined) { + return optionsManaged; + } else if (optionsManaged === undefined && objectManaged !== undefined) { + return objectManaged; + } else { + return false; + } +} diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts index a297d6b5a2b7e..56b9f433a9593 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.encryption_extension.test.ts @@ -22,8 +22,9 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-m import { kibanaMigratorMock } from '../mocks'; import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; import { - ISavedObjectsEncryptionExtension, - SavedObjectsRawDocSource, + MAIN_SAVED_OBJECT_INDEX, + type ISavedObjectsEncryptionExtension, + type SavedObjectsRawDocSource, } from '@kbn/core-saved-objects-server'; import { bulkCreateSuccess, @@ -41,8 +42,8 @@ import { mockVersion, mockVersionProps, MULTI_NAMESPACE_ENCRYPTED_TYPE, - TypeIdTuple, updateSuccess, + type TypeIdTuple, } from '../test_helpers/repository.test.common'; import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock'; @@ -633,7 +634,7 @@ describe('SavedObjectsRepository Encryption Extension', () => { total: 2, hits: [ { - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, _id: `${space ? `${space}:` : ''}${encryptedSO.type}:${encryptedSO.id}`, _score: 1, ...mockVersionProps, @@ -643,7 +644,7 @@ describe('SavedObjectsRepository Encryption Extension', () => { }, }, { - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, _id: `${space ? `${space}:` : ''}index-pattern:logstash-*`, _score: 2, ...mockVersionProps, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts index ccbe414bf82f4..372489c379a4b 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts @@ -47,13 +47,14 @@ import type { SavedObjectsBulkDeleteObject, SavedObjectsBulkDeleteOptions, } from '@kbn/core-saved-objects-api-server'; -import type { - SavedObjectsRawDoc, - SavedObjectsRawDocSource, - SavedObjectUnsanitizedDoc, - SavedObject, - SavedObjectReference, - BulkResolveError, +import { + type SavedObjectsRawDoc, + type SavedObjectsRawDocSource, + type SavedObjectUnsanitizedDoc, + type SavedObject, + type SavedObjectReference, + type BulkResolveError, + MAIN_SAVED_OBJECT_INDEX, } from '@kbn/core-saved-objects-server'; import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; @@ -189,6 +190,7 @@ describe('SavedObjectsRepository', () => { ...(doc.attributes?.title && { title: `${doc.attributes.title}!!` }), }, migrationVersion: mockMigrationVersion, + managed: doc.managed ?? false, references: [{ name: 'search_0', type: 'search', id: '123' }], }); @@ -205,12 +207,14 @@ describe('SavedObjectsRepository', () => { id: '6.0.0-alpha1', attributes: { title: 'Test One' }, references: [{ name: 'ref_0', type: 'test', id: '1' }], + managed: false, }; const obj2 = { type: 'index-pattern', id: 'logstash-*', attributes: { title: 'Test Two' }, references: [{ name: 'ref_0', type: 'test', id: '2' }], + managed: false, }; const namespace = 'foo-namespace'; @@ -259,7 +263,6 @@ describe('SavedObjectsRepository', () => { ...mockTimestampFields, }), ]; - describe('client calls', () => { it(`should use the ES bulk action by default`, async () => { await bulkCreateSuccess(client, repository, [obj1, obj2]); @@ -318,6 +321,7 @@ describe('SavedObjectsRepository', () => { const obj1WithSeq = { ...obj1, + managed: obj1.managed, if_seq_no: mockVersionProps._seq_no, if_primary_term: mockVersionProps._primary_term, }; @@ -330,6 +334,19 @@ describe('SavedObjectsRepository', () => { expectClientCallArgsAction([obj1, obj2], { method: 'create' }); }); + it(`should use the ES index method if ID is defined, overwrite=true and managed=true in a document`, async () => { + await bulkCreateSuccess(client, repository, [obj1, obj2], { + overwrite: true, + managed: true, + }); + expectClientCallArgsAction([obj1, obj2], { method: 'index' }); + }); + + it(`should use the ES create method if ID is defined, overwrite=false and managed=true in a document`, async () => { + await bulkCreateSuccess(client, repository, [obj1, obj2], { managed: true }); + expectClientCallArgsAction([obj1, obj2], { method: 'create' }); + }); + it(`formats the ES request`, async () => { await bulkCreateSuccess(client, repository, [obj1, obj2]); const body = [...expectObjArgs(obj1), ...expectObjArgs(obj2)]; @@ -338,6 +355,17 @@ describe('SavedObjectsRepository', () => { expect.anything() ); }); + // this test only ensures that the client accepts the managed field in a document + it(`formats the ES request with managed=true in a document`, async () => { + const obj1WithManagedTrue = { ...obj1, managed: true }; + const obj2WithManagedTrue = { ...obj2, managed: true }; + await bulkCreateSuccess(client, repository, [obj1WithManagedTrue, obj2WithManagedTrue]); + const body = [...expectObjArgs(obj1WithManagedTrue), ...expectObjArgs(obj2WithManagedTrue)]; + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + }); describe('originId', () => { it(`returns error if originId is set for non-multi-namespace type`, async () => { @@ -439,6 +467,27 @@ describe('SavedObjectsRepository', () => { ); }); + // this only ensures we don't override any other options + it(`adds managed=false to request body if declared for any types that are single-namespace`, async () => { + await bulkCreateSuccess(client, repository, [obj1, obj2], { namespace, managed: false }); + const expected = expect.objectContaining({ namespace, managed: false }); + const body = [expect.any(Object), expected, expect.any(Object), expected]; + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + }); + // this only ensures we don't override any other options + it(`adds managed=true to request body if declared for any types that are single-namespace`, async () => { + await bulkCreateSuccess(client, repository, [obj1, obj2], { namespace, managed: true }); + const expected = expect.objectContaining({ namespace, managed: true }); + const body = [expect.any(Object), expected, expect.any(Object), expected]; + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { await bulkCreateSuccess(client, repository, [obj1, obj2], { namespace: 'default' }); const expected = expect.not.objectContaining({ namespace: 'default' }); @@ -825,10 +874,8 @@ describe('SavedObjectsRepository', () => { it(`migrates the docs and serializes the migrated docs`, async () => { migrator.migrateDocument.mockImplementation(mockMigrateDocument); const modifiedObj1 = { ...obj1, coreMigrationVersion: '8.0.0' }; - await bulkCreateSuccess(client, repository, [modifiedObj1, obj2]); const docs = [modifiedObj1, obj2].map((x) => ({ ...x, ...mockTimestampFieldsWithCreated })); - expectMigrationArgs(docs[0], true, 1); expectMigrationArgs(docs[1], true, 2); @@ -964,6 +1011,96 @@ describe('SavedObjectsRepository', () => { expect.stringMatching(/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/) ); expect(result.saved_objects[1].id).toEqual(obj2.id); + + // Assert that managed is not changed + expect(result.saved_objects[0].managed).toBeFalsy(); + expect(result.saved_objects[1].managed).toEqual(obj2.managed); + }); + + it(`sets managed=false if not already set`, async () => { + const obj1WithoutManaged = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + references: [{ name: 'ref_0', type: 'test', id: '1' }], + }; + const obj2WithoutManaged = { + type: 'index-pattern', + id: 'logstash-*', + attributes: { title: 'Test Two' }, + references: [{ name: 'ref_0', type: 'test', id: '2' }], + }; + const result = await bulkCreateSuccess(client, repository, [ + obj1WithoutManaged, + obj2WithoutManaged, + ]); + expect(result).toEqual({ + saved_objects: [obj1, obj2].map((x) => expectCreateResult(x)), + }); + }); + + it(`sets managed=false only on documents without managed already set`, async () => { + const objWithoutManaged = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + references: [{ name: 'ref_0', type: 'test', id: '1' }], + }; + const result = await bulkCreateSuccess(client, repository, [objWithoutManaged, obj2]); + expect(result).toEqual({ + saved_objects: [obj1, obj2].map((x) => expectCreateResult(x)), + }); + }); + + it(`sets managed=true if provided as an override`, async () => { + const obj1WithoutManaged = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + references: [{ name: 'ref_0', type: 'test', id: '1' }], + }; + const obj2WithoutManaged = { + type: 'index-pattern', + id: 'logstash-*', + attributes: { title: 'Test Two' }, + references: [{ name: 'ref_0', type: 'test', id: '2' }], + }; + const result = await bulkCreateSuccess( + client, + repository, + [obj1WithoutManaged, obj2WithoutManaged], + { managed: true } + ); + expect(result).toEqual({ + saved_objects: [ + { ...obj1WithoutManaged, managed: true }, + { ...obj2WithoutManaged, managed: true }, + ].map((x) => expectCreateResult(x)), + }); + }); + + it(`sets managed=false if provided as an override`, async () => { + const obj1WithoutManaged = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + references: [{ name: 'ref_0', type: 'test', id: '1' }], + }; + const obj2WithoutManaged = { + type: 'index-pattern', + id: 'logstash-*', + attributes: { title: 'Test Two' }, + references: [{ name: 'ref_0', type: 'test', id: '2' }], + }; + const result = await bulkCreateSuccess( + client, + repository, + [obj1WithoutManaged, obj2WithoutManaged], + { managed: false } + ); + expect(result).toEqual({ + saved_objects: [obj1, obj2].map((x) => expectCreateResult(x)), + }); }); }); }); @@ -2392,18 +2529,42 @@ describe('SavedObjectsRepository', () => { }; describe('client calls', () => { - it(`should use the ES index action if ID is not defined and overwrite=true`, async () => { + it(`should use the ES index action if ID is not defined`, async () => { await createSuccess(type, attributes, { overwrite: true }); expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); expect(client.index).toHaveBeenCalled(); }); + it(`should use the ES index action if ID is not defined and a doc has managed=true`, async () => { + await createSuccess(type, attributes, { overwrite: true, managed: true }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.index).toHaveBeenCalled(); + }); + + it(`should use the ES index action if ID is not defined and a doc has managed=false`, async () => { + await createSuccess(type, attributes, { overwrite: true, managed: false }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.index).toHaveBeenCalled(); + }); + it(`should use the ES create action if ID is not defined and overwrite=false`, async () => { await createSuccess(type, attributes); expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); expect(client.create).toHaveBeenCalled(); }); + it(`should use the ES create action if ID is not defined, overwrite=false and a doc has managed=true`, async () => { + await createSuccess(type, attributes, { managed: true }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.create).toHaveBeenCalled(); + }); + + it(`should use the ES create action if ID is not defined, overwrite=false and a doc has managed=false`, async () => { + await createSuccess(type, attributes, { managed: false }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.create).toHaveBeenCalled(); + }); + it(`should use the ES index with version if ID and version are defined and overwrite=true`, async () => { await createSuccess(type, attributes, { id, overwrite: true, version: mockVersion }); expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); @@ -2883,6 +3044,60 @@ describe('SavedObjectsRepository', () => { }); it(`migrates a document and serializes the migrated doc`, async () => { + const migrationVersion = mockMigrationVersion; + const coreMigrationVersion = '8.0.0'; + const managed = false; + await createSuccess(type, attributes, { + id, + references, + migrationVersion, + coreMigrationVersion, + managed, + }); + const doc = { + type, + id, + attributes, + references, + managed, + migrationVersion, + coreMigrationVersion, + ...mockTimestampFieldsWithCreated, + }; + expectMigrationArgs(doc); + + const migratedDoc = migrator.migrateDocument(doc); + expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); + }); + + it(`migrates a document, adds managed=false and serializes the migrated doc`, async () => { + const migrationVersion = mockMigrationVersion; + const coreMigrationVersion = '8.0.0'; + await createSuccess(type, attributes, { + id, + references, + migrationVersion, + coreMigrationVersion, + managed: undefined, + }); + const doc = { + type, + id, + attributes, + references, + managed: undefined, + migrationVersion, + coreMigrationVersion, + ...mockTimestampFieldsWithCreated, + }; + expectMigrationArgs({ ...doc, managed: false }); + + const migratedDoc = migrator.migrateDocument(doc); + expect(migratedDoc.managed).toBe(false); + expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); + }); + + it(`migrates a document, does not change managed=true to managed=false and serializes the migrated doc`, async () => { const migrationVersion = mockMigrationVersion; const coreMigrationVersion = '8.0.0'; await createSuccess(type, attributes, { @@ -2890,12 +3105,14 @@ describe('SavedObjectsRepository', () => { references, migrationVersion, coreMigrationVersion, + managed: true, }); const doc = { type, id, attributes, references, + managed: true, migrationVersion, coreMigrationVersion, ...mockTimestampFieldsWithCreated, @@ -2903,6 +3120,7 @@ describe('SavedObjectsRepository', () => { expectMigrationArgs(doc); const migratedDoc = migrator.migrateDocument(doc); + expect(migratedDoc.managed).toBe(true); expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); }); @@ -2962,6 +3180,27 @@ describe('SavedObjectsRepository', () => { namespaces: [namespace ?? 'default'], coreMigrationVersion: expect.any(String), typeMigrationVersion: '1.1.1', + managed: false, + }); + }); + it(`allows setting 'managed' to true`, async () => { + const result = await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { + id, + namespace, + references, + managed: true, + }); + expect(result).toEqual({ + type: MULTI_NAMESPACE_TYPE, + id, + ...mockTimestampFieldsWithCreated, + version: mockVersion, + attributes, + references, + namespaces: [namespace ?? 'default'], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: '1.1.1', + managed: true, }); }); }); @@ -3549,6 +3788,7 @@ describe('SavedObjectsRepository', () => { 'migrationVersion', 'coreMigrationVersion', 'typeMigrationVersion', + 'managed', 'updated_at', 'created_at', 'originId', @@ -4125,7 +4365,7 @@ describe('SavedObjectsRepository', () => { body: { _id: params.id, ...mockVersionProps, - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, get: { found: true, _source: { @@ -4429,7 +4669,7 @@ describe('SavedObjectsRepository', () => { body: { _id: params.id, ...mockVersionProps, - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, get: { found: true, _source: { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts index a84f6313d462c..0bde80ea0f32f 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts @@ -120,6 +120,7 @@ import { type Either, isLeft, isRight, + setManaged, } from './internal_utils'; import { collectMultiNamespaceReferences } from './collect_multi_namespace_references'; import { updateObjectsSpaces } from './update_objects_spaces'; @@ -309,6 +310,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { migrationVersion, coreMigrationVersion, typeMigrationVersion, + managed, overwrite = false, references = [], refresh = DEFAULT_REFRESH_SETTING, @@ -389,6 +391,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { migrationVersion, coreMigrationVersion, typeMigrationVersion, + managed: setManaged({ optionsManaged: managed }), created_at: time, updated_at: time, ...(Array.isArray(references) && { references }), @@ -442,6 +445,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { migrationVersionCompatibility, overwrite = false, refresh = DEFAULT_REFRESH_SETTING, + managed: optionsManaged, } = options; const time = getCurrentTime(); @@ -455,9 +459,10 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { } >; const expectedResults = objects.map((object) => { - const { type, id: requestId, initialNamespaces, version } = object; + const { type, id: requestId, initialNamespaces, version, managed } = object; let error: DecoratedError | undefined; let id: string = ''; // Assign to make TS happy, the ID will be validated (or randomly generated if needed) during getValidId below + const objectManaged = managed; if (!this._allowedTypes.includes(type)) { error = SavedObjectsErrorHelpers.createUnsupportedTypeError(type); } else { @@ -484,7 +489,11 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { tag: 'Right', value: { method, - object: { ...object, id }, + object: { + ...object, + id, + managed: setManaged({ optionsManaged, objectManaged }), + }, ...(requiresNamespacesCheck && { preflightCheckIndex: preflightCheckIndexCounter++ }), }, }; @@ -606,6 +615,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { typeMigrationVersion: object.typeMigrationVersion, ...(savedObjectNamespace && { namespace: savedObjectNamespace }), ...(savedObjectNamespaces && { namespaces: savedObjectNamespaces }), + managed: setManaged({ optionsManaged, objectManaged: object.managed }), updated_at: time, created_at: time, references: object.references || [], @@ -686,7 +696,6 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { ); }), }; - return this.optionallyDecryptAndRedactBulkResult(result, authorizationResult?.typeMap, objects); } @@ -2338,6 +2347,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { refresh = DEFAULT_REFRESH_SETTING, initialize = false, upsertAttributes, + managed, } = options; if (!id) { @@ -2409,6 +2419,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { }, migrationVersion, typeMigrationVersion, + managed, updated_at: time, }); @@ -2462,6 +2473,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { references: body.get?._source.references ?? [], version: encodeHitVersion(body), attributes: body.get?._source[type], + ...(managed && { managed }), }; } diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts index 570a1704ab854..abeab2a8f2e7c 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts @@ -9,19 +9,20 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { schema } from '@kbn/config-schema'; import { loggerMock } from '@kbn/logging-mocks'; -import { Payload } from 'elastic-apm-node'; -import type { - AuthorizationTypeEntry, - AuthorizeAndRedactMultiNamespaceReferencesParams, - CheckAuthorizationResult, - ISavedObjectsSecurityExtension, - SavedObjectsMappingProperties, - SavedObjectsRawDocSource, - SavedObjectsType, - SavedObjectsTypeMappingDefinition, - SavedObject, - SavedObjectReference, - AuthorizeFindParams, +import type { Payload } from 'elastic-apm-node'; +import { + type AuthorizationTypeEntry, + type AuthorizeAndRedactMultiNamespaceReferencesParams, + type CheckAuthorizationResult, + type ISavedObjectsSecurityExtension, + type SavedObjectsMappingProperties, + type SavedObjectsRawDocSource, + type SavedObjectsType, + type SavedObjectsTypeMappingDefinition, + type SavedObject, + type SavedObjectReference, + type AuthorizeFindParams, + MAIN_SAVED_OBJECT_INDEX, } from '@kbn/core-saved-objects-server'; import type { SavedObjectsBaseOptions, @@ -47,9 +48,9 @@ import { } from '@kbn/core-elasticsearch-client-server-mocks'; import { DocumentMigrator } from '@kbn/core-saved-objects-migration-server-internal'; import { - AuthorizeAndRedactInternalBulkResolveParams, - GetFindRedactTypeMapParams, - AuthorizationTypeMap, + type AuthorizeAndRedactInternalBulkResolveParams, + type GetFindRedactTypeMapParams, + type AuthorizationTypeMap, SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; import { mockGetSearchDsl } from '../lib/repository.test.mock'; @@ -583,16 +584,24 @@ export const expectBulkGetResult = ( export const getMockBulkCreateResponse = ( objects: SavedObjectsBulkCreateObject[], - namespace?: string + namespace?: string, + managed?: boolean ) => { return { errors: false, took: 1, items: objects.map( - ({ type, id, originId, attributes, references, migrationVersion, typeMigrationVersion }) => ({ + ({ + type, + id, + originId, + attributes, + references, + migrationVersion, + typeMigrationVersion, + managed: docManaged, + }) => ({ create: { - // status: 1, - // _index: '.kibana', _id: `${namespace ? `${namespace}:` : ''}${type}:${id}`, _source: { [type]: attributes, @@ -602,6 +611,7 @@ export const getMockBulkCreateResponse = ( references, ...mockTimestampFieldsWithCreated, typeMigrationVersion: typeMigrationVersion || migrationVersion?.[type] || '1.1.1', + managed: managed ?? docManaged ?? false, }, ...mockVersionProps, }, @@ -616,7 +626,7 @@ export const bulkCreateSuccess = async ( objects: SavedObjectsBulkCreateObject[], options?: SavedObjectsCreateOptions ) => { - const mockResponse = getMockBulkCreateResponse(objects, options?.namespace); + const mockResponse = getMockBulkCreateResponse(objects, options?.namespace, options?.managed); client.bulk.mockResponse(mockResponse); const result = await repository.bulkCreate(objects, options); return result; @@ -626,10 +636,12 @@ export const expectCreateResult = (obj: { type: string; namespace?: string; namespaces?: string[]; + managed?: boolean; }) => ({ ...obj, coreMigrationVersion: expect.any(String), typeMigrationVersion: '1.1.1', + managed: obj.managed ?? false, version: mockVersion, namespaces: obj.namespaces ?? [obj.namespace ?? 'default'], ...mockTimestampFieldsWithCreated, @@ -713,7 +725,7 @@ export const generateIndexPatternSearchResults = (namespace?: string) => { total: 4, hits: [ { - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, _id: `${namespace ? `${namespace}:` : ''}index-pattern:logstash-*`, _score: 1, ...mockVersionProps, @@ -730,7 +742,7 @@ export const generateIndexPatternSearchResults = (namespace?: string) => { }, }, { - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, _id: `${namespace ? `${namespace}:` : ''}config:6.0.0-alpha1`, _score: 2, ...mockVersionProps, @@ -745,7 +757,7 @@ export const generateIndexPatternSearchResults = (namespace?: string) => { }, }, { - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, _id: `${namespace ? `${namespace}:` : ''}index-pattern:stocks-*`, _score: 3, ...mockVersionProps, @@ -761,7 +773,7 @@ export const generateIndexPatternSearchResults = (namespace?: string) => { }, }, { - _index: '.kibana', + _index: MAIN_SAVED_OBJECT_INDEX, _id: `${NAMESPACE_AGNOSTIC_TYPE}:something`, _score: 4, ...mockVersionProps, diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts index a933c1be438e9..276682995a4f1 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts @@ -55,4 +55,12 @@ export interface SavedObjectsBulkCreateObject { * * For global object types (registered with `namespaceType: 'agnostic'`): this option cannot be used. */ initialNamespaces?: string[]; + /** + * Flag indicating if a saved object is managed by Kibana (default=false) + * + * This can be leveraged by applications to e.g. prevent edits to a managed + * saved object. Instead, users can be guided to create a copy first and + * make their edits to the copy. + */ + managed?: boolean; } diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts index 60b219056f935..fe509b65252da 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts @@ -61,6 +61,14 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { * * For global object types (registered with `namespaceType: 'agnostic'`): this option cannot be used. */ initialNamespaces?: string[]; + /** + * Flag indicating if a saved object is managed by Kibana (default=false) + * + * This can be leveraged by applications to e.g. prevent edits to a managed + * saved object. Instead, users can be guided to create a copy first and + * make their edits to the copy. + */ + managed?: boolean; /** {@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility} */ migrationVersionCompatibility?: 'compatible' | 'raw'; } diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/increment_counter.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/increment_counter.ts index 3e186f38916de..7edb0fc97ae13 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/increment_counter.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/increment_counter.ts @@ -39,6 +39,16 @@ export interface SavedObjectsIncrementCounterOptions * Attributes to use when upserting the document if it doesn't exist. */ upsertAttributes?: Attributes; + /** + * Flag indicating if a saved object is managed by Kibana (default=false). + * Only used when upserting a saved object. If the saved object already + * exist this option has no effect. + * + * This can be leveraged by applications to e.g. prevent edits to a managed + * saved object. Instead, users can be guided to create a copy first and + * make their edits to the copy. + */ + managed?: boolean; } /** diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts index bfe48e43491a0..981783bb05fd5 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts @@ -14,6 +14,7 @@ export { getTypes, type IndexMapping, type IndexMappingMeta, + type IndexTypesMap, type SavedObjectsTypeMappingDefinitions, type IndexMappingMigrationStateMeta, } from './src/mappings'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts index 7b2bb933fab3f..ee51fa9aaf4bb 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts @@ -11,5 +11,6 @@ export type { SavedObjectsTypeMappingDefinitions, IndexMappingMeta, IndexMapping, + IndexTypesMap, IndexMappingMigrationStateMeta, } from './types'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts index 10faa1b03d31d..c756f0534db67 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts @@ -55,6 +55,9 @@ export interface IndexMapping { _meta?: IndexMappingMeta; } +/** @internal */ +export type IndexTypesMap = Record; + /** @internal */ export interface IndexMappingMeta { /** @@ -65,6 +68,12 @@ export interface IndexMappingMeta { * @remark: Only defined for indices using the v2 migration algorithm. */ migrationMappingPropertyHashes?: { [k: string]: string }; + /** + * A map that tells what are the SO types stored in each index + * + * @remark: Only defined for indices using the v2 migration algorithm. + */ + indexTypesMap?: IndexTypesMap; /** * The current model versions of the mapping of the index. * diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/migration/kibana_migrator.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/migration/kibana_migrator.ts index bb078135c8bcc..de569332ff9ce 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/migration/kibana_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/migration/kibana_migrator.ts @@ -69,7 +69,11 @@ export type MigrationStatus = /** @internal */ export type MigrationResult = | { status: 'skipped' } - | { status: 'patched' } + | { + status: 'patched'; + destIndex: string; + elapsedMs: number; + } | { status: 'migrated'; destIndex: string; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts index c3ac7222e20c8..85035bfc38610 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts @@ -218,6 +218,27 @@ describe('#rawToSavedObject', () => { expect(actual).not.toHaveProperty('coreMigrationVersion'); }); + test('if specified it copies the _source.managed property to managed', () => { + const actual = singleNamespaceSerializer.rawToSavedObject({ + _id: 'foo:bar', + _source: { + type: 'foo', + managed: false, + }, + }); + expect(actual).toHaveProperty('managed', false); + }); + + test(`if _source.managed is unspecified it doesn't set managed`, () => { + const actual = singleNamespaceSerializer.rawToSavedObject({ + _id: 'foo:bar', + _source: { + type: 'foo', + }, + }); + expect(actual).not.toHaveProperty('managed'); + }); + test(`if version is unspecified it doesn't set version`, () => { const actual = singleNamespaceSerializer.rawToSavedObject({ _id: 'foo:bar', @@ -756,6 +777,25 @@ describe('#savedObjectToRaw', () => { expect(actual._source).not.toHaveProperty('coreMigrationVersion'); }); + test('if specified, copies managed property to _source.managed', () => { + const actual = singleNamespaceSerializer.savedObjectToRaw({ + type: '', + attributes: {}, + managed: false, + } as any); + + expect(actual._source).toHaveProperty('managed', false); + }); + + test(`if unspecified it doesn't add managed property to _source`, () => { + const actual = singleNamespaceSerializer.savedObjectToRaw({ + type: '', + attributes: {}, + } as any); + + expect(actual._source.managed).toBe(undefined); + }); + test('it decodes the version property to _seq_no and _primary_term', () => { const actual = singleNamespaceSerializer.savedObjectToRaw({ type: '', diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts index f18cc1e832ada..96eded287975c 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts @@ -94,6 +94,7 @@ export class SavedObjectsSerializer implements ISavedObjectsSerializer { references, coreMigrationVersion, typeMigrationVersion, + managed, migrationVersion = migrationVersionCompatibility === 'compatible' && typeMigrationVersion ? { [type]: typeMigrationVersion } : undefined, @@ -116,6 +117,7 @@ export class SavedObjectsSerializer implements ISavedObjectsSerializer { ...(originId && { originId }), attributes: _source[type], references: references || [], + ...(managed != null ? { managed } : {}), ...(migrationVersion && { migrationVersion }), ...(coreMigrationVersion && { coreMigrationVersion }), ...(typeMigrationVersion != null ? { typeMigrationVersion } : {}), @@ -146,11 +148,13 @@ export class SavedObjectsSerializer implements ISavedObjectsSerializer { references, coreMigrationVersion, typeMigrationVersion, + managed, } = savedObj; const source = { [type]: attributes, type, references, + ...(managed != null ? { managed } : {}), ...(namespace && this.registry.isSingleNamespace(type) && { namespace }), ...(namespaces && this.registry.isMultiNamespace(type) && { namespaces }), ...(originId && { originId }), @@ -160,7 +164,6 @@ export class SavedObjectsSerializer implements ISavedObjectsSerializer { ...(updated_at && { updated_at }), ...(createdAt && { created_at: createdAt }), }; - return { _id: this.generateRawId(namespace, type, id), _source: source, diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_index_for_type.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_index_for_type.test.ts index 551c9dc1187eb..d5bc19a11d17e 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_index_for_type.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_index_for_type.test.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -import { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; +import { + type ISavedObjectTypeRegistry, + MAIN_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; import { getIndexForType } from './get_index_for_type'; const createTypeRegistry = () => { @@ -17,7 +20,7 @@ const createTypeRegistry = () => { describe('getIndexForType', () => { const kibanaVersion = '8.0.0'; - const defaultIndex = '.kibana'; + const defaultIndex = MAIN_SAVED_OBJECT_INDEX; let typeRegistry: ReturnType; beforeEach(() => { diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts index 21c4c04d25afe..009bf2d89cf46 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts @@ -47,4 +47,5 @@ export const createSavedObjectSanitizedDocSchema = (attributesSchema: SavedObjec created_at: schema.maybe(schema.string()), version: schema.maybe(schema.string()), originId: schema.maybe(schema.string()), + managed: schema.maybe(schema.boolean()), }); diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts index d500bbfcd7716..0493652b1192f 100644 --- a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts @@ -207,6 +207,7 @@ export class SavedObjectsClient implements SavedObjectsClientContract { attributes, migrationVersion: options.migrationVersion, typeMigrationVersion: options.typeMigrationVersion, + managed: options.managed, references: options.references, }), }); @@ -217,7 +218,7 @@ export class SavedObjectsClient implements SavedObjectsClientContract { /** * Creates multiple documents at once * - * @param {array} objects - [{ type, id, attributes, references, migrationVersion, typeMigrationVersion }] + * @param {array} objects - [{ type, id, attributes, references, migrationVersion, typeMigrationVersion, managed }] * @param {object} [options={}] * @property {boolean} [options.overwrite=false] * @returns The result of the create operation containing created saved objects. diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts index 751d33f69e7e0..0d57526c065e3 100644 --- a/packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts @@ -29,6 +29,7 @@ export class SimpleSavedObjectImpl implements SimpleSavedObject public migrationVersion: SavedObjectType['migrationVersion']; public coreMigrationVersion: SavedObjectType['coreMigrationVersion']; public typeMigrationVersion: SavedObjectType['typeMigrationVersion']; + public managed: SavedObjectType['managed']; public error: SavedObjectType['error']; public references: SavedObjectType['references']; public updatedAt: SavedObjectType['updated_at']; @@ -47,6 +48,7 @@ export class SimpleSavedObjectImpl implements SimpleSavedObject migrationVersion, coreMigrationVersion, typeMigrationVersion, + managed, namespaces, updated_at: updatedAt, created_at: createdAt, @@ -60,6 +62,7 @@ export class SimpleSavedObjectImpl implements SimpleSavedObject this.migrationVersion = migrationVersion; this.coreMigrationVersion = coreMigrationVersion; this.typeMigrationVersion = typeMigrationVersion; + this.managed = managed; this.namespaces = namespaces; this.updatedAt = updatedAt; this.createdAt = createdAt; @@ -96,6 +99,7 @@ export class SimpleSavedObjectImpl implements SimpleSavedObject migrationVersion: this.migrationVersion, coreMigrationVersion: this.coreMigrationVersion, typeMigrationVersion: this.typeMigrationVersion, + managed: this.managed, references: this.references, }) .then((sso) => { diff --git a/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts index 6268c796a5b82..396b7cfa3a965 100644 --- a/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts @@ -26,6 +26,7 @@ const simpleSavedObjectMockDefaults: Partial> = { updatedAt: '', createdAt: '', namespaces: undefined, + managed: false, }; const createSimpleSavedObjectMock = ( @@ -40,6 +41,7 @@ const createSimpleSavedObjectMock = ( migrationVersion: savedObject.migrationVersion, coreMigrationVersion: savedObject.coreMigrationVersion, typeMigrationVersion: savedObject.typeMigrationVersion, + managed: savedObject.managed, error: savedObject.error, references: savedObject.references, updatedAt: savedObject.updated_at, diff --git a/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts b/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts index fbdacb73309fa..9ea8f1c9f0668 100644 --- a/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts +++ b/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts @@ -101,4 +101,12 @@ export interface SavedObject { * space. */ originId?: string; + /** + * Flag indicating if a saved object is managed by Kibana (default=false) + * + * This can be leveraged by applications to e.g. prevent edits to a managed + * saved object. Instead, users can be guided to create a copy first and + * make their edits to the copy. + */ + managed?: boolean; } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap index efbdf0da12f26..b035c64617ca2 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/kibana_migrator.test.ts.snap @@ -8,6 +8,7 @@ Object { "bmap": "510f1f0adb69830cf8a1c5ce2923ed82", "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "created_at": "00da57df13e94e9d98437d13ace4bfe0", + "managed": "88cf246b441a6362458cb6a56ca3f7d7", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", "originId": "2f4316de49999235636386fe51dc06c1", @@ -39,6 +40,9 @@ Object { "created_at": Object { "type": "date", }, + "managed": Object { + "type": "boolean", + }, "namespace": Object { "type": "keyword", }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap index cd2b218d58dd4..0e05eed1e99b7 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap @@ -54,6 +54,11 @@ Object { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -138,6 +143,20 @@ Object { }, }, "indexPrefix": ".my-so-index", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "7.11.0", "knownTypes": Array [], "legacyIndex": ".my-so-index", @@ -154,6 +173,7 @@ Object { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { "bool": Object { @@ -167,6 +187,22 @@ Object { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "properties": Object {}, }, "tempIndex": ".my-so-index_7.11.0_reindex_temp", @@ -241,6 +277,11 @@ Object { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -325,6 +366,20 @@ Object { }, }, "indexPrefix": ".my-so-index", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "7.11.0", "knownTypes": Array [], "legacyIndex": ".my-so-index", @@ -345,6 +400,7 @@ Object { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { "bool": Object { @@ -358,6 +414,22 @@ Object { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "properties": Object {}, }, "tempIndex": ".my-so-index_7.11.0_reindex_temp", @@ -432,6 +504,11 @@ Object { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -516,6 +593,20 @@ Object { }, }, "indexPrefix": ".my-so-index", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "7.11.0", "knownTypes": Array [], "legacyIndex": ".my-so-index", @@ -540,6 +631,7 @@ Object { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { "bool": Object { @@ -553,6 +645,22 @@ Object { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "properties": Object {}, }, "tempIndex": ".my-so-index_7.11.0_reindex_temp", @@ -627,6 +735,11 @@ Object { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -711,6 +824,20 @@ Object { }, }, "indexPrefix": ".my-so-index", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "7.11.0", "knownTypes": Array [], "legacyIndex": ".my-so-index", @@ -739,6 +866,7 @@ Object { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocuments": Array [], "outdatedDocumentsQuery": Object { "bool": Object { @@ -752,6 +880,22 @@ Object { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "properties": Object {}, }, "tempIndex": ".my-so-index_7.11.0_reindex_temp", @@ -870,6 +1014,11 @@ Object { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -954,6 +1103,20 @@ Object { }, }, "indexPrefix": ".my-so-index", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "7.11.0", "knownTypes": Array [], "legacyIndex": ".my-so-index", @@ -970,6 +1133,7 @@ Object { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocuments": Array [ Object { "_id": "1234", @@ -988,6 +1152,22 @@ Object { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "properties": Object {}, }, "tempIndex": ".my-so-index_7.11.0_reindex_temp", @@ -1068,6 +1248,11 @@ Object { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -1152,6 +1337,20 @@ Object { }, }, "indexPrefix": ".my-so-index", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "7.11.0", "knownTypes": Array [], "legacyIndex": ".my-so-index", @@ -1172,6 +1371,7 @@ Object { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocuments": Array [ Object { "_id": "1234", @@ -1190,6 +1390,22 @@ Object { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "properties": Object {}, }, "tempIndex": ".my-so-index_7.11.0_reindex_temp", diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts index 652178fd25919..9080e2ce93dbe 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts @@ -83,6 +83,9 @@ export { cleanupUnknownAndExcluded } from './cleanup_unknown_and_excluded'; export { waitForDeleteByQueryTask } from './wait_for_delete_by_query_task'; export type { CreateIndexParams, ClusterShardLimitExceeded } from './create_index'; + +export { synchronizeMigrators } from './synchronize_migrators'; + export { createIndex } from './create_index'; export { checkTargetMappings } from './check_target_mappings'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/synchronize_migrators.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/synchronize_migrators.test.ts new file mode 100644 index 0000000000000..a5a8e9c25f929 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/synchronize_migrators.test.ts @@ -0,0 +1,156 @@ +/* + * 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 { synchronizeMigrators } from './synchronize_migrators'; +import { type Defer, defer } from '../kibana_migrator_utils'; + +describe('synchronizeMigrators', () => { + let defers: Array>; + let allDefersPromise: Promise; + let migratorsDefers: Array>; + + beforeEach(() => { + jest.clearAllMocks(); + + defers = ['.kibana_cases', '.kibana_task_manager', '.kibana'].map(defer); + allDefersPromise = Promise.all(defers.map(({ promise }) => promise)); + + migratorsDefers = defers.map(({ resolve, reject }) => ({ + resolve: jest.fn(resolve), + reject: jest.fn(reject), + promise: allDefersPromise, + })); + }); + + describe('when all migrators reach the synchronization point with a correct state', () => { + it('unblocks all migrators and resolves Right', async () => { + const tasks = migratorsDefers.map((migratorDefer) => synchronizeMigrators(migratorDefer)); + + const res = await Promise.all(tasks.map((task) => task())); + + migratorsDefers.forEach((migratorDefer) => + expect(migratorDefer.resolve).toHaveBeenCalledTimes(1) + ); + migratorsDefers.forEach((migratorDefer) => + expect(migratorDefer.reject).not.toHaveBeenCalled() + ); + + expect(res).toEqual([ + { _tag: 'Right', right: 'synchronized_successfully' }, + { _tag: 'Right', right: 'synchronized_successfully' }, + { _tag: 'Right', right: 'synchronized_successfully' }, + ]); + }); + + it('migrators are not unblocked until the last one reaches the synchronization point', async () => { + let resolved: number = 0; + migratorsDefers.forEach((migratorDefer) => migratorDefer.promise.then(() => ++resolved)); + const [casesDefer, ...otherMigratorsDefers] = migratorsDefers; + + // we simulate that only kibana_task_manager and kibana migrators get to the sync point + const tasks = otherMigratorsDefers.map((migratorDefer) => + synchronizeMigrators(migratorDefer) + ); + // we don't await for them, or we would be locked forever + Promise.all(tasks.map((task) => task())); + + const [taskManagerDefer, kibanaDefer] = otherMigratorsDefers; + expect(taskManagerDefer.resolve).toHaveBeenCalledTimes(1); + expect(kibanaDefer.resolve).toHaveBeenCalledTimes(1); + expect(casesDefer.resolve).not.toHaveBeenCalled(); + expect(resolved).toEqual(0); + + // finally, the last migrator gets to the synchronization point + await synchronizeMigrators(casesDefer)(); + expect(resolved).toEqual(3); + }); + }); + + describe('when one migrator fails and rejects the synchronization defer', () => { + describe('before the rest of the migrators reach the synchronization point', () => { + it('synchronizedMigrators resolves Left for the rest of migrators', async () => { + let resolved: number = 0; + let errors: number = 0; + migratorsDefers.forEach((migratorDefer) => + migratorDefer.promise.then(() => ++resolved).catch(() => ++errors) + ); + const [casesDefer, ...otherMigratorsDefers] = migratorsDefers; + + // we first make one random migrator fail and not reach the sync point + casesDefer.reject('Oops. The cases migrator failed unexpectedly.'); + + // the other migrators then try to synchronize + const tasks = otherMigratorsDefers.map((migratorDefer) => + synchronizeMigrators(migratorDefer) + ); + + expect(Promise.all(tasks.map((task) => task()))).resolves.toEqual([ + { + _tag: 'Left', + left: { + type: 'sync_failed', + error: 'Oops. The cases migrator failed unexpectedly.', + }, + }, + { + _tag: 'Left', + left: { + type: 'sync_failed', + error: 'Oops. The cases migrator failed unexpectedly.', + }, + }, + ]); + + // force next tick (as we did not await for Promises) + await new Promise((resolve) => setImmediate(resolve)); + expect(resolved).toEqual(0); + expect(errors).toEqual(3); + }); + }); + + describe('after the rest of the migrators reach the synchronization point', () => { + it('synchronizedMigrators resolves Left for the rest of migrators', async () => { + let resolved: number = 0; + let errors: number = 0; + migratorsDefers.forEach((migratorDefer) => + migratorDefer.promise.then(() => ++resolved).catch(() => ++errors) + ); + const [casesDefer, ...otherMigratorsDefers] = migratorsDefers; + + // some migrators try to synchronize + const tasks = otherMigratorsDefers.map((migratorDefer) => + synchronizeMigrators(migratorDefer) + ); + + // we then make one random migrator fail and not reach the sync point + casesDefer.reject('Oops. The cases migrator failed unexpectedly.'); + + expect(Promise.all(tasks.map((task) => task()))).resolves.toEqual([ + { + _tag: 'Left', + left: { + type: 'sync_failed', + error: 'Oops. The cases migrator failed unexpectedly.', + }, + }, + { + _tag: 'Left', + left: { + type: 'sync_failed', + error: 'Oops. The cases migrator failed unexpectedly.', + }, + }, + ]); + + // force next tick (as we did not await for Promises) + await new Promise((resolve) => setImmediate(resolve)); + expect(resolved).toEqual(0); + expect(errors).toEqual(3); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/synchronize_migrators.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/synchronize_migrators.ts new file mode 100644 index 0000000000000..26763f1f51ae2 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/synchronize_migrators.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 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 Either from 'fp-ts/lib/Either'; +import * as TaskEither from 'fp-ts/lib/TaskEither'; +import type { Defer } from '../kibana_migrator_utils'; + +export interface SyncFailed { + type: 'sync_failed'; + error: Error; +} + +export function synchronizeMigrators( + defer: Defer +): TaskEither.TaskEither { + return () => { + defer.resolve(); + return defer.promise + .then(() => Either.right('synchronized_successfully' as const)) + .catch((error) => Either.left({ type: 'sync_failed' as const, error })); + }; +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap index 42aaff1b7f0df..9941f2a70e696 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/__snapshots__/build_active_mappings.test.ts.snap @@ -8,6 +8,7 @@ Object { "bbb": "18c78c995965207ed3f6e7fc5c6e55fe", "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "created_at": "00da57df13e94e9d98437d13ace4bfe0", + "managed": "88cf246b441a6362458cb6a56ca3f7d7", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", "originId": "2f4316de49999235636386fe51dc06c1", @@ -31,6 +32,9 @@ Object { "created_at": Object { "type": "date", }, + "managed": Object { + "type": "boolean", + }, "namespace": Object { "type": "keyword", }, @@ -74,6 +78,7 @@ Object { "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "created_at": "00da57df13e94e9d98437d13ace4bfe0", "firstType": "635418ab953d81d93f1190b70a8d3f57", + "managed": "88cf246b441a6362458cb6a56ca3f7d7", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", "originId": "2f4316de49999235636386fe51dc06c1", @@ -101,6 +106,9 @@ Object { }, }, }, + "managed": Object { + "type": "boolean", + }, "namespace": Object { "type": "keyword", }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts index 12ed0931ce0c3..7dd13acbe8c7f 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts @@ -156,6 +156,9 @@ export function getBaseMappings(): IndexMapping { typeMigrationVersion: { type: 'version', }, + managed: { + type: 'boolean', + }, }, }; } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts index 46ddd23251217..2f9dd57afa294 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/unused_types.ts @@ -47,6 +47,8 @@ export const REMOVED_TYPES: string[] = [ 'csp_rule', // Removed in 8.8 https://github.com/elastic/kibana/pull/151116 'upgrade-assistant-telemetry', + // Removed in 8.8 https://github.com/elastic/kibana/pull/155204 + 'endpoint:user-artifact', ].sort(); export const excludeUnusedTypesQuery: QueryDslQueryContainer = { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts index de44f163ec94e..df710068da324 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts @@ -817,6 +817,7 @@ describe('DocumentMigrator', () => { references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], coreMigrationVersion: '8.8.0', typeMigrationVersion: '1.0.0', + managed: false, }, ]); }); @@ -832,6 +833,7 @@ describe('DocumentMigrator', () => { references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], coreMigrationVersion: '8.8.0', typeMigrationVersion: '1.0.0', + managed: false, namespace: 'foo-namespace', }, ]); @@ -865,6 +867,7 @@ describe('DocumentMigrator', () => { attributes: { name: 'Sweet Peach' }, references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change coreMigrationVersion: '8.8.0', + managed: false, }, ]); }); @@ -881,6 +884,7 @@ describe('DocumentMigrator', () => { references: [{ id: 'uuidv5', type: 'toy', name: 'BALL!' }], // changed coreMigrationVersion: '8.8.0', namespace: 'foo-namespace', + managed: false, }, ]); }); @@ -978,6 +982,7 @@ describe('DocumentMigrator', () => { references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change coreMigrationVersion: '8.8.0', typeMigrationVersion: '1.0.0', + managed: false, namespaces: ['default'], }, ]); @@ -1008,6 +1013,7 @@ describe('DocumentMigrator', () => { typeMigrationVersion: '1.0.0', namespaces: ['foo-namespace'], originId: 'cute', + managed: false, }, { id: 'foo-namespace:dog:cute', @@ -1063,6 +1069,7 @@ describe('DocumentMigrator', () => { references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change coreMigrationVersion: '8.8.0', typeMigrationVersion: '2.0.0', + managed: false, }, ]); }); @@ -1080,6 +1087,7 @@ describe('DocumentMigrator', () => { coreMigrationVersion: '8.8.0', typeMigrationVersion: '2.0.0', namespace: 'foo-namespace', + managed: false, }, ]); }); @@ -1190,6 +1198,7 @@ describe('DocumentMigrator', () => { coreMigrationVersion: '8.8.0', typeMigrationVersion: '2.0.0', namespaces: ['default'], + managed: false, }, ]); }); @@ -1219,6 +1228,7 @@ describe('DocumentMigrator', () => { typeMigrationVersion: '2.0.0', namespaces: ['foo-namespace'], originId: 'pretty', + managed: false, }, { id: 'foo-namespace:dog:pretty', diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/index.ts index f5a8b313a96e7..04b33a5cc2ac2 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/index.ts @@ -6,9 +6,17 @@ * Side Public License, v 1. */ +import { flow } from 'lodash'; +import get from 'lodash/fp/get'; import { TransformFn } from '../types'; import { transformMigrationVersion } from './transform_migration_version'; +import { transformSetManagedDefault } from './transform_set_managed_default'; export const migrations = { - '8.8.0': transformMigrationVersion, + '8.8.0': flow( + transformMigrationVersion, + // extract transformedDoc from TransformResult as input to next transform + get('transformedDoc'), + transformSetManagedDefault + ), } as Record; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/transform_set_managed_default.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/transform_set_managed_default.test.ts new file mode 100644 index 0000000000000..1b355a176ab6f --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/transform_set_managed_default.test.ts @@ -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 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 { transformSetManagedDefault } from './transform_set_managed_default'; + +describe('transformAddManaged', () => { + it('should add managed if not defined', () => { + expect( + transformSetManagedDefault({ + id: 'a', + attributes: {}, + type: 'something', + }) + ).toHaveProperty('transformedDoc.managed'); + }); + it('should not change managed if already defined', () => { + const docWithManagedFalse = transformSetManagedDefault({ + id: 'a', + attributes: {}, + type: 'something', + managed: false, + }); + const docWithManagedTrue = transformSetManagedDefault({ + id: 'a', + attributes: {}, + type: 'something', + managed: true, + }); + [docWithManagedFalse, docWithManagedTrue].forEach((doc) => { + expect(doc.transformedDoc.managed).toBeDefined(); + }); + expect(docWithManagedFalse.transformedDoc.managed).not.toBeTruthy(); + expect(docWithManagedTrue.transformedDoc.managed).toBeTruthy(); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/transform_set_managed_default.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/transform_set_managed_default.ts new file mode 100644 index 0000000000000..8e3e1b82bc8cd --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/migrations/transform_set_managed_default.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 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 { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server'; + +export const transformSetManagedDefault = (doc: SavedObjectUnsanitizedDoc) => ({ + transformedDoc: { ...doc, managed: doc.managed ?? false }, + additionalDocs: [], +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts index 2dcadc9ab0210..3ee201605f1ac 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.test.ts @@ -13,44 +13,63 @@ import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; import { type SavedObjectsMigrationConfigType, SavedObjectTypeRegistry, + type IndexMapping, } from '@kbn/core-saved-objects-base-server-internal'; +import type { Logger } from '@kbn/logging'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; -import { createInitialState } from './initial_state'; +import { createInitialState, type CreateInitialStateParams } from './initial_state'; const mockLogger = loggingSystemMock.create(); +const migrationsConfig = { + retryAttempts: 15, + batchSize: 1000, + maxBatchSizeBytes: ByteSizeValue.parse('100mb'), +} as unknown as SavedObjectsMigrationConfigType; + +const createInitialStateCommonParams = { + kibanaVersion: '8.1.0', + waitForMigrationCompletion: false, + mustRelocateDocuments: true, + indexTypesMap: { + '.kibana': ['typeA', 'typeB', 'typeC'], + '.kibana_task_manager': ['task'], + '.kibana_cases': ['typeD', 'typeE'], + }, + targetMappings: { + dynamic: 'strict', + properties: { my_type: { properties: { title: { type: 'text' } } } }, + } as IndexMapping, + migrationVersionPerType: {}, + indexPrefix: '.kibana_task_manager', + migrationsConfig, +}; + describe('createInitialState', () => { let typeRegistry: SavedObjectTypeRegistry; let docLinks: DocLinksServiceSetup; + let logger: Logger; + let createInitialStateParams: CreateInitialStateParams; beforeEach(() => { typeRegistry = new SavedObjectTypeRegistry(); docLinks = docLinksServiceMock.createSetupContract(); + logger = mockLogger.get(); + createInitialStateParams = { + ...createInitialStateCommonParams, + typeRegistry, + docLinks, + logger, + }; }); afterEach(() => jest.clearAllMocks()); - const migrationsConfig = { - retryAttempts: 15, - batchSize: 1000, - maxBatchSizeBytes: ByteSizeValue.parse('100mb'), - } as unknown as SavedObjectsMigrationConfigType; - it('creates the initial state for the model based on the passed in parameters', () => { expect( createInitialState({ - kibanaVersion: '8.1.0', + ...createInitialStateParams, waitForMigrationCompletion: true, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger: mockLogger.get(), }) ).toMatchInlineSnapshot(` Object { @@ -88,6 +107,11 @@ describe('createInitialState', () => { "type": "csp_rule", }, }, + Object { + "term": Object { + "type": "endpoint:user-artifact", + }, + }, Object { "term": Object { "type": "file-upload-telemetry", @@ -172,6 +196,20 @@ describe('createInitialState', () => { }, }, "indexPrefix": ".kibana_task_manager", + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, "kibanaVersion": "8.1.0", "knownTypes": Array [], "legacyIndex": ".kibana_task_manager", @@ -183,6 +221,7 @@ describe('createInitialState', () => { "resolveMigrationFailures": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html", "routingAllocationDisabled": "https://www.elastic.co/guide/en/kibana/test-branch/resolve-migrations-failures.html#routing-allocation-disabled", }, + "mustRelocateDocuments": true, "outdatedDocumentsQuery": Object { "bool": Object { "should": Array [], @@ -195,6 +234,22 @@ describe('createInitialState', () => { "retryCount": 0, "retryDelay": 0, "targetIndexMappings": Object { + "_meta": Object { + "indexTypesMap": Object { + ".kibana": Array [ + "typeA", + "typeB", + "typeC", + ], + ".kibana_cases": Array [ + "typeD", + "typeE", + ], + ".kibana_task_manager": Array [ + "task", + ], + }, + }, "dynamic": "strict", "properties": Object { "my_type": Object { @@ -227,22 +282,7 @@ describe('createInitialState', () => { }); it('creates the initial state for the model with waitForMigrationCompletion false,', () => { - expect( - createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger: mockLogger.get(), - }) - ).toMatchObject({ + expect(createInitialState(createInitialStateParams)).toMatchObject({ waitForMigrationCompletion: false, }); }); @@ -262,18 +302,10 @@ describe('createInitialState', () => { }); const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, + ...createInitialStateParams, typeRegistry, docLinks, - logger: mockLogger.get(), + logger, }); expect(initialState.knownTypes).toEqual(['foo', 'bar']); @@ -289,40 +321,15 @@ describe('createInitialState', () => { excludeOnUpgrade: fooExcludeOnUpgradeHook, }); - const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger: mockLogger.get(), - }); - + const initialState = createInitialState(createInitialStateParams); expect(initialState.excludeFromUpgradeFilterHooks).toEqual({ foo: fooExcludeOnUpgradeHook }); }); it('returns state with a preMigration script', () => { const preMigrationScript = "ctx._id = ctx._source.type + ':' + ctx._id"; const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, + ...createInitialStateParams, preMigrationScript, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger: mockLogger.get(), }); expect(Option.isSome(initialState.preMigrationScript)).toEqual(true); @@ -334,19 +341,8 @@ describe('createInitialState', () => { expect( Option.isNone( createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, + ...createInitialStateParams, preMigrationScript: undefined, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger: mockLogger.get(), }).preMigrationScript ) ).toEqual(true); @@ -354,19 +350,9 @@ describe('createInitialState', () => { it('returns state with an outdatedDocumentsQuery', () => { expect( createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, + ...createInitialStateParams, preMigrationScript: "ctx._id = ctx._source.type + ':' + ctx._id", migrationVersionPerType: { my_dashboard: '7.10.1', my_viz: '8.0.0' }, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger: mockLogger.get(), }).outdatedDocumentsQuery ).toMatchInlineSnapshot(` Object { @@ -473,44 +459,19 @@ describe('createInitialState', () => { }); it('initializes the `discardUnknownObjects` flag to false if the flag is not provided in the config', () => { - const logger = mockLogger.get(); - const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', - migrationsConfig, - typeRegistry, - docLinks, - logger, - }); + const initialState = createInitialState(createInitialStateParams); expect(logger.warn).not.toBeCalled(); expect(initialState.discardUnknownObjects).toEqual(false); }); it('initializes the `discardUnknownObjects` flag to false if the value provided in the config does not match the current kibana version', () => { - const logger = mockLogger.get(); const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', + ...createInitialStateParams, migrationsConfig: { ...migrationsConfig, discardUnknownObjects: '8.0.0', }, - typeRegistry, - docLinks, - logger, }); expect(initialState.discardUnknownObjects).toEqual(false); @@ -522,44 +483,23 @@ describe('createInitialState', () => { it('initializes the `discardUnknownObjects` flag to true if the value provided in the config matches the current kibana version', () => { const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', + ...createInitialStateParams, migrationsConfig: { ...migrationsConfig, discardUnknownObjects: '8.1.0', }, - typeRegistry, - docLinks, - logger: mockLogger.get(), }); expect(initialState.discardUnknownObjects).toEqual(true); }); it('initializes the `discardCorruptObjects` flag to false if the value provided in the config does not match the current kibana version', () => { - const logger = mockLogger.get(); const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', + ...createInitialStateParams, migrationsConfig: { ...migrationsConfig, discardCorruptObjects: '8.0.0', }, - typeRegistry, - docLinks, - logger, }); expect(initialState.discardCorruptObjects).toEqual(false); @@ -571,21 +511,11 @@ describe('createInitialState', () => { it('initializes the `discardCorruptObjects` flag to true if the value provided in the config matches the current kibana version', () => { const initialState = createInitialState({ - kibanaVersion: '8.1.0', - waitForMigrationCompletion: false, - targetMappings: { - dynamic: 'strict', - properties: { my_type: { properties: { title: { type: 'text' } } } }, - }, - migrationVersionPerType: {}, - indexPrefix: '.kibana_task_manager', + ...createInitialStateParams, migrationsConfig: { ...migrationsConfig, discardCorruptObjects: '8.1.0', }, - typeRegistry, - docLinks, - logger: mockLogger.get(), }); expect(initialState.discardCorruptObjects).toEqual(true); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.ts index ff15afa7c691a..6daa8887d5b72 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/initial_state.ts @@ -14,10 +14,27 @@ import type { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-commo import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; import type { IndexMapping, + IndexTypesMap, SavedObjectsMigrationConfigType, } from '@kbn/core-saved-objects-base-server-internal'; import type { InitState } from './state'; import { excludeUnusedTypesQuery } from './core'; +import { getTempIndexName } from './model/helpers'; + +export interface CreateInitialStateParams { + kibanaVersion: string; + waitForMigrationCompletion: boolean; + mustRelocateDocuments: boolean; + indexTypesMap: IndexTypesMap; + targetMappings: IndexMapping; + preMigrationScript?: string; + migrationVersionPerType: SavedObjectsMigrationVersion; + indexPrefix: string; + migrationsConfig: SavedObjectsMigrationConfigType; + typeRegistry: ISavedObjectTypeRegistry; + docLinks: DocLinksServiceStart; + logger: Logger; +} /** * Construct the initial state for the model @@ -25,6 +42,8 @@ import { excludeUnusedTypesQuery } from './core'; export const createInitialState = ({ kibanaVersion, waitForMigrationCompletion, + mustRelocateDocuments, + indexTypesMap, targetMappings, preMigrationScript, migrationVersionPerType, @@ -33,18 +52,7 @@ export const createInitialState = ({ typeRegistry, docLinks, logger, -}: { - kibanaVersion: string; - waitForMigrationCompletion: boolean; - targetMappings: IndexMapping; - preMigrationScript?: string; - migrationVersionPerType: SavedObjectsMigrationVersion; - indexPrefix: string; - migrationsConfig: SavedObjectsMigrationConfigType; - typeRegistry: ISavedObjectTypeRegistry; - docLinks: DocLinksServiceStart; - logger: Logger; -}): InitState => { +}: CreateInitialStateParams): InitState => { const outdatedDocumentsQuery: QueryDslQueryContainer = { bool: { should: Object.entries(migrationVersionPerType).map(([type, latestVersion]) => ({ @@ -117,18 +125,28 @@ export const createInitialState = ({ ); } + const targetIndexMappings: IndexMapping = { + ...targetMappings, + _meta: { + ...targetMappings._meta, + indexTypesMap, + }, + }; + return { controlState: 'INIT', waitForMigrationCompletion, + mustRelocateDocuments, + indexTypesMap, indexPrefix, legacyIndex: indexPrefix, currentAlias: indexPrefix, versionAlias: `${indexPrefix}_${kibanaVersion}`, versionIndex: `${indexPrefix}_${kibanaVersion}_001`, - tempIndex: `${indexPrefix}_${kibanaVersion}_reindex_temp`, + tempIndex: getTempIndexName(indexPrefix, kibanaVersion), kibanaVersion, preMigrationScript: Option.fromNullable(preMigrationScript), - targetIndexMappings: targetMappings, + targetIndexMappings, tempIndexMappings: reindexTargetMappings, outdatedDocumentsQuery, retryCount: 0, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts index e6855a1256b54..f726141c7294d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.test.ts @@ -18,6 +18,15 @@ import { DocumentMigrator } from './document_migrator'; import { ByteSizeValue } from '@kbn/config-schema'; import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; import { lastValueFrom } from 'rxjs'; +import { runResilientMigrator } from './run_resilient_migrator'; + +jest.mock('./run_resilient_migrator', () => { + const actual = jest.requireActual('./run_resilient_migrator'); + + return { + runResilientMigrator: jest.fn(actual.runResilientMigrator), + }; +}); jest.mock('./document_migrator', () => { return { @@ -29,6 +38,21 @@ jest.mock('./document_migrator', () => { }; }); +const mappingsResponseWithoutIndexTypesMap: estypes.IndicesGetMappingResponse = { + '.kibana_8.7.0_001': { + mappings: { + _meta: { + migrationMappingPropertyHashes: { + references: '7997cf5a56cc02bdc9c93361bde732b0', + // ... + }, + // we do not add a `indexTypesMap` + // simulating a Kibana < 8.8.0 that does not have one yet + }, + }, + }, +}; + const createRegistry = (types: Array>) => { const registry = new SavedObjectTypeRegistry(); types.forEach((type) => @@ -47,6 +71,7 @@ const createRegistry = (types: Array>) => { describe('KibanaMigrator', () => { beforeEach(() => { (DocumentMigrator as jest.Mock).mockClear(); + (runResilientMigrator as jest.MockedFunction).mockClear(); }); describe('getActiveMappings', () => { it('returns full index mappings w/ core properties', () => { @@ -60,7 +85,7 @@ describe('KibanaMigrator', () => { }, { name: 'bmap', - indexPattern: 'other-index', + indexPattern: '.other-index', mappings: { properties: { field: { type: 'text' } }, }, @@ -98,19 +123,34 @@ describe('KibanaMigrator', () => { describe('runMigrations', () => { it('throws if prepareMigrations is not called first', async () => { const options = mockOptions(); - - options.client.indices.get.mockResponse({}, { statusCode: 200 }); - const migrator = new KibanaMigrator(options); - await expect(() => migrator.runMigrations()).toThrowErrorMatchingInlineSnapshot( - `"Migrations are not ready. Make sure prepareMigrations is called first."` + await expect(migrator.runMigrations()).rejects.toThrowError( + 'Migrations are not ready. Make sure prepareMigrations is called first.' ); }); it('only runs migrations once if called multiple times', async () => { + const successfulRun: typeof runResilientMigrator = ({ indexPrefix }) => + Promise.resolve({ + sourceIndex: indexPrefix, + destIndex: indexPrefix, + elapsedMs: 28, + status: 'migrated', + }); + const mockRunResilientMigrator = runResilientMigrator as jest.MockedFunction< + typeof runResilientMigrator + >; + + mockRunResilientMigrator.mockImplementationOnce(successfulRun); + mockRunResilientMigrator.mockImplementationOnce(successfulRun); + mockRunResilientMigrator.mockImplementationOnce(successfulRun); + mockRunResilientMigrator.mockImplementationOnce(successfulRun); const options = mockOptions(); options.client.indices.get.mockResponse({}, { statusCode: 200 }); + options.client.indices.getMapping.mockResponse(mappingsResponseWithoutIndexTypesMap, { + statusCode: 200, + }); options.client.cluster.getSettings.mockResponse( { @@ -127,11 +167,42 @@ describe('KibanaMigrator', () => { await migrator.runMigrations(); // indices.get is called twice during a single migration - expect(options.client.indices.get).toHaveBeenCalledTimes(2); + expect(runResilientMigrator).toHaveBeenCalledTimes(4); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + indexPrefix: '.my-index', + mustRelocateDocuments: true, + }) + ); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + indexPrefix: '.other-index', + mustRelocateDocuments: true, + }) + ); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + indexPrefix: '.my-task-index', + mustRelocateDocuments: false, + }) + ); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 4, + expect.objectContaining({ + indexPrefix: '.my-complementary-index', + mustRelocateDocuments: true, + }) + ); }); it('emits results on getMigratorResult$()', async () => { const options = mockV2MigrationOptions(); + options.client.indices.getMapping.mockResponse(mappingsResponseWithoutIndexTypesMap, { + statusCode: 200, + }); const migrator = new KibanaMigrator(options); const migratorStatus = lastValueFrom(migrator.getStatus$().pipe(take(3))); migrator.prepareMigrations(); @@ -146,12 +217,12 @@ describe('KibanaMigrator', () => { status: 'migrated', }); expect(result![1]).toMatchObject({ - destIndex: 'other-index_8.2.3_001', + destIndex: '.other-index_8.2.3_001', elapsedMs: expect.any(Number), status: 'patched', }); }); - it('rejects when the migration state machine terminates in a FATAL state', () => { + it('rejects when the migration state machine terminates in a FATAL state', async () => { const options = mockV2MigrationOptions(); options.client.indices.get.mockResponse( { @@ -166,6 +237,9 @@ describe('KibanaMigrator', () => { }, { statusCode: 200 } ); + options.client.indices.getMapping.mockResponse(mappingsResponseWithoutIndexTypesMap, { + statusCode: 200, + }); const migrator = new KibanaMigrator(options); migrator.prepareMigrations(); @@ -181,6 +255,9 @@ describe('KibanaMigrator', () => { error: { type: 'elasticsearch_exception', reason: 'task failed with an error' }, task: { description: 'task description' } as any, }); + options.client.indices.getMapping.mockResponse(mappingsResponseWithoutIndexTypesMap, { + statusCode: 200, + }); const migrator = new KibanaMigrator(options); migrator.prepareMigrations(); @@ -193,6 +270,160 @@ describe('KibanaMigrator', () => { {"_tag":"Some","value":{"type":"elasticsearch_exception","reason":"task failed with an error"}}] `); }); + + describe('for V2 migrations', () => { + describe('where some SO types must be relocated', () => { + it('runs successfully', async () => { + const options = mockV2MigrationOptions(); + options.client.indices.getMapping.mockResponse(mappingsResponseWithoutIndexTypesMap, { + statusCode: 200, + }); + + const migrator = new KibanaMigrator(options); + migrator.prepareMigrations(); + const results = await migrator.runMigrations(); + + expect(results.length).toEqual(4); + expect(results[0]).toEqual( + expect.objectContaining({ + sourceIndex: '.my-index_pre8.2.3_001', + destIndex: '.my-index_8.2.3_001', + elapsedMs: expect.any(Number), + status: 'migrated', + }) + ); + expect(results[1]).toEqual( + expect.objectContaining({ + destIndex: '.other-index_8.2.3_001', + elapsedMs: expect.any(Number), + status: 'patched', + }) + ); + expect(results[2]).toEqual( + expect.objectContaining({ + destIndex: '.my-task-index_8.2.3_001', + elapsedMs: expect.any(Number), + status: 'patched', + }) + ); + expect(results[3]).toEqual( + expect.objectContaining({ + destIndex: '.my-complementary-index_8.2.3_001', + elapsedMs: expect.any(Number), + status: 'patched', + }) + ); + + expect(runResilientMigrator).toHaveBeenCalledTimes(4); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + kibanaVersion: '8.2.3', + indexPrefix: '.my-index', + indexTypesMap: { + '.my-index': ['testtype', 'testtype3'], + '.other-index': ['testtype2'], + '.my-task-index': ['testtasktype'], + }, + targetMappings: expect.objectContaining({ + properties: expect.objectContaining({ + testtype: expect.anything(), + testtype3: expect.anything(), + }), + }), + readyToReindex: expect.objectContaining({ + promise: expect.anything(), + resolve: expect.anything(), + reject: expect.anything(), + }), + mustRelocateDocuments: true, + doneReindexing: expect.objectContaining({ + promise: expect.anything(), + resolve: expect.anything(), + reject: expect.anything(), + }), + }) + ); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + kibanaVersion: '8.2.3', + indexPrefix: '.other-index', + indexTypesMap: { + '.my-index': ['testtype', 'testtype3'], + '.other-index': ['testtype2'], + '.my-task-index': ['testtasktype'], + }, + targetMappings: expect.objectContaining({ + properties: expect.objectContaining({ + testtype2: expect.anything(), + }), + }), + readyToReindex: expect.objectContaining({ + promise: expect.anything(), + resolve: expect.anything(), + reject: expect.anything(), + }), + mustRelocateDocuments: true, + doneReindexing: expect.objectContaining({ + promise: expect.anything(), + resolve: expect.anything(), + reject: expect.anything(), + }), + }) + ); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + kibanaVersion: '8.2.3', + indexPrefix: '.my-task-index', + indexTypesMap: { + '.my-index': ['testtype', 'testtype3'], + '.other-index': ['testtype2'], + '.my-task-index': ['testtasktype'], + }, + targetMappings: expect.objectContaining({ + properties: expect.objectContaining({ + testtasktype: expect.anything(), + }), + }), + // this migrator is NOT involved in any relocation, + // thus, it must not synchronize with other migrators + mustRelocateDocuments: false, + readyToReindex: undefined, + doneReindexing: undefined, + }) + ); + expect(runResilientMigrator).toHaveBeenNthCalledWith( + 4, + expect.objectContaining({ + kibanaVersion: '8.2.3', + indexPrefix: '.my-complementary-index', + indexTypesMap: { + '.my-index': ['testtype', 'testtype3'], + '.other-index': ['testtype2'], + '.my-task-index': ['testtasktype'], + }, + targetMappings: expect.objectContaining({ + properties: expect.not.objectContaining({ + // this index does no longer have any types associated to it + testtype: expect.anything(), + testtype2: expect.anything(), + testtype3: expect.anything(), + testtasktype: expect.anything(), + }), + }), + mustRelocateDocuments: true, + doneReindexing: expect.objectContaining({ + promise: expect.anything(), + resolve: expect.anything(), + reject: expect.anything(), + }), + }) + ); + }); + }); + }); }); }); @@ -254,7 +485,19 @@ const mockOptions = () => { logger: loggingSystemMock.create().get(), kibanaVersion: '8.2.3', waitForMigrationCompletion: false, + defaultIndexTypesMap: { + '.my-index': ['testtype', 'testtype2'], + '.my-task-index': ['testtasktype'], + // this index no longer has any types registered in typeRegistry + // but we still need a migrator for it, so that 'testtype3' documents + // are moved over to their new index (.my_index) + '.my-complementary-index': ['testtype3'], + }, typeRegistry: createRegistry([ + // typeRegistry depicts an updated index map: + // .my-index: ['testtype', 'testtype3'], + // .my-other-index: ['testtype2'], + // .my-task-index': ['testtasktype'], { name: 'testtype', hidden: false, @@ -270,7 +513,32 @@ const mockOptions = () => { name: 'testtype2', hidden: false, namespaceType: 'single', - indexPattern: 'other-index', + // We are moving 'testtype2' from '.my-index' to '.other-index' + indexPattern: '.other-index', + mappings: { + properties: { + name: { type: 'keyword' }, + }, + }, + migrations: {}, + }, + { + name: 'testtasktype', + hidden: false, + namespaceType: 'single', + indexPattern: '.my-task-index', + mappings: { + properties: { + name: { type: 'keyword' }, + }, + }, + migrations: {}, + }, + { + // We are moving 'testtype3' from '.my-complementary-index' to '.my-index' + name: 'testtype3', + hidden: false, + namespaceType: 'single', mappings: { properties: { name: { type: 'keyword' }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts index ef5166f8528fc..d9b9f19b785e4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator.ts @@ -16,10 +16,11 @@ import Semver from 'semver'; import type { Logger } from '@kbn/logging'; import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import type { - SavedObjectUnsanitizedDoc, - SavedObjectsRawDoc, - ISavedObjectTypeRegistry, +import { + MAIN_SAVED_OBJECT_INDEX, + type SavedObjectUnsanitizedDoc, + type SavedObjectsRawDoc, + type ISavedObjectTypeRegistry, } from '@kbn/core-saved-objects-server'; import { SavedObjectsSerializer, @@ -29,17 +30,17 @@ import { type IKibanaMigrator, type KibanaMigratorStatus, type MigrationResult, + type IndexTypesMap, } from '@kbn/core-saved-objects-base-server-internal'; +import { getIndicesInvolvedInRelocation } from './kibana_migrator_utils'; import { buildActiveMappings, buildTypesMappings } from './core'; import { DocumentMigrator } from './document_migrator'; import { createIndexMap } from './core/build_index_map'; import { runResilientMigrator } from './run_resilient_migrator'; import { migrateRawDocsSafely } from './core/migrate_raw_docs'; import { runZeroDowntimeMigration } from './zdt'; - -// ensure plugins don't try to convert SO namespaceTypes after 8.0.0 -// see https://github.com/elastic/kibana/issues/147344 -const ALLOWED_CONVERT_VERSION = '8.0.0'; +import { createMultiPromiseDefer, indexMapToIndexTypesMap } from './kibana_migrator_utils'; +import { ALLOWED_CONVERT_VERSION, DEFAULT_INDEX_TYPES_MAP } from './kibana_migrator_constants'; export interface KibanaMigratorOptions { client: ElasticsearchClient; @@ -50,6 +51,7 @@ export interface KibanaMigratorOptions { logger: Logger; docLinks: DocLinksServiceStart; waitForMigrationCompletion: boolean; + defaultIndexTypesMap?: IndexTypesMap; } /** @@ -71,6 +73,7 @@ export class KibanaMigrator implements IKibanaMigrator { private readonly soMigrationsConfig: SavedObjectsMigrationConfigType; private readonly docLinks: DocLinksServiceStart; private readonly waitForMigrationCompletion: boolean; + private readonly defaultIndexTypesMap: IndexTypesMap; public readonly kibanaVersion: string; /** @@ -84,6 +87,7 @@ export class KibanaMigrator implements IKibanaMigrator { kibanaVersion, logger, docLinks, + defaultIndexTypesMap = DEFAULT_INDEX_TYPES_MAP, waitForMigrationCompletion, }: KibanaMigratorOptions) { this.client = client; @@ -105,6 +109,7 @@ export class KibanaMigrator implements IKibanaMigrator { // operation so we cache the result this.activeMappings = buildActiveMappings(this.mappingProperties); this.docLinks = docLinks; + this.defaultIndexTypesMap = defaultIndexTypesMap; } public runMigrations({ rerun = false }: { rerun?: boolean } = {}): Promise { @@ -134,12 +139,12 @@ export class KibanaMigrator implements IKibanaMigrator { return this.status$.asObservable(); } - private runMigrationsInternal(): Promise { + private async runMigrationsInternal(): Promise { const migrationAlgorithm = this.soMigrationsConfig.algorithm; if (migrationAlgorithm === 'zdt') { - return this.runMigrationZdt(); + return await this.runMigrationZdt(); } else { - return this.runMigrationV2(); + return await this.runMigrationV2(); } } @@ -157,7 +162,7 @@ export class KibanaMigrator implements IKibanaMigrator { }); } - private runMigrationV2(): Promise { + private async runMigrationV2(): Promise { const indexMap = createIndexMap({ kibanaIndexName: this.kibanaIndex, indexMap: this.mappingProperties, @@ -173,16 +178,59 @@ export class KibanaMigrator implements IKibanaMigrator { this.log.debug(`migrationVersion: ${migrationVersion} saved object type: ${type}`); }); - const migrators = Object.keys(indexMap).map((index) => { + // build a indexTypesMap from the info present in tye typeRegistry, e.g.: + // { + // '.kibana': ['typeA', 'typeB', ...] + // '.kibana_task_manager': ['task', ...] + // '.kibana_cases': ['typeC', 'typeD', ...] + // ... + // } + const indexTypesMap = indexMapToIndexTypesMap(indexMap); + + // compare indexTypesMap with the one present (or not) in the .kibana index meta + // and check if some SO types have been moved to different indices + const indicesWithMovingTypes = await getIndicesInvolvedInRelocation({ + mainIndex: MAIN_SAVED_OBJECT_INDEX, + client: this.client, + indexTypesMap, + logger: this.log, + defaultIndexTypesMap: this.defaultIndexTypesMap, + }); + + // we create 2 synchronization objects (2 synchronization points) for each of the + // migrators involved in relocations, aka each of the migrators that will: + // A) reindex some documents TO other indices + // B) receive some documents FROM other indices + // C) both + const readyToReindexDefers = createMultiPromiseDefer(indicesWithMovingTypes); + const doneReindexingDefers = createMultiPromiseDefer(indicesWithMovingTypes); + + // build a list of all migrators that must be started + const migratorIndices = new Set(Object.keys(indexMap)); + // indices involved in a relocation might no longer be present in current mappings + // but if their SOs must be relocated to another index, we still need a migrator to do the job + indicesWithMovingTypes.forEach((index) => migratorIndices.add(index)); + + const migrators = Array.from(migratorIndices).map((indexName, i) => { return { migrate: (): Promise => { + const readyToReindex = readyToReindexDefers[indexName]; + const doneReindexing = doneReindexingDefers[indexName]; + // check if this migrator's index is involved in some document redistribution + const mustRelocateDocuments = !!readyToReindex; + return runResilientMigrator({ client: this.client, kibanaVersion: this.kibanaVersion, + mustRelocateDocuments, + indexTypesMap, waitForMigrationCompletion: this.waitForMigrationCompletion, - targetMappings: buildActiveMappings(indexMap[index].typeMappings), + // a migrator's index might no longer have any associated types to it + targetMappings: buildActiveMappings(indexMap[indexName]?.typeMappings ?? {}), logger: this.log, - preMigrationScript: indexMap[index].script, + preMigrationScript: indexMap[indexName]?.script, + readyToReindex, + doneReindexing, transformRawDocs: (rawDocs: SavedObjectsRawDoc[]) => migrateRawDocsSafely({ serializer: this.serializer, @@ -190,7 +238,7 @@ export class KibanaMigrator implements IKibanaMigrator { rawDocs, }), migrationVersionPerType: this.documentMigrator.migrationVersion, - indexPrefix: index, + indexPrefix: indexName, migrationsConfig: this.soMigrationsConfig, typeRegistry: this.typeRegistry, docLinks: this.docLinks, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_constants.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_constants.ts new file mode 100644 index 0000000000000..e18abed5caca7 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_constants.ts @@ -0,0 +1,131 @@ +/* + * 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 type { IndexTypesMap } from '@kbn/core-saved-objects-base-server-internal'; + +export enum TypeStatus { + Added = 'added', + Removed = 'removed', + Moved = 'moved', + Untouched = 'untouched', +} + +export interface TypeStatusDetails { + currentIndex?: string; + targetIndex?: string; + status: TypeStatus; +} + +// ensure plugins don't try to convert SO namespaceTypes after 8.0.0 +// see https://github.com/elastic/kibana/issues/147344 +export const ALLOWED_CONVERT_VERSION = '8.0.0'; + +export const DEFAULT_INDEX_TYPES_MAP: IndexTypesMap = { + '.kibana_task_manager': ['task'], + '.kibana': [ + 'action', + 'action_task_params', + 'alert', + 'api_key_pending_invalidation', + 'apm-indices', + 'apm-server-schema', + 'apm-service-group', + 'apm-telemetry', + 'app_search_telemetry', + 'application_usage_daily', + 'application_usage_totals', + 'book', + 'canvas-element', + 'canvas-workpad', + 'canvas-workpad-template', + 'cases', + 'cases-comments', + 'cases-configure', + 'cases-connector-mappings', + 'cases-telemetry', + 'cases-user-actions', + 'config', + 'config-global', + 'connector_token', + 'core-usage-stats', + 'csp-rule-template', + 'dashboard', + 'endpoint:user-artifact', + 'endpoint:user-artifact-manifest', + 'enterprise_search_telemetry', + 'epm-packages', + 'epm-packages-assets', + 'event_loop_delays_daily', + 'exception-list', + 'exception-list-agnostic', + 'file', + 'file-upload-usage-collection-telemetry', + 'fileShare', + 'fleet-fleet-server-host', + 'fleet-message-signing-keys', + 'fleet-preconfiguration-deletion-record', + 'fleet-proxy', + 'graph-workspace', + 'guided-onboarding-guide-state', + 'guided-onboarding-plugin-state', + 'index-pattern', + 'infrastructure-monitoring-log-view', + 'infrastructure-ui-source', + 'ingest-agent-policies', + 'ingest-download-sources', + 'ingest-outputs', + 'ingest-package-policies', + 'ingest_manager_settings', + 'inventory-view', + 'kql-telemetry', + 'legacy-url-alias', + 'lens', + 'lens-ui-telemetry', + 'map', + 'metrics-explorer-view', + 'ml-job', + 'ml-module', + 'ml-trained-model', + 'monitoring-telemetry', + 'osquery-manager-usage-metric', + 'osquery-pack', + 'osquery-pack-asset', + 'osquery-saved-query', + 'query', + 'rules-settings', + 'sample-data-telemetry', + 'search', + 'search-session', + 'search-telemetry', + 'searchableList', + 'security-rule', + 'security-solution-signals-migration', + 'siem-detection-engine-rule-actions', + 'siem-ui-timeline', + 'siem-ui-timeline-note', + 'siem-ui-timeline-pinned-event', + 'slo', + 'space', + 'spaces-usage-stats', + 'synthetics-monitor', + 'synthetics-param', + 'synthetics-privates-locations', + 'tag', + 'telemetry', + 'todo', + 'ui-metric', + 'upgrade-assistant-ml-upgrade-operation', + 'upgrade-assistant-reindex-operation', + 'uptime-dynamic-settings', + 'uptime-synthetics-api-key', + 'url', + 'usage-counters', + 'visualization', + 'workplace_search_telemetry', + ], +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.fixtures.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.fixtures.ts new file mode 100644 index 0000000000000..802167d733fb5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.fixtures.ts @@ -0,0 +1,3413 @@ +/* + * 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 { IndexMap } from './core'; + +export const INDEX_MAP_BEFORE_SPLIT: IndexMap = { + '.kibana': { + typeMappings: { + 'core-usage-stats': { + dynamic: false, + properties: {}, + }, + 'legacy-url-alias': { + dynamic: false, + properties: { + sourceId: { + type: 'keyword', + }, + targetNamespace: { + type: 'keyword', + }, + targetType: { + type: 'keyword', + }, + targetId: { + type: 'keyword', + }, + resolveCounter: { + type: 'long', + }, + disabled: { + type: 'boolean', + }, + }, + }, + config: { + dynamic: false, + properties: { + buildNum: { + type: 'keyword', + }, + }, + }, + 'config-global': { + dynamic: false, + properties: { + buildNum: { + type: 'keyword', + }, + }, + }, + 'usage-counters': { + dynamic: false, + properties: { + domainId: { + type: 'keyword', + }, + }, + }, + 'guided-onboarding-guide-state': { + dynamic: false, + properties: { + guideId: { + type: 'keyword', + }, + isActive: { + type: 'boolean', + }, + }, + }, + 'guided-onboarding-plugin-state': { + dynamic: false, + properties: {}, + }, + 'ui-metric': { + properties: { + count: { + type: 'integer', + }, + }, + }, + application_usage_totals: { + dynamic: false, + properties: {}, + }, + application_usage_daily: { + dynamic: false, + properties: { + timestamp: { + type: 'date', + }, + }, + }, + event_loop_delays_daily: { + dynamic: false, + properties: { + lastUpdatedAt: { + type: 'date', + }, + }, + }, + url: { + properties: { + slug: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + accessCount: { + type: 'long', + }, + accessDate: { + type: 'date', + }, + createDate: { + type: 'date', + }, + url: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 2048, + }, + }, + }, + locatorJSON: { + type: 'text', + index: false, + }, + }, + }, + 'index-pattern': { + dynamic: false, + properties: { + title: { + type: 'text', + }, + type: { + type: 'keyword', + }, + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + }, + }, + 'sample-data-telemetry': { + properties: { + installCount: { + type: 'long', + }, + unInstallCount: { + type: 'long', + }, + }, + }, + space: { + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 2048, + }, + }, + }, + description: { + type: 'text', + }, + initials: { + type: 'keyword', + }, + color: { + type: 'keyword', + }, + disabledFeatures: { + type: 'keyword', + }, + imageUrl: { + type: 'text', + index: false, + }, + _reserved: { + type: 'boolean', + }, + }, + }, + 'spaces-usage-stats': { + dynamic: false, + properties: {}, + }, + 'exception-list-agnostic': { + properties: { + _tags: { + type: 'keyword', + }, + created_at: { + type: 'keyword', + }, + created_by: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + immutable: { + type: 'boolean', + }, + list_id: { + type: 'keyword', + }, + list_type: { + type: 'keyword', + }, + meta: { + type: 'keyword', + }, + name: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + tags: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + tie_breaker_id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + updated_by: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + comments: { + properties: { + comment: { + type: 'keyword', + }, + created_at: { + type: 'keyword', + }, + created_by: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + updated_at: { + type: 'keyword', + }, + updated_by: { + type: 'keyword', + }, + }, + }, + entries: { + properties: { + entries: { + properties: { + field: { + type: 'keyword', + }, + operator: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + value: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + }, + }, + field: { + type: 'keyword', + }, + list: { + properties: { + id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + }, + }, + operator: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + value: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + }, + }, + expire_time: { + type: 'date', + }, + item_id: { + type: 'keyword', + }, + os_types: { + type: 'keyword', + }, + }, + }, + 'exception-list': { + properties: { + _tags: { + type: 'keyword', + }, + created_at: { + type: 'keyword', + }, + created_by: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + immutable: { + type: 'boolean', + }, + list_id: { + type: 'keyword', + }, + list_type: { + type: 'keyword', + }, + meta: { + type: 'keyword', + }, + name: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + tags: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + tie_breaker_id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + updated_by: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + comments: { + properties: { + comment: { + type: 'keyword', + }, + created_at: { + type: 'keyword', + }, + created_by: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + updated_at: { + type: 'keyword', + }, + updated_by: { + type: 'keyword', + }, + }, + }, + entries: { + properties: { + entries: { + properties: { + field: { + type: 'keyword', + }, + operator: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + value: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + }, + }, + field: { + type: 'keyword', + }, + list: { + properties: { + id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + }, + }, + operator: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + value: { + fields: { + text: { + type: 'text', + }, + }, + type: 'keyword', + }, + }, + }, + expire_time: { + type: 'date', + }, + item_id: { + type: 'keyword', + }, + os_types: { + type: 'keyword', + }, + }, + }, + telemetry: { + properties: { + enabled: { + type: 'boolean', + }, + sendUsageFrom: { + type: 'keyword', + }, + lastReported: { + type: 'date', + }, + lastVersionChecked: { + type: 'keyword', + }, + userHasSeenNotice: { + type: 'boolean', + }, + reportFailureCount: { + type: 'integer', + }, + reportFailureVersion: { + type: 'keyword', + }, + allowChangingOptInStatus: { + type: 'boolean', + }, + }, + }, + file: { + dynamic: false, + properties: { + created: { + type: 'date', + }, + Updated: { + type: 'date', + }, + name: { + type: 'text', + }, + user: { + type: 'flattened', + }, + Status: { + type: 'keyword', + }, + mime_type: { + type: 'keyword', + }, + extension: { + type: 'keyword', + }, + size: { + type: 'long', + }, + Meta: { + type: 'flattened', + }, + FileKind: { + type: 'keyword', + }, + }, + }, + fileShare: { + dynamic: false, + properties: { + created: { + type: 'date', + }, + valid_until: { + type: 'long', + }, + token: { + type: 'keyword', + }, + name: { + type: 'keyword', + }, + }, + }, + action: { + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + actionTypeId: { + type: 'keyword', + }, + isMissingSecrets: { + type: 'boolean', + }, + config: { + enabled: false, + type: 'object', + }, + secrets: { + type: 'binary', + }, + }, + }, + action_task_params: { + dynamic: false, + properties: { + actionId: { + type: 'keyword', + }, + consumer: { + type: 'keyword', + }, + params: { + enabled: false, + type: 'object', + }, + apiKey: { + type: 'binary', + }, + executionId: { + type: 'keyword', + }, + relatedSavedObjects: { + enabled: false, + type: 'object', + }, + }, + }, + connector_token: { + properties: { + connectorId: { + type: 'keyword', + }, + tokenType: { + type: 'keyword', + }, + token: { + type: 'binary', + }, + expiresAt: { + type: 'date', + }, + createdAt: { + type: 'date', + }, + updatedAt: { + type: 'date', + }, + }, + }, + query: { + properties: { + title: { + type: 'text', + }, + description: { + type: 'text', + }, + query: { + properties: { + language: { + type: 'keyword', + }, + query: { + type: 'keyword', + index: false, + }, + }, + }, + filters: { + dynamic: false, + properties: {}, + }, + timefilter: { + dynamic: false, + properties: {}, + }, + }, + }, + 'kql-telemetry': { + properties: { + optInCount: { + type: 'long', + }, + optOutCount: { + type: 'long', + }, + }, + }, + 'search-session': { + properties: { + sessionId: { + type: 'keyword', + }, + name: { + type: 'keyword', + }, + created: { + type: 'date', + }, + expires: { + type: 'date', + }, + appId: { + type: 'keyword', + }, + locatorId: { + type: 'keyword', + }, + initialState: { + dynamic: false, + properties: {}, + }, + restoreState: { + dynamic: false, + properties: {}, + }, + idMapping: { + dynamic: false, + properties: {}, + }, + realmType: { + type: 'keyword', + }, + realmName: { + type: 'keyword', + }, + username: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + isCanceled: { + type: 'boolean', + }, + }, + }, + 'search-telemetry': { + dynamic: false, + properties: {}, + }, + 'file-upload-usage-collection-telemetry': { + properties: { + file_upload: { + properties: { + index_creation_count: { + type: 'long', + }, + }, + }, + }, + }, + alert: { + properties: { + enabled: { + type: 'boolean', + }, + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + normalizer: 'lowercase', + }, + }, + }, + tags: { + type: 'keyword', + }, + alertTypeId: { + type: 'keyword', + }, + schedule: { + properties: { + interval: { + type: 'keyword', + }, + }, + }, + consumer: { + type: 'keyword', + }, + legacyId: { + type: 'keyword', + }, + actions: { + dynamic: false, + type: 'nested', + properties: { + group: { + type: 'keyword', + }, + actionRef: { + type: 'keyword', + }, + actionTypeId: { + type: 'keyword', + }, + params: { + dynamic: false, + properties: {}, + }, + frequency: { + properties: { + summary: { + index: false, + type: 'boolean', + }, + notifyWhen: { + index: false, + type: 'keyword', + }, + throttle: { + index: false, + type: 'keyword', + }, + }, + }, + }, + }, + params: { + type: 'flattened', + ignore_above: 4096, + }, + mapped_params: { + properties: { + risk_score: { + type: 'float', + }, + severity: { + type: 'keyword', + }, + }, + }, + scheduledTaskId: { + type: 'keyword', + }, + createdBy: { + type: 'keyword', + }, + updatedBy: { + type: 'keyword', + }, + createdAt: { + type: 'date', + }, + updatedAt: { + type: 'date', + }, + apiKey: { + type: 'binary', + }, + apiKeyOwner: { + type: 'keyword', + }, + throttle: { + type: 'keyword', + }, + notifyWhen: { + type: 'keyword', + }, + muteAll: { + type: 'boolean', + }, + mutedInstanceIds: { + type: 'keyword', + }, + meta: { + properties: { + versionApiKeyLastmodified: { + type: 'keyword', + }, + }, + }, + monitoring: { + properties: { + run: { + properties: { + history: { + properties: { + duration: { + type: 'long', + }, + success: { + type: 'boolean', + }, + timestamp: { + type: 'date', + }, + outcome: { + type: 'keyword', + }, + }, + }, + calculated_metrics: { + properties: { + p50: { + type: 'long', + }, + p95: { + type: 'long', + }, + p99: { + type: 'long', + }, + success_ratio: { + type: 'float', + }, + }, + }, + last_run: { + properties: { + timestamp: { + type: 'date', + }, + metrics: { + properties: { + duration: { + type: 'long', + }, + total_search_duration_ms: { + type: 'long', + }, + total_indexing_duration_ms: { + type: 'long', + }, + total_alerts_detected: { + type: 'float', + }, + total_alerts_created: { + type: 'float', + }, + gap_duration_s: { + type: 'float', + }, + }, + }, + }, + }, + }, + }, + }, + }, + snoozeSchedule: { + type: 'nested', + properties: { + id: { + type: 'keyword', + }, + duration: { + type: 'long', + }, + skipRecurrences: { + type: 'date', + format: 'strict_date_time', + }, + rRule: { + type: 'nested', + properties: { + freq: { + type: 'keyword', + }, + dtstart: { + type: 'date', + format: 'strict_date_time', + }, + tzid: { + type: 'keyword', + }, + until: { + type: 'date', + format: 'strict_date_time', + }, + count: { + type: 'long', + }, + interval: { + type: 'long', + }, + wkst: { + type: 'keyword', + }, + byweekday: { + type: 'keyword', + }, + bymonth: { + type: 'short', + }, + bysetpos: { + type: 'long', + }, + bymonthday: { + type: 'short', + }, + byyearday: { + type: 'short', + }, + byweekno: { + type: 'short', + }, + byhour: { + type: 'long', + }, + byminute: { + type: 'long', + }, + bysecond: { + type: 'long', + }, + }, + }, + }, + }, + nextRun: { + type: 'date', + }, + executionStatus: { + properties: { + numberOfTriggeredActions: { + type: 'long', + }, + status: { + type: 'keyword', + }, + lastExecutionDate: { + type: 'date', + }, + lastDuration: { + type: 'long', + }, + error: { + properties: { + reason: { + type: 'keyword', + }, + message: { + type: 'keyword', + }, + }, + }, + warning: { + properties: { + reason: { + type: 'keyword', + }, + message: { + type: 'keyword', + }, + }, + }, + }, + }, + lastRun: { + properties: { + outcome: { + type: 'keyword', + }, + outcomeOrder: { + type: 'float', + }, + warning: { + type: 'text', + }, + outcomeMsg: { + type: 'text', + }, + alertsCount: { + properties: { + active: { + type: 'float', + }, + new: { + type: 'float', + }, + recovered: { + type: 'float', + }, + ignored: { + type: 'float', + }, + }, + }, + }, + }, + running: { + type: 'boolean', + }, + }, + }, + api_key_pending_invalidation: { + properties: { + apiKeyId: { + type: 'keyword', + }, + createdAt: { + type: 'date', + }, + }, + }, + 'rules-settings': { + properties: { + flapping: { + properties: { + enabled: { + type: 'boolean', + index: false, + }, + lookBackWindow: { + type: 'long', + index: false, + }, + statusChangeThreshold: { + type: 'long', + index: false, + }, + createdBy: { + type: 'keyword', + index: false, + }, + updatedBy: { + type: 'keyword', + index: false, + }, + createdAt: { + type: 'date', + index: false, + }, + updatedAt: { + type: 'date', + index: false, + }, + }, + }, + }, + }, + search: { + properties: { + columns: { + type: 'keyword', + index: false, + doc_values: false, + }, + description: { + type: 'text', + }, + viewMode: { + type: 'keyword', + index: false, + doc_values: false, + }, + hideChart: { + type: 'boolean', + index: false, + doc_values: false, + }, + isTextBasedQuery: { + type: 'boolean', + index: false, + doc_values: false, + }, + usesAdHocDataView: { + type: 'boolean', + index: false, + doc_values: false, + }, + hideAggregatedPreview: { + type: 'boolean', + index: false, + doc_values: false, + }, + hits: { + type: 'integer', + index: false, + doc_values: false, + }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { + type: 'text', + index: false, + }, + }, + }, + sort: { + type: 'keyword', + index: false, + doc_values: false, + }, + title: { + type: 'text', + }, + grid: { + dynamic: false, + properties: {}, + }, + version: { + type: 'integer', + }, + rowHeight: { + type: 'text', + }, + timeRestore: { + type: 'boolean', + index: false, + doc_values: false, + }, + timeRange: { + dynamic: false, + properties: { + from: { + type: 'keyword', + index: false, + doc_values: false, + }, + to: { + type: 'keyword', + index: false, + doc_values: false, + }, + }, + }, + refreshInterval: { + dynamic: false, + properties: { + pause: { + type: 'boolean', + index: false, + doc_values: false, + }, + value: { + type: 'integer', + index: false, + doc_values: false, + }, + }, + }, + rowsPerPage: { + type: 'integer', + index: false, + doc_values: false, + }, + breakdownField: { + type: 'text', + }, + }, + }, + tag: { + properties: { + name: { + type: 'text', + }, + description: { + type: 'text', + }, + color: { + type: 'text', + }, + }, + }, + 'graph-workspace': { + properties: { + description: { + type: 'text', + }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { + type: 'text', + }, + }, + }, + numLinks: { + type: 'integer', + }, + numVertices: { + type: 'integer', + }, + title: { + type: 'text', + }, + version: { + type: 'integer', + }, + wsState: { + type: 'text', + }, + legacyIndexPatternRef: { + type: 'text', + index: false, + }, + }, + }, + visualization: { + properties: { + description: { + type: 'text', + }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { + type: 'text', + index: false, + }, + }, + }, + savedSearchRefName: { + type: 'keyword', + index: false, + doc_values: false, + }, + title: { + type: 'text', + }, + uiStateJSON: { + type: 'text', + index: false, + }, + version: { + type: 'integer', + }, + visState: { + type: 'text', + index: false, + }, + }, + }, + dashboard: { + properties: { + description: { + type: 'text', + }, + hits: { + type: 'integer', + index: false, + doc_values: false, + }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { + type: 'text', + index: false, + }, + }, + }, + optionsJSON: { + type: 'text', + index: false, + }, + panelsJSON: { + type: 'text', + index: false, + }, + refreshInterval: { + properties: { + display: { + type: 'keyword', + index: false, + doc_values: false, + }, + pause: { + type: 'boolean', + index: false, + doc_values: false, + }, + section: { + type: 'integer', + index: false, + doc_values: false, + }, + value: { + type: 'integer', + index: false, + doc_values: false, + }, + }, + }, + controlGroupInput: { + properties: { + controlStyle: { + type: 'keyword', + index: false, + doc_values: false, + }, + chainingSystem: { + type: 'keyword', + index: false, + doc_values: false, + }, + panelsJSON: { + type: 'text', + index: false, + }, + ignoreParentSettingsJSON: { + type: 'text', + index: false, + }, + }, + }, + timeFrom: { + type: 'keyword', + index: false, + doc_values: false, + }, + timeRestore: { + type: 'boolean', + index: false, + doc_values: false, + }, + timeTo: { + type: 'keyword', + index: false, + doc_values: false, + }, + title: { + type: 'text', + }, + version: { + type: 'integer', + }, + }, + }, + todo: { + properties: { + title: { + type: 'keyword', + }, + task: { + type: 'text', + }, + icon: { + type: 'keyword', + }, + }, + }, + book: { + properties: { + title: { + type: 'keyword', + }, + author: { + type: 'keyword', + }, + readIt: { + type: 'boolean', + }, + }, + }, + searchableList: { + properties: { + title: { + type: 'text', + }, + version: { + type: 'integer', + }, + }, + }, + lens: { + properties: { + title: { + type: 'text', + }, + description: { + type: 'text', + }, + visualizationType: { + type: 'keyword', + }, + state: { + type: 'flattened', + }, + expression: { + index: false, + doc_values: false, + type: 'keyword', + }, + }, + }, + 'lens-ui-telemetry': { + properties: { + name: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + date: { + type: 'date', + }, + count: { + type: 'integer', + }, + }, + }, + map: { + properties: { + description: { + type: 'text', + }, + title: { + type: 'text', + }, + version: { + type: 'integer', + }, + mapStateJSON: { + type: 'text', + }, + layerListJSON: { + type: 'text', + }, + uiStateJSON: { + type: 'text', + }, + bounds: { + dynamic: false, + properties: {}, + }, + }, + }, + 'cases-comments': { + dynamic: false, + properties: { + comment: { + type: 'text', + }, + owner: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + actions: { + properties: { + type: { + type: 'keyword', + }, + }, + }, + alertId: { + type: 'keyword', + }, + created_at: { + type: 'date', + }, + created_by: { + properties: { + username: { + type: 'keyword', + }, + }, + }, + externalReferenceAttachmentTypeId: { + type: 'keyword', + }, + persistableStateAttachmentTypeId: { + type: 'keyword', + }, + pushed_at: { + type: 'date', + }, + updated_at: { + type: 'date', + }, + }, + }, + 'cases-configure': { + dynamic: false, + properties: { + created_at: { + type: 'date', + }, + closure_type: { + type: 'keyword', + }, + owner: { + type: 'keyword', + }, + }, + }, + 'cases-connector-mappings': { + dynamic: false, + properties: { + owner: { + type: 'keyword', + }, + }, + }, + cases: { + dynamic: false, + properties: { + assignees: { + properties: { + uid: { + type: 'keyword', + }, + }, + }, + closed_at: { + type: 'date', + }, + closed_by: { + properties: { + username: { + type: 'keyword', + }, + full_name: { + type: 'keyword', + }, + email: { + type: 'keyword', + }, + profile_uid: { + type: 'keyword', + }, + }, + }, + created_at: { + type: 'date', + }, + created_by: { + properties: { + username: { + type: 'keyword', + }, + full_name: { + type: 'keyword', + }, + email: { + type: 'keyword', + }, + profile_uid: { + type: 'keyword', + }, + }, + }, + duration: { + type: 'unsigned_long', + }, + description: { + type: 'text', + }, + connector: { + properties: { + name: { + type: 'text', + }, + type: { + type: 'keyword', + }, + fields: { + properties: { + key: { + type: 'text', + }, + value: { + type: 'text', + }, + }, + }, + }, + }, + external_service: { + properties: { + pushed_at: { + type: 'date', + }, + pushed_by: { + properties: { + username: { + type: 'keyword', + }, + full_name: { + type: 'keyword', + }, + email: { + type: 'keyword', + }, + profile_uid: { + type: 'keyword', + }, + }, + }, + connector_name: { + type: 'keyword', + }, + external_id: { + type: 'keyword', + }, + external_title: { + type: 'text', + }, + external_url: { + type: 'text', + }, + }, + }, + owner: { + type: 'keyword', + }, + title: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + status: { + type: 'short', + }, + tags: { + type: 'keyword', + }, + updated_at: { + type: 'date', + }, + updated_by: { + properties: { + username: { + type: 'keyword', + }, + full_name: { + type: 'keyword', + }, + email: { + type: 'keyword', + }, + profile_uid: { + type: 'keyword', + }, + }, + }, + settings: { + properties: { + syncAlerts: { + type: 'boolean', + }, + }, + }, + severity: { + type: 'short', + }, + total_alerts: { + type: 'integer', + }, + total_comments: { + type: 'integer', + }, + }, + }, + 'cases-user-actions': { + dynamic: false, + properties: { + action: { + type: 'keyword', + }, + created_at: { + type: 'date', + }, + created_by: { + properties: { + username: { + type: 'keyword', + }, + }, + }, + payload: { + dynamic: false, + properties: { + connector: { + properties: { + type: { + type: 'keyword', + }, + }, + }, + comment: { + properties: { + type: { + type: 'keyword', + }, + externalReferenceAttachmentTypeId: { + type: 'keyword', + }, + persistableStateAttachmentTypeId: { + type: 'keyword', + }, + }, + }, + assignees: { + properties: { + uid: { + type: 'keyword', + }, + }, + }, + }, + }, + owner: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + }, + }, + 'cases-telemetry': { + dynamic: false, + properties: {}, + }, + 'canvas-element': { + dynamic: false, + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + help: { + type: 'text', + }, + content: { + type: 'text', + }, + image: { + type: 'text', + }, + '@timestamp': { + type: 'date', + }, + '@created': { + type: 'date', + }, + }, + }, + 'canvas-workpad': { + dynamic: false, + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + '@timestamp': { + type: 'date', + }, + '@created': { + type: 'date', + }, + }, + }, + 'canvas-workpad-template': { + dynamic: false, + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + help: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + tags: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + template_key: { + type: 'keyword', + }, + }, + }, + slo: { + dynamic: false, + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'keyword', + }, + description: { + type: 'text', + }, + indicator: { + properties: { + type: { + type: 'keyword', + }, + params: { + type: 'flattened', + }, + }, + }, + timeWindow: { + properties: { + duration: { + type: 'keyword', + }, + isRolling: { + type: 'boolean', + }, + calendar: { + properties: { + startTime: { + type: 'date', + }, + }, + }, + }, + }, + budgetingMethod: { + type: 'keyword', + }, + objective: { + properties: { + target: { + type: 'float', + }, + timesliceTarget: { + type: 'float', + }, + timesliceWindow: { + type: 'keyword', + }, + }, + }, + settings: { + properties: { + timestampField: { + type: 'keyword', + }, + syncDelay: { + type: 'keyword', + }, + frequency: { + type: 'keyword', + }, + }, + }, + revision: { + type: 'short', + }, + enabled: { + type: 'boolean', + }, + createdAt: { + type: 'date', + }, + updatedAt: { + type: 'date', + }, + }, + }, + ingest_manager_settings: { + properties: { + fleet_server_hosts: { + type: 'keyword', + }, + has_seen_add_data_notice: { + type: 'boolean', + index: false, + }, + prerelease_integrations_enabled: { + type: 'boolean', + }, + }, + }, + 'ingest-agent-policies': { + properties: { + name: { + type: 'keyword', + }, + schema_version: { + type: 'version', + }, + description: { + type: 'text', + }, + namespace: { + type: 'keyword', + }, + is_managed: { + type: 'boolean', + }, + is_default: { + type: 'boolean', + }, + is_default_fleet_server: { + type: 'boolean', + }, + status: { + type: 'keyword', + }, + unenroll_timeout: { + type: 'integer', + }, + inactivity_timeout: { + type: 'integer', + }, + updated_at: { + type: 'date', + }, + updated_by: { + type: 'keyword', + }, + revision: { + type: 'integer', + }, + monitoring_enabled: { + type: 'keyword', + index: false, + }, + is_preconfigured: { + type: 'keyword', + }, + data_output_id: { + type: 'keyword', + }, + monitoring_output_id: { + type: 'keyword', + }, + download_source_id: { + type: 'keyword', + }, + fleet_server_host_id: { + type: 'keyword', + }, + agent_features: { + properties: { + name: { + type: 'keyword', + }, + enabled: { + type: 'boolean', + }, + }, + }, + }, + }, + 'ingest-outputs': { + properties: { + output_id: { + type: 'keyword', + index: false, + }, + name: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + is_default: { + type: 'boolean', + }, + is_default_monitoring: { + type: 'boolean', + }, + hosts: { + type: 'keyword', + }, + ca_sha256: { + type: 'keyword', + index: false, + }, + ca_trusted_fingerprint: { + type: 'keyword', + index: false, + }, + config: { + type: 'flattened', + }, + config_yaml: { + type: 'text', + }, + is_preconfigured: { + type: 'boolean', + index: false, + }, + ssl: { + type: 'binary', + }, + proxy_id: { + type: 'keyword', + }, + shipper: { + dynamic: false, + properties: {}, + }, + }, + }, + 'ingest-package-policies': { + properties: { + name: { + type: 'keyword', + }, + description: { + type: 'text', + }, + namespace: { + type: 'keyword', + }, + enabled: { + type: 'boolean', + }, + is_managed: { + type: 'boolean', + }, + policy_id: { + type: 'keyword', + }, + package: { + properties: { + name: { + type: 'keyword', + }, + title: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + }, + }, + elasticsearch: { + dynamic: false, + properties: {}, + }, + vars: { + type: 'flattened', + }, + inputs: { + dynamic: false, + properties: {}, + }, + revision: { + type: 'integer', + }, + updated_at: { + type: 'date', + }, + updated_by: { + type: 'keyword', + }, + created_at: { + type: 'date', + }, + created_by: { + type: 'keyword', + }, + }, + }, + 'epm-packages': { + properties: { + name: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + internal: { + type: 'boolean', + }, + keep_policies_up_to_date: { + type: 'boolean', + index: false, + }, + es_index_patterns: { + dynamic: false, + properties: {}, + }, + verification_status: { + type: 'keyword', + }, + verification_key_id: { + type: 'keyword', + }, + installed_es: { + type: 'nested', + properties: { + id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + }, + }, + installed_kibana: { + dynamic: false, + properties: {}, + }, + installed_kibana_space_id: { + type: 'keyword', + }, + package_assets: { + dynamic: false, + properties: {}, + }, + install_started_at: { + type: 'date', + }, + install_version: { + type: 'keyword', + }, + install_status: { + type: 'keyword', + }, + install_source: { + type: 'keyword', + }, + install_format_schema_version: { + type: 'version', + }, + experimental_data_stream_features: { + type: 'nested', + properties: { + data_stream: { + type: 'keyword', + }, + features: { + type: 'nested', + dynamic: false, + properties: { + synthetic_source: { + type: 'boolean', + }, + tsdb: { + type: 'boolean', + }, + }, + }, + }, + }, + }, + }, + 'epm-packages-assets': { + properties: { + package_name: { + type: 'keyword', + }, + package_version: { + type: 'keyword', + }, + install_source: { + type: 'keyword', + }, + asset_path: { + type: 'keyword', + }, + media_type: { + type: 'keyword', + }, + data_utf8: { + type: 'text', + index: false, + }, + data_base64: { + type: 'binary', + }, + }, + }, + 'fleet-preconfiguration-deletion-record': { + properties: { + id: { + type: 'keyword', + }, + }, + }, + 'ingest-download-sources': { + properties: { + source_id: { + type: 'keyword', + index: false, + }, + name: { + type: 'keyword', + }, + is_default: { + type: 'boolean', + }, + host: { + type: 'keyword', + }, + }, + }, + 'fleet-fleet-server-host': { + properties: { + name: { + type: 'keyword', + }, + is_default: { + type: 'boolean', + }, + host_urls: { + type: 'keyword', + index: false, + }, + is_preconfigured: { + type: 'boolean', + }, + proxy_id: { + type: 'keyword', + }, + }, + }, + 'fleet-proxy': { + properties: { + name: { + type: 'keyword', + }, + url: { + type: 'keyword', + index: false, + }, + proxy_headers: { + type: 'text', + index: false, + }, + certificate_authorities: { + type: 'keyword', + index: false, + }, + certificate: { + type: 'keyword', + index: false, + }, + certificate_key: { + type: 'keyword', + index: false, + }, + is_preconfigured: { + type: 'boolean', + }, + }, + }, + 'fleet-message-signing-keys': { + dynamic: false, + properties: {}, + }, + 'osquery-manager-usage-metric': { + properties: { + count: { + type: 'long', + }, + errors: { + type: 'long', + }, + }, + }, + 'osquery-saved-query': { + dynamic: false, + properties: { + description: { + type: 'text', + }, + id: { + type: 'keyword', + }, + query: { + type: 'text', + }, + created_at: { + type: 'date', + }, + created_by: { + type: 'text', + }, + platform: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + updated_at: { + type: 'date', + }, + updated_by: { + type: 'text', + }, + interval: { + type: 'keyword', + }, + ecs_mapping: { + dynamic: false, + properties: {}, + }, + }, + }, + 'osquery-pack': { + properties: { + description: { + type: 'text', + }, + name: { + type: 'text', + }, + created_at: { + type: 'date', + }, + created_by: { + type: 'keyword', + }, + updated_at: { + type: 'date', + }, + updated_by: { + type: 'keyword', + }, + enabled: { + type: 'boolean', + }, + shards: { + dynamic: false, + properties: {}, + }, + version: { + type: 'long', + }, + queries: { + dynamic: false, + properties: { + id: { + type: 'keyword', + }, + query: { + type: 'text', + }, + interval: { + type: 'text', + }, + platform: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + ecs_mapping: { + dynamic: false, + properties: {}, + }, + }, + }, + }, + }, + 'osquery-pack-asset': { + dynamic: false, + properties: { + description: { + type: 'text', + }, + name: { + type: 'text', + }, + version: { + type: 'long', + }, + shards: { + dynamic: false, + properties: {}, + }, + queries: { + dynamic: false, + properties: { + id: { + type: 'keyword', + }, + query: { + type: 'text', + }, + interval: { + type: 'text', + }, + platform: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + ecs_mapping: { + dynamic: false, + properties: {}, + }, + }, + }, + }, + }, + 'csp-rule-template': { + dynamic: false, + properties: { + metadata: { + type: 'object', + properties: { + name: { + type: 'keyword', + fields: { + text: { + type: 'text', + }, + }, + }, + benchmark: { + type: 'object', + properties: { + id: { + type: 'keyword', + }, + }, + }, + }, + }, + }, + }, + 'ml-job': { + properties: { + job_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + datafeed_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + type: { + type: 'keyword', + }, + }, + }, + 'ml-trained-model': { + properties: { + model_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + job: { + properties: { + job_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + create_time: { + type: 'date', + }, + }, + }, + }, + }, + 'ml-module': { + dynamic: false, + properties: { + id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + title: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + description: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + type: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + logo: { + type: 'object', + }, + defaultIndexPattern: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + query: { + type: 'object', + }, + jobs: { + type: 'object', + }, + datafeeds: { + type: 'object', + }, + }, + }, + 'uptime-dynamic-settings': { + dynamic: false, + properties: {}, + }, + 'synthetics-privates-locations': { + dynamic: false, + properties: {}, + }, + 'synthetics-monitor': { + dynamic: false, + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + normalizer: 'lowercase', + }, + }, + }, + type: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + urls: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + hosts: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + journey_id: { + type: 'keyword', + }, + project_id: { + type: 'keyword', + fields: { + text: { + type: 'text', + }, + }, + }, + origin: { + type: 'keyword', + }, + hash: { + type: 'keyword', + }, + locations: { + properties: { + id: { + type: 'keyword', + ignore_above: 256, + fields: { + text: { + type: 'text', + }, + }, + }, + label: { + type: 'text', + }, + }, + }, + custom_heartbeat_id: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + tags: { + type: 'keyword', + fields: { + text: { + type: 'text', + }, + }, + }, + schedule: { + properties: { + number: { + type: 'integer', + }, + }, + }, + enabled: { + type: 'boolean', + }, + alert: { + properties: { + status: { + properties: { + enabled: { + type: 'boolean', + }, + }, + }, + }, + }, + }, + }, + 'uptime-synthetics-api-key': { + dynamic: false, + properties: { + apiKey: { + type: 'binary', + }, + }, + }, + 'synthetics-param': { + dynamic: false, + properties: {}, + }, + 'siem-ui-timeline-note': { + properties: { + eventId: { + type: 'keyword', + }, + note: { + type: 'text', + }, + created: { + type: 'date', + }, + createdBy: { + type: 'text', + }, + updated: { + type: 'date', + }, + updatedBy: { + type: 'text', + }, + }, + }, + 'siem-ui-timeline-pinned-event': { + properties: { + eventId: { + type: 'keyword', + }, + created: { + type: 'date', + }, + createdBy: { + type: 'text', + }, + updated: { + type: 'date', + }, + updatedBy: { + type: 'text', + }, + }, + }, + 'siem-detection-engine-rule-actions': { + properties: { + alertThrottle: { + type: 'keyword', + }, + ruleAlertId: { + type: 'keyword', + }, + ruleThrottle: { + type: 'keyword', + }, + actions: { + properties: { + actionRef: { + type: 'keyword', + }, + group: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + action_type_id: { + type: 'keyword', + }, + params: { + dynamic: false, + properties: {}, + }, + }, + }, + }, + }, + 'security-rule': { + dynamic: false, + properties: { + name: { + type: 'keyword', + }, + rule_id: { + type: 'keyword', + }, + version: { + type: 'long', + }, + }, + }, + 'siem-ui-timeline': { + properties: { + columns: { + properties: { + aggregatable: { + type: 'boolean', + }, + category: { + type: 'keyword', + }, + columnHeaderType: { + type: 'keyword', + }, + description: { + type: 'text', + }, + example: { + type: 'text', + }, + indexes: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + placeholder: { + type: 'text', + }, + searchable: { + type: 'boolean', + }, + type: { + type: 'keyword', + }, + }, + }, + dataProviders: { + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + enabled: { + type: 'boolean', + }, + excluded: { + type: 'boolean', + }, + kqlQuery: { + type: 'text', + }, + type: { + type: 'text', + }, + queryMatch: { + properties: { + field: { + type: 'text', + }, + displayField: { + type: 'text', + }, + value: { + type: 'text', + }, + displayValue: { + type: 'text', + }, + operator: { + type: 'text', + }, + }, + }, + and: { + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + enabled: { + type: 'boolean', + }, + excluded: { + type: 'boolean', + }, + kqlQuery: { + type: 'text', + }, + type: { + type: 'text', + }, + queryMatch: { + properties: { + field: { + type: 'text', + }, + displayField: { + type: 'text', + }, + value: { + type: 'text', + }, + displayValue: { + type: 'text', + }, + operator: { + type: 'text', + }, + }, + }, + }, + }, + }, + }, + description: { + type: 'text', + }, + eqlOptions: { + properties: { + eventCategoryField: { + type: 'text', + }, + tiebreakerField: { + type: 'text', + }, + timestampField: { + type: 'text', + }, + query: { + type: 'text', + }, + size: { + type: 'text', + }, + }, + }, + eventType: { + type: 'keyword', + }, + excludedRowRendererIds: { + type: 'text', + }, + favorite: { + properties: { + keySearch: { + type: 'text', + }, + fullName: { + type: 'text', + }, + userName: { + type: 'text', + }, + favoriteDate: { + type: 'date', + }, + }, + }, + filters: { + properties: { + meta: { + properties: { + alias: { + type: 'text', + }, + controlledBy: { + type: 'text', + }, + disabled: { + type: 'boolean', + }, + field: { + type: 'text', + }, + formattedValue: { + type: 'text', + }, + index: { + type: 'keyword', + }, + key: { + type: 'keyword', + }, + negate: { + type: 'boolean', + }, + params: { + type: 'text', + }, + type: { + type: 'keyword', + }, + value: { + type: 'text', + }, + }, + }, + exists: { + type: 'text', + }, + match_all: { + type: 'text', + }, + missing: { + type: 'text', + }, + query: { + type: 'text', + }, + range: { + type: 'text', + }, + script: { + type: 'text', + }, + }, + }, + indexNames: { + type: 'text', + }, + kqlMode: { + type: 'keyword', + }, + kqlQuery: { + properties: { + filterQuery: { + properties: { + kuery: { + properties: { + kind: { + type: 'keyword', + }, + expression: { + type: 'text', + }, + }, + }, + serializedQuery: { + type: 'text', + }, + }, + }, + }, + }, + title: { + type: 'text', + }, + templateTimelineId: { + type: 'text', + }, + templateTimelineVersion: { + type: 'integer', + }, + timelineType: { + type: 'keyword', + }, + dateRange: { + properties: { + start: { + type: 'date', + }, + end: { + type: 'date', + }, + }, + }, + sort: { + dynamic: false, + properties: { + columnId: { + type: 'keyword', + }, + columnType: { + type: 'keyword', + }, + sortDirection: { + type: 'keyword', + }, + }, + }, + status: { + type: 'keyword', + }, + created: { + type: 'date', + }, + createdBy: { + type: 'text', + }, + updated: { + type: 'date', + }, + updatedBy: { + type: 'text', + }, + }, + }, + 'endpoint:user-artifact': { + properties: { + identifier: { + type: 'keyword', + }, + compressionAlgorithm: { + type: 'keyword', + index: false, + }, + encryptionAlgorithm: { + type: 'keyword', + index: false, + }, + encodedSha256: { + type: 'keyword', + }, + encodedSize: { + type: 'long', + index: false, + }, + decodedSha256: { + type: 'keyword', + index: false, + }, + decodedSize: { + type: 'long', + index: false, + }, + created: { + type: 'date', + index: false, + }, + body: { + type: 'binary', + }, + }, + }, + 'endpoint:user-artifact-manifest': { + properties: { + created: { + type: 'date', + index: false, + }, + schemaVersion: { + type: 'keyword', + }, + semanticVersion: { + type: 'keyword', + index: false, + }, + artifacts: { + type: 'nested', + properties: { + policyId: { + type: 'keyword', + index: false, + }, + artifactId: { + type: 'keyword', + index: false, + }, + }, + }, + }, + }, + 'security-solution-signals-migration': { + properties: { + sourceIndex: { + type: 'keyword', + }, + destinationIndex: { + type: 'keyword', + index: false, + }, + version: { + type: 'long', + }, + error: { + type: 'text', + index: false, + }, + taskId: { + type: 'keyword', + index: false, + }, + status: { + type: 'keyword', + index: false, + }, + created: { + type: 'date', + index: false, + }, + createdBy: { + type: 'text', + index: false, + }, + updated: { + type: 'date', + index: false, + }, + updatedBy: { + type: 'text', + index: false, + }, + }, + }, + 'infrastructure-ui-source': { + dynamic: false, + properties: {}, + }, + 'metrics-explorer-view': { + dynamic: false, + properties: {}, + }, + 'inventory-view': { + dynamic: false, + properties: {}, + }, + 'infrastructure-monitoring-log-view': { + dynamic: false, + properties: { + name: { + type: 'text', + }, + }, + }, + 'upgrade-assistant-reindex-operation': { + dynamic: false, + properties: { + indexName: { + type: 'keyword', + }, + status: { + type: 'integer', + }, + }, + }, + 'upgrade-assistant-ml-upgrade-operation': { + dynamic: false, + properties: { + snapshotId: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + 'monitoring-telemetry': { + properties: { + reportedClusterUuids: { + type: 'keyword', + }, + }, + }, + enterprise_search_telemetry: { + dynamic: false, + properties: {}, + }, + app_search_telemetry: { + dynamic: false, + properties: {}, + }, + workplace_search_telemetry: { + dynamic: false, + properties: {}, + }, + 'apm-indices': { + dynamic: false, + properties: {}, + }, + 'apm-telemetry': { + dynamic: false, + properties: {}, + }, + 'apm-server-schema': { + properties: { + schemaJson: { + type: 'text', + index: false, + }, + }, + }, + 'apm-service-group': { + properties: { + groupName: { + type: 'keyword', + }, + kuery: { + type: 'text', + }, + description: { + type: 'text', + }, + color: { + type: 'text', + }, + }, + }, + }, + }, + '.kibana_task_manager': { + typeMappings: { + task: { + properties: { + taskType: { + type: 'keyword', + }, + scheduledAt: { + type: 'date', + }, + runAt: { + type: 'date', + }, + startedAt: { + type: 'date', + }, + retryAt: { + type: 'date', + }, + enabled: { + type: 'boolean', + }, + schedule: { + properties: { + interval: { + type: 'keyword', + }, + }, + }, + attempts: { + type: 'integer', + }, + status: { + type: 'keyword', + }, + traceparent: { + type: 'text', + }, + params: { + type: 'text', + }, + state: { + type: 'text', + }, + user: { + type: 'keyword', + }, + scope: { + type: 'keyword', + }, + ownerId: { + type: 'keyword', + }, + }, + }, + }, + script: 'ctx._id = ctx._source.type + \':\' + ctx._id; ctx._source.remove("kibana")', + }, +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.test.ts new file mode 100644 index 0000000000000..0698904c45d30 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.test.ts @@ -0,0 +1,397 @@ +/* + * 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 { errors } from '@elastic/elasticsearch'; +import type { IndicesGetMappingResponse } from '@elastic/elasticsearch/lib/api/types'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import type { IndexTypesMap } from '@kbn/core-saved-objects-base-server-internal'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { loggerMock } from '@kbn/logging-mocks'; +import { DEFAULT_INDEX_TYPES_MAP } from './kibana_migrator_constants'; +import { + calculateTypeStatuses, + createMultiPromiseDefer, + getCurrentIndexTypesMap, + getIndicesInvolvedInRelocation, + indexMapToIndexTypesMap, +} from './kibana_migrator_utils'; +import { INDEX_MAP_BEFORE_SPLIT } from './kibana_migrator_utils.fixtures'; + +describe('createMultiPromiseDefer', () => { + it('creates defer objects with the same Promise', () => { + const defers = createMultiPromiseDefer(['.kibana', '.kibana_cases']); + expect(Object.keys(defers)).toHaveLength(2); + expect(defers['.kibana'].promise).toEqual(defers['.kibana_cases'].promise); + expect(defers['.kibana'].resolve).not.toEqual(defers['.kibana_cases'].resolve); + expect(defers['.kibana'].reject).not.toEqual(defers['.kibana_cases'].reject); + }); + + it('the common Promise resolves when all defers resolve', async () => { + const defers = createMultiPromiseDefer(['.kibana', '.kibana_cases']); + let resolved = 0; + Object.values(defers).forEach((defer) => defer.promise.then(() => ++resolved)); + defers['.kibana'].resolve(); + await new Promise((resolve) => setImmediate(resolve)); // next tick + expect(resolved).toEqual(0); + defers['.kibana_cases'].resolve(); + await new Promise((resolve) => setImmediate(resolve)); // next tick + expect(resolved).toEqual(2); + }); +}); + +describe('getCurrentIndexTypesMap', () => { + const defaultIndexTypesMap: IndexTypesMap = { + '.my_index': ['type1', 'type2', 'type3'], + '.task_index': ['type4'], + }; + + describe('when mainIndex does NOT exist', () => { + it('assumes we are targeting a fresh ES cluster', async () => { + const notFoundError = new errors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 404, + body: { ok: false, message: 'Unknown resource.' }, + }) + ); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(notFoundError) + ); + + const currentIndexTypesMap = await getCurrentIndexTypesMap({ + client, + mainIndex: '.my_index', + defaultIndexTypesMap: {}, + logger: loggerMock.create(), + }); + + expect(currentIndexTypesMap).toBeUndefined(); + }); + }); + + describe('when mainIndex exists, but it does not have an indexTypesMap in its mapping._meta', () => { + it('returns the defaultIndexTypesMap', async () => { + const getMappingResponse: IndicesGetMappingResponse = { + '.my_index_8.8.0_001': { + mappings: { + _meta: { + migrationMappingPropertyHashes: { + application_usage_daily: '43b8830d5d0df85a6823d290885fc9fd', + application_usage_totals: '3d1b76c39bfb2cc8296b024d73854724', + application_usage_transactional: '3d1b76c39bfb2cc8296b024d73854724', + }, + }, + }, + }, + }; + + const client = elasticsearchClientMock.createInternalClient(); + client.indices.getMapping.mockResolvedValueOnce(getMappingResponse); + + const currentIndexTypesMap = await getCurrentIndexTypesMap({ + client, + mainIndex: '.my_index', + defaultIndexTypesMap, + logger: loggerMock.create(), + }); + + expect(currentIndexTypesMap).toEqual(defaultIndexTypesMap); + }); + }); + + describe('when mainIndex exists and it does have an indexTypesMap property in its mapping._meta', () => { + it('returns the stored indexTypesMap', async () => { + const storedIndexTypesMap: IndexTypesMap = { + '.my_index': ['type1', 'type2'], + '.task_index': ['type4'], + '.other_index': ['type3'], + }; + const getMappingResponse: IndicesGetMappingResponse = { + '.my_index_8.8.0_001': { + mappings: { + _meta: { + migrationMappingPropertyHashes: { + application_usage_daily: '43b8830d5d0df85a6823d290885fc9fd', + application_usage_totals: '3d1b76c39bfb2cc8296b024d73854724', + application_usage_transactional: '3d1b76c39bfb2cc8296b024d73854724', + }, + indexTypesMap: storedIndexTypesMap, + }, + }, + }, + }; + + const client = elasticsearchClientMock.createInternalClient(); + client.indices.getMapping.mockResolvedValueOnce(getMappingResponse); + + const currentIndexTypesMap = await getCurrentIndexTypesMap({ + client, + mainIndex: '.my_index', + defaultIndexTypesMap, + logger: loggerMock.create(), + }); + + expect(currentIndexTypesMap).toEqual(storedIndexTypesMap); + }); + }); + + describe('when retriable errors occur', () => { + it('keeps trying to fetch the index mappings forever', async () => { + const unavailable = new errors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 503, + headers: { 'Retry-After': '30' }, + body: 'Kibana server is not ready yet', + }) + ); + + const notFound = new errors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 404, + body: { ok: false, message: 'Unknown resource.' }, + }) + ); + + const client = elasticsearchClientMock.createInternalClient(); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(unavailable); + client.indices.getMapping.mockRejectedValueOnce(notFound); + + await getCurrentIndexTypesMap({ + client, + mainIndex: '.my_index', + defaultIndexTypesMap, + logger: loggerMock.create(), + retryDelay: 1, + }); + + expect(client.indices.getMapping).toHaveBeenCalledTimes(10); + }); + }); +}); + +describe('getIndicesInvolvedInRelocation', () => { + const getIndicesInvolvedInRelocationParams = () => { + const client = elasticsearchClientMock.createElasticsearchClient(); + (client as any).child = jest.fn().mockImplementation(() => client); + + return { + client, + mainIndex: MAIN_SAVED_OBJECT_INDEX, + indexTypesMap: {}, + defaultIndexTypesMap: DEFAULT_INDEX_TYPES_MAP, + logger: loggerMock.create(), + }; + }; + + it('tries to get the indexTypesMap from the mainIndex', async () => { + const params = getIndicesInvolvedInRelocationParams(); + try { + await getIndicesInvolvedInRelocation(params); + } catch (err) { + // ignore + } + + expect(params.client.indices.getMapping).toHaveBeenCalledTimes(1); + expect(params.client.indices.getMapping).toHaveBeenCalledWith({ + index: MAIN_SAVED_OBJECT_INDEX, + }); + }); + + it('fails if the query to get indexTypesMap fails with critical error', async () => { + const params = getIndicesInvolvedInRelocationParams(); + params.client.indices.getMapping.mockImplementation(() => + elasticsearchClientMock.createErrorTransportRequestPromise( + new errors.ResponseError({ + statusCode: 500, + body: { + error: { + type: 'error_type', + reason: 'error_reason', + }, + }, + warnings: [], + headers: {}, + meta: {} as any, + }) + ) + ); + expect(getIndicesInvolvedInRelocation(params)).rejects.toThrowErrorMatchingInlineSnapshot( + `"error_type"` + ); + }); + + it('assumes fresh deployment if the mainIndex does not exist, returns an empty list of moving types', async () => { + const params = getIndicesInvolvedInRelocationParams(); + params.client.indices.getMapping.mockImplementation(() => + elasticsearchClientMock.createErrorTransportRequestPromise( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: 'error_type', + reason: 'error_reason', + }, + }, + warnings: [], + headers: {}, + meta: {} as any, + }) + ) + ); + expect(getIndicesInvolvedInRelocation(params)).resolves.toEqual([]); + }); + + describe('if mainIndex exists', () => { + describe('but it does not have an indexTypeMap stored', () => { + it('uses the defaultIndexTypeMap and finds out which indices are involved in a relocation', async () => { + const params = getIndicesInvolvedInRelocationParams(); + params.client.indices.getMapping.mockReturnValue( + Promise.resolve({ + '.kibana_8.7.0_001': { + mappings: { + dynamic: 'strict', + _meta: { + migrationMappingPropertyHashes: { + someType: '7997cf5a56cc02bdc9c93361bde732b0', + }, + }, + properties: { + someProperty: {}, + }, + }, + }, + }) + ); + params.defaultIndexTypesMap = { + '.indexA': ['type1', 'type2', 'type3'], + '.indexB': ['type4', 'type5', 'type6'], + }; + + params.indexTypesMap = { + '.indexA': ['type1'], // move type2 and type 3 over to new indexC + '.indexB': ['type4', 'type5', 'type6'], // stays the same + '.indexC': ['type2', 'type3'], + }; + + expect(getIndicesInvolvedInRelocation(params)).resolves.toEqual(['.indexA', '.indexC']); + }); + }); + + describe('and it has an indexTypeMap stored', () => { + it('compares stored indexTypeMap against desired one, and finds out which indices are involved in a relocation', async () => { + const params = getIndicesInvolvedInRelocationParams(); + params.client.indices.getMapping.mockReturnValue( + Promise.resolve({ + '.kibana_8.8.0_001': { + mappings: { + dynamic: 'strict', + _meta: { + migrationMappingPropertyHashes: { + someType: '7997cf5a56cc02bdc9c93361bde732b0', + }, + // map stored on index + indexTypesMap: { + '.indexA': ['type1'], + '.indexB': ['type4', 'type5', 'type6'], + '.indexC': ['type2', 'type3'], + }, + }, + properties: { + someProperty: {}, + }, + }, + }, + }) + ); + + // exists on index, so this one will NOT be taken into account + params.defaultIndexTypesMap = { + '.indexA': ['type1', 'type2', 'type3'], + '.indexB': ['type4', 'type5', 'type6'], + }; + + params.indexTypesMap = { + '.indexA': ['type1'], + '.indexB': ['type4'], + '.indexC': ['type2', 'type3'], + '.indexD': ['type5', 'type6'], + }; + + expect(getIndicesInvolvedInRelocation(params)).resolves.toEqual(['.indexB', '.indexD']); + }); + }); + }); +}); + +describe('indexMapToIndexTypesMap', () => { + it('converts IndexMap to IndexTypesMap', () => { + expect(indexMapToIndexTypesMap(INDEX_MAP_BEFORE_SPLIT)).toEqual(DEFAULT_INDEX_TYPES_MAP); + }); +}); + +describe('calculateTypeStatuses', () => { + it('takes two indexTypesMaps and checks what types have been added, removed and relocated', () => { + const currentIndexTypesMap = { + '.indexA': ['type1', 'type2', 'type3'], + '.indexB': ['type4', 'type5', 'type6'], + }; + const desiredIndexTypesMap = { + '.indexA': ['type2'], + '.indexB': ['type3', 'type5'], + '.indexC': ['type4', 'type6', 'type7'], + '.indexD': ['type8'], + }; + + expect(calculateTypeStatuses(currentIndexTypesMap, desiredIndexTypesMap)).toEqual({ + type1: { + currentIndex: '.indexA', + status: 'removed', + }, + type2: { + currentIndex: '.indexA', + status: 'untouched', + targetIndex: '.indexA', + }, + type3: { + currentIndex: '.indexA', + status: 'moved', + targetIndex: '.indexB', + }, + type4: { + currentIndex: '.indexB', + status: 'moved', + targetIndex: '.indexC', + }, + type5: { + currentIndex: '.indexB', + status: 'untouched', + targetIndex: '.indexB', + }, + type6: { + currentIndex: '.indexB', + status: 'moved', + targetIndex: '.indexC', + }, + type7: { + status: 'added', + targetIndex: '.indexC', + }, + type8: { + status: 'added', + targetIndex: '.indexD', + }, + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.ts new file mode 100644 index 0000000000000..36d17cd1159e9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/kibana_migrator_utils.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 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 type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { migrationRetryCallCluster } from '@kbn/core-elasticsearch-server-internal'; +import type { IndexTypesMap } from '@kbn/core-saved-objects-base-server-internal'; +import type { Logger } from '@kbn/logging'; +import type { IndexMap } from './core'; +import { TypeStatus, type TypeStatusDetails } from './kibana_migrator_constants'; + +// even though this utility class is present in @kbn/kibana-utils-plugin, we can't easily import it from Core +// aka. one does not simply reuse code +export class Defer { + public resolve!: (data: T) => void; + public reject!: (error: any) => void; + public promise: Promise = new Promise((resolve, reject) => { + (this as any).resolve = resolve; + (this as any).reject = reject; + }); +} + +export const defer = () => new Defer(); + +export function createMultiPromiseDefer(indices: string[]): Record> { + const defers: Array> = indices.map(defer); + const all = Promise.all(defers.map(({ promise }) => promise)); + return indices.reduce>>((acc, indexName, i) => { + const { resolve, reject } = defers[i]; + acc[indexName] = { resolve, reject, promise: all }; + return acc; + }, {}); +} + +export async function getCurrentIndexTypesMap({ + client, + mainIndex, + defaultIndexTypesMap, + logger, + retryDelay = 2500, +}: { + client: ElasticsearchClient; + mainIndex: string; + defaultIndexTypesMap: IndexTypesMap; + logger: Logger; + retryDelay?: number; +}): Promise { + try { + // check if the main index (i.e. .kibana) exists + const mapping = await migrationRetryCallCluster( + () => + client.indices.getMapping({ + index: mainIndex, + }), + logger, + retryDelay + ); + + // main index exists, try to extract the indexTypesMap from _meta + const meta = Object.values(mapping)?.[0]?.mappings._meta; + return meta?.indexTypesMap ?? defaultIndexTypesMap; + } catch (error) { + if (error.meta?.statusCode === 404) { + logger.debug(`The ${mainIndex} index do NOT exist. Assuming this is a fresh deployment`); + return undefined; + } else { + logger.fatal(`Cannot query the meta information on the ${mainIndex} saved object index`); + throw error; + } + } +} + +export async function getIndicesInvolvedInRelocation({ + client, + mainIndex, + indexTypesMap, + defaultIndexTypesMap, + logger, +}: { + client: ElasticsearchClient; + mainIndex: string; + indexTypesMap: IndexTypesMap; + defaultIndexTypesMap: IndexTypesMap; + logger: Logger; +}): Promise { + const indicesWithMovingTypesSet = new Set(); + + const currentIndexTypesMap = await getCurrentIndexTypesMap({ + client, + mainIndex, + defaultIndexTypesMap, + logger, + }); + + if (!currentIndexTypesMap) { + // this is a fresh deployment, no indices must be relocated + return []; + } + + const typeIndexDistribution = calculateTypeStatuses(currentIndexTypesMap, indexTypesMap); + + Object.values(typeIndexDistribution) + .filter(({ status }) => status === TypeStatus.Moved) + .forEach(({ currentIndex, targetIndex }) => { + indicesWithMovingTypesSet.add(currentIndex!); + indicesWithMovingTypesSet.add(targetIndex!); + }); + + return Array.from(indicesWithMovingTypesSet); +} + +export function indexMapToIndexTypesMap(indexMap: IndexMap): IndexTypesMap { + return Object.entries(indexMap).reduce((acc, [indexAlias, { typeMappings }]) => { + acc[indexAlias] = Object.keys(typeMappings).sort(); + return acc; + }, {}); +} + +export function calculateTypeStatuses( + currentIndexTypesMap: IndexTypesMap, + desiredIndexTypesMap: IndexTypesMap +): Record { + const statuses: Record = {}; + + Object.entries(currentIndexTypesMap).forEach(([currentIndex, types]) => { + types.forEach((type) => { + statuses[type] = { + currentIndex, + status: TypeStatus.Removed, // type is removed unless we still have it + }; + }); + }); + + Object.entries(desiredIndexTypesMap).forEach(([targetIndex, types]) => { + types.forEach((type) => { + if (!statuses[type]) { + statuses[type] = { + targetIndex, + status: TypeStatus.Added, // type didn't exist, it must be new + }; + } else { + statuses[type].targetIndex = targetIndex; + statuses[type].status = + statuses[type].currentIndex === targetIndex ? TypeStatus.Untouched : TypeStatus.Moved; + } + }); + }); + + return statuses; +} diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts index d03b4f7378da4..15a5ef1ce14d7 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.test.ts @@ -6,13 +6,11 @@ * Side Public License, v 1. */ -import { cleanupMock } from './migrations_state_machine_cleanup.mocks'; import { migrationStateActionMachine } from './migrations_state_action_machine'; import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { LoggerAdapter } from '@kbn/core-logging-server-internal'; import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; -import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import * as Either from 'fp-ts/lib/Either'; import * as Option from 'fp-ts/lib/Option'; import { errors } from '@elastic/elasticsearch'; @@ -21,8 +19,6 @@ import type { AllControlStates, State } from './state'; import { createInitialState } from './initial_state'; import { ByteSizeValue } from '@kbn/config-schema'; -const esClient = elasticsearchServiceMock.createElasticsearchClient(); - describe('migrationsStateActionMachine', () => { beforeAll(() => { jest @@ -33,6 +29,7 @@ describe('migrationsStateActionMachine', () => { jest.clearAllMocks(); }); + const abort = jest.fn(); const mockLogger = loggingSystemMock.create(); const typeRegistry = typeRegistryMock.create(); const docLinks = docLinksServiceMock.createSetupContract(); @@ -40,6 +37,12 @@ describe('migrationsStateActionMachine', () => { const initialState = createInitialState({ kibanaVersion: '7.11.0', waitForMigrationCompletion: false, + mustRelocateDocuments: true, + indexTypesMap: { + '.kibana': ['typeA', 'typeB', 'typeC'], + '.kibana_task_manager': ['task'], + '.kibana_cases': ['typeD', 'typeE'], + }, targetMappings: { properties: {} }, migrationVersionPerType: {}, indexPrefix: '.my-so-index', @@ -92,7 +95,7 @@ describe('migrationsStateActionMachine', () => { logger: mockLogger.get(), model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'LEGACY_DELETE', 'DONE']), next, - client: esClient, + abort, }); const logs = loggingSystemMock.collect(mockLogger); const doneLog = logs.info.splice(8, 1)[0][0]; @@ -113,7 +116,7 @@ describe('migrationsStateActionMachine', () => { logger: mockLogger.get(), model: transitionModel(['LEGACY_DELETE', 'FATAL']), next, - client: esClient, + abort, }).catch((err) => err); expect(loggingSystemMock.collect(mockLogger)).toMatchSnapshot(); }); @@ -129,7 +132,7 @@ describe('migrationsStateActionMachine', () => { logger, model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'LEGACY_DELETE', 'DONE']), next, - client: esClient, + abort, }) ).resolves.toEqual(expect.anything()); @@ -155,7 +158,7 @@ describe('migrationsStateActionMachine', () => { logger: mockLogger.get(), model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'LEGACY_DELETE', 'DONE']), next, - client: esClient, + abort, }) ).resolves.toEqual(expect.anything()); }); @@ -167,7 +170,7 @@ describe('migrationsStateActionMachine', () => { logger: mockLogger.get(), model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'LEGACY_DELETE', 'DONE']), next, - client: esClient, + abort, }) ).resolves.toEqual(expect.objectContaining({ status: 'migrated' })); }); @@ -179,7 +182,7 @@ describe('migrationsStateActionMachine', () => { logger: mockLogger.get(), model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'LEGACY_DELETE', 'DONE']), next, - client: esClient, + abort, }) ).resolves.toEqual(expect.objectContaining({ status: 'patched' })); }); @@ -191,7 +194,7 @@ describe('migrationsStateActionMachine', () => { logger: mockLogger.get(), model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'FATAL']), next, - client: esClient, + abort, }) ).rejects.toMatchInlineSnapshot( `[Error: Unable to complete saved object migrations for the [.my-so-index] index: the fatal reason]` @@ -219,7 +222,7 @@ describe('migrationsStateActionMachine', () => { }) ); }, - client: esClient, + abort, }) ).rejects.toMatchInlineSnapshot( `[Error: Unable to complete saved object migrations for the [.my-so-index] index. Please check the health of your Elasticsearch cluster and try again. Unexpected Elasticsearch ResponseError: statusCode: 200, method: POST, url: /mock error: [snapshot_in_progress_exception]: Cannot delete indices that are being snapshotted,]` @@ -249,7 +252,7 @@ describe('migrationsStateActionMachine', () => { next: () => { throw new Error('this action throws'); }, - client: esClient, + abort, }) ).rejects.toMatchInlineSnapshot( `[Error: Unable to complete saved object migrations for the [.my-so-index] index. Error: this action throws]` @@ -271,10 +274,7 @@ describe('migrationsStateActionMachine', () => { `); }); describe('cleanup', () => { - beforeEach(() => { - cleanupMock.mockClear(); - }); - it('calls cleanup function when an action throws', async () => { + it('calls abort function when an action throws', async () => { await expect( migrationStateActionMachine({ initialState: { ...initialState, reason: 'the fatal reason' } as State, @@ -283,24 +283,24 @@ describe('migrationsStateActionMachine', () => { next: () => { throw new Error('this action throws'); }, - client: esClient, + abort, }) ).rejects.toThrow(); - expect(cleanupMock).toHaveBeenCalledTimes(1); + expect(abort).toHaveBeenCalledTimes(1); }); - it('calls cleanup function when reaching the FATAL state', async () => { + it('calls abort function when reaching the FATAL state', async () => { await expect( migrationStateActionMachine({ initialState: { ...initialState, reason: 'the fatal reason' } as State, logger: mockLogger.get(), model: transitionModel(['LEGACY_REINDEX', 'LEGACY_DELETE', 'FATAL']), next, - client: esClient, + abort, }) ).rejects.toThrow(); - expect(cleanupMock).toHaveBeenCalledTimes(1); + expect(abort).toHaveBeenCalledTimes(1); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts index 92b0054bd47c3..f96c52df52ab7 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/migrations_state_action_machine.ts @@ -9,15 +9,14 @@ import { errors as EsErrors } from '@elastic/elasticsearch'; import * as Option from 'fp-ts/lib/Option'; import type { Logger } from '@kbn/logging'; -import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { getErrorMessage, getRequestDebugMeta, } from '@kbn/core-elasticsearch-client-server-internal'; import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; +import type { MigrationResult } from '@kbn/core-saved-objects-base-server-internal'; import { logActionResponse, logStateTransition } from './common/utils/logs'; import { type Model, type Next, stateActionMachine } from './state_action_machine'; -import { cleanup } from './migrations_state_machine_cleanup'; import type { ReindexSourceToTempTransform, ReindexSourceToTempIndexBulk, State } from './state'; import { redactBulkOperationBatches } from './common/redact_state'; @@ -35,14 +34,14 @@ export async function migrationStateActionMachine({ logger, next, model, - client, + abort, }: { initialState: State; logger: Logger; next: Next; model: Model; - client: ElasticsearchClient; -}) { + abort: (state?: State) => Promise; +}): Promise { const startTime = Date.now(); // Since saved object index names usually start with a `.` and can be // configured by users to include several `.`'s we can't use a logger tag to @@ -112,22 +111,28 @@ export async function migrationStateActionMachine({ } } else if (finalState.controlState === 'FATAL') { try { - await cleanup(client, finalState); + await abort(finalState); } catch (e) { logger.warn('Failed to cleanup after migrations:', e.message); } - return Promise.reject( - new Error( - `Unable to complete saved object migrations for the [${initialState.indexPrefix}] index: ` + - finalState.reason - ) - ); + + const errorMessage = + `Unable to complete saved object migrations for the [${initialState.indexPrefix}] index: ` + + finalState.reason; + + if (finalState.throwDelayMillis) { + return new Promise((_, reject) => + setTimeout(() => reject(errorMessage), finalState.throwDelayMillis) + ); + } + + return Promise.reject(new Error(errorMessage)); } else { throw new Error('Invalid terminating control state'); } } catch (e) { try { - await cleanup(client, lastState); + await abort(lastState); } catch (err) { logger.warn('Failed to cleanup after migrations:', err.message); } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts index 3ae3b1c7f20d8..951ee86d92f0d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.test.ts @@ -7,7 +7,7 @@ */ import * as Either from 'fp-ts/lib/Either'; import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; -import { createBatches } from './create_batches'; +import { buildTempIndexMap, createBatches } from './create_batches'; describe('createBatches', () => { const documentToOperation = (document: SavedObjectsRawDoc) => [ @@ -17,56 +17,145 @@ describe('createBatches', () => { const DOCUMENT_SIZE_BYTES = 77; // 76 + \n - it('returns right one batch if all documents fit in maxBatchSizeBytes', () => { - const documents = [ - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ²' } }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ®' } }, - ]; + describe('when indexTypesMap and kibanaVersion are not provided', () => { + it('returns right one batch if all documents fit in maxBatchSizeBytes', () => { + const documents = [ + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ²' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ®' } }, + ]; - expect(createBatches({ documents, maxBatchSizeBytes: DOCUMENT_SIZE_BYTES * 3 })).toEqual( - Either.right([documents.map(documentToOperation)]) - ); - }); - it('creates multiple batches with each batch limited to maxBatchSizeBytes', () => { - const documents = [ - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ²' } }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title ®' } }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title 44' } }, - { _id: '', _source: { type: 'dashboard', title: 'my saved object title 55' } }, - ]; - expect(createBatches({ documents, maxBatchSizeBytes: DOCUMENT_SIZE_BYTES * 2 })).toEqual( - Either.right([ - documents.slice(0, 2).map(documentToOperation), - documents.slice(2, 4).map(documentToOperation), - documents.slice(4).map(documentToOperation), - ]) - ); - }); - it('creates a single empty batch if there are no documents', () => { - const documents = [] as SavedObjectsRawDoc[]; - expect(createBatches({ documents, maxBatchSizeBytes: 100 })).toEqual(Either.right([[]])); - }); - it('throws if any one document exceeds the maxBatchSizeBytes', () => { - const documents = [ - { _id: 'foo', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, - { - _id: 'bar', - _source: { - type: 'dashboard', - title: 'my saved object title ² with a very long title that exceeds max size bytes', + expect( + createBatches({ + documents, + maxBatchSizeBytes: DOCUMENT_SIZE_BYTES * 3, + }) + ).toEqual(Either.right([documents.map(documentToOperation)])); + }); + it('creates multiple batches with each batch limited to maxBatchSizeBytes', () => { + const documents = [ + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ²' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ®' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title 44' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title 55' } }, + ]; + expect( + createBatches({ + documents, + maxBatchSizeBytes: DOCUMENT_SIZE_BYTES * 2, + }) + ).toEqual( + Either.right([ + documents.slice(0, 2).map(documentToOperation), + documents.slice(2, 4).map(documentToOperation), + documents.slice(4).map(documentToOperation), + ]) + ); + }); + it('creates a single empty batch if there are no documents', () => { + const documents = [] as SavedObjectsRawDoc[]; + expect(createBatches({ documents, maxBatchSizeBytes: 100 })).toEqual(Either.right([[]])); + }); + it('throws if any one document exceeds the maxBatchSizeBytes', () => { + const documents = [ + { _id: 'foo', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, + { + _id: 'bar', + _source: { + type: 'dashboard', + title: 'my saved object title ² with a very long title that exceeds max size bytes', + }, }, - }, - { _id: 'baz', _source: { type: 'dashboard', title: 'my saved object title ®' } }, - ]; - expect(createBatches({ documents, maxBatchSizeBytes: 120 })).toEqual( - Either.left({ - maxBatchSizeBytes: 120, - docSizeBytes: 130, - type: 'document_exceeds_batch_size_bytes', - documentId: documents[1]._id, - }) - ); + { _id: 'baz', _source: { type: 'dashboard', title: 'my saved object title ®' } }, + ]; + expect(createBatches({ documents, maxBatchSizeBytes: 120 })).toEqual( + Either.left({ + maxBatchSizeBytes: 120, + docSizeBytes: 130, + type: 'document_exceeds_batch_size_bytes', + documentId: documents[1]._id, + }) + ); + }); + }); + + describe('when a type index map is provided', () => { + it('creates batches that contain the target index information for each type', () => { + const documents = [ + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ¹' } }, + { _id: '', _source: { type: 'dashboard', title: 'my saved object title ²' } }, + { _id: '', _source: { type: 'cases', title: 'a case' } }, + { _id: '', _source: { type: 'cases-comments', title: 'a case comment #1' } }, + { _id: '', _source: { type: 'cases-user-actions', title: 'a case user action' } }, + ]; + expect( + createBatches({ + documents, + maxBatchSizeBytes: (DOCUMENT_SIZE_BYTES + 43) * 2, // add extra length for 'index' property + typeIndexMap: buildTempIndexMap( + { + '.kibana': ['dashboard'], + '.kibana_cases': ['cases', 'cases-comments', 'cases-user-actions'], + }, + '8.8.0' + ), + }) + ).toEqual( + Either.right([ + [ + [ + { + index: { + _id: '', + _index: '.kibana_8.8.0_reindex_temp', + }, + }, + { type: 'dashboard', title: 'my saved object title ¹' }, + ], + [ + { + index: { + _id: '', + _index: '.kibana_8.8.0_reindex_temp', + }, + }, + { type: 'dashboard', title: 'my saved object title ²' }, + ], + ], + [ + [ + { + index: { + _id: '', + _index: '.kibana_cases_8.8.0_reindex_temp', + }, + }, + { type: 'cases', title: 'a case' }, + ], + [ + { + index: { + _id: '', + _index: '.kibana_cases_8.8.0_reindex_temp', + }, + }, + { type: 'cases-comments', title: 'a case comment #1' }, + ], + ], + [ + [ + { + index: { + _id: '', + _index: '.kibana_cases_8.8.0_reindex_temp', + }, + }, + { type: 'cases-user-actions', title: 'a case user action' }, + ], + ], + ]) + ); + }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts index 008d074b2cd6f..dc5b09d6c3379 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/create_batches.ts @@ -9,7 +9,12 @@ import * as Either from 'fp-ts/lib/Either'; import type { SavedObjectsRawDoc, SavedObjectsRawDocSource } from '@kbn/core-saved-objects-server'; import type { BulkOperationContainer } from '@elastic/elasticsearch/lib/api/types'; -import { createBulkDeleteOperationBody, createBulkIndexOperationTuple } from './helpers'; +import type { IndexTypesMap } from '@kbn/core-saved-objects-base-server-internal'; +import { + createBulkDeleteOperationBody, + createBulkIndexOperationTuple, + getTempIndexName, +} from './helpers'; import type { TransformErrorObjects } from '../core'; export type BulkIndexOperationTuple = [BulkOperationContainer, SavedObjectsRawDocSource]; @@ -21,6 +26,12 @@ export interface CreateBatchesParams { corruptDocumentIds?: string[]; transformErrors?: TransformErrorObjects[]; maxBatchSizeBytes: number; + /** This map holds a list of temporary index names for each SO type, e.g.: + * 'cases': '.kibana_cases_8.8.0_reindex_temp' + * 'task': '.kibana_task_manager_8.8.0_reindex_temp' + * ... + */ + typeIndexMap?: Record; } export interface DocumentExceedsBatchSize { @@ -30,6 +41,32 @@ export interface DocumentExceedsBatchSize { maxBatchSizeBytes: number; } +/** + * Build a relationship of temporary index names for each SO type, e.g.: + * 'cases': '.kibana_cases_8.8.0_reindex_temp' + * 'task': '.kibana_task_manager_8.8.0_reindex_temp' + * ... + * + * @param indexTypesMap information about which types are stored in each index + * @param kibanaVersion the target version of the indices + */ +export function buildTempIndexMap( + indexTypesMap: IndexTypesMap, + kibanaVersion: string +): Record { + return Object.entries(indexTypesMap || {}).reduce>( + (acc, [indexAlias, types]) => { + const tempIndex = getTempIndexName(indexAlias, kibanaVersion!); + + types.forEach((type) => { + acc[type] = tempIndex; + }); + return acc; + }, + {} + ); +} + /** * Creates batches of documents to be used by the bulk API. Each batch will * have a request body content length that's <= maxBatchSizeBytes @@ -39,6 +76,7 @@ export function createBatches({ corruptDocumentIds = [], transformErrors = [], maxBatchSizeBytes, + typeIndexMap, }: CreateBatchesParams): Either.Either { /* To build up the NDJSON request body we construct an array of objects like: * [ @@ -92,7 +130,7 @@ export function createBatches({ // create index (update) operations for all transformed documents for (const document of documents) { - const bulkIndexOperationBody = createBulkIndexOperationTuple(document); + const bulkIndexOperationBody = createBulkIndexOperationTuple(document, typeIndexMap); // take into account that this tuple's surrounding brackets `[]` won't be present in the NDJSON const docSizeBytes = Buffer.byteLength(JSON.stringify(bulkIndexOperationBody), 'utf8') - BRACKETS_BYTES; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts index 291f865f8c100..a20b517b7f3ba 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts @@ -16,6 +16,8 @@ import { buildRemoveAliasActions, versionMigrationCompleted, MigrationType, + getTempIndexName, + createBulkIndexOperationTuple, } from './helpers'; describe('addExcludedTypesToBoolQuery', () => { @@ -290,6 +292,46 @@ describe('buildRemoveAliasActions', () => { }); }); +describe('createBulkIndexOperationTuple', () => { + it('creates the proper request body to bulk index a document', () => { + const document = { _id: '', _source: { type: 'cases', title: 'a case' } }; + const typeIndexMap = { + cases: '.kibana_cases_8.8.0_reindex_temp', + }; + expect(createBulkIndexOperationTuple(document, typeIndexMap)).toMatchInlineSnapshot(` + Array [ + Object { + "index": Object { + "_id": "", + "_index": ".kibana_cases_8.8.0_reindex_temp", + }, + }, + Object { + "title": "a case", + "type": "cases", + }, + ] + `); + }); + + it('does not include the index property if it is not specified in the typeIndexMap', () => { + const document = { _id: '', _source: { type: 'cases', title: 'a case' } }; + expect(createBulkIndexOperationTuple(document)).toMatchInlineSnapshot(` + Array [ + Object { + "index": Object { + "_id": "", + }, + }, + Object { + "title": "a case", + "type": "cases", + }, + ] + `); + }); +}); + describe('getMigrationType', () => { it.each` isMappingsCompatible | isVersionMigrationCompleted | expected @@ -306,3 +348,9 @@ describe('getMigrationType', () => { } ); }); + +describe('getTempIndexName', () => { + it('composes a temporary index name for reindexing', () => { + expect(getTempIndexName('.kibana_cases', '8.8.0')).toEqual('.kibana_cases_8.8.0_reindex_temp'); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts index 9ffdf8508c8f5..c8d8884c980cd 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts @@ -65,6 +65,7 @@ export function mergeMigrationMappingPropertyHashes( return { ...targetMappings, _meta: { + ...targetMappings._meta, migrationMappingPropertyHashes: { ...indexMappings._meta?.migrationMappingPropertyHashes, ...targetMappings._meta?.migrationMappingPropertyHashes, @@ -218,11 +219,15 @@ export function buildRemoveAliasActions( /** * Given a document, creates a valid body to index the document using the Bulk API. */ -export const createBulkIndexOperationTuple = (doc: SavedObjectsRawDoc): BulkIndexOperationTuple => { +export const createBulkIndexOperationTuple = ( + doc: SavedObjectsRawDoc, + typeIndexMap: Record = {} +): BulkIndexOperationTuple => { return [ { index: { _id: doc._id, + ...(typeIndexMap[doc._source.type] && { _index: typeIndexMap[doc._source.type] }), // use optimistic concurrency control to ensure that outdated // documents are only overwritten once with the latest version ...(typeof doc._seq_no !== 'undefined' && { if_seq_no: doc._seq_no }), @@ -271,3 +276,12 @@ export function getMigrationType({ return MigrationType.Invalid; } + +/** + * Generate a temporary index name, to reindex documents into it + * @param index The name of the SO index + * @param kibanaVersion The current kibana version + * @returns A temporary index name to reindex documents + */ +export const getTempIndexName = (indexPrefix: string, kibanaVersion: string): string => + `${indexPrefix}_${kibanaVersion}_reindex_temp`; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts index deba537b3f81e..969a7704e4e75 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts @@ -20,7 +20,7 @@ import type { CheckVersionIndexReadyActions, CleanupUnknownAndExcluded, CleanupUnknownAndExcludedWaitForTaskState, - CloneTempToSource, + CloneTempToTarget, CreateNewTargetState, CreateReindexTempState, FatalState, @@ -51,6 +51,8 @@ import type { UpdateTargetMappingsPropertiesState, UpdateTargetMappingsPropertiesWaitForTaskState, WaitForYellowSourceState, + ReadyToReindexSyncState, + DoneReindexingSyncState, } from '../state'; import { type TransformErrorObjects, TransformSavedObjectDocumentError } from '../core'; import type { AliasAction, RetryableEsClientError } from '../actions'; @@ -58,6 +60,7 @@ import type { ResponseType } from '../next'; import { createInitialProgress } from './progress'; import { model } from './model'; import type { BulkIndexOperationTuple, BulkOperation } from './create_batches'; +import { DEFAULT_INDEX_TYPES_MAP } from '../kibana_migrator_constants'; describe('migrations v2 model', () => { const indexMapping: IndexMapping = { @@ -115,6 +118,8 @@ describe('migrations v2 model', () => { clusterShardLimitExceeded: 'clusterShardLimitExceeded', }, waitForMigrationCompletion: false, + mustRelocateDocuments: false, + indexTypesMap: DEFAULT_INDEX_TYPES_MAP, }; const postInitState = { ...baseState, @@ -732,7 +737,7 @@ describe('migrations v2 model', () => { expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); }); - test('INIT -> CREATE_NEW_TARGET when no indices/aliases exist', () => { + test('INIT -> CREATE_NEW_TARGET when the index does not exist and the migrator is NOT involved in a relocation', () => { const res: ResponseType<'INIT'> = Either.right({}); const newState = model(initState, res); @@ -744,6 +749,29 @@ describe('migrations v2 model', () => { expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); }); + test('INIT -> CREATE_REINDEX_TEMP when the index does not exist and the migrator is involved in a relocation', () => { + const res: ResponseType<'INIT'> = Either.right({}); + const newState = model( + { + ...initState, + mustRelocateDocuments: true, + }, + res + ); + + expect(newState).toMatchObject({ + controlState: 'CREATE_REINDEX_TEMP', + sourceIndex: Option.none, + targetIndex: '.kibana_7.11.0_001', + versionIndexReadyActions: Option.some([ + { add: { index: '.kibana_7.11.0_001', alias: '.kibana' } }, + { add: { index: '.kibana_7.11.0_001', alias: '.kibana_7.11.0' } }, + { remove_index: { index: '.kibana_7.11.0_reindex_temp' } }, + ]), + }); + expect(newState.retryCount).toEqual(0); + expect(newState.retryDelay).toEqual(0); + }); }); }); @@ -1146,12 +1174,29 @@ describe('migrations v2 model', () => { expect(newState.retryDelay).toEqual(0); }); - test('WAIT_FOR_YELLOW_SOURCE -> UPDATE_SOURCE_MAPPINGS_PROPERTIES', () => { - const res: ResponseType<'WAIT_FOR_YELLOW_SOURCE'> = Either.right({}); - const newState = model(waitForYellowSourceState, res); + describe('if the migrator is NOT involved in a relocation', () => { + test('WAIT_FOR_YELLOW_SOURCE -> UPDATE_SOURCE_MAPPINGS_PROPERTIES', () => { + const res: ResponseType<'WAIT_FOR_YELLOW_SOURCE'> = Either.right({}); + const newState = model(waitForYellowSourceState, res); - expect(newState).toMatchObject({ - controlState: 'UPDATE_SOURCE_MAPPINGS_PROPERTIES', + expect(newState).toMatchObject({ + controlState: 'UPDATE_SOURCE_MAPPINGS_PROPERTIES', + }); + }); + }); + + describe('if the migrator is involved in a relocation', () => { + // no need to attempt to update the mappings, we are going to reindex + test('WAIT_FOR_YELLOW_SOURCE -> CHECK_UNKNOWN_DOCUMENTS', () => { + const res: ResponseType<'WAIT_FOR_YELLOW_SOURCE'> = Either.right({}); + const newState = model( + { ...waitForYellowSourceState, mustRelocateDocuments: true }, + res + ); + + expect(newState).toMatchObject({ + controlState: 'CHECK_UNKNOWN_DOCUMENTS', + }); }); }); }); @@ -1630,13 +1675,27 @@ describe('migrations v2 model', () => { sourceIndexMappings: Option.some({}) as Option.Some, tempIndexMappings: { properties: {} }, }; - it('CREATE_REINDEX_TEMP -> REINDEX_SOURCE_TO_TEMP_OPEN_PIT if action succeeds', () => { - const res: ResponseType<'CREATE_REINDEX_TEMP'> = Either.right('create_index_succeeded'); - const newState = model(state, res); - expect(newState.controlState).toEqual('REINDEX_SOURCE_TO_TEMP_OPEN_PIT'); - expect(newState.retryCount).toEqual(0); - expect(newState.retryDelay).toEqual(0); + + describe('if the migrator is NOT involved in a relocation', () => { + it('CREATE_REINDEX_TEMP -> REINDEX_SOURCE_TO_TEMP_OPEN_PIT if action succeeds', () => { + const res: ResponseType<'CREATE_REINDEX_TEMP'> = Either.right('create_index_succeeded'); + const newState = model(state, res); + expect(newState.controlState).toEqual('REINDEX_SOURCE_TO_TEMP_OPEN_PIT'); + expect(newState.retryCount).toEqual(0); + expect(newState.retryDelay).toEqual(0); + }); + }); + + describe('if the migrator is involved in a relocation', () => { + it('CREATE_REINDEX_TEMP -> READY_TO_REINDEX_SYNC if action succeeds', () => { + const res: ResponseType<'CREATE_REINDEX_TEMP'> = Either.right('create_index_succeeded'); + const newState = model({ ...state, mustRelocateDocuments: true }, res); + expect(newState.controlState).toEqual('READY_TO_REINDEX_SYNC'); + expect(newState.retryCount).toEqual(0); + expect(newState.retryDelay).toEqual(0); + }); }); + it('CREATE_REINDEX_TEMP -> CREATE_REINDEX_TEMP if action fails with index_not_green_timeout', () => { const res: ResponseType<'CREATE_REINDEX_TEMP'> = Either.left({ message: '[index_not_green_timeout] Timeout waiting for ...', @@ -1677,6 +1736,52 @@ describe('migrations v2 model', () => { }); }); + describe('READY_TO_REINDEX_SYNC', () => { + const state: ReadyToReindexSyncState = { + ...postInitState, + controlState: 'READY_TO_REINDEX_SYNC', + }; + + describe('if the migrator source index did NOT exist', () => { + test('READY_TO_REINDEX_SYNC -> DONE_REINDEXING_SYNC', () => { + const res: ResponseType<'READY_TO_REINDEX_SYNC'> = Either.right( + 'synchronized_successfully' as const + ); + const newState = model(state, res); + expect(newState.controlState).toEqual('DONE_REINDEXING_SYNC'); + }); + }); + + describe('if the migrator source index did exist', () => { + test('READY_TO_REINDEX_SYNC -> REINDEX_SOURCE_TO_TEMP_OPEN_PIT', () => { + const res: ResponseType<'READY_TO_REINDEX_SYNC'> = Either.right( + 'synchronized_successfully' as const + ); + const newState = model( + { + ...state, + sourceIndex: Option.fromNullable('.kibana'), + sourceIndexMappings: Option.fromNullable({} as IndexMapping), + }, + res + ); + expect(newState.controlState).toEqual('REINDEX_SOURCE_TO_TEMP_OPEN_PIT'); + }); + }); + + test('READY_TO_REINDEX_SYNC -> FATAL if the synchronization between migrators fails', () => { + const res: ResponseType<'READY_TO_REINDEX_SYNC'> = Either.left({ + type: 'sync_failed', + error: new Error('Other migrators failed to reach the synchronization point'), + }); + const newState = model(state, res); + expect(newState.controlState).toEqual('FATAL'); + expect((newState as FatalState).reason).toMatchInlineSnapshot( + `"An error occurred whilst waiting for other migrators to get to this step."` + ); + }); + }); + describe('REINDEX_SOURCE_TO_TEMP_OPEN_PIT', () => { const state: ReindexSourceToTempOpenPit = { ...postInitState, @@ -1812,11 +1917,50 @@ describe('migrations v2 model', () => { tempIndexMappings: { properties: {} }, }; - it('REINDEX_SOURCE_TO_TEMP_CLOSE_PIT -> SET_TEMP_WRITE_BLOCK if action succeeded', () => { - const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_CLOSE_PIT'> = Either.right({}); - const newState = model(state, res) as ReindexSourceToTempTransform; - expect(newState.controlState).toBe('SET_TEMP_WRITE_BLOCK'); - expect(newState.sourceIndex).toEqual(state.sourceIndex); + describe('if the migrator is NOT involved in a relocation', () => { + it('REINDEX_SOURCE_TO_TEMP_CLOSE_PIT -> SET_TEMP_WRITE_BLOCK if action succeeded', () => { + const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_CLOSE_PIT'> = Either.right({}); + const newState = model(state, res) as ReindexSourceToTempTransform; + expect(newState.controlState).toBe('SET_TEMP_WRITE_BLOCK'); + expect(newState.sourceIndex).toEqual(state.sourceIndex); + }); + }); + + describe('if the migrator is involved in a relocation', () => { + it('REINDEX_SOURCE_TO_TEMP_CLOSE_PIT -> DONE_REINDEXING_SYNC if action succeeded', () => { + const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_CLOSE_PIT'> = Either.right({}); + const newState = model( + { ...state, mustRelocateDocuments: true }, + res + ) as ReindexSourceToTempTransform; + expect(newState.controlState).toBe('DONE_REINDEXING_SYNC'); + }); + }); + }); + + describe('DONE_REINDEXING_SYNC', () => { + const state: DoneReindexingSyncState = { + ...postInitState, + controlState: 'DONE_REINDEXING_SYNC', + }; + + test('DONE_REINDEXING_SYNC -> SET_TEMP_WRITE_BLOCK if synchronization succeeds', () => { + const res: ResponseType<'DONE_REINDEXING_SYNC'> = Either.right( + 'synchronized_successfully' as const + ); + const newState = model(state, res); + expect(newState.controlState).toEqual('SET_TEMP_WRITE_BLOCK'); + }); + test('DONE_REINDEXING_SYNC -> FATAL if the synchronization between migrators fails', () => { + const res: ResponseType<'DONE_REINDEXING_SYNC'> = Either.left({ + type: 'sync_failed', + error: new Error('Other migrators failed to reach the synchronization point'), + }); + const newState = model(state, res); + expect(newState.controlState).toEqual('FATAL'); + expect((newState as FatalState).reason).toMatchInlineSnapshot( + `"An error occurred whilst waiting for other migrators to get to this step."` + ); }); }); @@ -1977,7 +2121,7 @@ describe('migrations v2 model', () => { }); describe('CLONE_TEMP_TO_TARGET', () => { - const state: CloneTempToSource = { + const state: CloneTempToTarget = { ...postInitState, controlState: 'CLONE_TEMP_TO_TARGET', sourceIndex: Option.some('.kibana') as Option.Some, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts index 23ddee5043261..54657310912f8 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts @@ -44,7 +44,7 @@ import { buildRemoveAliasActions, MigrationType, } from './helpers'; -import { createBatches } from './create_batches'; +import { buildTempIndexMap, createBatches } from './create_batches'; import type { MigrationLog } from '../types'; import { CLUSTER_SHARD_LIMIT_EXCEEDED_REASON, @@ -121,6 +121,8 @@ export const model = (currentState: State, resW: ResponseType): // The source index .kibana is pointing to. E.g: ".kibana_8.7.0_001" const source = aliases[stateP.currentAlias]; + // The target index .kibana WILL be pointing to if we reindex. E.g: ".kibana_8.8.0_001" + const newVersionTarget = stateP.versionIndex; const postInitState = { aliases, @@ -137,7 +139,7 @@ export const model = (currentState: State, resW: ResponseType): ...stateP, ...postInitState, sourceIndex: Option.none, - targetIndex: `${stateP.indexPrefix}_${stateP.kibanaVersion}_001`, + targetIndex: newVersionTarget, controlState: 'WAIT_FOR_MIGRATION_COMPLETION', // Wait for 2s before checking again if the migration has completed retryDelay: 2000, @@ -153,7 +155,6 @@ export const model = (currentState: State, resW: ResponseType): // If the `.kibana` alias exists Option.isSome(postInitState.sourceIndex) ) { - // CHECKPOINT here we decide to go for yellow source return { ...stateP, ...postInitState, @@ -182,7 +183,6 @@ export const model = (currentState: State, resW: ResponseType): const legacyReindexTarget = `${stateP.indexPrefix}_${legacyVersion}_001`; - const target = stateP.versionIndex; return { ...stateP, ...postInitState, @@ -191,7 +191,7 @@ export const model = (currentState: State, resW: ResponseType): sourceIndexMappings: Option.some( indices[stateP.legacyIndex].mappings ) as Option.Some, - targetIndex: target, + targetIndex: newVersionTarget, legacyPreMigrationDoneActions: [ { remove_index: { index: stateP.legacyIndex } }, { @@ -209,24 +209,40 @@ export const model = (currentState: State, resW: ResponseType): must_exist: true, }, }, - { add: { index: target, alias: stateP.currentAlias } }, - { add: { index: target, alias: stateP.versionAlias } }, + { add: { index: newVersionTarget, alias: stateP.currentAlias } }, + { add: { index: newVersionTarget, alias: stateP.versionAlias } }, + { remove_index: { index: stateP.tempIndex } }, + ]), + }; + } else if ( + // if we must relocate documents to this migrator's index, but the index does NOT yet exist: + // this migrator must create a temporary index and synchronize with other migrators + // this is a similar flow to the reindex one, but this migrator will not reindexing anything + stateP.mustRelocateDocuments + ) { + return { + ...stateP, + ...postInitState, + controlState: 'CREATE_REINDEX_TEMP', + sourceIndex: Option.none as Option.None, + targetIndex: newVersionTarget, + versionIndexReadyActions: Option.some([ + { add: { index: newVersionTarget, alias: stateP.currentAlias } }, + { add: { index: newVersionTarget, alias: stateP.versionAlias } }, { remove_index: { index: stateP.tempIndex } }, ]), }; } else { - // This cluster doesn't have an existing Saved Object index, create a - // new version specific index. - const target = stateP.versionIndex; + // no need to copy anything over from other indices, we can start with a clean, empty index return { ...stateP, ...postInitState, controlState: 'CREATE_NEW_TARGET', sourceIndex: Option.none as Option.None, - targetIndex: target, + targetIndex: newVersionTarget, versionIndexReadyActions: Option.some([ - { add: { index: target, alias: stateP.currentAlias } }, - { add: { index: target, alias: stateP.versionAlias } }, + { add: { index: newVersionTarget, alias: stateP.currentAlias } }, + { add: { index: newVersionTarget, alias: stateP.versionAlias } }, ]) as Option.Some, }; } @@ -240,6 +256,7 @@ export const model = (currentState: State, resW: ResponseType): if ( // If this version's migration has already been completed we can proceed Either.isRight(aliasesRes) && + // TODO check that this behaves correctly when skipping reindexing versionMigrationCompleted(stateP.currentAlias, stateP.versionAlias, aliasesRes.right) ) { return { @@ -414,10 +431,21 @@ export const model = (currentState: State, resW: ResponseType): } else if (stateP.controlState === 'WAIT_FOR_YELLOW_SOURCE') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { - return { - ...stateP, - controlState: 'UPDATE_SOURCE_MAPPINGS_PROPERTIES', - }; + if (stateP.mustRelocateDocuments) { + // this migrator's index must dispatch documents to other indices, + // and/or it must receive documents from other indices + // we must reindex and synchronize with other migrators + return { + ...stateP, + controlState: 'CHECK_UNKNOWN_DOCUMENTS', + }; + } else { + // this migrator is not involved in a relocation, we can proceed with the standard flow + return { + ...stateP, + controlState: 'UPDATE_SOURCE_MAPPINGS_PROPERTIES', + }; + } } else if (Either.isLeft(res)) { const left = res.left; if (isTypeof(left, 'index_not_yellow_timeout')) { @@ -711,7 +739,18 @@ export const model = (currentState: State, resW: ResponseType): } else if (stateP.controlState === 'CREATE_REINDEX_TEMP') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { - return { ...stateP, controlState: 'REINDEX_SOURCE_TO_TEMP_OPEN_PIT' }; + if (stateP.mustRelocateDocuments) { + // we are reindexing, and this migrator's index is involved in document relocations + return { ...stateP, controlState: 'READY_TO_REINDEX_SYNC' }; + } else { + // we are reindexing but this migrator's index is not involved in any document relocation + return { + ...stateP, + controlState: 'REINDEX_SOURCE_TO_TEMP_OPEN_PIT', + sourceIndex: stateP.sourceIndex as Option.Some, + sourceIndexMappings: stateP.sourceIndexMappings as Option.Some, + }; + } } else if (Either.isLeft(res)) { const left = res.left; if (isTypeof(left, 'index_not_green_timeout')) { @@ -738,6 +777,32 @@ export const model = (currentState: State, resW: ResponseType): // left responses to handle here. throwBadResponse(stateP, res); } + } else if (stateP.controlState === 'READY_TO_REINDEX_SYNC') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { + if (Option.isSome(stateP.sourceIndex) && Option.isSome(stateP.sourceIndexMappings)) { + // this migrator's source index exist, reindex its entries + return { + ...stateP, + controlState: 'REINDEX_SOURCE_TO_TEMP_OPEN_PIT', + sourceIndex: stateP.sourceIndex as Option.Some, + sourceIndexMappings: stateP.sourceIndexMappings as Option.Some, + }; + } else { + // this migrator's source index did NOT exist + // this migrator does not need to reindex anything (others might need to) + return { ...stateP, controlState: 'DONE_REINDEXING_SYNC' }; + } + } else if (Either.isLeft(res)) { + return { + ...stateP, + controlState: 'FATAL', + reason: 'An error occurred whilst waiting for other migrators to get to this step.', + throwDelayMillis: 1000, // another migrator has failed for a reason, let it take Kibana down and log its problem + }; + } else { + return throwBadResponse(stateP, res as never); + } } else if (stateP.controlState === 'REINDEX_SOURCE_TO_TEMP_OPEN_PIT') { const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { @@ -816,14 +881,41 @@ export const model = (currentState: State, resW: ResponseType): const res = resW as ExcludeRetryableEsError>; if (Either.isRight(res)) { const { sourceIndexPitId, ...state } = stateP; + + if (stateP.mustRelocateDocuments) { + return { + ...state, + controlState: 'DONE_REINDEXING_SYNC', + }; + } else { + return { + ...stateP, + controlState: 'SET_TEMP_WRITE_BLOCK', + sourceIndex: stateP.sourceIndex as Option.Some, + sourceIndexMappings: Option.none, + }; + } + } else { + throwBadResponse(stateP, res); + } + } else if (stateP.controlState === 'DONE_REINDEXING_SYNC') { + const res = resW as ExcludeRetryableEsError>; + if (Either.isRight(res)) { return { - ...state, + ...stateP, controlState: 'SET_TEMP_WRITE_BLOCK', sourceIndex: stateP.sourceIndex as Option.Some, sourceIndexMappings: Option.none, }; + } else if (Either.isLeft(res)) { + return { + ...stateP, + controlState: 'FATAL', + reason: 'An error occurred whilst waiting for other migrators to get to this step.', + throwDelayMillis: 1000, // another migrator has failed for a reason, let it take Kibana down and log its problem + }; } else { - throwBadResponse(stateP, res); + return throwBadResponse(stateP, res as never); } } else if (stateP.controlState === 'REINDEX_SOURCE_TO_TEMP_TRANSFORM') { // We follow a similar control flow as for @@ -845,7 +937,11 @@ export const model = (currentState: State, resW: ResponseType): stateP.discardCorruptObjects ) { const documents = Either.isRight(res) ? res.right.processedDocs : res.left.processedDocs; - const batches = createBatches({ documents, maxBatchSizeBytes: stateP.maxBatchSizeBytes }); + const batches = createBatches({ + documents, + maxBatchSizeBytes: stateP.maxBatchSizeBytes, + typeIndexMap: buildTempIndexMap(stateP.indexTypesMap, stateP.kibanaVersion), + }); if (Either.isRight(batches)) { let corruptDocumentIds = stateP.corruptDocumentIds; let transformErrors = stateP.transformErrors; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.test.ts index cc4ee13673940..f7cabfb6e42db 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.test.ts @@ -7,6 +7,7 @@ */ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { defer } from './kibana_migrator_utils'; import { next } from './next'; import type { State } from './state'; @@ -14,12 +15,12 @@ describe('migrations v2 next', () => { it.todo('when state.retryDelay > 0 delays execution of the next action'); it('DONE returns null', () => { const state = { controlState: 'DONE' } as State; - const action = next({} as ElasticsearchClient, (() => {}) as any)(state); + const action = next({} as ElasticsearchClient, (() => {}) as any, defer(), defer())(state); expect(action).toEqual(null); }); it('FATAL returns null', () => { const state = { controlState: 'FATAL', reason: '' } as State; - const action = next({} as ElasticsearchClient, (() => {}) as any)(state); + const action = next({} as ElasticsearchClient, (() => {}) as any, defer(), defer())(state); expect(action).toEqual(null); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts index cc26bb543ef5e..426f73436cb71 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts @@ -7,8 +7,9 @@ */ import * as Option from 'fp-ts/lib/Option'; -import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { omit } from 'lodash'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { Defer } from './kibana_migrator_utils'; import type { AllActionStates, CalculateExcludeFiltersState, @@ -16,7 +17,7 @@ import type { CheckUnknownDocumentsState, CleanupUnknownAndExcluded, CleanupUnknownAndExcludedWaitForTaskState, - CloneTempToSource, + CloneTempToTarget, CreateNewTargetState, CreateReindexTempState, InitState, @@ -68,7 +69,12 @@ export type ResponseType = Awaited< ReturnType> >; -export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: TransformRawDocs) => { +export const nextActionMap = ( + client: ElasticsearchClient, + transformRawDocs: TransformRawDocs, + readyToReindex: Defer, + doneReindexing: Defer +) => { return { INIT: (state: InitState) => Actions.initAction({ client, indices: [state.currentAlias, state.versionAlias] }), @@ -135,6 +141,7 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra indexName: state.tempIndex, mappings: state.tempIndexMappings, }), + READY_TO_REINDEX_SYNC: () => Actions.synchronizeMigrators(readyToReindex), REINDEX_SOURCE_TO_TEMP_OPEN_PIT: (state: ReindexSourceToTempOpenPit) => Actions.openPit({ client, index: state.sourceIndex.value }), REINDEX_SOURCE_TO_TEMP_READ: (state: ReindexSourceToTempRead) => @@ -167,9 +174,10 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra */ refresh: false, }), + DONE_REINDEXING_SYNC: () => Actions.synchronizeMigrators(doneReindexing), SET_TEMP_WRITE_BLOCK: (state: SetTempWriteBlock) => Actions.setWriteBlock({ client, index: state.tempIndex }), - CLONE_TEMP_TO_TARGET: (state: CloneTempToSource) => + CLONE_TEMP_TO_TARGET: (state: CloneTempToTarget) => Actions.cloneIndex({ client, source: state.tempIndex, target: state.targetIndex }), REFRESH_TARGET: (state: RefreshTarget) => Actions.refreshIndex({ client, index: state.targetIndex }), @@ -192,12 +200,13 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra taskId: state.updateTargetMappingsTaskId, timeout: '60s', }), - UPDATE_TARGET_MAPPINGS_META: (state: UpdateTargetMappingsMeta) => - Actions.updateMappings({ + UPDATE_TARGET_MAPPINGS_META: (state: UpdateTargetMappingsMeta) => { + return Actions.updateMappings({ client, index: state.targetIndex, - mappings: omit(state.targetIndexMappings, 'properties'), - }), + mappings: omit(state.targetIndexMappings, ['properties']), // properties already updated on a previous step + }); + }, CHECK_VERSION_INDEX_READY_ACTIONS: () => Actions.noop, OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT: (state: OutdatedDocumentsSearchOpenPit) => Actions.openPit({ client, index: state.targetIndex }), @@ -257,8 +266,13 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra }; }; -export const next = (client: ElasticsearchClient, transformRawDocs: TransformRawDocs) => { - const map = nextActionMap(client, transformRawDocs); +export const next = ( + client: ElasticsearchClient, + transformRawDocs: TransformRawDocs, + readyToReindex: Defer, + doneReindexing: Defer +) => { + const map = nextActionMap(client, transformRawDocs, readyToReindex, doneReindexing); return (state: State) => { const delay = createDelayFn(state); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/run_resilient_migrator.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/run_resilient_migrator.ts index b94a94a715056..f3ae3f2c09f75 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/run_resilient_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/run_resilient_migrator.ts @@ -15,12 +15,16 @@ import type { IndexMapping, SavedObjectsMigrationConfigType, MigrationResult, + IndexTypesMap, } from '@kbn/core-saved-objects-base-server-internal'; +import type { Defer } from './kibana_migrator_utils'; import type { TransformRawDocs } from './types'; import { next } from './next'; import { model } from './model'; import { createInitialState } from './initial_state'; import { migrationStateActionMachine } from './migrations_state_action_machine'; +import { cleanup } from './migrations_state_machine_cleanup'; +import type { State } from './state'; /** * To avoid the Elasticsearch-js client aborting our requests before we @@ -45,9 +49,13 @@ export async function runResilientMigrator({ client, kibanaVersion, waitForMigrationCompletion, + mustRelocateDocuments, + indexTypesMap, targetMappings, logger, preMigrationScript, + readyToReindex, + doneReindexing, transformRawDocs, migrationVersionPerType, indexPrefix, @@ -58,8 +66,12 @@ export async function runResilientMigrator({ client: ElasticsearchClient; kibanaVersion: string; waitForMigrationCompletion: boolean; + mustRelocateDocuments: boolean; + indexTypesMap: IndexTypesMap; targetMappings: IndexMapping; preMigrationScript?: string; + readyToReindex: Defer; + doneReindexing: Defer; logger: Logger; transformRawDocs: TransformRawDocs; migrationVersionPerType: SavedObjectsMigrationVersion; @@ -71,6 +83,8 @@ export async function runResilientMigrator({ const initialState = createInitialState({ kibanaVersion, waitForMigrationCompletion, + mustRelocateDocuments, + indexTypesMap, targetMappings, preMigrationScript, migrationVersionPerType, @@ -84,8 +98,12 @@ export async function runResilientMigrator({ return migrationStateActionMachine({ initialState, logger, - next: next(migrationClient, transformRawDocs), + next: next(migrationClient, transformRawDocs, readyToReindex, doneReindexing), model, - client: migrationClient, + abort: async (state?: State) => { + // At this point, we could reject this migrator's defers and unblock other migrators + // but we are going to throw and shutdown Kibana anyway, so there's no real point in it + await cleanup(client, state); + }, }); } diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts index 5dd135d4b32fe..6a483da04a399 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts @@ -13,7 +13,7 @@ import type { SavedObjectsRawDoc, SavedObjectTypeExcludeFromUpgradeFilterHook, } from '@kbn/core-saved-objects-server'; -import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; +import type { IndexMapping, IndexTypesMap } from '@kbn/core-saved-objects-base-server-internal'; import type { ControlState } from './state_action_machine'; import type { AliasAction } from './actions'; import type { TransformErrorObjects } from './core'; @@ -152,6 +152,23 @@ export interface BaseState extends ControlState { */ readonly migrationDocLinks: DocLinks['kibanaUpgradeSavedObjects']; readonly waitForMigrationCompletion: boolean; + + /** + * This flag tells the migrator that SO documents must be redistributed, + * i.e. stored in different system indices, compared to where they are currently stored. + * This requires reindexing documents. + */ + readonly mustRelocateDocuments: boolean; + + /** + * This object holds a relation of all the types that are stored in each index, e.g.: + * { + * '.kibana': [ 'type_1', 'type_2', ... 'type_N' ], + * '.kibana_cases': [ 'type_N+1', 'type_N+2', ... 'type_N+M' ], + * ... + * } + */ + readonly indexTypesMap: IndexTypesMap; } export interface InitState extends BaseState { @@ -231,6 +248,8 @@ export interface FatalState extends BaseState { readonly controlState: 'FATAL'; /** The reason the migration was terminated */ readonly reason: string; + /** The delay in milliseconds before throwing the FATAL exception */ + readonly throwDelayMillis?: number; } export interface WaitForYellowSourceState extends SourceExistsState { @@ -263,7 +282,7 @@ export interface CreateNewTargetState extends PostInitState { readonly versionIndexReadyActions: Option.Some; } -export interface CreateReindexTempState extends SourceExistsState { +export interface CreateReindexTempState extends PostInitState { /** * Create a target index with mappings from the source index and registered * plugins @@ -271,6 +290,11 @@ export interface CreateReindexTempState extends SourceExistsState { readonly controlState: 'CREATE_REINDEX_TEMP'; } +export interface ReadyToReindexSyncState extends PostInitState { + /** Open PIT to the source index */ + readonly controlState: 'READY_TO_REINDEX_SYNC'; +} + export interface ReindexSourceToTempOpenPit extends SourceExistsState { /** Open PIT to the source index */ readonly controlState: 'REINDEX_SOURCE_TO_TEMP_OPEN_PIT'; @@ -304,11 +328,16 @@ export interface ReindexSourceToTempIndexBulk extends ReindexSourceToTempBatch { readonly currentBatch: number; } +export interface DoneReindexingSyncState extends PostInitState { + /** Open PIT to the source index */ + readonly controlState: 'DONE_REINDEXING_SYNC'; +} + export interface SetTempWriteBlock extends PostInitState { readonly controlState: 'SET_TEMP_WRITE_BLOCK'; } -export interface CloneTempToSource extends PostInitState { +export interface CloneTempToTarget extends PostInitState { /** * Clone the temporary reindex index into */ @@ -482,9 +511,10 @@ export type State = Readonly< | CheckVersionIndexReadyActions | CleanupUnknownAndExcluded | CleanupUnknownAndExcludedWaitForTaskState - | CloneTempToSource + | CloneTempToTarget | CreateNewTargetState | CreateReindexTempState + | DoneReindexingSyncState | DoneState | FatalState | InitState @@ -501,6 +531,7 @@ export type State = Readonly< | OutdatedDocumentsSearchRead | OutdatedDocumentsTransform | PrepareCompatibleMigration + | ReadyToReindexSyncState | RefreshSource | RefreshTarget | ReindexSourceToTempClosePit diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json index e56ee4e2c3234..d21138e25db84 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json @@ -26,11 +26,11 @@ "@kbn/core-doc-links-server-mocks", "@kbn/core-logging-server-internal", "@kbn/core-saved-objects-base-server-mocks", - "@kbn/core-elasticsearch-server-mocks", "@kbn/doc-links", "@kbn/safer-lodash-set", "@kbn/logging-mocks", "@kbn/core-saved-objects-base-server-mocks", + "@kbn/core-elasticsearch-server-internal", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/deprecations/unknown_object_types.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/deprecations/unknown_object_types.test.ts index 2199a7fb32b5e..defcbd591e2a4 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/deprecations/unknown_object_types.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/deprecations/unknown_object_types.test.ts @@ -12,7 +12,7 @@ import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import { deleteUnknownTypeObjects, getUnknownTypesDeprecations } from './unknown_object_types'; import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { type SavedObjectsType, MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; const createAggregateTypesSearchResponse = ( typesIds: Record = {} @@ -50,7 +50,7 @@ describe('unknown saved object types deprecation', () => { let typeRegistry: ReturnType; let esClient: ReturnType; - const kibanaIndex = '.kibana'; + const kibanaIndex = MAIN_SAVED_OBJECT_INDEX; beforeEach(() => { typeRegistry = typeRegistryMock.create(); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts index 68d1a52e63eda..c9341a0af49a4 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts @@ -76,6 +76,7 @@ export const registerBulkCreateRoute = ( if (!allowHttpApiAccess) { throwIfAnyTypeNotVisibleByAPI(typesToCheck, savedObjects.typeRegistry); } + const result = await savedObjects.client.bulkCreate(req.body, { overwrite, migrationVersionCompatibility: 'compatible', diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.test.ts index 0496db94fa1d6..d6382efd673fa 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.test.ts @@ -26,7 +26,12 @@ describe('importDashboards(req)', () => { references: [], version: 'foo', }, - { id: 'panel-01', type: 'visualization', attributes: { visState: '{}' }, references: [] }, + { + id: 'panel-01', + type: 'visualization', + attributes: { visState: '{}' }, + references: [], + }, ]; }); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.ts index 0c32f158abd67..808ed9aa9d592 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/import_dashboards.ts @@ -21,8 +21,8 @@ export async function importDashboards( // docs are not seen as automatically up-to-date. const docs = objects .filter((item) => !exclude.includes(item.type)) - // filter out any document version, if present - .map(({ version, ...doc }) => ({ + // filter out any document version and managed, if present + .map(({ version, managed, ...doc }) => ({ ...doc, ...(!doc.migrationVersion && !doc.typeMigrationVersion ? { typeMigrationVersion: '' } : {}), })); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts index fc2cc5a4c91bc..72e36826bb89e 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.test.ts @@ -23,6 +23,7 @@ import { type RawPackageInfo, Env } from '@kbn/config'; import { ByteSizeValue } from '@kbn/config-schema'; import { REPO_ROOT } from '@kbn/repo-info'; import { getEnvOptions } from '@kbn/config-mocks'; +import { SavedObjectsType, MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; import { nodeServiceMock } from '@kbn/core-node-server-mocks'; import { mockCoreContext } from '@kbn/core-base-server-mocks'; @@ -55,6 +56,14 @@ import { getSavedObjectsDeprecationsProvider } from './deprecations'; jest.mock('./object_types'); jest.mock('./deprecations'); +const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test-type', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, +}); + describe('SavedObjectsService', () => { let deprecationsSetup: ReturnType; @@ -630,5 +639,85 @@ describe('SavedObjectsService', () => { expect(includedHiddenTypes).toEqual(['someHiddenType']); }); }); + + describe('index retrieval APIs', () => { + let soService: SavedObjectsService; + + beforeEach(async () => { + const coreContext = createCoreContext({ skipMigration: false }); + soService = new SavedObjectsService(coreContext); + + typeRegistryInstanceMock.getType.mockImplementation((type: string) => { + if (type === 'dashboard') { + return createType({ + name: 'dashboard', + }); + } else if (type === 'foo') { + return createType({ + name: 'foo', + indexPattern: '.kibana_foo', + }); + } else if (type === 'bar') { + return createType({ + name: 'bar', + indexPattern: '.kibana_bar', + }); + } else if (type === 'bar_too') { + return createType({ + name: 'bar_too', + indexPattern: '.kibana_bar', + }); + } else { + return undefined; + } + }); + + await soService.setup(createSetupDeps()); + }); + + describe('#getDefaultIndex', () => { + it('return the default index', async () => { + const { getDefaultIndex } = await soService.start(createStartDeps()); + expect(getDefaultIndex()).toEqual(MAIN_SAVED_OBJECT_INDEX); + }); + }); + + describe('#getIndexForType', () => { + it('return the correct index for type specifying its indexPattern', async () => { + const { getIndexForType } = await soService.start(createStartDeps()); + expect(getIndexForType('bar')).toEqual('.kibana_bar'); + }); + it('return the correct index for type not specifying its indexPattern', async () => { + const { getIndexForType } = await soService.start(createStartDeps()); + expect(getIndexForType('dashboard')).toEqual(MAIN_SAVED_OBJECT_INDEX); + }); + it('return the default index for unknown type', async () => { + const { getIndexForType } = await soService.start(createStartDeps()); + expect(getIndexForType('unknown_type')).toEqual(MAIN_SAVED_OBJECT_INDEX); + }); + }); + + describe('#getIndicesForTypes', () => { + it('return the correct indices for specified types', async () => { + const { getIndicesForTypes } = await soService.start(createStartDeps()); + expect(getIndicesForTypes(['dashboard', 'foo', 'bar'])).toEqual([ + MAIN_SAVED_OBJECT_INDEX, + '.kibana_foo', + '.kibana_bar', + ]); + }); + it('ignore duplicate indices', async () => { + const { getIndicesForTypes } = await soService.start(createStartDeps()); + expect(getIndicesForTypes(['bar', 'bar_too'])).toEqual(['.kibana_bar']); + }); + it('return the default index for unknown type', async () => { + const { getIndicesForTypes } = await soService.start(createStartDeps()); + expect(getIndicesForTypes(['unknown', 'foo'])).toEqual([ + MAIN_SAVED_OBJECT_INDEX, + '.kibana_foo', + ]); + }); + }); + }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts index dbb04577d3f7e..36b515f5b0cb1 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/saved_objects_service.ts @@ -52,13 +52,12 @@ import { import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import type { DeprecationRegistryProvider } from '@kbn/core-deprecations-server'; import type { NodeInfo } from '@kbn/core-node-server'; +import { MAIN_SAVED_OBJECT_INDEX, ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import { registerRoutes } from './routes'; import { calculateStatus$ } from './status'; import { registerCoreObjectTypes } from './object_types'; import { getSavedObjectsDeprecationsProvider } from './deprecations'; -const kibanaIndex = '.kibana'; - /** * @internal */ @@ -125,7 +124,7 @@ export class SavedObjectsService this.config = new SavedObjectConfig(savedObjectsConfig, savedObjectsMigrationConfig); deprecations.getRegistry('savedObjects').registerDeprecations( getSavedObjectsDeprecationsProvider({ - kibanaIndex, + kibanaIndex: MAIN_SAVED_OBJECT_INDEX, savedObjectsConfig: this.config, kibanaVersion: this.kibanaVersion, typeRegistry: this.typeRegistry, @@ -140,7 +139,7 @@ export class SavedObjectsService logger: this.logger, config: this.config, migratorPromise: firstValueFrom(this.migrator$), - kibanaIndex, + kibanaIndex: MAIN_SAVED_OBJECT_INDEX, kibanaVersion: this.kibanaVersion, }); @@ -198,7 +197,8 @@ export class SavedObjectsService this.typeRegistry.registerType(type); }, getTypeRegistry: () => this.typeRegistry, - getKibanaIndex: () => kibanaIndex, + getDefaultIndex: () => MAIN_SAVED_OBJECT_INDEX, + getAllIndices: () => [...ALL_SAVED_OBJECT_INDICES], }; } @@ -280,7 +280,7 @@ export class SavedObjectsService return SavedObjectsRepository.createRepository( migrator, this.typeRegistry, - kibanaIndex, + MAIN_SAVED_OBJECT_INDEX, esClient, this.logger.get('repository'), includedHiddenTypes, @@ -341,6 +341,21 @@ export class SavedObjectsService importSizeLimit: options?.importSizeLimit ?? this.config!.maxImportExportSize, }), getTypeRegistry: () => this.typeRegistry, + getDefaultIndex: () => MAIN_SAVED_OBJECT_INDEX, + getIndexForType: (type: string) => { + const definition = this.typeRegistry.getType(type); + return definition?.indexPattern ?? MAIN_SAVED_OBJECT_INDEX; + }, + getIndicesForTypes: (types: string[]) => { + const indices = new Set(); + types.forEach((type) => { + const definition = this.typeRegistry.getType(type); + const index = definition?.indexPattern ?? MAIN_SAVED_OBJECT_INDEX; + indices.add(index); + }); + return [...indices]; + }, + getAllIndices: () => [...ALL_SAVED_OBJECT_INDICES], }; } @@ -357,7 +372,7 @@ export class SavedObjectsService logger: this.logger, kibanaVersion: this.kibanaVersion, soMigrationsConfig, - kibanaIndex, + kibanaIndex: MAIN_SAVED_OBJECT_INDEX, client, docLinks, waitForMigrationCompletion, diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts index a6c07ee6ea1ba..8f7f1281f89ea 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/status.test.ts @@ -81,7 +81,13 @@ describe('calculateStatus$', () => { it('is available after migrations have ran', async () => { await expect( calculateStatus$( - of({ status: 'completed', result: [{ status: 'skipped' }, { status: 'patched' }] }), + of({ + status: 'completed', + result: [ + { status: 'skipped' }, + { status: 'patched', destIndex: '.kibana', elapsedMs: 28 }, + ], + }), esStatus$ ) .pipe(take(2)) diff --git a/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts b/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts index eccdb9ca16c54..2d7d5ff847330 100644 --- a/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-server-mocks/src/saved_objects_service.mock.ts @@ -29,6 +29,7 @@ import { savedObjectsImporterMock, } from '@kbn/core-saved-objects-import-export-server-mocks'; import { migrationMocks } from '@kbn/core-saved-objects-migration-server-mocks'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; type SavedObjectsServiceContract = PublicMethodsOf; @@ -41,6 +42,10 @@ const createStartContractMock = (typeRegistry?: jest.Mocked { setSecurityExtension: jest.fn(), setSpacesExtension: jest.fn(), registerType: jest.fn(), - getKibanaIndex: jest.fn(), + getDefaultIndex: jest.fn(), + getAllIndices: jest.fn(), }; - setupContract.getKibanaIndex.mockReturnValue('.kibana'); + setupContract.getDefaultIndex.mockReturnValue(MAIN_SAVED_OBJECT_INDEX); + setupContract.getAllIndices.mockReturnValue([MAIN_SAVED_OBJECT_INDEX]); return setupContract; }; diff --git a/packages/core/saved-objects/core-saved-objects-server/index.ts b/packages/core/saved-objects/core-saved-objects-server/index.ts index d3d2ffe71aa34..cad75db293a67 100644 --- a/packages/core/saved-objects/core-saved-objects-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-server/index.ts @@ -52,6 +52,15 @@ export type { SavedObjectsExportablePredicate, } from './src/saved_objects_management'; export type { SavedObjectStatusMeta } from './src/saved_objects_status'; +export { + MAIN_SAVED_OBJECT_INDEX, + TASK_MANAGER_SAVED_OBJECT_INDEX, + INGEST_SAVED_OBJECT_INDEX, + ALERTING_CASES_SAVED_OBJECT_INDEX, + SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + ANALYTICS_SAVED_OBJECT_INDEX, + ALL_SAVED_OBJECT_INDICES, +} from './src/saved_objects_index_pattern'; export type { SavedObjectsType, SavedObjectTypeExcludeFromUpgradeFilterHook, diff --git a/packages/core/saved-objects/core-saved-objects-server/src/contracts.ts b/packages/core/saved-objects/core-saved-objects-server/src/contracts.ts index 3ee6e9c262c54..e08da10172f3c 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/contracts.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/contracts.ts @@ -136,7 +136,14 @@ export interface SavedObjectsServiceSetup { /** * Returns the default index used for saved objects. */ - getKibanaIndex: () => string; + getDefaultIndex: () => string; + + /** + * Returns all (aliases to) kibana system indices used for saved object storage. + * + * @deprecated use the `start` contract counterpart. + */ + getAllIndices: () => string[]; } /** @@ -209,4 +216,25 @@ export interface SavedObjectsServiceStart { * {@link SavedObjectsType | saved object types} */ getTypeRegistry: () => ISavedObjectTypeRegistry; + /** + * Returns the (alias to the) index that the specified saved object type is stored in. + * + * @param type The SO type to retrieve the index/alias for. + */ + getIndexForType: (type: string) => string; + /** + * Returns the (alias to the) index that the specified saved object type is stored in. + * + * @remark if multiple types are living in the same index, duplicates will be removed. + * @param types The SO types to retrieve the index/alias for. + */ + getIndicesForTypes: (types: string[]) => string[]; + /** + * Returns the default index used for saved objects. + */ + getDefaultIndex: () => string; + /** + * Returns all (aliases to) kibana system indices used for saved object storage. + */ + getAllIndices: () => string[]; } diff --git a/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_index_pattern.ts b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_index_pattern.ts new file mode 100644 index 0000000000000..93160c6b9bd45 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_index_pattern.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. + */ + +/** + * Collect and centralize the names of the different saved object indices. + * Note that all of them start with the '.kibana' prefix. + * There are multiple places in the code that these indices have the form .kibana*. + * However, beware that there are some system indices that have the same prefix + * but are NOT used to store saved objects, e.g.: .kibana_security_session_1 + */ +export const MAIN_SAVED_OBJECT_INDEX = '.kibana'; +export const TASK_MANAGER_SAVED_OBJECT_INDEX = `${MAIN_SAVED_OBJECT_INDEX}_task_manager`; +export const INGEST_SAVED_OBJECT_INDEX = `${MAIN_SAVED_OBJECT_INDEX}_ingest`; +export const ALERTING_CASES_SAVED_OBJECT_INDEX = `${MAIN_SAVED_OBJECT_INDEX}_alerting_cases`; +export const SECURITY_SOLUTION_SAVED_OBJECT_INDEX = `${MAIN_SAVED_OBJECT_INDEX}_security_solution`; +export const ANALYTICS_SAVED_OBJECT_INDEX = `${MAIN_SAVED_OBJECT_INDEX}_analytics`; +export const ALL_SAVED_OBJECT_INDICES = [ + MAIN_SAVED_OBJECT_INDEX, + TASK_MANAGER_SAVED_OBJECT_INDEX, + ALERTING_CASES_SAVED_OBJECT_INDEX, + INGEST_SAVED_OBJECT_INDEX, + SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + ANALYTICS_SAVED_OBJECT_INDEX, +]; diff --git a/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts b/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts index 873dff4ac1b33..8b3b6b4924d79 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts @@ -84,6 +84,7 @@ export interface SavedObjectsRawDocSource { created_at?: string; references?: SavedObjectReference[]; originId?: string; + managed?: boolean; [typeMapping: string]: any; } @@ -106,6 +107,7 @@ interface SavedObjectDoc { updated_at?: string; created_at?: string; originId?: string; + managed?: boolean; } /** diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/get_upgradeable_config.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/get_upgradeable_config.ts index dafbeaaa34cb6..18479db1ebd28 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/get_upgradeable_config.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/get_upgradeable_config.ts @@ -34,7 +34,7 @@ export async function getUpgradeableConfig({ }: { savedObjectsClient: SavedObjectsClientContract; version: string; - type: 'config' | 'config-global'; + type: 'config' | 'config-global' | 'config-user'; }) { // attempt to find a config we can upgrade const { saved_objects: savedConfigs } = diff --git a/packages/core/ui-settings/core-ui-settings-server/src/contracts.ts b/packages/core/ui-settings/core-ui-settings-server/src/contracts.ts index a63596e28e84f..18d25a9031b24 100644 --- a/packages/core/ui-settings/core-ui-settings-server/src/contracts.ts +++ b/packages/core/ui-settings/core-ui-settings-server/src/contracts.ts @@ -56,7 +56,7 @@ export interface UiSettingsServiceStart { * Creates a {@link IUiSettingsClient} with provided *scoped* saved objects client. * * This should only be used in the specific case where the client needs to be accessed - * from outside of the scope of a {@link RequestHandler}. + * from outside the scope of a {@link RequestHandler}. * * @example * ```ts @@ -72,7 +72,7 @@ export interface UiSettingsServiceStart { * Creates a global {@link IUiSettingsClient} with provided *scoped* saved objects client. * * This should only be used in the specific case where the client needs to be accessed - * from outside of the scope of a {@link RequestHandler}. + * from outside the scope of a {@link RequestHandler}. * * @example * ```ts diff --git a/packages/core/ui-settings/core-ui-settings-server/tsconfig.json b/packages/core/ui-settings/core-ui-settings-server/tsconfig.json index dce14385f9e70..0895961777aa9 100644 --- a/packages/core/ui-settings/core-ui-settings-server/tsconfig.json +++ b/packages/core/ui-settings/core-ui-settings-server/tsconfig.json @@ -12,7 +12,7 @@ ], "kbn_references": [ "@kbn/core-saved-objects-api-server", - "@kbn/core-ui-settings-common" + "@kbn/core-ui-settings-common", ], "exclude": [ "target/**/*", diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts index bd9fe1ac47859..63a8da844e090 100644 --- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts +++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts @@ -40,7 +40,10 @@ import { type InternalCoreUsageDataSetup, } from '@kbn/core-usage-data-base-server-internal'; import type { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; -import type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; +import { + MAIN_SAVED_OBJECT_INDEX, + type SavedObjectsServiceStart, +} from '@kbn/core-saved-objects-server'; import { isConfigured } from './is_configured'; import { coreUsageStatsType } from './saved_objects'; @@ -61,26 +64,6 @@ export interface StartDeps { exposedConfigsToUsage: ExposedConfigsToUsage; } -const kibanaIndex = '.kibana'; - -/** - * Because users can configure their Saved Object to any arbitrary index name, - * we need to map customized index names back to a "standard" index name. - * - * e.g. If a user configures `kibana.index: .my_saved_objects` we want to the - * collected data to be grouped under `.kibana` not ".my_saved_objects". - * - * This is rather brittle, but the option to configure index names might go - * away completely anyway (see #60053). - * - * @param index The index name configured for this SO type - * @param kibanaConfigIndex The default kibana index as configured by the user - * with `kibana.index` - */ -const kibanaOrTaskManagerIndex = (index: string, kibanaConfigIndex: string) => { - return index === kibanaConfigIndex ? '.kibana' : '.kibana_task_manager'; -}; - interface UsageDataAggs extends AggregationsMultiBucketAggregateBase { buckets: { disabled: AggregationsSingleBucketAggregateBase; @@ -133,7 +116,7 @@ export class CoreUsageDataService .getTypeRegistry() .getAllTypes() .reduce((acc, type) => { - const index = type.indexPattern ?? kibanaIndex; + const index = type.indexPattern ?? MAIN_SAVED_OBJECT_INDEX; return acc.add(index); }, new Set()) .values() @@ -151,7 +134,7 @@ export class CoreUsageDataService const stats = body[0]; return { - alias: kibanaOrTaskManagerIndex(index, kibanaIndex), + alias: index, docsCount: stats['docs.count'] ? parseInt(stats['docs.count'], 10) : 0, docsDeleted: stats['docs.deleted'] ? parseInt(stats['docs.deleted'], 10) : 0, storeSizeBytes: stats['store.size'] ? parseInt(stats['store.size'], 10) : 0, @@ -192,7 +175,7 @@ export class CoreUsageDataService unknown, { aliases: UsageDataAggs } >({ - index: kibanaIndex, + index: MAIN_SAVED_OBJECT_INDEX, // depends on the .kibana split (assuming 'legacy-url-alias' is stored in '.kibana') body: { track_total_hits: true, query: { match: { type: LEGACY_URL_ALIAS_TYPE } }, diff --git a/packages/core/user-settings/core-user-settings-server-internal/README.md b/packages/core/user-settings/core-user-settings-server-internal/README.md new file mode 100644 index 0000000000000..dfa07a921b8f1 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/README.md @@ -0,0 +1,3 @@ +# @kbn/core-user-settings-server-internal + +Contains the implementation and internal types of the server-side `userSettings` service. \ No newline at end of file diff --git a/packages/core/user-settings/core-user-settings-server-internal/index.ts b/packages/core/user-settings/core-user-settings-server-internal/index.ts new file mode 100644 index 0000000000000..e4cd3e0e42f30 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/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 { UserSettingsService } from './user_settings_service'; +export type { InternalUserSettingsServiceSetup } from './user_settings_service'; diff --git a/packages/core/user-settings/core-user-settings-server-internal/jest.config.js b/packages/core/user-settings/core-user-settings-server-internal/jest.config.js new file mode 100644 index 0000000000000..7cd35335c52e5 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/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/core/user-settings/core-user-settings-server-internal'], +}; diff --git a/packages/core/user-settings/core-user-settings-server-internal/kibana.jsonc b/packages/core/user-settings/core-user-settings-server-internal/kibana.jsonc new file mode 100644 index 0000000000000..48655c00cfee4 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/core-user-settings-server-internal", + "owner": "@elastic/platform-security", +} diff --git a/packages/core/user-settings/core-user-settings-server-internal/package.json b/packages/core/user-settings/core-user-settings-server-internal/package.json new file mode 100644 index 0000000000000..28a1c09f288ab --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/core-user-settings-server-internal", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/user-settings/core-user-settings-server-internal/tsconfig.json b/packages/core/user-settings/core-user-settings-server-internal/tsconfig.json new file mode 100644 index 0000000000000..089523eaaecc1 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "kbn_references": [ + "@kbn/core-base-server-mocks", + "@kbn/core-http-server", + "@kbn/core-base-server-internal", + "@kbn/logging", + "@kbn/core-http-server-mocks", + "@kbn/core-user-settings-server", + ], + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/core/user-settings/core-user-settings-server-internal/user_settings_service.test.ts b/packages/core/user-settings/core-user-settings-server-internal/user_settings_service.test.ts new file mode 100644 index 0000000000000..15715e62823d2 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/user_settings_service.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { mockCoreContext } from '@kbn/core-base-server-mocks'; +import { UserSettingsService } from './user_settings_service'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; + +describe('#setup', () => { + const coreContext: ReturnType = mockCoreContext.create(); + const { createKibanaRequest } = httpServerMock; + + it('fetches userSettings when client is set and returns `true` when `darkMode` is set to `dark`', async () => { + const service = new UserSettingsService(coreContext); + const { setUserProfileSettings, getUserSettingDarkMode } = service.setup(); + + const userProfileContract = { + get: jest.fn().mockReturnValueOnce(Promise.resolve({ darkMode: 'dark' })), + }; + + setUserProfileSettings(userProfileContract); + + const kibanaRequest = createKibanaRequest(); + const darkMode = await getUserSettingDarkMode(kibanaRequest); + + expect(darkMode).toEqual(true); + expect(userProfileContract.get).toHaveBeenCalledTimes(1); + expect(userProfileContract.get).toHaveBeenCalledWith(kibanaRequest); + }); + + it('fetches userSettings when client is set and returns `false` when `darkMode` is set to `light`', async () => { + const service = new UserSettingsService(coreContext); + const { setUserProfileSettings, getUserSettingDarkMode } = service.setup(); + + const userProfileContract = { + get: jest.fn().mockReturnValueOnce(Promise.resolve({ darkMode: 'light' })), + }; + + setUserProfileSettings(userProfileContract); + + const kibanaRequest = createKibanaRequest(); + const darkMode = await getUserSettingDarkMode(kibanaRequest); + + expect(darkMode).toEqual(false); + expect(userProfileContract.get).toHaveBeenCalledTimes(1); + expect(userProfileContract.get).toHaveBeenCalledWith(kibanaRequest); + }); + + it('fetches userSettings when client is set and returns `undefined` when `darkMode` is set to `` (the default value)', async () => { + const service = new UserSettingsService(coreContext); + const { setUserProfileSettings, getUserSettingDarkMode } = service.setup(); + + const userProfileContract = { + get: jest.fn().mockReturnValueOnce(Promise.resolve({ darkMode: '' })), + }; + + setUserProfileSettings(userProfileContract); + + const kibanaRequest = createKibanaRequest(); + const darkMode = await getUserSettingDarkMode(kibanaRequest); + + expect(darkMode).toEqual(undefined); + expect(userProfileContract.get).toHaveBeenCalledTimes(1); + expect(userProfileContract.get).toHaveBeenCalledWith(kibanaRequest); + }); + + it('does not fetch userSettings when client is not set, returns `undefined`, and logs a debug statement', async () => { + const service = new UserSettingsService(coreContext); + const { getUserSettingDarkMode } = service.setup(); + const kibanaRequest = createKibanaRequest(); + const darkMode = await getUserSettingDarkMode(kibanaRequest); + + expect(darkMode).toEqual(undefined); + expect(coreContext.logger.get().debug).toHaveBeenCalledWith( + 'UserProfileSettingsClient not set' + ); + }); +}); diff --git a/packages/core/user-settings/core-user-settings-server-internal/user_settings_service.ts b/packages/core/user-settings/core-user-settings-server-internal/user_settings_service.ts new file mode 100644 index 0000000000000..1ffcdf20a50c3 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-internal/user_settings_service.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 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 { CoreContext } from '@kbn/core-base-server-internal'; +import { Logger } from '@kbn/logging'; +import { KibanaRequest } from '@kbn/core-http-server'; +import { UserProfileSettingsClientContract } from '@kbn/core-user-settings-server'; + +/** + * @internal + */ +export interface InternalUserSettingsServiceSetup { + setUserProfileSettings: (client: UserProfileSettingsClientContract) => void; + getUserSettingDarkMode: (request: KibanaRequest) => Promise; +} + +export class UserSettingsService { + private logger: Logger; + private client?: UserProfileSettingsClientContract; + + constructor(coreContext: CoreContext) { + this.logger = coreContext.logger.get('user-settings-service'); + } + + public setup(): InternalUserSettingsServiceSetup { + return { + setUserProfileSettings: (client: UserProfileSettingsClientContract) => { + this.client = client; + }, + getUserSettingDarkMode: async (request: KibanaRequest) => { + const userSettings = await this.getSettings(request); + return this.getUserSettingDarkMode(userSettings); + }, + }; + } + + private async getSettings(request: KibanaRequest): Promise> { + let result = {}; + if (this.client) { + result = (await this.client.get(request)) as Record; + } else { + this.logger.debug('UserProfileSettingsClient not set'); + } + + return result; + } + + private async getUserSettingDarkMode( + userSettings: Record + ): Promise { + let result; + + if (userSettings?.darkMode) { + result = userSettings.darkMode.toUpperCase() === 'DARK'; + } + + return result; + } +} diff --git a/packages/core/user-settings/core-user-settings-server-mocks/README.md b/packages/core/user-settings/core-user-settings-server-mocks/README.md new file mode 100644 index 0000000000000..715a63357f9b9 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/README.md @@ -0,0 +1,3 @@ +# @kbn/core-custom-branding-server-mocks + +Contains the mocks for Core's internal `userSettings` server-side service. diff --git a/packages/core/user-settings/core-user-settings-server-mocks/index.ts b/packages/core/user-settings/core-user-settings-server-mocks/index.ts new file mode 100644 index 0000000000000..d8454f816cf64 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/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 { userSettingsServiceMock } from './src/user_settings_service.mock'; diff --git a/packages/core/user-settings/core-user-settings-server-mocks/jest.config.js b/packages/core/user-settings/core-user-settings-server-mocks/jest.config.js new file mode 100644 index 0000000000000..1cefc9e15f74d --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/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/core/user-settings/core-user-settings-server-mocks'], +}; diff --git a/packages/core/user-settings/core-user-settings-server-mocks/kibana.jsonc b/packages/core/user-settings/core-user-settings-server-mocks/kibana.jsonc new file mode 100644 index 0000000000000..f3f598b16f68a --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/core-user-settings-server-mocks", + "owner": "@elastic/platform-security", +} diff --git a/packages/core/user-settings/core-user-settings-server-mocks/package.json b/packages/core/user-settings/core-user-settings-server-mocks/package.json new file mode 100644 index 0000000000000..ba8aaf6c12acc --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/core-user-settings-server-mocks", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/user-settings/core-user-settings-server-mocks/src/service_contract.mock.ts b/packages/core/user-settings/core-user-settings-server-mocks/src/service_contract.mock.ts new file mode 100644 index 0000000000000..d4176ee2bbfb6 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/src/service_contract.mock.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 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 { UserSettingsService } from '@kbn/core-user-settings-server-internal'; + +export const serviceContractMock = (): jest.Mocked => { + return { + setup: jest.fn(), + } as unknown as jest.Mocked; +}; diff --git a/packages/core/user-settings/core-user-settings-server-mocks/src/user_settings_service.mock.ts b/packages/core/user-settings/core-user-settings-server-mocks/src/user_settings_service.mock.ts new file mode 100644 index 0000000000000..74cbc491bad5f --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/src/user_settings_service.mock.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 { serviceContractMock } from './service_contract.mock'; + +const createSetupContractMock = () => { + return { + setUserProfileSettings: jest.fn(), + getUserSettingDarkMode: jest.fn(), + }; +}; + +const createMock = () => { + const mocked = serviceContractMock(); + mocked.setup.mockReturnValue(createSetupContractMock()); + // mocked.start.mockReturnValue(createStartContractMock()); + return mocked; +}; + +export const userSettingsServiceMock = { + create: createMock, + createSetupContract: createSetupContractMock, +}; diff --git a/packages/core/user-settings/core-user-settings-server-mocks/tsconfig.json b/packages/core/user-settings/core-user-settings-server-mocks/tsconfig.json new file mode 100644 index 0000000000000..44431c1ebcf0e --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server-mocks/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "kbn_references": [ + "@kbn/core-user-settings-server-internal", + ], + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/core/user-settings/core-user-settings-server/README.md b/packages/core/user-settings/core-user-settings-server/README.md new file mode 100644 index 0000000000000..c5e0ad96400f5 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server/README.md @@ -0,0 +1,3 @@ +# @kbn/core-user-settings-server + +Contains the public types of Core's server-side `userSettings` service. diff --git a/packages/core/user-settings/core-user-settings-server/index.ts b/packages/core/user-settings/core-user-settings-server/index.ts new file mode 100644 index 0000000000000..c13d17fb4b955 --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server/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 type { UserSettingsServiceSetup, UserProfileSettingsClientContract } from './types'; diff --git a/packages/core/user-settings/core-user-settings-server/kibana.jsonc b/packages/core/user-settings/core-user-settings-server/kibana.jsonc new file mode 100644 index 0000000000000..5bf834b25ba3c --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/core-user-settings-server", + "owner": "@elastic/platform-security", +} diff --git a/packages/core/user-settings/core-user-settings-server/package.json b/packages/core/user-settings/core-user-settings-server/package.json new file mode 100644 index 0000000000000..d942fb29686aa --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/core-user-settings-server", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/user-settings/core-user-settings-server/tsconfig.json b/packages/core/user-settings/core-user-settings-server/tsconfig.json new file mode 100644 index 0000000000000..3270f469296eb --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "node" + ] + }, + "kbn_references": [ + "@kbn/core-http-server", + ], + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/core/user-settings/core-user-settings-server/types.ts b/packages/core/user-settings/core-user-settings-server/types.ts new file mode 100644 index 0000000000000..f852e0e188bfd --- /dev/null +++ b/packages/core/user-settings/core-user-settings-server/types.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 { KibanaRequest } from '@kbn/core-http-server'; + +/** @public */ +export interface UserSettingsServiceSetup { + setUserProfileSettings: (client: UserProfileSettingsClientContract) => void; +} + +export interface UserProfileSettingsClientContract { + get: (request: KibanaRequest) => Promise>; +} diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts index 72fe68fa9cf59..87c40e617f90c 100644 --- a/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts @@ -13,6 +13,7 @@ import { ALERT_END, ALERT_FLAPPING, ALERT_FLAPPING_HISTORY, + ALERT_MAINTENANCE_WINDOW_IDS, ALERT_INSTANCE_ID, ALERT_LAST_DETECTED, ALERT_REASON, @@ -29,6 +30,7 @@ import { ALERT_START, ALERT_STATUS, ALERT_TIME_RANGE, + ALERT_URL, ALERT_UUID, ALERT_WORKFLOW_STATUS, SPACE_IDS, @@ -67,6 +69,11 @@ export const alertFieldMap = { array: true, required: false, }, + [ALERT_MAINTENANCE_WINDOW_IDS]: { + type: 'keyword', + array: true, + required: false, + }, [ALERT_INSTANCE_ID]: { type: 'keyword', array: false, @@ -149,6 +156,13 @@ export const alertFieldMap = { array: false, required: false, }, + [ALERT_URL]: { + type: 'keyword', + array: false, + index: false, + required: false, + ignore_above: 2048, + }, [ALERT_UUID]: { type: 'keyword', array: false, diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts index 8c8445ad6761a..eff46f46adbc5 100644 --- a/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/legacy_experimental_field_map.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils'; +import { + ALERT_EVALUATION_THRESHOLD, + ALERT_EVALUATION_VALUE, + ALERT_EVALUATION_VALUES, +} from '@kbn/rule-data-utils'; export const legacyExperimentalFieldMap = { [ALERT_EVALUATION_THRESHOLD]: { @@ -15,6 +19,12 @@ export const legacyExperimentalFieldMap = { required: false, }, [ALERT_EVALUATION_VALUE]: { type: 'scaled_float', scaling_factor: 100, required: false }, + [ALERT_EVALUATION_VALUES]: { + type: 'scaled_float', + scaling_factor: 100, + required: false, + array: true, + }, } as const; export type ExperimentalRuleFieldMap = typeof legacyExperimentalFieldMap; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts index 808894dab55ef..d5efb1dbc2c97 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts @@ -22,6 +22,7 @@ export type ApmApplicationMetricFields = Partial<{ 'faas.timeout': number; 'faas.coldstart_duration': number; 'faas.duration': number; + 'application.launch.time': number; }>; export type ApmUserAgentFields = Partial<{ @@ -88,6 +89,7 @@ export type ApmFields = Fields<{ 'error.grouping_key': string; 'error.grouping_name': string; 'error.id': string; + 'error.type': string; 'event.ingested': number; 'event.name': string; 'event.outcome': string; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts index f0766301b238e..bedae100bc933 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { createHash } from 'crypto'; import { ApmError } from './apm_error'; import { Entity } from '../entity'; import { Metricset } from './metricset'; @@ -63,19 +64,11 @@ export class Instance extends Entity { }); } - error({ - message, - type, - groupingName, - }: { - message: string; - type?: string; - groupingName?: string; - }) { + error({ message, type }: { message: string; type?: string }) { return new ApmError({ ...this.fields, 'error.exception': [{ message, ...(type ? { type } : {}) }], - 'error.grouping_name': groupingName || message, + 'error.grouping_name': getErrorGroupingKey(message), }); } @@ -97,3 +90,7 @@ export class Instance extends Entity { }); } } + +export function getErrorGroupingKey(content: string) { + return createHash('sha256').update(content).digest('hex'); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts index eddb7d6c99d18..252590104e7a2 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts @@ -9,8 +9,10 @@ import { Entity } from '../entity'; import { Span } from './span'; import { Transaction } from './transaction'; -import { ApmFields, SpanParams, GeoLocation } from './apm_fields'; +import { ApmFields, SpanParams, GeoLocation, ApmApplicationMetricFields } from './apm_fields'; import { generateLongId } from '../utils/generate_id'; +import { Metricset } from './metricset'; +import { ApmError } from './apm_error'; export interface DeviceInfo { manufacturer: string; @@ -115,6 +117,7 @@ export class MobileDevice extends Entity { return this; } + // FIXME synthtrace shouldn't have side-effects like this. We should use an API like .session() which returns a session startNewSession() { this.fields['session.id'] = generateLongId(); return this; @@ -238,4 +241,21 @@ export class MobileDevice extends Entity { return this.span(spanParameters); } + + appMetrics(metrics: ApmApplicationMetricFields) { + return new Metricset({ + ...this.fields, + 'metricset.name': 'app', + ...metrics, + }); + } + + crash({ message, groupingName }: { message: string; groupingName?: string }) { + return new ApmError({ + ...this.fields, + 'error.type': 'crash', + 'error.exception': [{ message, ...{ type: 'crash' } }], + 'error.grouping_name': groupingName || message, + }); + } } diff --git a/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts b/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts index b7456be994b8b..f344c8f79fa6a 100644 --- a/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts +++ b/packages/kbn-apm-synthtrace/src/lib/utils/get_synthtrace_environment.ts @@ -8,6 +8,6 @@ import path from 'path'; -export function getSynthtraceEnvironment(filename: string) { - return `Synthtrace: ${path.parse(filename).name}`; +export function getSynthtraceEnvironment(filename: string, suffix = '') { + return `Synthtrace: ${path.parse(filename).name} ${suffix}`.trim(); } diff --git a/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts b/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts index c4eba8b8c7d5f..0a346dcf99542 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/continuous_rollups.ts @@ -10,6 +10,11 @@ import { apm, ApmFields } from '@kbn/apm-synthtrace-client'; import { merge, range as lodashRange } from 'lodash'; import { Scenario } from '../cli/scenario'; import { ComponentTemplateName } from '../lib/apm/client/apm_synthtrace_es_client'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENTS = ['production', 'development'].map((env) => + getSynthtraceEnvironment(__filename, env) +); const scenario: Scenario = async ({ logger, scenarioOpts }) => { const { @@ -37,7 +42,6 @@ const scenario: Scenario = async ({ logger, scenarioOpts }) => { }, generate: ({ range }) => { const TRANSACTION_TYPES = ['request', 'custom']; - const ENVIRONMENTS = ['production', 'development']; const MIN_DURATION = 10; const MAX_DURATION = 1000; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts b/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts index f03d909db835f..2e7d8c22a4696 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/high_throughput.ts @@ -9,6 +9,9 @@ import { random } from 'lodash'; import { apm, Instance, ApmFields } from '@kbn/apm-synthtrace-client'; import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); const scenario: Scenario = async ({ logger }) => { const languages = ['go', 'dotnet', 'java', 'python']; @@ -22,7 +25,7 @@ const scenario: Scenario = async ({ logger }) => { apm .service({ name: `${service}-${languages[index % languages.length]}`, - environment: 'production', + environment: ENVIRONMENT, agentName: languages[index % languages.length], }) .instance(`instance-${index}`) diff --git a/packages/kbn-apm-synthtrace/src/scenarios/many_transactions_per_service.ts b/packages/kbn-apm-synthtrace/src/scenarios/many_transactions_per_service.ts new file mode 100644 index 0000000000000..e814ed51b08e6 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/many_transactions_per_service.ts @@ -0,0 +1,84 @@ +/* + * 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, apm, Instance } from '@kbn/apm-synthtrace-client'; +import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); + +const scenario: Scenario = async (runOptions) => { + const { logger } = runOptions; + const { numServices = 3 } = runOptions.scenarioOpts || {}; + const numTransactions = 100; + + return { + generate: ({ range }) => { + const urls = ['GET /order', 'POST /basket', 'DELETE /basket', 'GET /products']; + + const successfulTimestamps = range.ratePerMinute(180); + const failedTimestamps = range.interval('1m').rate(180); + + const instances = [...Array(numServices).keys()].map((index) => + apm + .service({ name: `synth-go-${index}`, environment: ENVIRONMENT, agentName: 'go' }) + .instance(`instance-${index}`) + ); + + const transactionNames = [...Array(numTransactions).keys()].map( + (index) => `${urls[index % urls.length]}/${index}` + ); + + const instanceSpans = (instance: Instance, transactionName: string) => { + const successfulTraceEvents = successfulTimestamps.generator((timestamp) => + instance.transaction({ transactionName }).timestamp(timestamp).duration(1000).success() + ); + + const failedTraceEvents = failedTimestamps.generator((timestamp) => + instance + .transaction({ transactionName }) + .timestamp(timestamp) + .duration(1000) + .failure() + .errors( + instance + .error({ message: '[ResponseError] index_not_found_exception' }) + .timestamp(timestamp + 50) + ) + ); + + const metricsets = range + .interval('30s') + .rate(1) + .generator((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) + ); + + return [successfulTraceEvents, failedTraceEvents, metricsets]; + }; + + return logger.perf('generating_apm_events', () => + instances + .flatMap((instance) => + transactionNames.map((transactionName) => ({ instance, transactionName })) + ) + .flatMap(({ instance, transactionName }, index) => + instanceSpans(instance, transactionName) + ) + ); + }, + }; +}; + +export default scenario; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts b/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts index 6db2d17b624f9..0ca4abf07bf91 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/mobile.ts @@ -20,6 +20,16 @@ const ENVIRONMENT = getSynthtraceEnvironment(__filename); type DeviceMetadata = DeviceInfo & OSInfo; +const modelIdentifiersWithCrashes = [ + 'SM-G930F', + 'HUAWEI P2-0000', + 'Pixel 3a', + 'LG K10', + 'iPhone11,8', + 'Watch6,8', + 'iPad12,2', +]; + const ANDROID_DEVICES: DeviceMetadata[] = [ { manufacturer: 'Samsung', @@ -354,34 +364,40 @@ const scenario: Scenario = async ({ scenarioOpts, logger }) => { device.startNewSession(); const framework = device.fields['device.manufacturer'] === 'Apple' ? 'iOS' : 'Android Activity'; + const couldCrash = modelIdentifiersWithCrashes.includes( + device.fields['device.model.identifier'] ?? '' + ); + const startTx = device + .transaction('Start View - View Appearing', framework) + .timestamp(timestamp) + .duration(500) + .success() + .children( + device + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + device + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .failure() + .timestamp(timestamp + 400) + ); return [ - device - .transaction('Start View - View Appearing', framework) - .timestamp(timestamp) - .duration(500) - .success() - .children( - device - .span({ - spanName: 'onCreate', - spanType: 'app', - spanSubtype: 'external', - 'service.target.type': 'http', - 'span.destination.service.resource': 'external', - }) - .duration(50) - .success() - .timestamp(timestamp + 20), - device - .httpSpan({ - spanName: 'GET backend:1234', - httpMethod: 'GET', - httpUrl: 'https://backend:1234/api/start', - }) - .duration(800) - .failure() - .timestamp(timestamp + 400) - ), + couldCrash && index % 2 === 0 + ? startTx.errors(device.crash({ message: 'error' }).timestamp(timestamp)) + : startTx, device .transaction('Second View - View Appearing', framework) .timestamp(10000 + timestamp) @@ -418,7 +434,23 @@ const scenario: Scenario = async ({ scenarioOpts, logger }) => { }); }; - return [...androidDevices, ...iOSDevices].map((device) => sessionTransactions(device)); + const appLaunchMetrics = (device: MobileDevice) => { + return clickRate.generator((timestamp, index) => + device + .appMetrics({ + 'application.launch.time': 100 * (index + 1), + }) + .timestamp(timestamp) + ); + }; + + return [ + ...androidDevices.flatMap((device) => [ + sessionTransactions(device), + appLaunchMetrics(device), + ]), + ...iOSDevices.map((device) => sessionTransactions(device)), + ]; }, }; }; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts b/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts index 8ad994e4ff848..49fcc2f6ac47b 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/other_bucket_group.ts @@ -8,6 +8,11 @@ import { apm, ApmFields } from '@kbn/apm-synthtrace-client'; import { range as lodashRange } from 'lodash'; import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENTS = ['production', 'development'].map((env) => + getSynthtraceEnvironment(__filename, env) +); const scenario: Scenario = async ({ logger, scenarioOpts }) => { const { services: numServices = 10, txGroups: numTxGroups = 10 } = scenarioOpts ?? {}; @@ -15,7 +20,6 @@ const scenario: Scenario = async ({ logger, scenarioOpts }) => { return { generate: ({ range }) => { const TRANSACTION_TYPES = ['request']; - const ENVIRONMENTS = ['production', 'development']; const MIN_DURATION = 10; const MAX_DURATION = 1000; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts b/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts index 94e3ea20ba787..c7d535028a1af 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/services_without_transactions.ts @@ -7,20 +7,23 @@ */ import { apm, ApmFields } from '@kbn/apm-synthtrace-client'; import { Scenario } from '../cli/scenario'; +import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment'; + +const ENVIRONMENT = getSynthtraceEnvironment(__filename); const scenario: Scenario = async ({ logger, scenarioOpts }) => { return { generate: ({ range }) => { const withTx = apm - .service('service-with-transactions', 'production', 'java') + .service('service-with-transactions', ENVIRONMENT, 'java') .instance('instance'); const withErrorsOnly = apm - .service('service-with-errors-only', 'production', 'java') + .service('service-with-errors-only', ENVIRONMENT, 'java') .instance('instance'); const withAppMetricsOnly = apm - .service('service-with-app-metrics-only', 'production', 'java') + .service('service-with-app-metrics-only', ENVIRONMENT, 'java') .instance('instance'); return range diff --git a/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts b/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts index fdb80693bf74c..a8848c1954e40 100644 --- a/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts +++ b/packages/kbn-apm-synthtrace/src/test/scenarios/05_transactions_with_errors.test.ts @@ -60,7 +60,11 @@ describe('transactions with errors', () => { .errors(instance.error({ message: 'test error' }).timestamp(timestamp)) .serialize(); - expect(error['error.grouping_name']).toEqual('test error'); - expect(error['error.grouping_key']).toMatchInlineSnapshot(`"0000000000000000000000test error"`); + expect(error['error.grouping_name']).toEqual( + '4274b1899eba687801198c89f64a3fdade080a475c8a54881ba8fa10e7f45691' + ); + expect(error['error.grouping_key']).toMatchInlineSnapshot( + `"4274b1899eba687801198c89f64a3fdade080a475c8a54881ba8fa10e7f45691"` + ); }); }); diff --git a/packages/kbn-cell-actions/README.md b/packages/kbn-cell-actions/README.md index 7cacff6becda6..e1ae1a73a8369 100644 --- a/packages/kbn-cell-actions/README.md +++ b/packages/kbn-cell-actions/README.md @@ -6,7 +6,7 @@ Example: ```JSX [...] - + Hover me diff --git a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx index 657dcc93416b8..49f812cd9005a 100644 --- a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx +++ b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx @@ -50,8 +50,8 @@ export const DefaultWithControls = CellActionsTemplate.bind({}); DefaultWithControls.argTypes = { mode: { - options: [CellActionsMode.HOVER, CellActionsMode.INLINE], - defaultValue: CellActionsMode.HOVER, + options: [CellActionsMode.HOVER_DOWN, CellActionsMode.INLINE], + defaultValue: CellActionsMode.HOVER_DOWN, control: { type: 'radio', }, @@ -72,8 +72,14 @@ export const CellActionInline = ({}: {}) => ( ); -export const CellActionHoverPopup = ({}: {}) => ( - +export const CellActionHoverPopoverDown = ({}: {}) => ( + + Hover me + +); + +export const CellActionHoverPopoverRight = ({}: {}) => ( + Hover me ); diff --git a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx index ec266889fe255..36d0482ebf84d 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx @@ -15,6 +15,11 @@ import { CellActionsProvider } from '../context/cell_actions_context'; const TRIGGER_ID = 'test-trigger-id'; const FIELD = { name: 'name', value: '123', type: 'text' }; +jest.mock('./hover_actions_popover', () => ({ + HoverActionsPopover: jest.fn((props) => ( + {props.anchorPosition} + )), +})); describe('CellActions', () => { it('renders', async () => { const getActionsPromise = Promise.resolve([]); @@ -54,13 +59,33 @@ describe('CellActions', () => { expect(queryByTestId('inlineActions')).toBeInTheDocument(); }); - it('renders HoverActionsPopover when mode is HOVER', async () => { + it('renders HoverActionsPopover when mode is HOVER_DOWN', async () => { const getActionsPromise = Promise.resolve([]); const getActions = () => getActionsPromise; - const { queryByTestId } = render( + const { getByTestId } = render( + + + Field value + + + ); + + await act(async () => { + await getActionsPromise; + }); + + expect(getByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toHaveTextContent('downCenter'); + }); + + it('renders HoverActionsPopover when mode is HOVER_RIGHT', async () => { + const getActionsPromise = Promise.resolve([]); + const getActions = () => getActionsPromise; + + const { getByTestId } = render( - + Field value @@ -70,6 +95,7 @@ describe('CellActions', () => { await getActionsPromise; }); - expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toHaveTextContent('rightCenter'); }); }); diff --git a/packages/kbn-cell-actions/src/components/cell_actions.tsx b/packages/kbn-cell-actions/src/components/cell_actions.tsx index f6d3288ed6f7d..df6f957575c20 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.tsx @@ -36,11 +36,17 @@ export const CellActions: React.FC = ({ [field, triggerId, metadata] ); + const anchorPosition = useMemo( + () => (mode === CellActionsMode.HOVER_DOWN ? 'downCenter' : 'rightCenter'), + [mode] + ); + const dataTestSubj = `cellActions-renderContent-${field.name}`; - if (mode === CellActionsMode.HOVER) { + if (mode === CellActionsMode.HOVER_DOWN || mode === CellActionsMode.HOVER_RIGHT) { return (
= ({ {children} {}, + actions: [], + button: , +}; describe('ExtraActionsPopOver', () => { it('renders', () => { - const { queryByTestId } = render( - {}} - actions={[]} - button={} - /> - ); + const { queryByTestId } = render(); expect(queryByTestId('extraActionsPopOver')).toBeInTheDocument(); }); @@ -33,11 +33,10 @@ describe('ExtraActionsPopOver', () => { const action = { ...makeAction('test-action'), execute: executeAction }; const { getByLabelText } = render( } /> ); @@ -56,13 +55,7 @@ describe('ExtraActionsPopOverWithAnchor', () => { it('renders', () => { const { queryByTestId } = render( - {}} - actions={[]} - anchorRef={{ current: anchorElement }} - /> + ); expect(queryByTestId('extraActionsPopOverWithAnchor')).toBeInTheDocument(); @@ -74,7 +67,7 @@ describe('ExtraActionsPopOverWithAnchor', () => { const action = { ...makeAction('test-action'), execute: executeAction }; const { getByLabelText } = render( void; @@ -32,6 +33,7 @@ interface ActionsPopOverProps { } export const ExtraActionsPopOver: React.FC = ({ + anchorPosition, actions, actionContext, isOpen, @@ -43,7 +45,7 @@ export const ExtraActionsPopOver: React.FC = ({ isOpen={isOpen} closePopover={closePopOver} panelPaddingSize="xs" - anchorPosition={'downCenter'} + anchorPosition={anchorPosition} hasArrow repositionOnScroll ownFocus @@ -59,11 +61,15 @@ export const ExtraActionsPopOver: React.FC = ({ ); interface ExtraActionsPopOverWithAnchorProps - extends Pick { + extends Pick< + ActionsPopOverProps, + 'anchorPosition' | 'actionContext' | 'closePopOver' | 'isOpen' | 'actions' + > { anchorRef: React.RefObject; } export const ExtraActionsPopOverWithAnchor = ({ + anchorPosition, anchorRef, actionContext, isOpen, @@ -77,7 +83,7 @@ export const ExtraActionsPopOverWithAnchor = ({ isOpen={isOpen} closePopover={closePopOver} panelPaddingSize="xs" - anchorPosition={'downCenter'} + anchorPosition={anchorPosition} hasArrow={false} repositionOnScroll ownFocus diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx index b30ca63e52ec0..de68ccd6fca51 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx @@ -13,11 +13,17 @@ import { makeAction } from '../mocks/helpers'; import { CellActionExecutionContext } from '../types'; import { HoverActionsPopover } from './hover_actions_popover'; -describe('HoverActionsPopover', () => { - const actionContext = { +const defaultProps = { + anchorPosition: 'rightCenter' as const, + disabledActionTypes: [], + visibleCellActions: 4, + actionContext: { trigger: { id: 'triggerId' }, field: { name: 'fieldName' }, - } as CellActionExecutionContext; + } as CellActionExecutionContext, + showActionTooltips: false, +}; +describe('HoverActionsPopover', () => { const TestComponent = () => ; jest.useFakeTimers(); @@ -25,13 +31,7 @@ describe('HoverActionsPopover', () => { const getActions = () => Promise.resolve([]); const { queryByTestId } = render( - + ); expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); @@ -44,12 +44,7 @@ describe('HoverActionsPopover', () => { const { queryByLabelText, getByTestId } = render( - + @@ -70,12 +65,7 @@ describe('HoverActionsPopover', () => { const { queryByLabelText, getByTestId } = render( - + @@ -101,12 +91,7 @@ describe('HoverActionsPopover', () => { const { getByTestId } = render( - + @@ -127,12 +112,7 @@ describe('HoverActionsPopover', () => { const { getByTestId, getByLabelText } = render( - + @@ -162,12 +142,7 @@ describe('HoverActionsPopover', () => { const { getByTestId, queryByLabelText } = render( - + @@ -191,6 +166,44 @@ describe('HoverActionsPopover', () => { expect(queryByLabelText('test-action-2')).toBeInTheDocument(); expect(queryByLabelText('test-action-3')).toBeInTheDocument(); }); + it('does not add css positioning when anchorPosition = downCenter', async () => { + const getActionsPromise = Promise.resolve([makeAction('test-action')]); + const getActions = () => getActionsPromise; + + const { getByLabelText, getByTestId } = render( + + + + + + ); + + await hoverElement(getByTestId('test-component'), async () => { + await getActionsPromise; + jest.runAllTimers(); + }); + + expect(Object.values(getByLabelText('Actions').style).includes('margin-top')).toEqual(false); + }); + it('adds css positioning when anchorPosition = rightCenter', async () => { + const getActionsPromise = Promise.resolve([makeAction('test-action')]); + const getActions = () => getActionsPromise; + + const { getByLabelText, getByTestId } = render( + + + + + + ); + + await hoverElement(getByTestId('test-component'), async () => { + await getActionsPromise; + jest.runAllTimers(); + }); + + expect(Object.values(getByLabelText('Actions').style).includes('margin-top')).toEqual(true); + }); }); const hoverElement = async (element: Element, waitForChange: () => Promise) => { diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx index 62ea3e766eaf7..dd087aa755307 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx @@ -36,6 +36,7 @@ const hoverContentWrapperCSS = css` const HOVER_INTENT_DELAY = 100; // ms interface Props { + anchorPosition: 'downCenter' | 'rightCenter'; children: React.ReactNode; visibleCellActions: number; actionContext: CellActionExecutionContext; @@ -44,6 +45,7 @@ interface Props { } export const HoverActionsPopover: React.FC = ({ + anchorPosition, children, visibleCellActions, actionContext, @@ -115,12 +117,17 @@ export const HoverActionsPopover: React.FC = ({ ); }, [onMouseEnter, closeExtraActions, children]); + const panelStyle = useMemo( + () => (anchorPosition === 'rightCenter' ? { marginTop: euiThemeVars.euiSizeS } : {}), + [anchorPosition] + ); + return ( <>
= ({
{ - const actionContext = { trigger: { id: 'triggerId' } } as CellActionExecutionContext; it('renders', async () => { const getActionsPromise = Promise.resolve([]); const getActions = () => getActionsPromise; const { queryByTestId } = render( - + ); @@ -47,12 +48,7 @@ describe('InlineActions', () => { const getActions = () => getActionsPromise; const { queryAllByRole } = render( - + ); diff --git a/packages/kbn-cell-actions/src/components/inline_actions.tsx b/packages/kbn-cell-actions/src/components/inline_actions.tsx index cd2cfcc88edf0..c46fb5c57af76 100644 --- a/packages/kbn-cell-actions/src/components/inline_actions.tsx +++ b/packages/kbn-cell-actions/src/components/inline_actions.tsx @@ -17,6 +17,7 @@ import { useLoadActions } from '../hooks/use_load_actions'; interface InlineActionsProps { actionContext: CellActionExecutionContext; + anchorPosition: 'rightCenter' | 'downCenter'; showActionTooltips: boolean; visibleCellActions: number; disabledActionTypes: string[]; @@ -24,6 +25,7 @@ interface InlineActionsProps { export const InlineActions: React.FC = ({ actionContext, + anchorPosition, showActionTooltips, visibleCellActions, disabledActionTypes, @@ -47,10 +49,9 @@ export const InlineActions: React.FC = ({ data-test-subj="inlineActions" className={`inlineActions ${isPopoverOpen ? 'inlineActions-popoverOpen' : ''}`} > - {visibleActions.map((action, index) => ( - + {visibleActions.map((action) => ( + = ({ ; + +/* eslint-disable-next-line @typescript-eslint/consistent-type-definitions */ +export type MapAttributes = { + title: string; + description?: string; + mapStateJSON?: string; + layerListJSON?: string; + uiStateJSON?: string; +}; + +export type MapItem = MapCrudTypes['Item']; +export type PartialMapItem = MapCrudTypes['PartialItem']; + +// ----------- GET -------------- + +export type MapGetIn = MapCrudTypes['GetIn']; +export type MapGetOut = MapCrudTypes['GetOut']; + +// ----------- CREATE -------------- + +export type MapCreateIn = MapCrudTypes['CreateIn']; +export type MapCreateOut = MapCrudTypes['CreateOut']; +export type MapCreateOptions = CreateOptions; + +// ----------- UPDATE -------------- + +export type MapUpdateIn = MapCrudTypes['UpdateIn']; +export type MapUpdateOut = MapCrudTypes['UpdateOut']; +export type MapUpdateOptions = UpdateOptions; + +// ----------- DELETE -------------- + +export type MapDeleteIn = MapCrudTypes['DeleteIn']; +export type MapDeleteOut = MapCrudTypes['DeleteOut']; + +// ----------- SEARCH -------------- + +export type MapSearchIn = MapCrudTypes['SearchIn']; +export type MapSearchOut = MapCrudTypes['SearchOut']; +export type MapSearchOptions = SearchOptions; + +``` + + diff --git a/packages/kbn-content-management-utils/index.ts b/packages/kbn-content-management-utils/index.ts new file mode 100644 index 0000000000000..12594660136d8 --- /dev/null +++ b/packages/kbn-content-management-utils/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 './types'; diff --git a/packages/kbn-content-management-utils/jest.config.js b/packages/kbn-content-management-utils/jest.config.js new file mode 100644 index 0000000000000..b1e7646521e26 --- /dev/null +++ b/packages/kbn-content-management-utils/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-content-management-utils'], +}; diff --git a/packages/kbn-content-management-utils/kibana.jsonc b/packages/kbn-content-management-utils/kibana.jsonc new file mode 100644 index 0000000000000..06779896a47c4 --- /dev/null +++ b/packages/kbn-content-management-utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/content-management-utils", + "owner": "@elastic/kibana-data-discovery" +} diff --git a/packages/kbn-content-management-utils/package.json b/packages/kbn-content-management-utils/package.json new file mode 100644 index 0000000000000..8b59a10c3d429 --- /dev/null +++ b/packages/kbn-content-management-utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/content-management-utils", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-content-management-utils/tsconfig.json b/packages/kbn-content-management-utils/tsconfig.json new file mode 100644 index 0000000000000..7de04c3c13451 --- /dev/null +++ b/packages/kbn-content-management-utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/content-management-plugin", + "@kbn/core-saved-objects-api-server", + ] +} diff --git a/packages/kbn-content-management-utils/types.ts b/packages/kbn-content-management-utils/types.ts new file mode 100644 index 0000000000000..1f15c69947f63 --- /dev/null +++ b/packages/kbn-content-management-utils/types.ts @@ -0,0 +1,283 @@ +/* + * 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 type { + GetIn, + GetResult, + CreateIn, + CreateResult, + SearchIn, + SearchResult, + UpdateIn, + UpdateResult, + DeleteIn, + DeleteResult, +} from '@kbn/content-management-plugin/common'; + +import type { + SortOrder, + AggregationsAggregationContainer, + SortResults, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { + MutatingOperationRefreshSetting, + SavedObjectsPitParams, + SavedObjectsFindOptionsReference, +} from '@kbn/core-saved-objects-api-server'; + +type KueryNode = any; + +export interface Reference { + type: string; + id: string; + name: string; +} + +/** Saved Object create options - Pick and Omit to customize */ +export interface SavedObjectCreateOptions { + /** (not recommended) Specify an id for the document */ + id?: string; + /** Overwrite existing documents (defaults to false) */ + overwrite?: boolean; + /** + * An opaque version number which changes on each successful write operation. + * Can be used in conjunction with `overwrite` for implementing optimistic concurrency control. + **/ + version?: string; + /** Array of referenced saved objects. */ + references?: Reference[]; + /** The Elasticsearch Refresh setting for this operation */ + refresh?: boolean; + /** + * Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in + * {@link SavedObjectsCreateOptions}. + * + * * For shareable object types (registered with `namespaceType: 'multiple'`): this option can be used to specify one or more spaces, + * including the "All spaces" identifier (`'*'`). + * * For isolated object types (registered with `namespaceType: 'single'` or `namespaceType: 'multiple-isolated'`): this option can only + * be used to specify a single space, and the "All spaces" identifier (`'*'`) is not allowed. + * * For global object types (registered with `namespaceType: 'agnostic'`): this option cannot be used. + */ + initialNamespaces?: string[]; +} + +/** Saved Object search options - Pick and Omit to customize */ +export interface SavedObjectSearchOptions { + /** the page of results to return */ + page?: number; + /** the number of objects per page */ + perPage?: number; + /** which field to sort by */ + sortField?: string; + /** sort order, ascending or descending */ + sortOrder?: SortOrder; + /** + * An array of fields to include in the results + * @example + * SavedObjects.find({type: 'dashboard', fields: ['attributes.name', 'attributes.location']}) + */ + fields?: string[]; + /** Search documents using the Elasticsearch Simple Query String syntax. See Elasticsearch Simple Query String `query` argument for more information */ + search?: string; + /** The fields to perform the parsed query against. See Elasticsearch Simple Query String `fields` argument for more information */ + searchFields?: string[]; + /** + * Use the sort values from the previous page to retrieve the next page of results. + */ + searchAfter?: SortResults; + /** + * The fields to perform the parsed query against. Unlike the `searchFields` argument, these are expected to be root fields and will not + * be modified. If used in conjunction with `searchFields`, both are concatenated together. + */ + rootSearchFields?: string[]; + /** + * Search for documents having a reference to the specified objects. + * Use `hasReferenceOperator` to specify the operator to use when searching for multiple references. + */ + hasReference?: SavedObjectsFindOptionsReference | SavedObjectsFindOptionsReference[]; + /** + * The operator to use when searching by multiple references using the `hasReference` option. Defaults to `OR` + */ + hasReferenceOperator?: 'AND' | 'OR'; + /** + * Search for documents *not* having a reference to the specified objects. + * Use `hasNoReferenceOperator` to specify the operator to use when searching for multiple references. + */ + hasNoReference?: SavedObjectsFindOptionsReference | SavedObjectsFindOptionsReference[]; + /** + * The operator to use when searching by multiple references using the `hasNoReference` option. Defaults to `OR` + */ + hasNoReferenceOperator?: 'AND' | 'OR'; + /** + * The search operator to use with the provided filter. Defaults to `OR` + */ + defaultSearchOperator?: 'AND' | 'OR'; + /** filter string for the search query */ + filter?: string | KueryNode; + /** + * A record of aggregations to perform. + * The API currently only supports a limited set of metrics and bucket aggregation types. + * Additional aggregation types can be contributed to Core. + * + * @example + * Aggregating on SO attribute field + * ```ts + * const aggs = { latest_version: { max: { field: 'dashboard.attributes.version' } } }; + * return client.find({ type: 'dashboard', aggs }) + * ``` + * + * @example + * Aggregating on SO root field + * ```ts + * const aggs = { latest_update: { max: { field: 'dashboard.updated_at' } } }; + * return client.find({ type: 'dashboard', aggs }) + * ``` + * + * @alpha + */ + aggs?: Record; + /** array of namespaces to search */ + namespaces?: string[]; + /** + * Search against a specific Point In Time (PIT) that you've opened with {@link SavedObjectsClient.openPointInTimeForType}. + */ + pit?: SavedObjectsPitParams; +} + +/** Saved Object update options - Pick and Omit to customize */ +export interface SavedObjectUpdateOptions { + /** Array of referenced saved objects. */ + references?: Reference[]; + version?: string; + /** The Elasticsearch Refresh setting for this operation */ + refresh?: MutatingOperationRefreshSetting; + /** If specified, will be used to perform an upsert if the object doesn't exist */ + upsert?: Attributes; + /** + * The Elasticsearch `retry_on_conflict` setting for this operation. + * Defaults to `0` when `version` is provided, `3` otherwise. + */ + retryOnConflict?: number; +} + +/** Return value for Saved Object get, T is item returned */ +export type GetResultSO = GetResult< + T, + { + outcome: 'exactMatch' | 'aliasMatch' | 'conflict'; + aliasTargetId?: string; + aliasPurpose?: 'savedObjectConversion' | 'savedObjectImport'; + } +>; + +/** + * Saved object with metadata + */ +export interface SOWithMetadata { + id: string; + type: string; + version?: string; + createdAt?: string; + updatedAt?: string; + error?: { + error: string; + message: string; + statusCode: number; + metadata?: Record; + }; + attributes: Attributes; + references: Reference[]; + namespaces?: string[]; + originId?: string; +} + +type PartialItem = Omit< + SOWithMetadata, + 'attributes' | 'references' +> & { + attributes: Partial; + references: Reference[] | undefined; +}; + +/** + * Types used by content management storage + * @argument ContentType - content management type. assumed to be the same as saved object type + * @argument Attributes - attributes of the saved object + */ +export interface ContentManagementCrudTypes< + ContentType extends string, + Attributes extends object, + CreateOptions extends object, + UpdateOptions extends object, + SearchOptions extends object +> { + /** + * Complete saved object + */ + Item: SOWithMetadata; + /** + * Partial saved object, used as output for update + */ + PartialItem: PartialItem; + /** + * Create options + */ + CreateOptions: CreateOptions; + /** + * Update options + */ + UpdateOptions: UpdateOptions; + /** + * Search options + */ + SearchOptions: SearchOptions; + /** + * Get item params + */ + GetIn: GetIn; + /** + * Get item result + */ + GetOut: GetResultSO>; + /** + * Create item params + */ + CreateIn: CreateIn; + /** + * Create item result + */ + CreateOut: CreateResult>; + + /** + * Search item params + */ + SearchIn: SearchIn; + /** + * Search item result + */ + SearchOut: SearchResult>; + + /** + * Update item params + */ + UpdateIn: UpdateIn; + /** + * Update item result + */ + UpdateOut: UpdateResult>; + + /** + * Delete item params + */ + DeleteIn: DeleteIn; + /** + * Delete item result + */ + DeleteOut: DeleteResult; +} diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index e5b1f29bec25f..29ac5472e37dd 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -129,8 +129,15 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { bulkApi: `${ELASTICSEARCH_DOCS}docs-bulk.html`, configuration: `${ENTERPRISE_SEARCH_DOCS}configuration.html`, connectors: `${ENTERPRISE_SEARCH_DOCS}connectors.html`, + connectorsAzureBlobStorage: `${ENTERPRISE_SEARCH_DOCS}connectors-azure-blob.html`, + connectorsGoogleCloudStorage: `${ENTERPRISE_SEARCH_DOCS}connectors-google-cloud.html`, connectorsMongoDB: `${ENTERPRISE_SEARCH_DOCS}connectors-mongodb.html`, connectorsMySQL: `${ENTERPRISE_SEARCH_DOCS}connectors-mysql.html`, + connectorsMicrosoftSQL: `${ENTERPRISE_SEARCH_DOCS}connectors-ms-sql.html`, + connectorsNetworkDrive: `${ENTERPRISE_SEARCH_DOCS}connectors-network-drive.html`, + connectorsOracle: `${ENTERPRISE_SEARCH_DOCS}connectors-oracle.html`, + connectorsPostgreSQL: `${ENTERPRISE_SEARCH_DOCS}connectors-postgresql.html`, + connectorsS3: `${ENTERPRISE_SEARCH_DOCS}connectors-s3.html`, connectorsWorkplaceSearch: `${ENTERPRISE_SEARCH_DOCS}connectors.html#connectors-workplace-search`, crawlerExtractionRules: `${ENTERPRISE_SEARCH_DOCS}crawler-extraction-rules.html`, crawlerManaging: `${ENTERPRISE_SEARCH_DOCS}crawler-managing.html`, @@ -139,12 +146,14 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { documentLevelSecurity: `${ELASTICSEARCH_DOCS}document-level-security.html`, elser: `${MACHINE_LEARNING_DOCS}ml-nlp-elser.html`, engines: `${ENTERPRISE_SEARCH_DOCS}engines.html`, + ingestionApis: `${ENTERPRISE_SEARCH_DOCS}ingestion-apis.html`, ingestPipelines: `${ENTERPRISE_SEARCH_DOCS}ingest-pipelines.html`, languageAnalyzers: `${ELASTICSEARCH_DOCS}analysis-lang-analyzer.html`, languageClients: `${ENTERPRISE_SEARCH_DOCS}programming-language-clients.html`, licenseManagement: `${ENTERPRISE_SEARCH_DOCS}license-management.html`, machineLearningStart: `${ENTERPRISE_SEARCH_DOCS}machine-learning-start.html`, mailService: `${ENTERPRISE_SEARCH_DOCS}mailer-configuration.html`, + mlDocumentEnrichment: `${ENTERPRISE_SEARCH_DOCS}document-enrichment.html`, start: `${ENTERPRISE_SEARCH_DOCS}start.html`, syncRules: `${ENTERPRISE_SEARCH_DOCS}sync-rules.html`, troubleshootSetup: `${ENTERPRISE_SEARCH_DOCS}troubleshoot-setup.html`, @@ -410,6 +419,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { dashboardSettings: `${KIBANA_DOCS}advanced-options.html#kibana-dashboard-settings`, indexManagement: `${ELASTICSEARCH_DOCS}index-mgmt.html`, kibanaSearchSettings: `${KIBANA_DOCS}advanced-options.html#kibana-search-settings`, + discoverSettings: `${KIBANA_DOCS}advanced-options.html#kibana-discover-settings`, visualizationSettings: `${KIBANA_DOCS}advanced-options.html#kibana-visualization-settings`, timelionSettings: `${KIBANA_DOCS}advanced-options.html#kibana-timelion-settings`, savedObjectsApiList: `${KIBANA_DOCS}saved-objects-api.html#saved-objects-api`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index d1efdada21026..efe5e95f238d0 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -114,8 +114,15 @@ export interface DocLinks { readonly bulkApi: string; readonly configuration: string; readonly connectors: string; + readonly connectorsAzureBlobStorage: string; + readonly connectorsGoogleCloudStorage: string; + readonly connectorsMicrosoftSQL: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; + readonly connectorsNetworkDrive: string; + readonly connectorsOracle: string; + readonly connectorsPostgreSQL: string; + readonly connectorsS3: string; readonly connectorsWorkplaceSearch: string; readonly crawlerExtractionRules: string; readonly crawlerManaging: string; @@ -124,12 +131,14 @@ export interface DocLinks { readonly documentLevelSecurity: string; readonly elser: string; readonly engines: string; + readonly ingestionApis: string; readonly ingestPipelines: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly machineLearningStart: string; readonly mailService: string; + readonly mlDocumentEnrichment: string; readonly start: string; readonly syncRules: string; readonly troubleshootSetup: string; diff --git a/packages/kbn-dom-drag-drop/index.ts b/packages/kbn-dom-drag-drop/index.ts index 618613deca73e..7a7f599934890 100644 --- a/packages/kbn-dom-drag-drop/index.ts +++ b/packages/kbn-dom-drag-drop/index.ts @@ -16,6 +16,8 @@ export { RootDragDropProvider, ChildDragDropProvider, ReorderProvider, + DropOverlayWrapper, + type DropOverlayWrapperProps, } from './src'; export { DropTargetSwapDuplicateCombine } from './src/drop_targets'; diff --git a/packages/kbn-dom-drag-drop/src/constants.ts b/packages/kbn-dom-drag-drop/src/constants.ts index 2cc21a99c7013..e6b211c314048 100644 --- a/packages/kbn-dom-drag-drop/src/constants.ts +++ b/packages/kbn-dom-drag-drop/src/constants.ts @@ -7,3 +7,5 @@ */ export const DEFAULT_DATA_TEST_SUBJ = 'domDragDrop'; +export const REORDER_ITEM_HEIGHT = 32; +export const REORDER_ITEM_MARGIN = 8; diff --git a/packages/kbn-dom-drag-drop/src/drag_drop.test.tsx b/packages/kbn-dom-drag-drop/src/drag_drop.test.tsx index 498d6a0fa8664..fac4b76f2a33d 100644 --- a/packages/kbn-dom-drag-drop/src/drag_drop.test.tsx +++ b/packages/kbn-dom-drag-drop/src/drag_drop.test.tsx @@ -468,7 +468,7 @@ describe('DragDrop', () => { ghost: { children: , style: { - height: 0, + minHeight: 0, width: 0, }, }, @@ -1100,12 +1100,12 @@ describe('DragDrop', () => { expect( component.find('[data-test-subj="testDragDrop-translatableDrop"]').at(0).prop('style') ).toEqual({ - transform: 'translateY(-8px)', + transform: 'translateY(-4px)', }); expect( component.find('[data-test-subj="testDragDrop-translatableDrop"]').at(1).prop('style') ).toEqual({ - transform: 'translateY(-8px)', + transform: 'translateY(-4px)', }); component @@ -1258,12 +1258,12 @@ describe('DragDrop', () => { expect( component.find('[data-test-subj="testDragDrop-reorderableDrag"]').at(0).prop('style') ).toEqual({ - transform: 'translateY(+8px)', + transform: 'translateY(+4px)', }); expect( component.find('[data-test-subj="testDragDrop-translatableDrop"]').at(0).prop('style') ).toEqual({ - transform: 'translateY(-40px)', + transform: 'translateY(-32px)', }); expect( component.find('[data-test-subj="testDragDrop-translatableDrop"]').at(1).prop('style') diff --git a/packages/kbn-dom-drag-drop/src/drag_drop.tsx b/packages/kbn-dom-drag-drop/src/drag_drop.tsx index 47909a818cea9..ce3d6147d813f 100644 --- a/packages/kbn-dom-drag-drop/src/drag_drop.tsx +++ b/packages/kbn-dom-drag-drop/src/drag_drop.tsx @@ -24,6 +24,7 @@ import { Ghost, } from './providers'; import { DropType } from './types'; +import { REORDER_ITEM_MARGIN } from './constants'; import './sass/drag_drop.scss'; /** @@ -71,11 +72,15 @@ interface BaseProps { * The React element which will be passed the draggable handlers */ children: ReactElement; + + /** + * Disable any drag & drop behaviour + */ + isDisabled?: boolean; /** * Indicates whether or not this component is draggable. */ draggable?: boolean; - /** * Additional class names to apply when another element is over the drop target */ @@ -152,7 +157,7 @@ interface DropsInnerProps extends BaseProps { isNotDroppable: boolean; } -const lnsLayerPanelDimensionMargin = 8; +const REORDER_OFFSET = REORDER_ITEM_MARGIN / 2; /** * DragDrop component @@ -174,6 +179,10 @@ export const DragDrop = (props: BaseProps) => { onTrackUICounterEvent, } = useContext(DragContext); + if (props.isDisabled) { + return props.children; + } + const { value, draggable, dropTypes, reorderableGroup } = props; const isDragging = !!(draggable && value.id === dragging?.id); @@ -358,7 +367,7 @@ const DragInner = memo(function DragInner({ ghost: keyboardModeOn ? { children, - style: { width: currentTarget.offsetWidth, height: currentTarget.offsetHeight }, + style: { width: currentTarget.offsetWidth, minHeight: currentTarget?.offsetHeight }, } : undefined, }); @@ -417,12 +426,14 @@ const DragInner = memo(function DragInner({ keyboardMode && activeDropTarget && activeDropTarget.dropType !== 'reorder'; + return (
@@ -772,7 +783,7 @@ const ReorderableDrag = memo(function ReorderableDrag( | KeyboardEvent['currentTarget'] ) => { if (currentTarget) { - const height = currentTarget.offsetHeight + lnsLayerPanelDimensionMargin; + const height = currentTarget.offsetHeight + REORDER_OFFSET; setReorderState((s: ReorderState) => ({ ...s, draggingHeight: height, @@ -875,7 +886,7 @@ const ReorderableDrag = memo(function ReorderableDrag( areItemsReordered ? { transform: `translateY(${direction === '+' ? '-' : '+'}${reorderedItems.reduce( - (acc, cur) => acc + Number(cur.height || 0) + lnsLayerPanelDimensionMargin, + (acc, cur) => acc + Number(cur.height || 0) + REORDER_OFFSET, 0 )}px)`, } diff --git a/packages/kbn-dom-drag-drop/src/drop_overlay_wrapper.tsx b/packages/kbn-dom-drag-drop/src/drop_overlay_wrapper.tsx new file mode 100644 index 0000000000000..590106157f304 --- /dev/null +++ b/packages/kbn-dom-drag-drop/src/drop_overlay_wrapper.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 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 classnames from 'classnames'; + +/** + * DropOverlayWrapper Props + */ +export interface DropOverlayWrapperProps { + isVisible: boolean; + className?: string; + overlayProps?: object; +} + +/** + * This prevents the in-place droppable styles (under children) and allows to rather show an overlay with droppable styles (on top of children) + * @param isVisible + * @param children + * @param overlayProps + * @param className + * @param otherProps + * @constructor + */ +export const DropOverlayWrapper: React.FC = ({ + isVisible, + children, + overlayProps, + className, + ...otherProps +}) => { + return ( +
+ {children} + {isVisible && ( +
+ )} +
+ ); +}; diff --git a/packages/kbn-dom-drag-drop/src/index.ts b/packages/kbn-dom-drag-drop/src/index.ts index 340dba349612c..9fa4f37103522 100644 --- a/packages/kbn-dom-drag-drop/src/index.ts +++ b/packages/kbn-dom-drag-drop/src/index.ts @@ -9,3 +9,4 @@ export * from './types'; export * from './providers'; export * from './drag_drop'; +export { DropOverlayWrapper, type DropOverlayWrapperProps } from './drop_overlay_wrapper'; diff --git a/packages/kbn-dom-drag-drop/src/providers/reorder_provider.tsx b/packages/kbn-dom-drag-drop/src/providers/reorder_provider.tsx index 5dc83dbce915f..c2aa11eb8bc8d 100644 --- a/packages/kbn-dom-drag-drop/src/providers/reorder_provider.tsx +++ b/packages/kbn-dom-drag-drop/src/providers/reorder_provider.tsx @@ -8,7 +8,7 @@ import React, { useState, useMemo } from 'react'; import classNames from 'classnames'; -import { DEFAULT_DATA_TEST_SUBJ } from '../constants'; +import { DEFAULT_DATA_TEST_SUBJ, REORDER_ITEM_HEIGHT } from '../constants'; /** * Reorder state @@ -54,7 +54,7 @@ export const ReorderContext = React.createContext({ reorderState: { reorderedItems: [], direction: '-', - draggingHeight: 40, + draggingHeight: REORDER_ITEM_HEIGHT, isReorderOn: false, groupId: '', }, @@ -66,6 +66,7 @@ export const ReorderContext = React.createContext({ * @param id * @param children * @param className + * @param draggingHeight * @param dataTestSubj * @constructor */ @@ -73,17 +74,19 @@ export function ReorderProvider({ id, children, className, + draggingHeight = REORDER_ITEM_HEIGHT, dataTestSubj = DEFAULT_DATA_TEST_SUBJ, }: { id: string; children: React.ReactNode; className?: string; + draggingHeight?: number; dataTestSubj?: string; }) { const [state, setState] = useState({ reorderedItems: [], direction: '-', - draggingHeight: 40, + draggingHeight, isReorderOn: false, groupId: id, }); diff --git a/packages/kbn-dom-drag-drop/src/sass/drag_drop.scss b/packages/kbn-dom-drag-drop/src/sass/drag_drop.scss index d5587ae8ec6a4..56ce648266e7b 100644 --- a/packages/kbn-dom-drag-drop/src/sass/drag_drop.scss +++ b/packages/kbn-dom-drag-drop/src/sass/drag_drop.scss @@ -1,7 +1,6 @@ @import './drag_drop_mixins'; .domDragDrop { - user-select: none; transition: $euiAnimSpeedFast ease-in-out; transition-property: background-color, border-color, opacity; z-index: $domDragDropZLevel1; @@ -12,11 +11,11 @@ border: $euiBorderWidthThin dashed $euiBorderColor; position: absolute !important; // sass-lint:disable-line no-important margin: 0 !important; // sass-lint:disable-line no-important - bottom: 100%; + top: 0; width: 100%; left: 0; opacity: .9; - transform: translate(-12px, 8px); + transform: translate($euiSizeXS, $euiSizeXS); z-index: $domDragDropZLevel3; pointer-events: none; outline: $euiFocusRingSize solid currentColor; // Safari & Firefox @@ -29,7 +28,8 @@ @include mixinDomDragDropHover; // Include a possible nested button like when using FieldButton - > .kbnFieldButton__button { + & .kbnFieldButton__button, + & .euiLink { cursor: grab; } @@ -39,14 +39,17 @@ } // Drop area -.domDragDrop-isDroppable { +.domDragDrop-isDroppable:not(.domDragDrop__dropOverlayWrapper) { @include mixinDomDroppable; } // Drop area when there's an item being dragged .domDragDrop-isDropTarget { - @include mixinDomDroppable; - @include mixinDomDroppableActive; + &:not(.domDragDrop__dropOverlayWrapper) { + @include mixinDomDroppable; + @include mixinDomDroppableActive; + } + > * { pointer-events: none; } @@ -57,7 +60,7 @@ } // Drop area while hovering with item -.domDragDrop-isActiveDropTarget { +.domDragDrop-isActiveDropTarget:not(.domDragDrop__dropOverlayWrapper) { z-index: $domDragDropZLevel3; @include mixinDomDroppableActiveHover; } @@ -72,9 +75,9 @@ text-decoration: line-through; } -.domDragDrop-notCompatible { +.domDragDrop-notCompatible:not(.domDragDrop__dropOverlayWrapper) { background-color: $euiColorHighlight !important; - border: $euiBorderWidthThin dashed $euiBorderColor !important; + border: $euiBorderWidthThin dashed $euiColorVis5 !important; &.domDragDrop-isActiveDropTarget { background-color: rgba(251, 208, 17, .25) !important; border-color: $euiColorVis5 !important; @@ -91,12 +94,12 @@ } } -$lnsLayerPanelDimensionMargin: 8px; +$reorderItemMargin: $euiSizeS; .domDragDrop__reorderableDrop { position: absolute; width: 100%; top: 0; - height: calc(100% + #{$lnsLayerPanelDimensionMargin}); + height: calc(100% + #{$reorderItemMargin / 2}); } .domDragDrop-translatableDrop { @@ -146,6 +149,10 @@ $lnsLayerPanelDimensionMargin: 8px; } } +.domDragDrop--isDragStarted { + opacity: .5; +} + .domDragDrop__extraDrops { opacity: 0; visibility: hidden; @@ -193,7 +200,7 @@ $lnsLayerPanelDimensionMargin: 8px; .domDragDrop__extraDrop { position: relative; - height: $euiSizeXS * 10; + height: $euiSizeXS * 8; min-width: $euiSize * 7; color: $euiColorSuccessText; padding: $euiSizeXS; @@ -201,3 +208,28 @@ $lnsLayerPanelDimensionMargin: 8px; color: $euiColorWarningText; } } + +.domDragDrop__dropOverlayWrapper { + position: relative; + height: 100%; +} + +.domDragDrop__dropOverlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: $domDragDropZLevel3; + transition: $euiAnimSpeedFast ease-in-out; + transition-property: background-color, border-color, opacity; + + .domDragDrop-isDropTarget & { + @include mixinDomDroppable($euiBorderWidthThick); + @include mixinDomDroppableActive($euiBorderWidthThick); + } + + .domDragDrop-isActiveDropTarget & { + @include mixinDomDroppableActiveHover($euiBorderWidthThick); + } +} diff --git a/packages/kbn-dom-drag-drop/src/sass/drag_drop_mixins.scss b/packages/kbn-dom-drag-drop/src/sass/drag_drop_mixins.scss index fbd460a67f7d0..fb495b4fcfba4 100644 --- a/packages/kbn-dom-drag-drop/src/sass/drag_drop_mixins.scss +++ b/packages/kbn-dom-drag-drop/src/sass/drag_drop_mixins.scss @@ -13,31 +13,32 @@ $domDragDropZLevel3: 3; @mixin mixinDomDraggable { @include euiSlightShadow; background: $euiColorEmptyShade; - border: $euiBorderWidthThin dashed transparent; cursor: grab; } // Static styles for a drop area -@mixin mixinDomDroppable { - border: $euiBorderWidthThin dashed $euiBorderColor !important; +@mixin mixinDomDroppable($borderWidth: $euiBorderWidthThin) { + border: $borderWidth dashed transparent; } // Hovering state for drag item and drop area @mixin mixinDomDragDropHover { &:hover { - border: $euiBorderWidthThin dashed $euiColorMediumShade !important; + transform: translateX($euiSizeXS); + transition: transform $euiAnimSpeedSlow ease-out; } } // Style for drop area when there's an item being dragged -@mixin mixinDomDroppableActive { +@mixin mixinDomDroppableActive($borderWidth: $euiBorderWidthThin) { background-color: transparentize($euiColorVis0, .9) !important; + border: $borderWidth dashed $euiColorVis0 !important; } // Style for drop area while hovering with item -@mixin mixinDomDroppableActiveHover { +@mixin mixinDomDroppableActiveHover($borderWidth: $euiBorderWidthThin) { background-color: transparentize($euiColorVis0, .75) !important; - border: $euiBorderWidthThin dashed $euiColorVis0 !important; + border: $borderWidth dashed $euiColorVis0 !important; } // Style for drop area that is not allowed for current item diff --git a/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts b/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts index c9b67e4745d45..2f02b51be5501 100644 --- a/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts +++ b/packages/kbn-es-archiver/src/actions/empty_kibana_index.ts @@ -10,7 +10,8 @@ import type { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; import { KbnClient } from '@kbn/test'; -import { migrateKibanaIndex, createStats, cleanKibanaIndices } from '../lib'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; +import { migrateSavedObjectIndices, createStats, cleanSavedObjectIndices } from '../lib'; export async function emptyKibanaIndexAction({ client, @@ -23,8 +24,8 @@ export async function emptyKibanaIndexAction({ }) { const stats = createStats('emptyKibanaIndex', log); - await cleanKibanaIndices({ client, stats, log }); - await migrateKibanaIndex(kbnClient); - stats.createdIndex('.kibana'); + await cleanSavedObjectIndices({ client, stats, log }); + await migrateSavedObjectIndices(kbnClient); + ALL_SAVED_OBJECT_INDICES.forEach((indexPattern) => stats.createdIndex(indexPattern)); return stats.toJSON(); } diff --git a/packages/kbn-es-archiver/src/actions/load.ts b/packages/kbn-es-archiver/src/actions/load.ts index b6a4c46d42743..1cbdff1883271 100644 --- a/packages/kbn-es-archiver/src/actions/load.ts +++ b/packages/kbn-es-archiver/src/actions/load.ts @@ -14,6 +14,7 @@ import { REPO_ROOT } from '@kbn/repo-info'; import type { KbnClient } from '@kbn/test'; import type { Client } from '@elastic/elasticsearch'; import { createPromiseFromStreams, concatStreamProviders } from '@kbn/utils'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { ES_CLIENT_HEADERS } from '../client_headers'; import { @@ -24,7 +25,7 @@ import { createParseArchiveStreams, createCreateIndexStream, createIndexDocRecordsStream, - migrateKibanaIndex, + migrateSavedObjectIndices, Progress, createDefaultSpace, } from '../lib'; @@ -104,14 +105,15 @@ export async function loadAction({ } ); - // If we affected the Kibana index, we need to ensure it's migrated... - if (Object.keys(result).some((k) => k.startsWith('.kibana'))) { - await migrateKibanaIndex(kbnClient); + // If we affected saved objects indices, we need to ensure they are migrated... + if (Object.keys(result).some((k) => k.startsWith(MAIN_SAVED_OBJECT_INDEX))) { + await migrateSavedObjectIndices(kbnClient); log.debug('[%s] Migrated Kibana index after loading Kibana data', name); if (kibanaPluginIds.includes('spaces')) { - await createDefaultSpace({ client, index: '.kibana' }); - log.debug('[%s] Ensured that default space exists in .kibana', name); + // WARNING affected by #104081. Assumes 'spaces' saved objects are stored in MAIN_SAVED_OBJECT_INDEX + await createDefaultSpace({ client, index: MAIN_SAVED_OBJECT_INDEX }); + log.debug(`[%s] Ensured that default space exists in ${MAIN_SAVED_OBJECT_INDEX}`, name); } } diff --git a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts index 6e3310a7347e7..f0c6db9c89fcb 100644 --- a/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/generate_doc_records_stream.ts @@ -8,6 +8,7 @@ import { Transform } from 'stream'; import type { Client } from '@elastic/elasticsearch'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Stats } from '../stats'; import { Progress } from '../progress'; import { ES_CLIENT_HEADERS } from '../../client_headers'; @@ -78,7 +79,9 @@ export function createGenerateDocRecordsStream({ // if keepIndexNames is false, rewrite the .kibana_* index to .kibana_1 so that // when it is loaded it can skip migration, if possible index: - hit._index.startsWith('.kibana') && !keepIndexNames ? '.kibana_1' : hit._index, + hit._index.startsWith(MAIN_SAVED_OBJECT_INDEX) && !keepIndexNames + ? `${MAIN_SAVED_OBJECT_INDEX}_1` + : hit._index, data_stream: dataStream, id: hit._id, source: hit._source, diff --git a/packages/kbn-es-archiver/src/lib/index.ts b/packages/kbn-es-archiver/src/lib/index.ts index 8a857fb24002a..b9c8a8688c654 100644 --- a/packages/kbn-es-archiver/src/lib/index.ts +++ b/packages/kbn-es-archiver/src/lib/index.ts @@ -12,9 +12,9 @@ export { createCreateIndexStream, createDeleteIndexStream, createGenerateIndexRecordsStream, - deleteKibanaIndices, - migrateKibanaIndex, - cleanKibanaIndices, + deleteSavedObjectIndices, + migrateSavedObjectIndices, + cleanSavedObjectIndices, createDefaultSpace, } from './indices'; diff --git a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.mock.ts b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.mock.ts index d17bd33fa07ab..7c0041ca46221 100644 --- a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.mock.ts +++ b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.mock.ts @@ -6,10 +6,12 @@ * Side Public License, v 1. */ -import type { deleteKibanaIndices } from './kibana_index'; +import type { deleteSavedObjectIndices } from './kibana_index'; -export const mockDeleteKibanaIndices = jest.fn() as jest.MockedFunction; +export const mockdeleteSavedObjectIndices = jest.fn() as jest.MockedFunction< + typeof deleteSavedObjectIndices +>; jest.mock('./kibana_index', () => ({ - deleteKibanaIndices: mockDeleteKibanaIndices, + deleteSavedObjectIndices: mockdeleteSavedObjectIndices, })); diff --git a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts index 15efa53921743..9e1caf53eb06b 100644 --- a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts +++ b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { mockDeleteKibanaIndices } from './create_index_stream.test.mock'; +import { mockdeleteSavedObjectIndices } from './create_index_stream.test.mock'; import sinon from 'sinon'; import Chance from 'chance'; @@ -28,7 +28,7 @@ const chance = new Chance(); const log = createStubLogger(); beforeEach(() => { - mockDeleteKibanaIndices.mockClear(); + mockdeleteSavedObjectIndices.mockClear(); }); describe('esArchiver: createCreateIndexStream()', () => { @@ -187,7 +187,7 @@ describe('esArchiver: createCreateIndexStream()', () => { }); }); - describe('deleteKibanaIndices', () => { + describe('deleteSavedObjectIndices', () => { function doTest(...indices: string[]) { return createPromiseFromStreams([ createListStream(indices.map((index) => createStubIndexRecord(index))), @@ -199,15 +199,15 @@ describe('esArchiver: createCreateIndexStream()', () => { it('does not delete Kibana indices for indexes that do not start with .kibana', async () => { await doTest('.foo'); - expect(mockDeleteKibanaIndices).not.toHaveBeenCalled(); + expect(mockdeleteSavedObjectIndices).not.toHaveBeenCalled(); }); it('deletes Kibana indices at most once for indices that start with .kibana', async () => { // If we are loading the main Kibana index, we should delete all Kibana indices for backwards compatibility reasons. await doTest('.kibana_7.16.0_001', '.kibana_task_manager_7.16.0_001'); - expect(mockDeleteKibanaIndices).toHaveBeenCalledTimes(1); - expect(mockDeleteKibanaIndices).toHaveBeenCalledWith( + expect(mockdeleteSavedObjectIndices).toHaveBeenCalledTimes(1); + expect(mockdeleteSavedObjectIndices).toHaveBeenCalledWith( expect.not.objectContaining({ onlyTaskManager: true }) ); }); @@ -216,8 +216,8 @@ describe('esArchiver: createCreateIndexStream()', () => { // If we are loading the Kibana task manager index, we should only delete that index, not any other Kibana indices. await doTest('.kibana_task_manager_7.16.0_001', '.kibana_task_manager_7.16.0_002'); - expect(mockDeleteKibanaIndices).toHaveBeenCalledTimes(1); - expect(mockDeleteKibanaIndices).toHaveBeenCalledWith( + expect(mockdeleteSavedObjectIndices).toHaveBeenCalledTimes(1); + expect(mockdeleteSavedObjectIndices).toHaveBeenCalledWith( expect.objectContaining({ onlyTaskManager: true }) ); }); @@ -227,12 +227,12 @@ describe('esArchiver: createCreateIndexStream()', () => { // So, we first delete only the Kibana task manager indices, then we wind up deleting all Kibana indices. await doTest('.kibana_task_manager_7.16.0_001', '.kibana_7.16.0_001'); - expect(mockDeleteKibanaIndices).toHaveBeenCalledTimes(2); - expect(mockDeleteKibanaIndices).toHaveBeenNthCalledWith( + expect(mockdeleteSavedObjectIndices).toHaveBeenCalledTimes(2); + expect(mockdeleteSavedObjectIndices).toHaveBeenNthCalledWith( 1, expect.objectContaining({ onlyTaskManager: true }) ); - expect(mockDeleteKibanaIndices).toHaveBeenNthCalledWith( + expect(mockdeleteSavedObjectIndices).toHaveBeenNthCalledWith( 2, expect.not.objectContaining({ onlyTaskManager: true }) ); diff --git a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts index 38f4bed755262..5b6245407f23f 100644 --- a/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/create_index_stream.ts @@ -14,8 +14,12 @@ import type { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { + MAIN_SAVED_OBJECT_INDEX, + TASK_MANAGER_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; import { Stats } from '../stats'; -import { deleteKibanaIndices } from './kibana_index'; +import { deleteSavedObjectIndices } from './kibana_index'; import { deleteIndex } from './delete_index'; import { deleteDataStream } from './delete_data_stream'; import { ES_CLIENT_HEADERS } from '../../client_headers'; @@ -96,8 +100,8 @@ export function createCreateIndexStream({ async function handleIndex(record: DocRecord) { const { index, settings, mappings, aliases } = record.value; - const isKibanaTaskManager = index.startsWith('.kibana_task_manager'); - const isKibana = index.startsWith('.kibana') && !isKibanaTaskManager; + const isKibanaTaskManager = index.startsWith(TASK_MANAGER_SAVED_OBJECT_INDEX); + const isKibana = index.startsWith(MAIN_SAVED_OBJECT_INDEX) && !isKibanaTaskManager; if (docsOnly) { return; @@ -106,10 +110,10 @@ export function createCreateIndexStream({ async function attemptToCreate(attemptNumber = 1) { try { if (isKibana && !kibanaIndexAlreadyDeleted) { - await deleteKibanaIndices({ client, stats, log }); // delete all .kibana* indices + await deleteSavedObjectIndices({ client, stats, log }); // delete all .kibana* indices kibanaIndexAlreadyDeleted = kibanaTaskManagerIndexAlreadyDeleted = true; } else if (isKibanaTaskManager && !kibanaTaskManagerIndexAlreadyDeleted) { - await deleteKibanaIndices({ client, stats, onlyTaskManager: true, log }); // delete only .kibana_task_manager* indices + await deleteSavedObjectIndices({ client, stats, onlyTaskManager: true, log }); // delete only .kibana_task_manager* indices kibanaTaskManagerIndexAlreadyDeleted = true; } diff --git a/packages/kbn-es-archiver/src/lib/indices/delete_index_stream.ts b/packages/kbn-es-archiver/src/lib/indices/delete_index_stream.ts index c7633465ccc4c..f9b04b447f124 100644 --- a/packages/kbn-es-archiver/src/lib/indices/delete_index_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/delete_index_stream.ts @@ -10,9 +10,10 @@ import { Transform } from 'stream'; import type { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Stats } from '../stats'; import { deleteIndex } from './delete_index'; -import { cleanKibanaIndices } from './kibana_index'; +import { cleanSavedObjectIndices } from './kibana_index'; import { deleteDataStream } from './delete_data_stream'; export function createDeleteIndexStream(client: Client, stats: Stats, log: ToolingLog) { @@ -28,8 +29,8 @@ export function createDeleteIndexStream(client: Client, stats: Stats, log: Tooli if (record.type === 'index') { const { index } = record.value; - if (index.startsWith('.kibana')) { - await cleanKibanaIndices({ client, stats, log }); + if (index.startsWith(MAIN_SAVED_OBJECT_INDEX)) { + await cleanSavedObjectIndices({ client, stats, log }); } else { await deleteIndex({ client, stats, log, index }); } diff --git a/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts b/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts index de32e93e27398..2f2dd60982a94 100644 --- a/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/indices/generate_index_records_stream.ts @@ -9,6 +9,7 @@ import type { Client } from '@elastic/elasticsearch'; import { Transform } from 'stream'; import { ToolingLog } from '@kbn/tooling-log'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Stats } from '../stats'; import { ES_CLIENT_HEADERS } from '../../client_headers'; import { getIndexTemplate } from '..'; @@ -100,7 +101,10 @@ export function createGenerateIndexRecordsStream({ value: { // if keepIndexNames is false, rewrite the .kibana_* index to .kibana_1 so that // when it is loaded it can skip migration, if possible - index: index.startsWith('.kibana') && !keepIndexNames ? '.kibana_1' : index, + index: + index.startsWith(MAIN_SAVED_OBJECT_INDEX) && !keepIndexNames + ? `${MAIN_SAVED_OBJECT_INDEX}_1` + : index, settings, mappings, aliases, diff --git a/packages/kbn-es-archiver/src/lib/indices/index.ts b/packages/kbn-es-archiver/src/lib/indices/index.ts index 0816ec31d091d..32abb365f1076 100644 --- a/packages/kbn-es-archiver/src/lib/indices/index.ts +++ b/packages/kbn-es-archiver/src/lib/indices/index.ts @@ -10,8 +10,8 @@ export { createCreateIndexStream } from './create_index_stream'; export { createDeleteIndexStream } from './delete_index_stream'; export { createGenerateIndexRecordsStream } from './generate_index_records_stream'; export { - migrateKibanaIndex, - deleteKibanaIndices, - cleanKibanaIndices, + migrateSavedObjectIndices, + deleteSavedObjectIndices, + cleanSavedObjectIndices, createDefaultSpace, } from './kibana_index'; diff --git a/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts b/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts index 6a02113bbf733..d45f9e67737a2 100644 --- a/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts +++ b/packages/kbn-es-archiver/src/lib/indices/kibana_index.ts @@ -11,14 +11,19 @@ import { inspect } from 'util'; import type { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; import { KbnClient } from '@kbn/test'; +import { + MAIN_SAVED_OBJECT_INDEX, + ALL_SAVED_OBJECT_INDICES, + TASK_MANAGER_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; import { Stats } from '../stats'; import { deleteIndex } from './delete_index'; import { ES_CLIENT_HEADERS } from '../../client_headers'; /** - * Deletes all indices that start with `.kibana`, or if onlyTaskManager==true, all indices that start with `.kibana_task_manager` + * Deletes all saved object indices, or if onlyTaskManager==true, it deletes task_manager indices */ -export async function deleteKibanaIndices({ +export async function deleteSavedObjectIndices({ client, stats, onlyTaskManager = false, @@ -29,8 +34,9 @@ export async function deleteKibanaIndices({ onlyTaskManager?: boolean; log: ToolingLog; }) { - const indexPattern = onlyTaskManager ? '.kibana_task_manager*' : '.kibana*'; - const indexNames = await fetchKibanaIndices(client, indexPattern); + const indexNames = (await fetchSavedObjectIndices(client)).filter( + (indexName) => !onlyTaskManager || indexName.includes(TASK_MANAGER_SAVED_OBJECT_INDEX) + ); if (!indexNames.length) { return; } @@ -60,27 +66,33 @@ export async function deleteKibanaIndices({ * builds up an object that implements just enough of the kbnMigrations interface * as is required by migrations. */ -export async function migrateKibanaIndex(kbnClient: KbnClient) { +export async function migrateSavedObjectIndices(kbnClient: KbnClient) { await kbnClient.savedObjects.migrate(); } /** - * Migrations mean that the Kibana index will look something like: - * .kibana, .kibana_1, .kibana_323, etc. This finds all indices starting - * with .kibana, then filters out any that aren't actually Kibana's core - * index (e.g. we don't want to remove .kibana_task_manager or the like). + * Check if the given index is a Kibana saved object index. + * This includes most .kibana_* + * but we must make sure that indices such as '.kibana_security_session_1' are NOT deleted. + * + * IMPORTANT + * Note that we can have more than 2 system indices (different SO types can go to different indices) + * ATM we have '.kibana', '.kibana_task_manager', '.kibana_cases' + * This method also takes into account legacy indices: .kibana_1, .kibana_task_manager_1. + * @param [index] the name of the index to check + * @returns boolean 'true' if the index is a Kibana saved object index. */ -function isKibanaIndex(index?: string): index is string { - return Boolean( - index && - (/^\.kibana(:?_\d*)?$/.test(index) || - /^\.kibana(_task_manager)?_(pre)?\d+\.\d+\.\d+/.test(index)) - ); + +const LEGACY_INDICES_REGEXP = new RegExp(`^(${ALL_SAVED_OBJECT_INDICES.join('|')})(:?_\\d*)?$`); +const INDICES_REGEXP = new RegExp(`^(${ALL_SAVED_OBJECT_INDICES.join('|')})_(pre)?\\d+.\\d+.\\d+`); + +function isSavedObjectIndex(index?: string): index is string { + return Boolean(index && (LEGACY_INDICES_REGEXP.test(index) || INDICES_REGEXP.test(index))); } -async function fetchKibanaIndices(client: Client, indexPattern: string) { +async function fetchSavedObjectIndices(client: Client) { const resp = await client.cat.indices( - { index: indexPattern, format: 'json' }, + { index: `${MAIN_SAVED_OBJECT_INDEX}*`, format: 'json' }, { headers: ES_CLIENT_HEADERS, } @@ -90,12 +102,12 @@ async function fetchKibanaIndices(client: Client, indexPattern: string) { throw new Error(`expected response to be an array ${inspect(resp)}`); } - return resp.map((x: { index?: string }) => x.index).filter(isKibanaIndex); + return resp.map((x: { index?: string }) => x.index).filter(isSavedObjectIndex); } const delay = (delayInMs: number) => new Promise((resolve) => setTimeout(resolve, delayInMs)); -export async function cleanKibanaIndices({ +export async function cleanSavedObjectIndices({ client, stats, log, @@ -107,7 +119,7 @@ export async function cleanKibanaIndices({ while (true) { const resp = await client.deleteByQuery( { - index: `.kibana,.kibana_task_manager`, + index: ALL_SAVED_OBJECT_INDICES, body: { query: { bool: { @@ -144,7 +156,7 @@ export async function cleanKibanaIndices({ `.kibana rather than deleting the whole index` ); - stats.deletedIndex('.kibana'); + ALL_SAVED_OBJECT_INDICES.forEach((indexPattern) => stats.deletedIndex(indexPattern)); } export async function createDefaultSpace({ index, client }: { index: string; client: Client }) { diff --git a/packages/kbn-es-archiver/tsconfig.json b/packages/kbn-es-archiver/tsconfig.json index 0301480548fc7..15fccdf68be4f 100644 --- a/packages/kbn-es-archiver/tsconfig.json +++ b/packages/kbn-es-archiver/tsconfig.json @@ -11,6 +11,7 @@ "**/*.ts" ], "kbn_references": [ + "@kbn/core-saved-objects-server", "@kbn/dev-utils", "@kbn/test", "@kbn/tooling-log", diff --git a/packages/kbn-expandable-flyout/index.ts b/packages/kbn-expandable-flyout/index.ts index e2ce15d85a399..cc423eb275090 100644 --- a/packages/kbn-expandable-flyout/index.ts +++ b/packages/kbn-expandable-flyout/index.ts @@ -7,7 +7,13 @@ */ export { ExpandableFlyout } from './src'; -export { ExpandableFlyoutProvider, useExpandableFlyoutContext } from './src/context'; +export { + ExpandableFlyoutProvider, + useExpandableFlyoutContext, + type ExpandableFlyoutContext, +} from './src/context'; + +export type { ExpandableFlyoutApi } from './src/context'; export type { ExpandableFlyoutProps } from './src'; export type { FlyoutPanel } from './src/types'; diff --git a/packages/kbn-expandable-flyout/src/context.tsx b/packages/kbn-expandable-flyout/src/context.tsx index 89e8210e9578f..b7ad721a2b9fd 100644 --- a/packages/kbn-expandable-flyout/src/context.tsx +++ b/packages/kbn-expandable-flyout/src/context.tsx @@ -6,7 +6,15 @@ * Side Public License, v 1. */ -import React, { createContext, useCallback, useContext, useMemo, useReducer } from 'react'; +import React, { + createContext, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useMemo, + useReducer, +} from 'react'; import { ActionType } from './actions'; import { reducer, State } from './reducer'; import type { FlyoutPanel } from './types'; @@ -59,19 +67,44 @@ export const ExpandableFlyoutContext = createContext & { + getState: () => State; +}; + export interface ExpandableFlyoutProviderProps { /** * React children */ children: React.ReactNode; + /** + * Triggered whenever flyout state changes. You can use it to store it's state somewhere for instance. + */ + onChanges?: (state: State) => void; + /** + * Triggered whenever flyout is closed. This is independent from the onChanges above. + */ + onClosePanels?: () => void; } /** * Wrap your plugin with this context for the ExpandableFlyout React component. */ -export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderProps) => { +export const ExpandableFlyoutProvider = React.forwardRef< + ExpandableFlyoutApi, + ExpandableFlyoutProviderProps +>(({ children, onChanges = () => {}, onClosePanels = () => {} }, ref) => { const [state, dispatch] = useReducer(reducer, initialState); + useEffect(() => { + const closed = !state.right; + if (closed) { + // manual close is singalled via separate callback + return; + } + + onChanges(state); + }, [state, onChanges]); + const openPanels = useCallback( ({ right, @@ -87,40 +120,45 @@ export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderP const openRightPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openRightPanel, payload: panel }), - [dispatch] + [] ); const openLeftPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openLeftPanel, payload: panel }), - [dispatch] + [] ); const openPreviewPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openPreviewPanel, payload: panel }), - [dispatch] + [] ); - const closeRightPanel = useCallback( - () => dispatch({ type: ActionType.closeRightPanel }), - [dispatch] - ); + const closeRightPanel = useCallback(() => dispatch({ type: ActionType.closeRightPanel }), []); - const closeLeftPanel = useCallback( - () => dispatch({ type: ActionType.closeLeftPanel }), - [dispatch] - ); + const closeLeftPanel = useCallback(() => dispatch({ type: ActionType.closeLeftPanel }), []); - const closePreviewPanel = useCallback( - () => dispatch({ type: ActionType.closePreviewPanel }), - [dispatch] - ); + const closePreviewPanel = useCallback(() => dispatch({ type: ActionType.closePreviewPanel }), []); const previousPreviewPanel = useCallback( () => dispatch({ type: ActionType.previousPreviewPanel }), - [dispatch] + [] ); - const closePanels = useCallback(() => dispatch({ type: ActionType.closeFlyout }), [dispatch]); + const closePanels = useCallback(() => { + dispatch({ type: ActionType.closeFlyout }); + onClosePanels(); + }, [onClosePanels]); + + useImperativeHandle( + ref, + () => { + return { + openFlyout: openPanels, + getState: () => state, + }; + }, + [openPanels, state] + ); const contextValue = useMemo( () => ({ @@ -154,7 +192,7 @@ export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderP {children} ); -}; +}); /** * Retrieve context's properties diff --git a/packages/kbn-expandable-flyout/src/reducer.ts b/packages/kbn-expandable-flyout/src/reducer.ts index 4901eccfc6bb4..bb0ff125f546e 100644 --- a/packages/kbn-expandable-flyout/src/reducer.ts +++ b/packages/kbn-expandable-flyout/src/reducer.ts @@ -105,5 +105,8 @@ export function reducer(state: State, action: Action) { preview: [], }; } + + default: + return state; } } diff --git a/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/get_failures.ts b/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/get_failures.ts index bb9ea6a425246..e3230e3cdddce 100644 --- a/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/get_failures.ts +++ b/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/get_failures.ts @@ -35,7 +35,10 @@ const getText = (node?: Array) => { const isLikelyIrrelevant = (name: string, failure: string) => { if ( failure.includes('NoSuchSessionError: This driver instance does not have a valid session ID') || - failure.includes('NoSuchSessionError: Tried to run command without establishing a connection') + failure.includes( + 'NoSuchSessionError: Tried to run command without establishing a connection' + ) || + failure.includes('NoSuchSessionError: invalid session id') ) { return true; } diff --git a/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/report_failure.ts b/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/report_failure.ts index e2750f15b8720..ed282b44325e3 100644 --- a/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/report_failure.ts +++ b/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/report_failure.ts @@ -19,12 +19,24 @@ export async function createFailureIssue( ) { const title = `Failing test: ${failure.classname} - ${failure.name}`; + // Github API body length maximum is 65536 characters + // Let's keep consistency with Mocha output that is truncated to 8192 characters + const failureMaxCharacters = 8192; + + const failureBody = + failure.failure.length <= failureMaxCharacters + ? failure.failure + : [ + failure.failure.substring(0, failureMaxCharacters), + `[report_failure] output truncated to ${failureMaxCharacters} characters`, + ].join('\n'); + const body = updateIssueMetadata( [ 'A test failed on a tracked branch', '', '```', - failure.failure, + failureBody, '```', '', `First failure: [CI Build - ${branch}](${buildUrl})`, diff --git a/packages/kbn-ftr-common-functional-services/services/kibana_server/extend_es_archiver.ts b/packages/kbn-ftr-common-functional-services/services/kibana_server/extend_es_archiver.ts index 98c28960bf523..4c2613d273c4a 100644 --- a/packages/kbn-ftr-common-functional-services/services/kibana_server/extend_es_archiver.ts +++ b/packages/kbn-ftr-common-functional-services/services/kibana_server/extend_es_archiver.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { ProvidedType } from '@kbn/test'; import type { EsArchiverProvider } from '../es_archiver'; @@ -13,7 +14,6 @@ import type { RetryService } from '../retry'; import type { KibanaServerProvider } from './kibana_server'; const ES_ARCHIVER_LOAD_METHODS = ['load', 'loadIfNeeded', 'unload', 'emptyKibanaIndex'] as const; -const KIBANA_INDEX = '.kibana'; interface Options { esArchiver: ProvidedType; @@ -38,7 +38,7 @@ export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults }: const statsKeys = Object.keys(stats); const kibanaKeys = statsKeys.filter( // this also matches stats keys like '.kibana_1' and '.kibana_2,.kibana_1' - (key) => key.includes(KIBANA_INDEX) && stats[key].created + (key) => key.includes(MAIN_SAVED_OBJECT_INDEX) && stats[key].created ); // if the kibana index was created by the esArchiver then update the uiSettings diff --git a/packages/kbn-ftr-common-functional-services/tsconfig.json b/packages/kbn-ftr-common-functional-services/tsconfig.json index 639991bb2ce77..3641c807e4d6d 100644 --- a/packages/kbn-ftr-common-functional-services/tsconfig.json +++ b/packages/kbn-ftr-common-functional-services/tsconfig.json @@ -11,6 +11,7 @@ "**/*.ts", ], "kbn_references": [ + "@kbn/core-saved-objects-server", "@kbn/tooling-log", "@kbn/es-archiver", "@kbn/test" diff --git a/packages/kbn-guided-onboarding/index.ts b/packages/kbn-guided-onboarding/index.ts index 9ccaae3901b48..a72c60ac8d25e 100644 --- a/packages/kbn-guided-onboarding/index.ts +++ b/packages/kbn-guided-onboarding/index.ts @@ -16,6 +16,7 @@ export type { GuideConfig, StepConfig, StepDescriptionWithLink, + GuideParams, } from './src/types'; export { GuideCards, GuideFilters } from './src/components/landing_page'; export type { GuideFilterValues } from './src/components/landing_page'; diff --git a/packages/kbn-guided-onboarding/src/common/test_guide_config.ts b/packages/kbn-guided-onboarding/src/common/test_guide_config.ts index a7944ef1d8bb8..875500fbb827c 100644 --- a/packages/kbn-guided-onboarding/src/common/test_guide_config.ts +++ b/packages/kbn-guided-onboarding/src/common/test_guide_config.ts @@ -75,5 +75,14 @@ export const testGuideConfig: GuideConfig = { path: 'stepThree', }, }, + { + id: 'step4', + title: 'Step 4 (dynamic url)', + description: 'This step navigates to a dynamic URL with a param indexName passed in step 1.', + location: { + appID: 'guidedOnboardingExample', + path: 'stepFour/{indexName}', + }, + }, ], }; diff --git a/packages/kbn-guided-onboarding/src/types.ts b/packages/kbn-guided-onboarding/src/types.ts index 5f6da4b83ef54..bcf658ece6d3d 100644 --- a/packages/kbn-guided-onboarding/src/types.ts +++ b/packages/kbn-guided-onboarding/src/types.ts @@ -17,15 +17,18 @@ export type GuideId = type KubernetesStepIds = 'add_data' | 'view_dashboard' | 'tour_observability'; type SiemStepIds = 'add_data' | 'rules' | 'alertsCases'; type SearchStepIds = 'add_data' | 'search_experience'; -type TestGuideIds = 'step1' | 'step2' | 'step3'; +type TestGuideIds = 'step1' | 'step2' | 'step3' | 'step4'; export type GuideStepIds = KubernetesStepIds | SiemStepIds | SearchStepIds | TestGuideIds; +export type GuideParams = Record; + export interface GuideState { guideId: GuideId; status: GuideStatus; isActive?: boolean; // Drives the current guide shown in the dropdown panel steps: GuideStep[]; + params?: GuideParams; } /** @@ -92,6 +95,16 @@ export interface StepConfig { description?: string | StepDescriptionWithLink; // description list is displayed as an unordered list, can be combined with description descriptionList?: Array; + /* + * Kibana location where the user will be redirected when starting or continuing a guide step. + * The property `path` can use dynamic parameters, for example `testPath/{indexID}/{pageID}. + * For the dynamic path to be configured correctly, the values of the parameters need to be passed to + * the api service when completing one of the previous steps. + * For example, if step 2 has a dynamic parameter `indexID` in its location path + * { appID: 'test', path: 'testPath/{indexID}', params: ['indexID'] }, + * its value needs to be passed to the api service when completing step 1. For example, + * `guidedOnboardingAPI.completeGuideStep('testGuide', 'step1', { indexID: 'testIndex' }) + */ location?: { appID: string; path: string; diff --git a/packages/kbn-io-ts-utils/index.ts b/packages/kbn-io-ts-utils/index.ts index 4e3eaeb9af92b..e8e6da2e7b59e 100644 --- a/packages/kbn-io-ts-utils/index.ts +++ b/packages/kbn-io-ts-utils/index.ts @@ -7,7 +7,7 @@ */ export type { IndexPatternType } from './src/index_pattern_rt'; -export type { NonEmptyStringBrand } from './src/non_empty_string_rt'; +export type { NonEmptyString, NonEmptyStringBrand } from './src/non_empty_string_rt'; export { deepExactRt } from './src/deep_exact_rt'; export { indexPatternRt } from './src/index_pattern_rt'; @@ -20,3 +20,11 @@ export { toBooleanRt } from './src/to_boolean_rt'; export { toJsonSchema } from './src/to_json_schema'; export { nonEmptyStringRt } from './src/non_empty_string_rt'; export { createLiteralValueFromUndefinedRT } from './src/literal_value_from_undefined_rt'; +export { createRouteValidationFunction } from './src/route_validation'; +export { inRangeRt, type InRangeBrand, type InRange } from './src/in_range_rt'; +export { dateRt } from './src/date_rt'; +export { + isGreaterOrEqualRt, + type IsGreaterOrEqualBrand, + type IsGreaterOrEqual, +} from './src/is_greater_or_equal'; diff --git a/packages/kbn-io-ts-utils/src/date_rt/index.test.ts b/packages/kbn-io-ts-utils/src/date_rt/index.test.ts new file mode 100644 index 0000000000000..088caf133e62c --- /dev/null +++ b/packages/kbn-io-ts-utils/src/date_rt/index.test.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 { dateRt } from '.'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('dateRt', () => { + it('passes if it is a valide date/time', () => { + expect(isRight(dateRt.decode('2019-08-20T11:18:31.407Z'))).toBe(true); + }); + + it('passes if it is a valide date', () => { + expect(isRight(dateRt.decode('2019-08-20'))).toBe(true); + }); + + it('fails if it is an invalide date/time', () => { + expect(isRight(dateRt.decode('2019-02-30T11:18:31.407Z'))).toBe(false); + }); +}); diff --git a/packages/kbn-io-ts-utils/src/date_rt/index.ts b/packages/kbn-io-ts-utils/src/date_rt/index.ts new file mode 100644 index 0000000000000..c3a8d40a1db57 --- /dev/null +++ b/packages/kbn-io-ts-utils/src/date_rt/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 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 rt from 'io-ts'; +import moment from 'moment'; + +export interface DateBrand { + readonly Date: unique symbol; +} + +export type Date = rt.Branded; + +export const dateRt = rt.brand( + rt.string, + (date): date is Date => moment(date, true).isValid(), + 'Date' +); diff --git a/packages/kbn-io-ts-utils/src/in_range_rt/index.test.ts b/packages/kbn-io-ts-utils/src/in_range_rt/index.test.ts new file mode 100644 index 0000000000000..488e8d2bbd4fa --- /dev/null +++ b/packages/kbn-io-ts-utils/src/in_range_rt/index.test.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 { inRangeRt } from '.'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('inRangeRT', () => { + test('passes if value is within range', () => { + expect(isRight(inRangeRt(1, 100).decode(50))).toBe(true); + }); + + test('fails if value above the range', () => { + expect(isRight(inRangeRt(1, 100).decode(101))).toBe(false); + }); + + test('fails if value below the range', () => { + expect(isRight(inRangeRt(1, 100).decode(0))).toBe(false); + }); +}); diff --git a/packages/kbn-io-ts-utils/src/in_range_rt/index.ts b/packages/kbn-io-ts-utils/src/in_range_rt/index.ts new file mode 100644 index 0000000000000..ae3da3d7d2d69 --- /dev/null +++ b/packages/kbn-io-ts-utils/src/in_range_rt/index.ts @@ -0,0 +1,23 @@ +/* + * 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 rt from 'io-ts'; + +export interface InRangeBrand { + readonly InRange: unique symbol; +} + +export type InRange = rt.Branded; + +export const inRangeRt = (start: number, end: number) => + rt.brand( + rt.number, // codec + (n): n is InRange => n >= start && n <= end, + // refinement of the number type + 'InRange' // name of this codec + ); diff --git a/packages/kbn-io-ts-utils/src/is_greater_or_equal/index.test.ts b/packages/kbn-io-ts-utils/src/is_greater_or_equal/index.test.ts new file mode 100644 index 0000000000000..0698fe0485436 --- /dev/null +++ b/packages/kbn-io-ts-utils/src/is_greater_or_equal/index.test.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 { isGreaterOrEqualRt } from '.'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('inRangeRT', () => { + test('passes if value is a positive number', () => { + expect(isRight(isGreaterOrEqualRt(0).decode(1))).toBe(true); + }); + + test('passes if value is 0', () => { + expect(isRight(isGreaterOrEqualRt(0).decode(0))).toBe(true); + }); + + test('fails if value is a negative number', () => { + expect(isRight(isGreaterOrEqualRt(0).decode(-1))).toBe(false); + }); +}); diff --git a/packages/kbn-io-ts-utils/src/is_greater_or_equal/index.ts b/packages/kbn-io-ts-utils/src/is_greater_or_equal/index.ts new file mode 100644 index 0000000000000..0eb59269485b2 --- /dev/null +++ b/packages/kbn-io-ts-utils/src/is_greater_or_equal/index.ts @@ -0,0 +1,23 @@ +/* + * 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 rt from 'io-ts'; + +export interface IsGreaterOrEqualBrand { + readonly IsGreaterOrEqual: unique symbol; +} + +export type IsGreaterOrEqual = rt.Branded; + +export const isGreaterOrEqualRt = (value: number) => + rt.brand( + rt.number, // codec + (n): n is IsGreaterOrEqual => n >= value, + // refinement of the number type + 'IsGreaterOrEqual' // name of this codec + ); diff --git a/packages/kbn-io-ts-utils/src/route_validation/index.ts b/packages/kbn-io-ts-utils/src/route_validation/index.ts new file mode 100644 index 0000000000000..85532c2d5f9eb --- /dev/null +++ b/packages/kbn-io-ts-utils/src/route_validation/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 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 type { RouteValidationFunction } from '@kbn/core/server'; +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { Context, Errors, IntersectionType, Type, UnionType, ValidationError } from 'io-ts'; + +type ValdidationResult = ReturnType>; + +export const createRouteValidationFunction = + ( + runtimeType: Type + ): RouteValidationFunction => + (inputValue, { badRequest, ok }) => + pipe( + runtimeType.decode(inputValue), + fold>( + (errors: Errors) => badRequest(formatErrors(errors)), + (result: DecodedValue) => ok(result) + ) + ); + +const getErrorPath = ([first, ...rest]: Context): string[] => { + if (typeof first === 'undefined') { + return []; + } else if (first.type instanceof IntersectionType) { + const [, ...next] = rest; + return getErrorPath(next); + } else if (first.type instanceof UnionType) { + const [, ...next] = rest; + return [first.key, ...getErrorPath(next)]; + } + + return [first.key, ...getErrorPath(rest)]; +}; + +const getErrorType = ({ context }: ValidationError) => + context[context.length - 1]?.type?.name ?? 'unknown'; + +const formatError = (error: ValidationError) => + error.message ?? + `in ${getErrorPath(error.context).join('/')}: ${JSON.stringify( + error.value + )} does not match expected type ${getErrorType(error)}`; + +export const formatErrors = (errors: ValidationError[]) => + `Failed to validate: \n${errors.map((error) => ` ${formatError(error)}`).join('\n')}`; diff --git a/packages/kbn-io-ts-utils/tsconfig.json b/packages/kbn-io-ts-utils/tsconfig.json index d5fd475db97a1..e44c93a0c24b0 100644 --- a/packages/kbn-io-ts-utils/tsconfig.json +++ b/packages/kbn-io-ts-utils/tsconfig.json @@ -11,7 +11,8 @@ "**/*.ts" ], "kbn_references": [ - "@kbn/config-schema" + "@kbn/config-schema", + "@kbn/core" ], "exclude": [ "target/**/*", diff --git a/packages/kbn-journeys/index.ts b/packages/kbn-journeys/index.ts index def8e5f3b0d59..6c0df15667c27 100644 --- a/packages/kbn-journeys/index.ts +++ b/packages/kbn-journeys/index.ts @@ -7,6 +7,7 @@ */ export { JourneyConfig } from './journey/journey_config'; +export { JOURNEY_APM_CONFIG } from './journey/journey_apm_config'; export type { ScalabilityAction, ScalabilitySetup, diff --git a/packages/kbn-journeys/journey/journey_apm_config.ts b/packages/kbn-journeys/journey/journey_apm_config.ts new file mode 100644 index 0000000000000..c14a78a438418 --- /dev/null +++ b/packages/kbn-journeys/journey/journey_apm_config.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. + */ + +// 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://kibana-ops-e2e-perf.apm.us-central1.gcp.cloud.es.io:443'; +const APM_PUBLIC_TOKEN = 'CTs9y3cvcfq13bQqsB'; + +export const JOURNEY_APM_CONFIG = { + serverUrl: APM_SERVER_URL, + secretToken: APM_PUBLIC_TOKEN, + active: 'true', + contextPropagationOnly: 'false', + environment: process.env.CI ? 'ci' : 'development', + transactionSampleRate: '1.0', + // capture request body for both errors and request transactions + // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#capture-body + captureBody: 'all', + // capture request headers + // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#capture-headers + captureRequestHeaders: true, + // request body with bigger size will be trimmed. + // 300_000 is the default of the APM server. + // for a body with larger size, we might need to reconfigure the APM server to increase the limit. + // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#long-field-max-length + longFieldMaxLength: 300_000, + globalLabels: { + performancePhase: process.env.TEST_PERFORMANCE_PHASE, + branch: process.env.BUILDKITE_BRANCH, + gitRev: process.env.BUILDKITE_COMMIT, + ciBuildName: process.env.BUILDKITE_PIPELINE_SLUG, + }, +}; diff --git a/packages/kbn-journeys/journey/journey_ftr_config.ts b/packages/kbn-journeys/journey/journey_ftr_config.ts index 3933d0d7e195a..2d855ab8fb22c 100644 --- a/packages/kbn-journeys/journey/journey_ftr_config.ts +++ b/packages/kbn-journeys/journey/journey_ftr_config.ts @@ -15,10 +15,7 @@ import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; import { AnyStep } from './journey'; import { JourneyConfig } from './journey_config'; - -// 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://kibana-ops-e2e-perf.apm.us-central1.gcp.cloud.es.io:443'; -const APM_PUBLIC_TOKEN = 'CTs9y3cvcfq13bQqsB'; +import { JOURNEY_APM_CONFIG } from './journey_apm_config'; export function makeFtrConfigProvider( config: JourneyConfig, @@ -92,33 +89,22 @@ export function makeFtrConfigProvider( ], env: { - ELASTIC_APM_ACTIVE: 'true', - ELASTIC_APM_CONTEXT_PROPAGATION_ONLY: 'false', - ELASTIC_APM_ENVIRONMENT: process.env.CI ? 'ci' : 'development', - ELASTIC_APM_TRANSACTION_SAMPLE_RATE: '1.0', - ELASTIC_APM_SERVER_URL: APM_SERVER_URL, - ELASTIC_APM_SECRET_TOKEN: APM_PUBLIC_TOKEN, - // capture request body for both errors and request transactions - // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#capture-body - ELASTIC_APM_CAPTURE_BODY: 'all', - // capture request headers - // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#capture-headers - ELASTIC_APM_CAPTURE_HEADERS: true, - // request body with bigger size will be trimmed. - // 300_000 is the default of the APM server. - // for a body with larger size, we might need to reconfigure the APM server to increase the limit. - // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#long-field-max-length - ELASTIC_APM_LONG_FIELD_MAX_LENGTH: 300_000, + ELASTIC_APM_ACTIVE: JOURNEY_APM_CONFIG.active, + ELASTIC_APM_CONTEXT_PROPAGATION_ONLY: JOURNEY_APM_CONFIG.contextPropagationOnly, + ELASTIC_APM_ENVIRONMENT: JOURNEY_APM_CONFIG.environment, + ELASTIC_APM_TRANSACTION_SAMPLE_RATE: JOURNEY_APM_CONFIG.transactionSampleRate, + ELASTIC_APM_SERVER_URL: JOURNEY_APM_CONFIG.serverUrl, + ELASTIC_APM_SECRET_TOKEN: JOURNEY_APM_CONFIG.secretToken, + ELASTIC_APM_CAPTURE_BODY: JOURNEY_APM_CONFIG.captureBody, + ELASTIC_APM_CAPTURE_HEADERS: JOURNEY_APM_CONFIG.captureRequestHeaders, + ELASTIC_APM_LONG_FIELD_MAX_LENGTH: JOURNEY_APM_CONFIG.longFieldMaxLength, ELASTIC_APM_GLOBAL_LABELS: Object.entries({ ...config.getExtraApmLabels(), testJobId, testBuildId, journeyName: config.getName(), ftrConfig: config.getRepoRelPath(), - performancePhase: process.env.TEST_PERFORMANCE_PHASE, - branch: process.env.BUILDKITE_BRANCH, - gitRev: process.env.BUILDKITE_COMMIT, - ciBuildName: process.env.BUILDKITE_PIPELINE_SLUG, + ...JOURNEY_APM_CONFIG.globalLabels, }) .flatMap(([key, value]) => (value == null ? [] : `${key}=${value}`)) .join(','), diff --git a/packages/kbn-object-versioning/lib/content_management_services_schemas.ts b/packages/kbn-object-versioning/lib/content_management_services_schemas.ts index 90e8400689dc2..c98a3f62ca3a0 100644 --- a/packages/kbn-object-versioning/lib/content_management_services_schemas.ts +++ b/packages/kbn-object-versioning/lib/content_management_services_schemas.ts @@ -103,6 +103,20 @@ const searchSchemas = getOptionalInOutSchemas({ ), }); +// Schema to validate the "msearch" service objects +const mSearchSchemas = schema.maybe( + schema.object({ + out: schema.maybe( + schema.object( + { + result: schema.maybe(versionableObjectSchema), + }, + { unknowns: 'forbid' } + ) + ), + }) +); + export const serviceDefinitionSchema = schema.object( { get: getSchemas, @@ -111,6 +125,7 @@ export const serviceDefinitionSchema = schema.object( update: createSchemas, delete: getSchemas, search: searchSchemas, + mSearch: mSearchSchemas, }, { unknowns: 'forbid' } ); diff --git a/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts b/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts index 49b55b0da44fc..08bd66609ab7e 100644 --- a/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts +++ b/packages/kbn-object-versioning/lib/content_management_services_versioning.test.ts @@ -240,6 +240,7 @@ describe('CM services getTransforms()', () => { 'delete.out.result', 'search.in.options', 'search.out.result', + 'mSearch.out.result', ].sort() ); }); diff --git a/packages/kbn-object-versioning/lib/content_management_services_versioning.ts b/packages/kbn-object-versioning/lib/content_management_services_versioning.ts index 5655a094f5e42..eb38de643f630 100644 --- a/packages/kbn-object-versioning/lib/content_management_services_versioning.ts +++ b/packages/kbn-object-versioning/lib/content_management_services_versioning.ts @@ -34,6 +34,7 @@ const serviceObjectPaths = [ 'delete.out.result', 'search.in.options', 'search.out.result', + 'mSearch.out.result', ]; const validateServiceDefinitions = (definitions: ServiceDefinitionVersioned) => { @@ -175,6 +176,11 @@ const getDefaultServiceTransforms = (): ServiceTransforms => ({ result: getDefaultTransforms(), }, }, + mSearch: { + out: { + result: getDefaultTransforms(), + }, + }, }); export const getTransforms = ( diff --git a/packages/kbn-object-versioning/lib/content_management_types.ts b/packages/kbn-object-versioning/lib/content_management_types.ts index 56d72013335cd..1594b450661ba 100644 --- a/packages/kbn-object-versioning/lib/content_management_types.ts +++ b/packages/kbn-object-versioning/lib/content_management_types.ts @@ -59,6 +59,11 @@ export interface ServicesDefinition { result?: VersionableObject; }; }; + mSearch?: { + out?: { + result?: VersionableObject; + }; + }; } export interface ServiceTransforms { @@ -112,6 +117,11 @@ export interface ServiceTransforms { result: ObjectTransforms; }; }; + mSearch: { + out: { + result: ObjectTransforms; + }; + }; } export interface ServiceDefinitionVersioned { diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 93d5507c53a1e..2e3a5b30bdd05 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -7,7 +7,7 @@ pageLoadAssetSize: banners: 17946 bfetch: 22837 canvas: 1066647 - cases: 170000 + cases: 175000 charts: 55000 cloud: 21076 cloudChat: 19894 @@ -94,6 +94,7 @@ pageLoadAssetSize: navigation: 37269 newsfeed: 42228 observability: 95000 + observabilityOnboarding: 19573 observabilityShared: 21266 osquery: 107090 painlessLab: 179748 diff --git a/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap b/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap index e65b5fcb8fbbd..0b4ceaf06e863 100644 --- a/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap +++ b/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap @@ -2,7 +2,7 @@ exports[`fieldAction is rendered 1`] = `
@@ -50,7 +58,7 @@ exports[`fieldIcon is rendered 1`] = ` exports[`isActive defaults to false 1`] = `
@@ -67,7 +79,7 @@ exports[`isActive defaults to false 1`] = ` exports[`isActive renders true 1`] = `
`; -exports[`isDraggable is rendered 1`] = ` +exports[`sizes s is applied 1`] = `
`; -exports[`sizes m is applied 1`] = ` +exports[`sizes xs is applied 1`] = `
`; -exports[`sizes s is applied 1`] = ` +exports[`with drag handle is rendered 1`] = `
+
+ + drag + +
diff --git a/packages/kbn-react-field/src/field_button/field_button.scss b/packages/kbn-react-field/src/field_button/field_button.scss index f71e097ab7138..5fd87c38b930e 100644 --- a/packages/kbn-react-field/src/field_button/field_button.scss +++ b/packages/kbn-react-field/src/field_button/field_button.scss @@ -2,8 +2,9 @@ @include euiFontSizeS; border-radius: $euiBorderRadius; margin-bottom: $euiSizeXS; + padding: 0 $euiSizeS; display: flex; - align-items: center; + align-items: flex-start; transition: box-shadow $euiAnimSpeedFast $euiAnimSlightResistance, background-color $euiAnimSpeedFast $euiAnimSlightResistance; // sass-lint:disable-line indentation @@ -13,29 +14,10 @@ } } -.kbnFieldButton--isDraggable { - background: lightOrDarkTheme($euiColorEmptyShade, $euiColorLightestShade); - - &:hover, - &:focus, - &:focus-within { - @include euiBottomShadowMedium; - border-radius: $euiBorderRadius; - z-index: 2; - } - - .kbnFieldButton__button { - &:hover, - &:focus { - cursor: grab; - } - } -} - .kbnFieldButton__button { flex-grow: 1; text-align: left; - padding: $euiSizeS; + padding: $euiSizeS 0; display: flex; align-items: flex-start; line-height: normal; @@ -44,33 +26,55 @@ .kbnFieldButton__fieldIcon { flex-shrink: 0; line-height: 0; - margin-right: $euiSizeS; } .kbnFieldButton__name { flex-grow: 1; word-break: break-word; + padding: 0 $euiSizeS; } .kbnFieldButton__infoIcon { + display: flex; + align-items: center; + justify-content: center; + min-height: $euiSize; flex-shrink: 0; - margin-left: $euiSizeXS; + line-height: 0; } .kbnFieldButton__fieldAction { + margin-left: $euiSizeS; +} + +.kbnFieldButton__dragHandle { margin-right: $euiSizeS; } +.kbnFieldButton__fieldAction, +.kbnFieldButton__dragHandle { + line-height: $euiSizeXL; +} + // Reduce text size and spacing for the small size -.kbnFieldButton--small { +.kbnFieldButton--xs { font-size: $euiFontSizeXS; .kbnFieldButton__button { - padding: $euiSizeXS; + padding: $euiSizeXS 0; } - .kbnFieldButton__fieldIcon, .kbnFieldButton__fieldAction { - margin-right: $euiSizeXS; + margin-left: $euiSizeXS; + } + + .kbnFieldButton__fieldAction, + .kbnFieldButton__dragHandle { + line-height: $euiSizeL; } } + +.kbnFieldButton--flushBoth { + padding-left: 0; + padding-right: 0; +} diff --git a/packages/kbn-react-field/src/field_button/field_button.test.tsx b/packages/kbn-react-field/src/field_button/field_button.test.tsx index e61b41187ba7e..270144c7d346b 100644 --- a/packages/kbn-react-field/src/field_button/field_button.test.tsx +++ b/packages/kbn-react-field/src/field_button/field_button.test.tsx @@ -21,9 +21,11 @@ describe('sizes', () => { }); }); -describe('isDraggable', () => { +describe('with drag handle', () => { it('is rendered', () => { - const component = shallow(); + const component = shallow( + drag} /> + ); expect(component).toMatchSnapshot(); }); }); @@ -31,7 +33,7 @@ describe('isDraggable', () => { describe('fieldIcon', () => { it('is rendered', () => { const component = shallow( - fieldIcon} /> + fieldIcon} /> ); expect(component).toMatchSnapshot(); }); @@ -40,7 +42,12 @@ describe('fieldIcon', () => { describe('fieldAction', () => { it('is rendered', () => { const component = shallow( - fieldAction} /> + fieldAction} + /> ); expect(component).toMatchSnapshot(); }); @@ -48,11 +55,11 @@ describe('fieldAction', () => { describe('isActive', () => { it('defaults to false', () => { - const component = shallow(); + const component = shallow(); expect(component).toMatchSnapshot(); }); it('renders true', () => { - const component = shallow(); + const component = shallow(); expect(component).toMatchSnapshot(); }); }); diff --git a/packages/kbn-react-field/src/field_button/field_button.tsx b/packages/kbn-react-field/src/field_button/field_button.tsx index 4871fea049710..8d87e0e04cb63 100644 --- a/packages/kbn-react-field/src/field_button/field_button.tsx +++ b/packages/kbn-react-field/src/field_button/field_button.tsx @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import './field_button.scss'; import classNames from 'classnames'; import React, { ReactNode, HTMLAttributes, ButtonHTMLAttributes } from 'react'; import { CommonProps } from '@elastic/eui'; +import './field_button.scss'; export interface FieldButtonProps extends HTMLAttributes { /** @@ -34,13 +34,20 @@ export interface FieldButtonProps extends HTMLAttributes { */ isActive?: boolean; /** - * Styles the component differently to indicate it is draggable + * Custom drag handle element + */ + dragHandle?: React.ReactElement; + /** + * Use the xs size in condensed areas */ - isDraggable?: boolean; + size: ButtonSize; /** - * Use the small size in condensed areas + * Whether to skip side paddings + */ + flush?: 'both'; + /** + * Custom class name */ - size?: ButtonSize; className?: string; /** * The component will render a ` + + + +
+ ); +} +``` + +In this example, the count state is synced to the URL using the `useSyncToUrl` hook. +Whenever the count state changes, the hook will update the URL with the new value. +When the user copies the updated url or refreshes the page, `restore` function will be called to update the count state. \ No newline at end of file diff --git a/packages/kbn-url-state/index.test.ts b/packages/kbn-url-state/index.test.ts new file mode 100644 index 0000000000000..33dc285e100e5 --- /dev/null +++ b/packages/kbn-url-state/index.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { renderHook, act } from '@testing-library/react-hooks'; +import { useSyncToUrl } from '.'; +import { encode } from '@kbn/rison'; + +describe('useSyncToUrl', () => { + let originalLocation: Location; + let originalHistory: History; + + beforeEach(() => { + originalLocation = window.location; + originalHistory = window.history; + delete (window as any).location; + delete (window as any).history; + + window.location = { + ...originalLocation, + search: '', + }; + window.history = { + ...originalHistory, + replaceState: jest.fn(), + }; + }); + + afterEach(() => { + window.location = originalLocation; + window.history = originalHistory; + }); + + it('should restore the value from the query string on mount', () => { + const key = 'testKey'; + const restoredValue = { test: 'value' }; + const encodedValue = encode(restoredValue); + const restore = jest.fn(); + + window.location.search = `?${key}=${encodedValue}`; + + renderHook(() => useSyncToUrl(key, restore)); + + expect(restore).toHaveBeenCalledWith(restoredValue); + }); + + it('should sync the value to the query string', () => { + const key = 'testKey'; + const valueToSerialize = { test: 'value' }; + + const { result } = renderHook(() => useSyncToUrl(key, jest.fn())); + + act(() => { + result.current(valueToSerialize); + }); + + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + '/?testKey=%28test%3Avalue%29' + ); + }); + + it('should clear the value from the query string on unmount', () => { + const key = 'testKey'; + + const { unmount } = renderHook(() => useSyncToUrl(key, jest.fn())); + + act(() => { + unmount(); + }); + + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + expect.any(String) + ); + }); + + it('should clear the value from the query string when history back or forward is pressed', () => { + const key = 'testKey'; + const restore = jest.fn(); + + renderHook(() => useSyncToUrl(key, restore, true)); + + act(() => { + window.dispatchEvent(new Event('popstate')); + }); + + expect(window.history.replaceState).toHaveBeenCalledTimes(1); + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + '/?' + ); + }); +}); diff --git a/packages/kbn-url-state/index.ts b/packages/kbn-url-state/index.ts new file mode 100644 index 0000000000000..73568222fb4c0 --- /dev/null +++ b/packages/kbn-url-state/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 { useSyncToUrl } from './use_sync_to_url'; diff --git a/packages/kbn-url-state/jest.config.js b/packages/kbn-url-state/jest.config.js new file mode 100644 index 0000000000000..256a51239206c --- /dev/null +++ b/packages/kbn-url-state/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-url-state'], +}; diff --git a/packages/kbn-url-state/kibana.jsonc b/packages/kbn-url-state/kibana.jsonc new file mode 100644 index 0000000000000..b0ab56d6af8b7 --- /dev/null +++ b/packages/kbn-url-state/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/url-state", + "owner": "@elastic/security-threat-hunting-investigations" +} diff --git a/packages/kbn-url-state/package.json b/packages/kbn-url-state/package.json new file mode 100644 index 0000000000000..2cd753f16b872 --- /dev/null +++ b/packages/kbn-url-state/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/url-state", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-url-state/tsconfig.json b/packages/kbn-url-state/tsconfig.json new file mode 100644 index 0000000000000..3bd03b7f37b84 --- /dev/null +++ b/packages/kbn-url-state/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/rison", + ] +} diff --git a/packages/kbn-url-state/use_sync_to_url.ts b/packages/kbn-url-state/use_sync_to_url.ts new file mode 100644 index 0000000000000..e38f9bc688a8b --- /dev/null +++ b/packages/kbn-url-state/use_sync_to_url.ts @@ -0,0 +1,87 @@ +/* + * 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 { useCallback, useEffect } from 'react'; +import { encode, decode } from '@kbn/rison'; + +// https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event +const POPSTATE_EVENT = 'popstate' as const; + +/** + * Sync any object with browser query string using @knb/rison + * @param key query string param to use + * @param restore use this to handle restored state + * @param cleanupOnHistoryNavigation use history events to cleanup state on back / forward naviation. true by default + */ +export const useSyncToUrl = ( + key: string, + restore: (data: TValueToSerialize) => void, + cleanupOnHistoryNavigation = true +) => { + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const param = params.get(key); + + if (!param) { + return; + } + + const decodedQuery = decode(param); + + if (!decodedQuery) { + return; + } + + // Only restore the value if it is not falsy + restore(decodedQuery as unknown as TValueToSerialize); + }, [key, restore]); + + /** + * Synces value with the url state, under specified key. If payload is undefined, the value will be removed from the query string althogether. + */ + const syncValueToQueryString = useCallback( + (valueToSerialize?: TValueToSerialize) => { + const searchParams = new URLSearchParams(window.location.search); + + if (valueToSerialize) { + const serializedPayload = encode(valueToSerialize); + searchParams.set(key, serializedPayload); + } else { + searchParams.delete(key); + } + + const newSearch = searchParams.toString(); + + // Update query string without unnecessary re-render + const newUrl = `${window.location.pathname}?${newSearch}`; + window.history.replaceState({ path: newUrl }, '', newUrl); + }, + [key] + ); + + // Clear remove state from the url on unmount / when history back or forward is pressed + useEffect(() => { + const clearState = () => { + syncValueToQueryString(undefined); + }; + + if (cleanupOnHistoryNavigation) { + window.addEventListener(POPSTATE_EVENT, clearState); + } + + return () => { + clearState(); + + if (cleanupOnHistoryNavigation) { + window.removeEventListener(POPSTATE_EVENT, clearState); + } + }; + }, [cleanupOnHistoryNavigation, syncValueToQueryString]); + + return syncValueToQueryString; +}; diff --git a/packages/shared-ux/button_toolbar/src/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap b/packages/shared-ux/button_toolbar/src/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap index 3780fa1bcddd6..c472f58ec3e2f 100644 --- a/packages/shared-ux/button_toolbar/src/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap +++ b/packages/shared-ux/button_toolbar/src/buttons/icon_button_group/__snapshots__/icon_button_group.test.tsx.snap @@ -2,7 +2,7 @@ exports[` is rendered 1`] = `
is rendered 1`] = `
diff --git a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx index eef0ce05eed6e..f5b5c2bce0cd0 100644 --- a/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx +++ b/packages/shared-ux/button_toolbar/src/toolbar/toolbar.tsx @@ -59,7 +59,7 @@ export const Toolbar = ({ children }: Props) => { {primaryButton} - + {iconButtonGroup ? {iconButtonGroup} : null} {extra} diff --git a/packages/shared-ux/file/types/base_file_client.ts b/packages/shared-ux/file/types/base_file_client.ts index 4a00f2de00516..52d1ca09fd170 100644 --- a/packages/shared-ux/file/types/base_file_client.ts +++ b/packages/shared-ux/file/types/base_file_client.ts @@ -27,6 +27,7 @@ export interface BaseFilesClient { find: ( args: { kind?: string | string[]; + kindToExclude?: string | string[]; status?: string | string[]; extension?: string | string[]; name?: string | string[]; diff --git a/packages/shared-ux/file/types/index.ts b/packages/shared-ux/file/types/index.ts index 4c49124f7149f..86b9e47fdab43 100644 --- a/packages/shared-ux/file/types/index.ts +++ b/packages/shared-ux/file/types/index.ts @@ -250,6 +250,22 @@ export interface FileKindBrowser extends FileKindBase { * @default 4MiB */ maxSizeBytes?: number; + /** + * Allowed actions that can be done in the File Management UI. If not provided, all actions are allowed + * + */ + managementUiActions?: { + /** Allow files to be listed in management UI */ + list?: { + enabled: boolean; + }; + /** Allow files to be deleted in management UI */ + delete?: { + enabled: boolean; + /** If delete is not enabled in management UI, specify the reason (will appear in a tooltip). */ + reason?: string; + }; + }; } /** diff --git a/src/cli/dev.js b/src/cli/dev.js index fb61b53b6f210..f7a50ea16a7a9 100644 --- a/src/cli/dev.js +++ b/src/cli/dev.js @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -require('@kbn/babel-register').install(); -require('./apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy'); require('../setup_node_env'); +require('./apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy'); require('./cli'); diff --git a/src/cli/dist.js b/src/cli/dist.js index fccc93cd53631..9bd7696a44561 100644 --- a/src/cli/dist.js +++ b/src/cli/dist.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -require('./apm')(); require('../setup_node_env/dist'); +require('./apm')(); require('../setup_node_env/root'); require('./cli'); diff --git a/src/cli/tsconfig.json b/src/cli/tsconfig.json index 5ee6fa3616614..ebbbc19f75c79 100644 --- a/src/cli/tsconfig.json +++ b/src/cli/tsconfig.json @@ -17,7 +17,6 @@ "@kbn/config", "@kbn/dev-utils", "@kbn/apm-config-loader", - "@kbn/babel-register", ], "exclude": [ "target/**/*", diff --git a/src/core/server/integration_tests/http/versioned_router.test.ts b/src/core/server/integration_tests/http/versioned_router.test.ts index 4206bed642bba..d8dc15d3a2d1c 100644 --- a/src/core/server/integration_tests/http/versioned_router.test.ts +++ b/src/core/server/integration_tests/http/versioned_router.test.ts @@ -15,6 +15,7 @@ import { contextServiceMock } from '@kbn/core-http-context-server-mocks'; import { createHttpServer } from '@kbn/core-http-server-mocks'; import type { HttpService } from '@kbn/core-http-server-internal'; import type { IRouterWithVersion } from '@kbn/core-http-server'; +import type { CliArgs } from '@kbn/config'; let server: HttpService; let logger: ReturnType; @@ -23,18 +24,26 @@ describe('Routing versioned requests', () => { let router: IRouterWithVersion; let supertest: Supertest.SuperTest; + async function setupServer(cliArgs: Partial = {}) { + logger = loggingSystemMock.create(); + await server?.stop(); // stop the already started server + server = createHttpServer({ + logger, + env: createTestEnv({ envOptions: getEnvOptions({ cliArgs }) }), + }); + await server.preboot({ context: contextServiceMock.createPrebootContract() }); + const { server: innerServer, createRouter } = await server.setup(setupDeps); + router = createRouter('/'); + supertest = Supertest(innerServer.listener); + } + const setupDeps = { context: contextServiceMock.createSetupContract(), executionContext: executionContextServiceMock.createInternalSetupContract(), }; beforeEach(async () => { - logger = loggingSystemMock.create(); - server = createHttpServer({ logger }); - await server.preboot({ context: contextServiceMock.createPrebootContract() }); - const { server: innerServer, createRouter } = await server.setup(setupDeps); - router = createRouter('/'); - supertest = Supertest(innerServer.listener); + await setupServer(); }); afterEach(async () => { @@ -191,17 +200,8 @@ describe('Routing versioned requests', () => { }); it('does not run response validation in prod', async () => { - logger = loggingSystemMock.create(); - await server.stop(); // stop the already started server - server = createHttpServer({ - logger, - env: createTestEnv({ envOptions: getEnvOptions({ cliArgs: { dev: false } }) }), - }); - await server.preboot({ context: contextServiceMock.createPrebootContract() }); - const { server: innerServer, createRouter } = await server.setup(setupDeps); + await setupServer({ dev: false }); - router = createRouter('/'); - supertest = Supertest(innerServer.listener); router.versioned .get({ path: '/my-path', access: 'internal' }) .addVersion( @@ -237,4 +237,48 @@ describe('Routing versioned requests', () => { expect.objectContaining({ message: expect.stringMatching(/No handlers registered/) }) ); }); + + it('resolves the newest handler on serverless', async () => { + await setupServer({ serverless: true }); + + router.versioned + .get({ path: '/my-path', access: 'public' }) + .addVersion({ validate: false, version: '2023-04-04' }, async (ctx, req, res) => { + return res.ok({ body: { v: 'oldest' } }); + }) + .addVersion({ validate: false, version: '2024-04-04' }, async (ctx, req, res) => { + return res.ok({ body: { v: 'newest' } }); + }); + + await server.start(); + + await expect( + supertest + .get('/my-path') + .expect(200) + .then(({ body }) => body.v) + ).resolves.toEqual('newest'); + }); + + it('resolves the oldest handler on anything other than serverless', async () => { + await setupServer({ serverless: false }); + + router.versioned + .get({ path: '/my-path', access: 'public' }) + .addVersion({ validate: false, version: '2023-04-04' }, async (ctx, req, res) => { + return res.ok({ body: { v: 'oldest' } }); + }) + .addVersion({ validate: false, version: '2024-04-04' }, async (ctx, req, res) => { + return res.ok({ body: { v: 'newest' } }); + }); + + await server.start(); + + await expect( + supertest + .get('/my-path') + .expect(200) + .then(({ body }) => body.v) + ).resolves.toEqual('oldest'); + }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group1/7.7.2_xpack_100k.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group1/7.7.2_xpack_100k.test.ts index b8010eacbbae0..487938bbfde3a 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group1/7.7.2_xpack_100k.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group1/7.7.2_xpack_100k.test.ts @@ -8,9 +8,6 @@ import path from 'path'; import { unlink } from 'fs/promises'; -import { REPO_ROOT } from '@kbn/repo-info'; -import { Env } from '@kbn/config'; -import { getEnvOptions } from '@kbn/config-mocks'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { InternalCoreStart } from '@kbn/core-lifecycle-server-internal'; import { Root } from '@kbn/core-root-server-internal'; @@ -19,8 +16,8 @@ import { createRootWithCorePlugins, type TestElasticsearchUtils, } from '@kbn/core-test-helpers-kbn-server'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; -const kibanaVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version; const logFilePath = path.join(__dirname, '7.7.2_xpack_100k.log'); async function removeLogFile() { @@ -105,8 +102,6 @@ describe('migration from 7.7.2-xpack with 100k objects', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }; - const migratedIndex = `.kibana_${kibanaVersion}_001`; - beforeAll(async () => { await removeLogFile(); await startServers({ @@ -121,7 +116,7 @@ describe('migration from 7.7.2-xpack with 100k objects', () => { it('copies all the document of the previous index to the new one', async () => { const migratedIndexResponse = await esClient.count({ - index: migratedIndex, + index: ALL_SAVED_OBJECT_INDICES, }); const oldIndexResponse = await esClient.count({ index: '.kibana_1', diff --git a/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_transform_failures.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_transform_failures.test.ts index 8a798508ce18f..6ef36d46b4a27 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_transform_failures.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_transform_failures.test.ts @@ -21,6 +21,11 @@ import { TestElasticsearchUtils, createTestServers as createkbnServerTestServers, } from '@kbn/core-test-helpers-kbn-server'; +import { + MAIN_SAVED_OBJECT_INDEX, + TASK_MANAGER_SAVED_OBJECT_INDEX, + ANALYTICS_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; const migrationDocLink = getMigrationDocLink().resolveMigrationFailures; const logFilePath = Path.join(__dirname, '7_13_corrupt_transform_failures.log'); @@ -114,18 +119,33 @@ describe('migration v2', () => { const esClient: ElasticsearchClient = esServer.es.getClient(); const docs = await esClient.search({ - index: '.kibana', + index: [ + MAIN_SAVED_OBJECT_INDEX, + TASK_MANAGER_SAVED_OBJECT_INDEX, + ANALYTICS_SAVED_OBJECT_INDEX, + ], _source: false, fields: ['_id'], size: 50, }); - // 23 saved objects + 14 corrupt (discarded) = 37 total in the old index - expect((docs.hits.total as SearchTotalHits).value).toEqual(23); + // 34 saved objects (11 tasks + 23 misc) + 14 corrupt (discarded) = 48 total in the old indices + expect((docs.hits.total as SearchTotalHits).value).toEqual(34); expect(docs.hits.hits.map(({ _id }) => _id).sort()).toEqual([ 'config:7.13.0', 'index-pattern:logs-*', 'index-pattern:metrics-*', + 'task:Actions-actions_telemetry', + 'task:Actions-cleanup_failed_action_executions', + 'task:Alerting-alerting_health_check', + 'task:Alerting-alerting_telemetry', + 'task:Alerts-alerts_invalidate_api_keys', + 'task:Lens-lens_telemetry', + 'task:apm-telemetry-task', + 'task:data_enhanced_search_sessions_monitor', + 'task:endpoint:user-artifact-packager:1.0.0', + 'task:security:endpoint-diagnostics:1.0.0', + 'task:session_cleanup', 'ui-metric:console:DELETE_delete', 'ui-metric:console:GET_get', 'ui-metric:console:GET_search', diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts index cba827a6e4d5a..4a64b26e1125a 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts @@ -118,7 +118,7 @@ describe('migration v2', () => { await root.preboot(); await root.setup(); await expect(root.start()).rejects.toMatchInlineSnapshot( - `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715248 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` + `[Error: Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715312 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.]` ); await retryAsync( @@ -131,7 +131,7 @@ describe('migration v2', () => { expect( records.find((rec) => rec.message.startsWith( - `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715248 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` + `Unable to complete saved object migrations for the [.kibana] index: The document with _id "canvas-workpad-template:workpad-template-061d7868-2b4e-4dc8-8bf7-3772b52926e5" is 1715312 bytes which exceeds the configured maximum batch size of 1015275 bytes. To proceed, please increase the 'migrations.maxBatchSizeBytes' Kibana configuration option and ensure that the Elasticsearch 'http.max_content_length' configuration option is set to an equal or larger value.` ) ) ).toBeDefined(); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index a6428387f65f3..05124ffad8140 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -81,10 +81,9 @@ describe('checking migration metadata changes on all registered SO types', () => "core-usage-stats": "b3c04da317c957741ebcdedfea4524049fdc79ff", "csp-rule-template": "099c229bf97578d9ca72b3a672d397559b84ee0b", "dashboard": "71e3f8dfcffeb5fbd410dec81ce46f5691763c43", - "endpoint:user-artifact": "a5b154962fb6cdf5d9e7452e58690054c95cc72a", - "endpoint:user-artifact-manifest": "5989989c0f84dd2d02da1eb46b6254e334bd2ccd", + "endpoint:user-artifact-manifest": "8ad9bd235dcfdc18b567aef0dc36ac686193dc89", "enterprise_search_telemetry": "4b41830e3b28a16eb92dee0736b44ae6276ced9b", - "epm-packages": "83235af7c95fd9bfb1d70996a5511e05b3fcc9ef", + "epm-packages": "8755f947a00613f994b1bc5d5580e104043e27f6", "epm-packages-assets": "00c8b5e5bf059627ffc9fbde920e1ac75926c5f6", "event_loop_delays_daily": "ef49e7f15649b551b458c7ea170f3ed17f89abd0", "exception-list": "38181294f64fc406c15f20d85ca306c8a4feb3c0", @@ -130,7 +129,7 @@ describe('checking migration metadata changes on all registered SO types', () => "search-session": "fae0dfc63274d6a3b90ca583802c48cab8760637", "search-telemetry": "1bbaf2db531b97fa04399440fa52d46e86d54dd8", "security-rule": "151108f4906744a137ddc89f5988310c5b9ba8b6", - "security-solution-signals-migration": "c2db409c1857d330beb3d6fd188fa186f920302c", + "security-solution-signals-migration": "0be3bed0f2ff4fe460493751b8be610a785c5c98", "siem-detection-engine-rule-actions": "123c130dc38120a470d8db9fed9a4cebd2046445", "siem-ui-timeline": "e9d6b3a9fd7af6dc502293c21cbdb309409f3996", "siem-ui-timeline-note": "13c9d4c142f96624a93a623c6d7cba7e1ae9b5a6", @@ -138,7 +137,7 @@ describe('checking migration metadata changes on all registered SO types', () => "slo": "aefffabdb35d15a6c388634af2cee1fa59ede83c", "space": "7fc578a1f9f7708cb07479f03953d664ad9f1dae", "spaces-usage-stats": "084bd0f080f94fb5735d7f3cf12f13ec92f36bad", - "synthetics-monitor": "7136a2669a65323c56da849f26c369cdeeb3b381", + "synthetics-monitor": "fa988678e5d6791c75515fa1acdbbb3b2d1f916d", "synthetics-param": "9776c9b571d35f0d0397e8915e035ea1dc026db7", "synthetics-privates-locations": "7d032fc788905e32152029ae7ab3d6038c48ae44", "tag": "87f21f07df9cc37001b15a26e413c18f50d1fbfe", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts index e51e0ef12a89f..057498745ca43 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts @@ -93,7 +93,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - operations: docs.map(createBulkIndexOperationTuple), + operations: docs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', })(); @@ -106,7 +106,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_write_block', - operations: docs.map(createBulkIndexOperationTuple), + operations: docs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', })(); await setWriteBlock({ client, index: 'existing_index_with_write_block' })(); @@ -307,7 +307,7 @@ describe('migration actions', () => { const res = (await bulkOverwriteTransformedDocuments({ client, index: 'new_index_without_write_block', - operations: sourceDocs.map(createBulkIndexOperationTuple), + operations: sourceDocs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', })()) as Either.Left; @@ -887,7 +887,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'reindex_target_4', - operations: sourceDocs.map(createBulkIndexOperationTuple), + operations: sourceDocs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', })(); @@ -1445,7 +1445,7 @@ describe('migration actions', () => { await bulkOverwriteTransformedDocuments({ client, index: 'existing_index_without_mappings', - operations: sourceDocs.map(createBulkIndexOperationTuple), + operations: sourceDocs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', })(); @@ -1895,7 +1895,7 @@ describe('migration actions', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - operations: newDocs.map(createBulkIndexOperationTuple), + operations: newDocs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', }); @@ -1921,7 +1921,7 @@ describe('migration actions', () => { operations: [ ...existingDocs, { _source: { title: 'doc 8' } } as unknown as SavedObjectsRawDoc, - ].map(createBulkIndexOperationTuple), + ].map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', }); await expect(task()).resolves.toMatchInlineSnapshot(` @@ -1941,7 +1941,7 @@ describe('migration actions', () => { bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_write_block', - operations: newDocs.map(createBulkIndexOperationTuple), + operations: newDocs.map((doc) => createBulkIndexOperationTuple(doc)), refresh: 'wait_for', })() ).resolves.toMatchInlineSnapshot(` @@ -1964,7 +1964,7 @@ describe('migration actions', () => { const task = bulkOverwriteTransformedDocuments({ client, index: 'existing_index_with_docs', - operations: newDocs.map(createBulkIndexOperationTuple), + operations: newDocs.map((doc) => createBulkIndexOperationTuple(doc)), }); await expect(task()).resolves.toMatchInlineSnapshot(` Object { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts index b5cad9ec53449..6079469cf4982 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts @@ -189,6 +189,7 @@ describe('migration v2', () => { namespaces: ['default'], coreMigrationVersion: expect.any(String), typeMigrationVersion: '8.0.0', + managed: false, }, { id: `foo:${newFooId}`, @@ -199,6 +200,7 @@ describe('migration v2', () => { originId: '1', coreMigrationVersion: expect.any(String), typeMigrationVersion: '8.0.0', + managed: false, }, { // new object for spacex:foo:1 @@ -223,6 +225,7 @@ describe('migration v2', () => { namespaces: ['default'], coreMigrationVersion: expect.any(String), typeMigrationVersion: '8.0.0', + managed: false, }, { id: `bar:${newBarId}`, @@ -233,6 +236,7 @@ describe('migration v2', () => { originId: '1', coreMigrationVersion: expect.any(String), typeMigrationVersion: '8.0.0', + managed: false, }, { // new object for spacex:bar:1 diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/split_kibana_index.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/split_kibana_index.test.ts new file mode 100644 index 0000000000000..06b8c169cc396 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/split_kibana_index.test.ts @@ -0,0 +1,385 @@ +/* + * 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 type { TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { + type ISavedObjectTypeRegistry, + type SavedObjectsType, + MAIN_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; +import { + readLog, + startElasticsearch, + getKibanaMigratorTestKit, + getCurrentVersionTypeRegistry, + overrideTypeRegistry, + clearLog, + getAggregatedTypesCount, + currentVersion, + type KibanaMigratorTestKit, +} from '../kibana_migrator_test_kit'; +import { delay } from '../test_utils'; + +// define a type => index distribution +const RELOCATE_TYPES: Record = { + dashboard: '.kibana_so_ui', + visualization: '.kibana_so_ui', + 'canvas-workpad': '.kibana_so_ui', + search: '.kibana_so_search', + task: '.kibana_task_manager', + // the remaining types will be forced to go to '.kibana', + // overriding `indexPattern: foo` defined in the registry +}; + +describe('split .kibana index into multiple system indices', () => { + let esServer: TestElasticsearchUtils['es']; + let typeRegistry: ISavedObjectTypeRegistry; + let migratorTestKitFactory: () => Promise; + + beforeAll(async () => { + typeRegistry = await getCurrentVersionTypeRegistry({ oss: false }); + + esServer = await startElasticsearch({ + dataArchive: Path.join(__dirname, '..', 'archives', '7.3.0_xpack_sample_saved_objects.zip'), + }); + }); + + beforeEach(async () => { + await clearLog(); + }); + + describe('when migrating from a legacy version', () => { + it('performs v1 migration and then relocates saved objects into different indices, depending on their types', async () => { + const updatedTypeRegistry = overrideTypeRegistry( + typeRegistry, + (type: SavedObjectsType) => { + return { + ...type, + indexPattern: RELOCATE_TYPES[type.name] ?? MAIN_SAVED_OBJECT_INDEX, + }; + } + ); + + migratorTestKitFactory = () => + getKibanaMigratorTestKit({ + types: updatedTypeRegistry.getAllTypes(), + kibanaIndex: '.kibana', + }); + + const { runMigrations, client } = await migratorTestKitFactory(); + + // count of types in the legacy index + expect(await getAggregatedTypesCount(client, '.kibana_1')).toEqual({ + 'canvas-workpad': 3, + config: 1, + dashboard: 3, + 'index-pattern': 3, + map: 3, + 'maps-telemetry': 1, + 'sample-data-telemetry': 3, + search: 2, + telemetry: 1, + space: 1, + visualization: 39, + }); + + await runMigrations(); + + await client.indices.refresh({ + index: ['.kibana', '.kibana_so_search', '.kibana_so_ui'], + }); + + expect(await getAggregatedTypesCount(client, '.kibana')).toEqual({ + 'index-pattern': 3, + map: 3, + 'sample-data-telemetry': 3, + config: 1, + telemetry: 1, + space: 1, + }); + expect(await getAggregatedTypesCount(client, '.kibana_so_search')).toEqual({ + search: 2, + }); + expect(await getAggregatedTypesCount(client, '.kibana_so_ui')).toEqual({ + visualization: 39, + 'canvas-workpad': 3, + dashboard: 3, + }); + + const indicesInfo = await client.indices.get({ index: '.kibana*' }); + expect(indicesInfo).toEqual( + expect.objectContaining({ + '.kibana_8.8.0_001': { + aliases: { '.kibana': expect.any(Object), '.kibana_8.8.0': expect.any(Object) }, + mappings: { + dynamic: 'strict', + _meta: { + migrationMappingPropertyHashes: expect.any(Object), + indexTypesMap: expect.any(Object), + }, + properties: expect.any(Object), + }, + settings: { index: expect.any(Object) }, + }, + '.kibana_so_search_8.8.0_001': { + aliases: { + '.kibana_so_search': expect.any(Object), + '.kibana_so_search_8.8.0': expect.any(Object), + }, + mappings: { + dynamic: 'strict', + _meta: { + migrationMappingPropertyHashes: expect.any(Object), + indexTypesMap: expect.any(Object), + }, + properties: expect.any(Object), + }, + settings: { index: expect.any(Object) }, + }, + '.kibana_so_ui_8.8.0_001': { + aliases: { + '.kibana_so_ui': expect.any(Object), + '.kibana_so_ui_8.8.0': expect.any(Object), + }, + mappings: { + dynamic: 'strict', + _meta: { + migrationMappingPropertyHashes: expect.any(Object), + indexTypesMap: expect.any(Object), + }, + properties: expect.any(Object), + }, + settings: { index: expect.any(Object) }, + }, + }) + ); + + expect(indicesInfo[`.kibana_${currentVersion}_001`].mappings?._meta?.indexTypesMap) + .toMatchInlineSnapshot(` + Object { + ".kibana": Array [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "canvas-element", + "canvas-workpad-template", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "config", + "config-global", + "connector_token", + "core-usage-stats", + "csp-rule-template", + "endpoint:user-artifact-manifest", + "enterprise_search_telemetry", + "epm-packages", + "epm-packages-assets", + "event_loop_delays_daily", + "exception-list", + "exception-list-agnostic", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "graph-workspace", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "index-pattern", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings", + "inventory-view", + "kql-telemetry", + "legacy-url-alias", + "lens", + "lens-ui-telemetry", + "maintenance-window", + "map", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "query", + "rules-settings", + "sample-data-telemetry", + "search-session", + "search-telemetry", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry", + ], + ".kibana_so_search": Array [ + "search", + ], + ".kibana_so_ui": Array [ + "canvas-workpad", + "dashboard", + "visualization", + ], + ".kibana_task_manager": Array [ + "task", + ], + } + `); + + const logs = await readLog(); + + // .kibana_task_manager index exists and has no aliases => LEGACY_* migration path + expect(logs).toMatch('[.kibana_task_manager] INIT -> LEGACY_SET_WRITE_BLOCK.'); + // .kibana_task_manager migrator is NOT involved in relocation, must not sync + expect(logs).not.toMatch('[.kibana_task_manager] READY_TO_REINDEX_SYNC'); + + // newer indices migrators did not exist, so they all have to reindex (create temp index + sync) + ['.kibana_so_ui', '.kibana_so_search'].forEach((newIndex) => { + expect(logs).toMatch(`[${newIndex}] INIT -> CREATE_REINDEX_TEMP.`); + expect(logs).toMatch(`[${newIndex}] CREATE_REINDEX_TEMP -> READY_TO_REINDEX_SYNC.`); + // no docs to reindex, as source index did NOT exist + expect(logs).toMatch(`[${newIndex}] READY_TO_REINDEX_SYNC -> DONE_REINDEXING_SYNC.`); + }); + + // the .kibana migrator is involved in a relocation, it must also reindex + expect(logs).toMatch('[.kibana] INIT -> WAIT_FOR_YELLOW_SOURCE.'); + expect(logs).toMatch('[.kibana] WAIT_FOR_YELLOW_SOURCE -> CHECK_UNKNOWN_DOCUMENTS.'); + expect(logs).toMatch('[.kibana] CHECK_UNKNOWN_DOCUMENTS -> SET_SOURCE_WRITE_BLOCK.'); + expect(logs).toMatch('[.kibana] SET_SOURCE_WRITE_BLOCK -> CALCULATE_EXCLUDE_FILTERS.'); + expect(logs).toMatch('[.kibana] CALCULATE_EXCLUDE_FILTERS -> CREATE_REINDEX_TEMP.'); + expect(logs).toMatch('[.kibana] CREATE_REINDEX_TEMP -> READY_TO_REINDEX_SYNC.'); + expect(logs).toMatch('[.kibana] READY_TO_REINDEX_SYNC -> REINDEX_SOURCE_TO_TEMP_OPEN_PIT.'); + expect(logs).toMatch( + '[.kibana] REINDEX_SOURCE_TO_TEMP_OPEN_PIT -> REINDEX_SOURCE_TO_TEMP_READ.' + ); + expect(logs).toMatch('[.kibana] Starting to process 59 documents.'); + expect(logs).toMatch( + '[.kibana] REINDEX_SOURCE_TO_TEMP_READ -> REINDEX_SOURCE_TO_TEMP_TRANSFORM.' + ); + expect(logs).toMatch( + '[.kibana] REINDEX_SOURCE_TO_TEMP_TRANSFORM -> REINDEX_SOURCE_TO_TEMP_INDEX_BULK.' + ); + expect(logs).toMatch('[.kibana_task_manager] LEGACY_REINDEX_WAIT_FOR_TASK -> LEGACY_DELETE.'); + expect(logs).toMatch( + '[.kibana] REINDEX_SOURCE_TO_TEMP_INDEX_BULK -> REINDEX_SOURCE_TO_TEMP_READ.' + ); + expect(logs).toMatch('[.kibana] Processed 59 documents out of 59.'); + expect(logs).toMatch( + '[.kibana] REINDEX_SOURCE_TO_TEMP_READ -> REINDEX_SOURCE_TO_TEMP_CLOSE_PIT.' + ); + expect(logs).toMatch('[.kibana] REINDEX_SOURCE_TO_TEMP_CLOSE_PIT -> DONE_REINDEXING_SYNC.'); + + // after .kibana migrator is done relocating documents + // the 3 migrators share the final part of the flow + [ + ['.kibana', 8], + ['.kibana_so_ui', 45], + ['.kibana_so_search', 2], + ].forEach(([index, docCount]) => { + expect(logs).toMatch(`[${index}] DONE_REINDEXING_SYNC -> SET_TEMP_WRITE_BLOCK.`); + expect(logs).toMatch(`[${index}] SET_TEMP_WRITE_BLOCK -> CLONE_TEMP_TO_TARGET.`); + + expect(logs).toMatch(`[${index}] CLONE_TEMP_TO_TARGET -> REFRESH_TARGET.`); + expect(logs).toMatch(`[${index}] REFRESH_TARGET -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT.`); + expect(logs).toMatch( + `[${index}] OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT -> OUTDATED_DOCUMENTS_SEARCH_READ.` + ); + expect(logs).toMatch(`[${index}] Starting to process ${docCount} documents.`); + expect(logs).toMatch( + `[${index}] OUTDATED_DOCUMENTS_SEARCH_READ -> OUTDATED_DOCUMENTS_TRANSFORM.` + ); + expect(logs).toMatch( + `[${index}] OUTDATED_DOCUMENTS_TRANSFORM -> TRANSFORMED_DOCUMENTS_BULK_INDEX.` + ); + expect(logs).toMatch( + `[${index}] OUTDATED_DOCUMENTS_SEARCH_READ -> OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT.` + ); + expect(logs).toMatch( + `[${index}] OUTDATED_DOCUMENTS_SEARCH_CLOSE_PIT -> OUTDATED_DOCUMENTS_REFRESH.` + ); + expect(logs).toMatch(`[${index}] OUTDATED_DOCUMENTS_REFRESH -> CHECK_TARGET_MAPPINGS.`); + expect(logs).toMatch( + `[${index}] CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_PROPERTIES.` + ); + + expect(logs).toMatch( + `[${index}] UPDATE_TARGET_MAPPINGS_PROPERTIES -> UPDATE_TARGET_MAPPINGS_PROPERTIES_WAIT_FOR_TASK.` + ); + expect(logs).toMatch( + `[${index}] UPDATE_TARGET_MAPPINGS_PROPERTIES_WAIT_FOR_TASK -> UPDATE_TARGET_MAPPINGS_META.` + ); + expect(logs).toMatch( + `[${index}] UPDATE_TARGET_MAPPINGS_META -> CHECK_VERSION_INDEX_READY_ACTIONS.` + ); + expect(logs).toMatch( + `[${index}] CHECK_VERSION_INDEX_READY_ACTIONS -> MARK_VERSION_INDEX_READY.` + ); + + expect(logs).toMatch(`[${index}] MARK_VERSION_INDEX_READY -> DONE.`); + expect(logs).toMatch(`[${index}] Migration completed`); + }); + }); + }); + + afterEach(async () => { + // we run the migrator again to ensure that the next time state is loaded everything still works as expected + const { runMigrations } = await migratorTestKitFactory(); + await clearLog(); + await runMigrations(); + + const logs = await readLog(); + expect(logs).not.toMatch('REINDEX'); + expect(logs).not.toMatch('CREATE'); + expect(logs).not.toMatch('UPDATE_TARGET_MAPPINGS'); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts index 64da61be1f0d3..22e3fe218a495 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts @@ -36,7 +36,7 @@ import { type LoggingConfigType, LoggingSystem } from '@kbn/core-logging-server- import type { ISavedObjectTypeRegistry, SavedObjectsType } from '@kbn/core-saved-objects-server'; import { esTestConfig, kibanaServerTestUser } from '@kbn/test'; import type { LoggerFactory } from '@kbn/logging'; -import { createTestServers } from '@kbn/core-test-helpers-kbn-server'; +import { createRootWithCorePlugins, createTestServers } from '@kbn/core-test-helpers-kbn-server'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { registerServiceConfig } from '@kbn/core-root-server-internal'; import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server'; @@ -72,7 +72,7 @@ export interface KibanaMigratorTestKitParams { export interface KibanaMigratorTestKit { client: ElasticsearchClient; migrator: IKibanaMigrator; - runMigrations: (rerun?: boolean) => Promise; + runMigrations: () => Promise; typeRegistry: ISavedObjectTypeRegistry; savedObjectsRepository: ISavedObjectsRepository; } @@ -282,6 +282,42 @@ const getMigrator = async ( }); }; +export const getAggregatedTypesCount = async (client: ElasticsearchClient, index: string) => { + await client.indices.refresh(); + const response = await client.search({ + index, + _source: false, + aggs: { + typesAggregation: { + terms: { + // assign type __UNKNOWN__ to those documents that don't define one + missing: '__UNKNOWN__', + field: 'type', + size: 100, + }, + aggs: { + docs: { + top_hits: { + size: 10, + _source: { + excludes: ['*'], + }, + }, + }, + }, + }, + }, + }); + + return (response.aggregations!.typesAggregation.buckets as unknown as any).reduce( + (acc: any, current: any) => { + acc[current.key] = current.doc_count; + return acc; + }, + {} + ); +}; + const registerTypes = ( typeRegistry: SavedObjectTypeRegistry, types?: Array> @@ -390,6 +426,28 @@ export const getIncompatibleMappingsMigrator = async ({ }); }; +export const getCurrentVersionTypeRegistry = async ({ + oss, +}: { + oss: boolean; +}): Promise => { + const root = createRootWithCorePlugins({}, { oss }); + await root.preboot(); + const coreSetup = await root.setup(); + const typeRegistry = coreSetup.savedObjects.getTypeRegistry(); + root.shutdown(); // do not await for it, or we might block the tests + return typeRegistry; +}; + +export const overrideTypeRegistry = ( + typeRegistry: ISavedObjectTypeRegistry, + transform: (type: SavedObjectsType) => SavedObjectsType +): ISavedObjectTypeRegistry => { + const updatedTypeRegistry = new SavedObjectTypeRegistry(); + typeRegistry.getAllTypes().forEach((type) => updatedTypeRegistry.registerType(transform(type))); + return updatedTypeRegistry; +}; + export const readLog = async (logFilePath: string = defaultLogFilePath): Promise => { await delay(0.1); return await fs.readFile(logFilePath, 'utf-8'); diff --git a/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy.test.ts b/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy.test.ts index e38231a6e0ea4..255658d7fbe7a 100644 --- a/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy.test.ts +++ b/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy.test.ts @@ -9,7 +9,7 @@ import Hapi from '@hapi/hapi'; import h2o2 from '@hapi/h2o2'; import { URL } from 'url'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; +import { SavedObject, ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server'; import type { InternalCoreSetup, InternalCoreStart } from '@kbn/core-lifecycle-server-internal'; import { Root } from '@kbn/core-root-server-internal'; @@ -18,6 +18,7 @@ import { createTestServers, type TestElasticsearchUtils, } from '@kbn/core-test-helpers-kbn-server'; +import { kibanaPackageJson as pkg } from '@kbn/repo-info'; import { declareGetRoute, declareDeleteRoute, @@ -30,6 +31,7 @@ import { declarePostUpdateByQueryRoute, declarePassthroughRoute, setProxyInterrupt, + allCombinationsPermutations, } from './repository_with_proxy_utils'; let esServer: TestElasticsearchUtils; @@ -98,17 +100,24 @@ describe('404s from proxies', () => { await hapiServer.register(h2o2); // register specific routes to modify the response and a catch-all to relay the request/response as-is - declareGetRoute(hapiServer, esHostname, esPort); - declareDeleteRoute(hapiServer, esHostname, esPort); - declarePostUpdateRoute(hapiServer, esHostname, esPort); + allCombinationsPermutations( + ALL_SAVED_OBJECT_INDICES.map((indexPattern) => `${indexPattern}_${pkg.version}`) + ) + .map((indices) => indices.join(',')) + .forEach((kbnIndexPath) => { + declareGetRoute(hapiServer, esHostname, esPort, kbnIndexPath); + declareDeleteRoute(hapiServer, esHostname, esPort, kbnIndexPath); + declarePostUpdateRoute(hapiServer, esHostname, esPort, kbnIndexPath); + + declareGetSearchRoute(hapiServer, esHostname, esPort, kbnIndexPath); + declarePostSearchRoute(hapiServer, esHostname, esPort, kbnIndexPath); + declarePostPitRoute(hapiServer, esHostname, esPort, kbnIndexPath); + declarePostUpdateByQueryRoute(hapiServer, esHostname, esPort, kbnIndexPath); + }); - declareGetSearchRoute(hapiServer, esHostname, esPort); - declarePostSearchRoute(hapiServer, esHostname, esPort); + // register index-agnostic routes declarePostBulkRoute(hapiServer, esHostname, esPort); declarePostMgetRoute(hapiServer, esHostname, esPort); - declarePostPitRoute(hapiServer, esHostname, esPort); - declarePostUpdateByQueryRoute(hapiServer, esHostname, esPort); - declarePassthroughRoute(hapiServer, esHostname, esPort); await hapiServer.start(); diff --git a/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy_utils.ts b/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy_utils.ts index 499d0d01d9de1..35b6b37b9c413 100644 --- a/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy_utils.ts +++ b/src/core/server/integration_tests/saved_objects/service/lib/repository_with_proxy_utils.ts @@ -7,7 +7,6 @@ */ import Hapi from '@hapi/hapi'; import { IncomingMessage } from 'http'; -import { kibanaPackageJson as pkg } from '@kbn/repo-info'; // proxy setup const defaultProxyOptions = (hostname: string, port: string) => ({ @@ -52,10 +51,13 @@ const proxyOnResponseHandler = async (res: IncomingMessage, h: Hapi.ResponseTool .code(404); }; -const kbnIndex = `.kibana_${pkg.version}`; - // GET /.kibana_8.0.0/_doc/{type*} route (repository.get calls) -export const declareGetRoute = (hapiServer: Hapi.Server, hostname: string, port: string) => +export const declareGetRoute = ( + hapiServer: Hapi.Server, + hostname: string, + port: string, + kbnIndex: string +) => hapiServer.route({ method: 'GET', path: `/${kbnIndex}/_doc/{type*}`, @@ -70,7 +72,12 @@ export const declareGetRoute = (hapiServer: Hapi.Server, hostname: string, port: }, }); // DELETE /.kibana_8.0.0/_doc/{type*} route (repository.delete calls) -export const declareDeleteRoute = (hapiServer: Hapi.Server, hostname: string, port: string) => +export const declareDeleteRoute = ( + hapiServer: Hapi.Server, + hostname: string, + port: string, + kbnIndex: string +) => hapiServer.route({ method: 'DELETE', path: `/${kbnIndex}/_doc/{_id*}`, @@ -133,7 +140,12 @@ export const declarePostMgetRoute = (hapiServer: Hapi.Server, hostname: string, }, }); // GET _search route -export const declareGetSearchRoute = (hapiServer: Hapi.Server, hostname: string, port: string) => +export const declareGetSearchRoute = ( + hapiServer: Hapi.Server, + hostname: string, + port: string, + kbnIndex: string +) => hapiServer.route({ method: 'GET', path: `/${kbnIndex}/_search`, @@ -149,7 +161,12 @@ export const declareGetSearchRoute = (hapiServer: Hapi.Server, hostname: string, }, }); // POST _search route (`find` calls) -export const declarePostSearchRoute = (hapiServer: Hapi.Server, hostname: string, port: string) => +export const declarePostSearchRoute = ( + hapiServer: Hapi.Server, + hostname: string, + port: string, + kbnIndex: string +) => hapiServer.route({ method: 'POST', path: `/${kbnIndex}/_search`, @@ -168,7 +185,12 @@ export const declarePostSearchRoute = (hapiServer: Hapi.Server, hostname: string }, }); // POST _update -export const declarePostUpdateRoute = (hapiServer: Hapi.Server, hostname: string, port: string) => +export const declarePostUpdateRoute = ( + hapiServer: Hapi.Server, + hostname: string, + port: string, + kbnIndex: string +) => hapiServer.route({ method: 'POST', path: `/${kbnIndex}/_update/{_id*}`, @@ -187,7 +209,12 @@ export const declarePostUpdateRoute = (hapiServer: Hapi.Server, hostname: string }, }); // POST _pit -export const declarePostPitRoute = (hapiServer: Hapi.Server, hostname: string, port: string) => +export const declarePostPitRoute = ( + hapiServer: Hapi.Server, + hostname: string, + port: string, + kbnIndex: string +) => hapiServer.route({ method: 'POST', path: `/${kbnIndex}/_pit`, @@ -209,7 +236,8 @@ export const declarePostPitRoute = (hapiServer: Hapi.Server, hostname: string, p export const declarePostUpdateByQueryRoute = ( hapiServer: Hapi.Server, hostname: string, - port: string + port: string, + kbnIndex: string ) => hapiServer.route({ method: 'POST', @@ -244,3 +272,22 @@ export const declarePassthroughRoute = (hapiServer: Hapi.Server, hostname: strin }, }, }); + +export function allCombinationsPermutations(collection: T[]): T[][] { + const recur = (subcollection: T[], size: number): T[][] => { + if (size <= 0) { + return [[]]; + } + const permutations: T[][] = []; + subcollection.forEach((value, index, array) => { + array = array.slice(); + array.splice(index, 1); + recur(array, size - 1).forEach((permutation) => { + permutation.unshift(value); + permutations.push(permutation); + }); + }); + return permutations; + }; + return collection.map((_, n) => recur(collection, n + 1)).flat(); +} diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index d2b8652375998..4f351bba980b2 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -32,6 +32,7 @@ it('build default and oss dist for current platform, without packages, by defaul "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, + "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, "createGenericFolders": true, @@ -69,6 +70,7 @@ it('builds packages if --all-platforms is passed', () => { "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, + "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": true, "createGenericFolders": true, @@ -106,6 +108,7 @@ it('limits packages if --rpm passed with --all-platforms', () => { "createDebPackage": false, "createDockerCloud": false, "createDockerContexts": true, + "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, "createGenericFolders": true, @@ -143,6 +146,7 @@ it('limits packages if --deb passed with --all-platforms', () => { "createDebPackage": true, "createDockerCloud": false, "createDockerContexts": true, + "createDockerServerless": false, "createDockerUBI": false, "createDockerUbuntu": false, "createGenericFolders": true, @@ -181,6 +185,7 @@ it('limits packages if --docker passed with --all-platforms', () => { "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, + "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": true, "createGenericFolders": true, @@ -226,6 +231,7 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "createDebPackage": false, "createDockerCloud": true, "createDockerContexts": true, + "createDockerServerless": true, "createDockerUBI": false, "createDockerUbuntu": true, "createGenericFolders": true, @@ -264,6 +270,7 @@ it('limits packages if --all-platforms passed with --skip-docker-ubuntu', () => "createDebPackage": true, "createDockerCloud": true, "createDockerContexts": true, + "createDockerServerless": true, "createDockerUBI": true, "createDockerUbuntu": false, "createGenericFolders": true, diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index edc4db2ca49c7..68e111912722f 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -31,6 +31,7 @@ export function readCliArgs(argv: string[]) { 'skip-docker-ubi', 'skip-docker-ubuntu', 'skip-docker-cloud', + 'skip-docker-serverless', 'release', 'skip-node-download', 'skip-cloud-dependencies-download', @@ -136,6 +137,8 @@ export function readCliArgs(argv: string[]) { createDockerUbuntu: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-ubuntu']), createDockerCloud: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-cloud']), + createDockerServerless: + isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-serverless']), createDockerUBI: isOsPackageDesired('docker-images') && !Boolean(flags['skip-docker-ubi']), createDockerContexts: !Boolean(flags['skip-docker-contexts']), targetAllPlatforms: Boolean(flags['all-platforms']), diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index f101f05dcd7c6..23203491e0d13 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -31,6 +31,7 @@ export interface BuildOptions { createDockerUBI: boolean; createDockerUbuntu: boolean; createDockerCloud: boolean; + createDockerServerless: boolean; createDockerContexts: boolean; versionQualifier: string | undefined; targetAllPlatforms: boolean; @@ -152,6 +153,11 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.CreateDockerCloud); } + if (options.createDockerServerless) { + // control w/ --docker-images and --skip-docker-serverless + await run(Tasks.CreateDockerServerless); + } + if (options.createDockerContexts) { // control w/ --skip-docker-contexts await run(Tasks.CreateDockerContexts); diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts index 69a272d39f4a0..13a2209e2e47e 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts @@ -80,6 +80,29 @@ export const CreateDockerUbuntu: Task = { }, }; +export const CreateDockerServerless: Task = { + description: 'Creating Docker Ubuntu image', + + async run(config, log, build) { + await runDockerGenerator(config, log, build, { + architecture: 'x64', + baseImage: 'ubuntu', + context: false, + serverless: true, + image: true, + dockerBuildDate, + }); + await runDockerGenerator(config, log, build, { + architecture: 'aarch64', + baseImage: 'ubuntu', + context: false, + serverless: true, + image: true, + dockerBuildDate, + }); + }, +}; + export const CreateDockerUBI: Task = { description: 'Creating Docker UBI image', @@ -152,5 +175,11 @@ export const CreateDockerContexts: Task = { context: true, image: false, }); + await runDockerGenerator(config, log, build, { + baseImage: 'ubuntu', + serverless: true, + context: true, + image: false, + }); }, }; diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 6801686f90f57..3123aa7f391df 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -253,6 +253,8 @@ kibana_vars=( xpack.banners.placement xpack.banners.textColor xpack.banners.textContent + xpack.cases.files.allowedMimeTypes + xpack.cases.files.maxSize xpack.code.disk.thresholdEnabled xpack.code.disk.watermarkLow xpack.code.indexRepoFrequencyMs @@ -412,6 +414,7 @@ kibana_vars=( xpack.task_manager.event_loop_delay.monitor xpack.task_manager.event_loop_delay.warn_threshold xpack.uptime.index + serverless ) longopts='' diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index a4b128f42be5c..f9da19183d866 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -34,6 +34,7 @@ export async function runDockerGenerator( image: boolean; ironbank?: boolean; cloud?: boolean; + serverless?: boolean; dockerBuildDate?: string; } ) { @@ -47,13 +48,14 @@ export async function runDockerGenerator( if (flags.baseImage === 'ubi9') imageFlavor += `-ubi9`; if (flags.ironbank) imageFlavor += '-ironbank'; if (flags.cloud) imageFlavor += '-cloud'; + if (flags.serverless) imageFlavor += '-serverless'; // General docker var config const license = 'Elastic License'; const configuredNamespace = config.getDockerNamespace(); const imageNamespace = configuredNamespace ? configuredNamespace - : flags.cloud + : flags.cloud || flags.serverless ? 'kibana-ci' : 'kibana'; const imageTag = `docker.elastic.co/${imageNamespace}/kibana`; @@ -104,6 +106,7 @@ export async function runDockerGenerator( dockerBuildDate, baseImage: flags.baseImage, cloud: flags.cloud, + serverless: flags.serverless, metricbeatTarball, filebeatTarball, ironbank: flags.ironbank, @@ -131,6 +134,14 @@ export async function runDockerGenerator( await write(resolve(dockerBuildDir, dockerTemplate.name), dockerTemplate.generator(scope)); } + // Copy serverless-only configuration files + if (flags.serverless) { + await mkdirp(resolve(dockerBuildDir, 'config')); + await copyAll(config.resolveFromRepo('config'), resolve(dockerBuildDir, 'config'), { + select: ['serverless*.yml'], + }); + } + // Copy all the needed resources into kibana-docker folder // in order to build the docker image accordingly the dockerfile defined // under templates/kibana_yml.template/js diff --git a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts index 62e76cbd31b77..61609203edcc0 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/template_context.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/template_context.ts @@ -27,6 +27,7 @@ export interface TemplateContext { baseImage: 'none' | 'ubi8' | 'ubi9' | 'ubuntu'; baseImageName: string; cloud?: boolean; + serverless?: boolean; metricbeatTarball?: string; filebeatTarball?: string; ironbank?: boolean; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index da897e858867a..beeed1d12dd04 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -120,6 +120,12 @@ ENV PATH=/usr/share/kibana/bin:$PATH # Set some Kibana configuration defaults. COPY --chown=1000:0 config/kibana.yml /usr/share/kibana/config/kibana.yml +{{#serverless}} +COPY --chown=1000:0 config/serverless.yml /usr/share/kibana/config/serverless.yml +COPY --chown=1000:0 config/serverless.es.yml /usr/share/kibana/config/serverless.es.yml +COPY --chown=1000:0 config/serverless.oblt.yml /usr/share/kibana/config/serverless.oblt.yml +COPY --chown=1000:0 config/serverless.security.yml /usr/share/kibana/config/serverless.security.yml +{{/serverless}} # Add the launcher/wrapper script. It knows how to interpret environment # variables and translate them to Kibana CLI options. diff --git a/src/dev/performance/run_performance_cli.ts b/src/dev/performance/run_performance_cli.ts index 3539ada07e405..3c5807e1c208b 100644 --- a/src/dev/performance/run_performance_cli.ts +++ b/src/dev/performance/run_performance_cli.ts @@ -11,6 +11,7 @@ import { run } from '@kbn/dev-cli-runner'; import { REPO_ROOT } from '@kbn/repo-info'; import fs from 'fs'; import path from 'path'; +import { JOURNEY_APM_CONFIG } from '@kbn/journeys'; const JOURNEY_BASE_PATH = 'x-pack/performance/journeys'; @@ -103,7 +104,25 @@ run( process.stdout.write(`--- Starting ES\n`); await procRunner.run('es', { cmd: 'node', - args: ['scripts/es', 'snapshot', '--license=trial'], + args: [ + 'scripts/es', + 'snapshot', + '--license=trial', + ...(JOURNEY_APM_CONFIG.active + ? [ + '-E', + 'tracing.apm.enabled=true', + '-E', + 'tracing.apm.agent.transaction_sample_rate=1.0', + '-E', + `tracing.apm.agent.server_url=${JOURNEY_APM_CONFIG.serverUrl}`, + '-E', + `tracing.apm.agent.secret_token=${JOURNEY_APM_CONFIG.secretToken}`, + '-E', + `tracing.apm.agent.environment=${JOURNEY_APM_CONFIG.environment}`, + ] + : []), + ], cwd: REPO_ROOT, wait: /kbn\/es setup complete/, }); diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index da762f8f55725..c612ef05d4b7a 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -37,6 +37,7 @@ export const storybookAliases = { expression_shape: 'src/plugins/expression_shape/.storybook', expression_tagcloud: 'src/plugins/chart_expressions/expression_tagcloud/.storybook', fleet: 'x-pack/plugins/fleet/.storybook', + grouping: 'packages/kbn-securitysolution-grouping/.storybook', home: 'src/plugins/home/.storybook', infra: 'x-pack/plugins/infra/.storybook', kibana_react: 'src/plugins/kibana_react/.storybook', diff --git a/src/dev/tsconfig.json b/src/dev/tsconfig.json index 7ceac215ee3f2..b4fb983d63e52 100644 --- a/src/dev/tsconfig.json +++ b/src/dev/tsconfig.json @@ -38,5 +38,6 @@ "@kbn/repo-file-maps", "@kbn/get-repo-files", "@kbn/import-locator", + "@kbn/journeys", ] } diff --git a/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx b/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx index 0e65e1b67b012..25948bbd04317 100644 --- a/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx +++ b/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx @@ -84,7 +84,10 @@ export async function mountManagementSection( { + if (!splitColumnAccessor && !splitRowAccessor) return null; + + return ( + <> + { + const splitTypeAccessor = splitColumnAccessor || splitRowAccessor; + if (splitTypeAccessor) { + return typeof splitTypeAccessor === 'function' + ? splitTypeAccessor(datum) + : datum[splitTypeAccessor]; + } + return spec.id; + }} + sort={Predicate.DataIndex} + /> + + + ); +}; diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index 8bae0d35e85c2..f243fa4d67263 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -50,6 +50,8 @@ import { LegendColorPickerWrapper, } from '../utils/get_color_picker'; import { defaultPaletteParams } from '../constants'; +import { ChartSplit } from './chart_split'; +import { getSplitDimensionAccessor, createSplitPoint } from '../utils/get_split_dimension_utils'; import './index.scss'; declare global { @@ -207,7 +209,6 @@ export const HeatmapComponent: FC = memo( () => findMinMaxByColumnId([valueAccessor!], table), [valueAccessor, table] ); - const paletteParams = args.palette?.params; const xAccessor = args.xAccessor ? getAccessorByDimension(args.xAccessor, table.columns) @@ -215,6 +216,12 @@ export const HeatmapComponent: FC = memo( const yAccessor = args.yAccessor ? getAccessorByDimension(args.yAccessor, table.columns) : undefined; + const splitChartRowAccessor = args.splitRowAccessor + ? getSplitDimensionAccessor(data.columns, args.splitRowAccessor, formatFactory) + : undefined; + const splitChartColumnAccessor = args.splitColumnAccessor + ? getSplitDimensionAccessor(data.columns, args.splitColumnAccessor, formatFactory) + : undefined; const xAxisColumnIndex = table.columns.findIndex((v) => v.id === xAccessor); const yAxisColumnIndex = table.columns.findIndex((v) => v.id === yAccessor); @@ -252,7 +259,7 @@ export const HeatmapComponent: FC = memo( const onElementClick = useCallback( (e: HeatmapElementEvent[]) => { const cell = e[0][0]; - const { x, y } = cell.datum; + const { x, y, smVerticalAccessorValue, smHorizontalAccessorValue } = cell.datum; const points = [ { @@ -284,6 +291,28 @@ export const HeatmapComponent: FC = memo( : []), ]; + if (smHorizontalAccessorValue && args.splitColumnAccessor) { + const point = createSplitPoint( + args.splitColumnAccessor, + smHorizontalAccessorValue, + formatFactory, + table + ); + if (point) { + points.push(point); + } + } + if (smVerticalAccessorValue && args.splitRowAccessor) { + const point = createSplitPoint( + args.splitRowAccessor, + smVerticalAccessorValue, + formatFactory, + table + ); + if (point) { + points.push(point); + } + } const context: FilterEvent['data'] = { data: points.map((point) => ({ row: point.row, @@ -295,6 +324,9 @@ export const HeatmapComponent: FC = memo( onClickValue(context); }, [ + args.splitColumnAccessor, + args.splitRowAccessor, + formatFactory, formattedTable.formattedColumns, onClickValue, table, @@ -579,6 +611,10 @@ export const HeatmapComponent: FC = memo( }} > + { + const defaultFormatter = jest.fn((...args) => fieldFormatsMock.deserialize(...args)); + + beforeEach(() => { + defaultFormatter.mockClear(); + }); + + const splitDimension: ExpressionValueVisDimension = { + type: 'vis_dimension', + accessor: { + id: data.columns[2].id, + name: data.columns[2].name, + meta: data.columns[2].meta, + }, + format: { + params: {}, + }, + }; + + it('returns accessor which is using formatter, if meta.params are present at accessing column', () => { + const accessor = getSplitDimensionAccessor(data.columns, splitDimension, defaultFormatter); + + expect(defaultFormatter).toHaveBeenCalledTimes(1); + expect(typeof accessor).toBe('function'); + accessor(data.rows[0]); + }); + + it('returns accessor which is using default formatter, if meta.params and format are not present', () => { + const column: Partial = { + ...data.columns[2], + meta: { type: 'number' }, + }; + const columns = [data.columns[0], column, data.columns[2]] as DatatableColumn[]; + const defaultFormatterReturnedVal = fieldFormatsMock.deserialize(); + const spyOnDefaultFormatterConvert = jest.spyOn(defaultFormatterReturnedVal, 'convert'); + + defaultFormatter.mockReturnValueOnce(defaultFormatterReturnedVal); + const accessor = getSplitDimensionAccessor(columns, splitDimension, defaultFormatter); + + expect(defaultFormatter).toHaveBeenCalledTimes(1); + + expect(typeof accessor).toBe('function'); + accessor(data.rows[0]); + expect(spyOnDefaultFormatterConvert).toHaveBeenCalledTimes(1); + }); + + it('returns accessor which returns undefined, if such column is not present', () => { + const accessor1 = getSplitDimensionAccessor(data.columns, splitDimension, defaultFormatter); + + expect(typeof accessor1).toBe('function'); + const result1 = accessor1({}); + expect(result1).toBeUndefined(); + + const column2: Partial = { + ...data.columns[2], + meta: { type: 'string' }, + }; + const columns2 = [data.columns[0], data.columns[1], column2] as DatatableColumn[]; + const accessor2 = getSplitDimensionAccessor(columns2, splitDimension, defaultFormatter); + + expect(typeof accessor2).toBe('function'); + const result2 = accessor1({}); + expect(result2).toBeUndefined(); + + const column3 = { + ...data.columns[2], + meta: { type: 'string' }, + format: { + id: 'string', + params: {}, + }, + }; + const columns3 = [data.columns[0], data.columns[1], column3] as DatatableColumn[]; + + const accessor3 = getSplitDimensionAccessor(columns3, splitDimension, defaultFormatter); + expect(typeof accessor3).toBe('function'); + const result3 = accessor3({}); + expect(result3).toBeUndefined(); + }); +}); + +describe('createSplitPoint', () => { + const defaultFormatter = jest.fn((...args) => fieldFormatsMock.deserialize(...args)); + + beforeEach(() => { + defaultFormatter.mockClear(); + }); + + const splitDimension: ExpressionValueVisDimension = { + type: 'vis_dimension', + accessor: { + id: data.columns[2].id, + name: data.columns[2].name, + meta: data.columns[2].meta, + }, + format: { + params: {}, + }, + }; + + it('returns point if value is found in the table', () => { + const point = createSplitPoint(splitDimension, 'c', defaultFormatter, data); + + expect(defaultFormatter).toHaveBeenCalledTimes(1); + expect(point).toStrictEqual({ column: 2, row: 1, value: 'c' }); + }); + + it('returns undefined if value is not found in the table', () => { + const point = createSplitPoint(splitDimension, 'test', defaultFormatter, data); + + expect(defaultFormatter).toHaveBeenCalledTimes(1); + expect(point).toBeUndefined(); + }); +}); diff --git a/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.ts b/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.ts new file mode 100644 index 0000000000000..e59d6c1aec28b --- /dev/null +++ b/src/plugins/chart_expressions/expression_heatmap/public/utils/get_split_dimension_utils.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 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 type { AccessorFn } from '@elastic/charts'; +import type { DatatableColumn, Datatable } from '@kbn/expressions-plugin/public'; +import type { FormatFactory } from '@kbn/field-formats-plugin/common'; +import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; + +export const getSplitDimensionAccessor = ( + columns: DatatableColumn[], + splitDimension: ExpressionValueVisDimension | string, + formatFactory: FormatFactory +): AccessorFn => { + const splitChartColumn = getColumnByAccessor(splitDimension, columns)!; + const accessor = splitChartColumn.id; + + const formatter = formatFactory(splitChartColumn.meta?.params); + const fn: AccessorFn = (d) => { + const v = d[accessor]; + if (v === undefined) { + return; + } + + const f = formatter.convert(v); + return f; + }; + + return fn; +}; + +export function createSplitPoint( + splitDimension: ExpressionValueVisDimension | string, + value: string | number, + formatFactory: FormatFactory, + table: Datatable +) { + const splitChartColumn = getColumnByAccessor(splitDimension, table.columns)!; + const accessor = splitChartColumn.id; + + const formatter = formatFactory(splitChartColumn.meta?.params); + const splitPointRowIndex = table.rows.findIndex((row) => { + return formatter.convert(row[accessor]) === value; + }); + if (splitPointRowIndex !== -1) { + return { + row: splitPointRowIndex, + column: table.columns.findIndex((column) => column.id === accessor), + value: table.rows[splitPointRowIndex][accessor], + }; + } +} diff --git a/src/plugins/content_management/docs/content_onboarding.md b/src/plugins/content_management/docs/content_onboarding.md index 411c718945b03..d170d80f87981 100644 --- a/src/plugins/content_management/docs/content_onboarding.md +++ b/src/plugins/content_management/docs/content_onboarding.md @@ -239,7 +239,7 @@ import { cmServicesDefinition } from '../../common/content_management/cm_service * that we won't leak any additional fields in our Response, even when the SO client adds new fields to its responses. */ function savedObjectToMapItem( - savedObject: SavedObject + savedObject: SavedObject ): MapItem { const { id, @@ -293,7 +293,7 @@ export class MapsStorage implements ContentStorage(SO_TYPE, id); + } = await soClient.resolve(SO_TYPE, id); const response: MapGetOut = { item: savedObjectToMapItem(savedObject), @@ -327,8 +327,8 @@ export class MapsStorage implements ContentStorage(data); if (dataError) { throw Boom.badRequest(`Invalid payload. ${dataError.message}`); @@ -345,7 +345,7 @@ export class MapsStorage implements ContentStorage( + const savedObject = await soClient.create( SO_TYPE, dataToLatest, optionsToLatest diff --git a/src/plugins/content_management/server/core/core.ts b/src/plugins/content_management/server/core/core.ts index 8062e4ca23ea5..4d055ed6783f4 100644 --- a/src/plugins/content_management/server/core/core.ts +++ b/src/plugins/content_management/server/core/core.ts @@ -28,7 +28,7 @@ export interface CoreApi { export interface CoreInitializerContext { logger: Logger; - eventStream: EventStreamService; + eventStream?: EventStreamService; } export interface CoreSetup { @@ -63,14 +63,20 @@ export class Core { } private setupEventStream() { + const eventStream = this.ctx.eventStream; + // TODO: This should be cleaned up and support added for all CRUD events. - this.eventBus.on('deleteItemSuccess', (event) => { - this.ctx.eventStream.addEvent({ - // TODO: add "subject" field to event - predicate: ['delete'], - // TODO: the `.contentId` should be easily available on most events. - object: [event.contentTypeId, (event as any).contentId], + // The work is tracked here: https://github.com/elastic/kibana/issues/153258 + // and here: https://github.com/elastic/kibana/issues/153260 + if (eventStream) { + this.eventBus.on('deleteItemSuccess', (event) => { + eventStream.addEvent({ + // TODO: add "subject" field to event + predicate: ['delete'], + // TODO: the `.contentId` should be easily available on most events. + object: [event.contentTypeId, (event as any).contentId], + }); }); - }); + } } } diff --git a/src/plugins/content_management/server/core/index.ts b/src/plugins/content_management/server/core/index.ts index c765076f39e94..1ac7228c6f163 100644 --- a/src/plugins/content_management/server/core/index.ts +++ b/src/plugins/content_management/server/core/index.ts @@ -17,6 +17,7 @@ export type { ContentTypeDefinition, StorageContext, StorageContextGetTransformFn, + MSearchConfig, } from './types'; export type { ContentRegistry } from './registry'; diff --git a/src/plugins/content_management/server/core/registry.ts b/src/plugins/content_management/server/core/registry.ts index 7d36fa20fad1a..00adb4b04a403 100644 --- a/src/plugins/content_management/server/core/registry.ts +++ b/src/plugins/content_management/server/core/registry.ts @@ -9,7 +9,7 @@ import { validateVersion } from '@kbn/object-versioning/lib/utils'; import { ContentType } from './content_type'; import { EventBus } from './event_bus'; -import type { ContentStorage, ContentTypeDefinition } from './types'; +import type { ContentStorage, ContentTypeDefinition, MSearchConfig } from './types'; import type { ContentCrud } from './crud'; export class ContentRegistry { @@ -23,7 +23,9 @@ export class ContentRegistry { * @param contentType The content type to register * @param config The content configuration */ - register = ContentStorage>(definition: ContentTypeDefinition) { + register> = ContentStorage>( + definition: ContentTypeDefinition + ) { if (this.types.has(definition.id)) { throw new Error(`Content [${definition.id}] is already registered`); } diff --git a/src/plugins/content_management/server/core/types.ts b/src/plugins/content_management/server/core/types.ts index c9e66c2563b61..d26c6ac72fa41 100644 --- a/src/plugins/content_management/server/core/types.ts +++ b/src/plugins/content_management/server/core/types.ts @@ -41,7 +41,11 @@ export interface StorageContext { }; } -export interface ContentStorage { +export interface ContentStorage< + T = unknown, + U = T, + TMSearchConfig extends MSearchConfig = MSearchConfig +> { /** Get a single item */ get(ctx: StorageContext, id: string, options?: object): Promise>; @@ -69,7 +73,7 @@ export interface ContentStorage { * Opt-in to multi-type search. * Can only be supported if the content type is backed by a saved object since `mSearch` is using the `savedObjects.find` API. **/ - mSearch?: MSearchConfig; + mSearch?: TMSearchConfig; } export interface ContentTypeDefinition { @@ -87,7 +91,7 @@ export interface ContentTypeDefinition { +export interface MSearchConfig { /** * The saved object type that corresponds to this content type. */ @@ -98,7 +102,7 @@ export interface MSearchConfig { */ toItemResult: ( ctx: StorageContext, - savedObject: SavedObjectsFindResult + savedObject: SavedObjectsFindResult ) => T; /** diff --git a/src/plugins/content_management/server/index.ts b/src/plugins/content_management/server/index.ts index 659b59ed6880c..cdd69cb99b296 100644 --- a/src/plugins/content_management/server/index.ts +++ b/src/plugins/content_management/server/index.ts @@ -14,4 +14,4 @@ export function plugin(initializerContext: PluginInitializerContext) { } export type { ContentManagementServerSetup, ContentManagementServerStart } from './types'; -export type { ContentStorage, StorageContext } from './core'; +export type { ContentStorage, StorageContext, MSearchConfig } from './core'; diff --git a/src/plugins/content_management/server/plugin.ts b/src/plugins/content_management/server/plugin.ts index e70bb5da14a65..4913c5cac08f6 100755 --- a/src/plugins/content_management/server/plugin.ts +++ b/src/plugins/content_management/server/plugin.ts @@ -21,7 +21,7 @@ import { ContentManagementServerStart, SetupDependencies, } from './types'; -import { EventStreamService, EsEventStreamClientFactory } from './event_stream'; +import { EventStreamService } from './event_stream'; import { procedureNames } from '../common/rpc'; export class ContentManagementPlugin @@ -29,20 +29,22 @@ export class ContentManagementPlugin { private readonly logger: Logger; private readonly core: Core; - readonly #eventStream: EventStreamService; + readonly #eventStream?: EventStreamService; constructor(initializerContext: PluginInitializerContext) { - const kibanaVersion = initializerContext.env.packageInfo.version; - this.logger = initializerContext.logger.get(); - this.#eventStream = new EventStreamService({ - logger: this.logger, - clientFactory: new EsEventStreamClientFactory({ - baseName: '.kibana', - kibanaVersion, - logger: this.logger, - }), - }); + + // TODO: Enable Event Stream once we ready to log events. + // const kibanaVersion = initializerContext.env.packageInfo.version; + // this.#eventStream = new EventStreamService({ + // logger: this.logger, + // clientFactory: new EsEventStreamClientFactory({ + // baseName: '.kibana', + // kibanaVersion, + // logger: this.logger, + // }), + // }); + this.core = new Core({ logger: this.logger, eventStream: this.#eventStream, @@ -50,7 +52,9 @@ export class ContentManagementPlugin } public setup(core: CoreSetup) { - this.#eventStream.setup({ core }); + if (this.#eventStream) { + this.#eventStream.setup({ core }); + } const { api: coreApi, contentRegistry } = this.core.setup(); @@ -69,16 +73,20 @@ export class ContentManagementPlugin } public start(core: CoreStart) { - this.#eventStream.start(); + if (this.#eventStream) { + this.#eventStream.start(); + } return {}; } public async stop(): Promise { - try { - await this.#eventStream.stop(); - } catch (e) { - this.logger.error(`Error during event stream stop: ${e}`); + if (this.#eventStream) { + try { + await this.#eventStream.stop(); + } catch (e) { + this.logger.error(`Error during event stream stop: ${e}`); + } } } } diff --git a/src/plugins/controls/common/index.ts b/src/plugins/controls/common/index.ts index 346cb53ce4244..de492adb399f3 100644 --- a/src/plugins/controls/common/index.ts +++ b/src/plugins/controls/common/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export type { ControlWidth } from './types'; +export type { ControlWidth, ControlInputTransform } from './types'; // Control Group exports export { diff --git a/src/plugins/controls/common/options_list/mocks.tsx b/src/plugins/controls/common/options_list/mocks.tsx index 943e78c370fc6..936a620ec288c 100644 --- a/src/plugins/controls/common/options_list/mocks.tsx +++ b/src/plugins/controls/common/options_list/mocks.tsx @@ -6,19 +6,15 @@ * Side Public License, v 1. */ -import { createReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public/redux_embeddables/create_redux_embeddable_tools'; - import { OptionsListEmbeddable, OptionsListEmbeddableFactory } from '../../public'; import { OptionsListComponentState, OptionsListReduxState } from '../../public/options_list/types'; -import { - getDefaultComponentState, - optionsListReducers, -} from '../../public/options_list/options_list_reducers'; import { ControlFactory, ControlOutput } from '../../public/types'; import { OptionsListEmbeddableInput } from './types'; +import * as optionsListStateModule from '../../public/options_list/options_list_reducers'; + const mockOptionsListComponentState = { - ...getDefaultComponentState(), + searchString: { value: '', valid: true }, field: undefined, totalCardinality: 0, availableOptions: { @@ -29,6 +25,8 @@ const mockOptionsListComponentState = { moo: { doc_count: 5 }, }, invalidSelections: [], + allowExpensiveQueries: true, + popoverOpen: false, validSelections: [], } as OptionsListComponentState; @@ -46,26 +44,24 @@ const mockOptionsListOutput = { loading: false, } as ControlOutput; -export const mockOptionsListReduxEmbeddableTools = async ( - partialState?: Partial -) => { +export const mockOptionsListEmbeddable = async (partialState?: Partial) => { const optionsListFactoryStub = new OptionsListEmbeddableFactory(); const optionsListControlFactory = optionsListFactoryStub as unknown as ControlFactory; optionsListControlFactory.getDefaultInput = () => ({}); + + // initial component state can be provided by overriding the defaults. + const initialComponentState = { + ...mockOptionsListComponentState, + ...partialState?.componentState, + }; + jest + .spyOn(optionsListStateModule, 'getDefaultComponentState') + .mockImplementation(() => initialComponentState); + const mockEmbeddable = (await optionsListControlFactory.create({ ...mockOptionsListEmbeddableInput, ...partialState?.explicitInput, })) as OptionsListEmbeddable; mockEmbeddable.getOutput = jest.fn().mockReturnValue(mockOptionsListOutput); - - const mockReduxEmbeddableTools = createReduxEmbeddableTools({ - embeddable: mockEmbeddable, - reducers: optionsListReducers, - initialComponentState: { - ...mockOptionsListComponentState, - ...partialState?.componentState, - }, - }); - - return mockReduxEmbeddableTools; + return mockEmbeddable; }; diff --git a/src/plugins/controls/common/types.ts b/src/plugins/controls/common/types.ts index 5f37ef2c72871..7eb0d69f98a4f 100644 --- a/src/plugins/controls/common/types.ts +++ b/src/plugins/controls/common/types.ts @@ -32,3 +32,8 @@ export type DataControlInput = ControlInput & { fieldName: string; dataViewId: string; }; + +export type ControlInputTransform = ( + newState: Partial, + controlType: string +) => Partial; diff --git a/src/plugins/controls/public/control_group/actions/delete_control_action.test.tsx b/src/plugins/controls/public/control_group/actions/delete_control_action.test.tsx index 19d036ae5a6b3..0f64334b94787 100644 --- a/src/plugins/controls/public/control_group/actions/delete_control_action.test.tsx +++ b/src/plugins/controls/public/control_group/actions/delete_control_action.test.tsx @@ -6,10 +6,6 @@ * Side Public License, v 1. */ -import { - lazyLoadReduxEmbeddablePackage, - ReduxEmbeddablePackage, -} from '@kbn/presentation-util-plugin/public'; import { ErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { ControlOutput } from '../../types'; @@ -17,17 +13,15 @@ import { ControlGroupInput } from '../types'; import { pluginServices } from '../../services'; import { DeleteControlAction } from './delete_control_action'; import { OptionsListEmbeddableInput } from '../../options_list'; -import { controlGroupInputBuilder } from '../control_group_input_builder'; +import { controlGroupInputBuilder } from '../external_api/control_group_input_builder'; import { ControlGroupContainer } from '../embeddable/control_group_container'; import { OptionsListEmbeddable } from '../../options_list/embeddable/options_list_embeddable'; +import { mockedReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public/mocks'; let container: ControlGroupContainer; let embeddable: OptionsListEmbeddable; -let reduxEmbeddablePackage: ReduxEmbeddablePackage; beforeAll(async () => { - reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); - const controlGroupInput = { chainingSystem: 'NONE', panels: {} } as ControlGroupInput; controlGroupInputBuilder.addOptionsListControl(controlGroupInput, { dataViewId: 'test-data-view', @@ -36,7 +30,7 @@ beforeAll(async () => { width: 'medium', grow: false, }); - container = new ControlGroupContainer(reduxEmbeddablePackage, controlGroupInput); + container = new ControlGroupContainer(mockedReduxEmbeddablePackage, controlGroupInput); await container.untilInitialized(); embeddable = container.getChild(container.getChildIds()[0]); @@ -53,7 +47,7 @@ test('Action is incompatible with Error Embeddables', async () => { test('Execute throws an error when called with an embeddable not in a parent', async () => { const deleteControlAction = new DeleteControlAction(); const optionsListEmbeddable = new OptionsListEmbeddable( - reduxEmbeddablePackage, + mockedReduxEmbeddablePackage, {} as OptionsListEmbeddableInput, {} as ControlOutput ); diff --git a/src/plugins/controls/public/control_group/actions/edit_control_action.test.tsx b/src/plugins/controls/public/control_group/actions/edit_control_action.test.tsx index 7c4f15f091b9c..d8a1bdb30832f 100644 --- a/src/plugins/controls/public/control_group/actions/edit_control_action.test.tsx +++ b/src/plugins/controls/public/control_group/actions/edit_control_action.test.tsx @@ -6,10 +6,6 @@ * Side Public License, v 1. */ -import { - lazyLoadReduxEmbeddablePackage, - ReduxEmbeddablePackage, -} from '@kbn/presentation-util-plugin/public'; import { ErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { ControlOutput } from '../../types'; @@ -21,16 +17,11 @@ import { TimeSliderEmbeddableFactory } from '../../time_slider'; import { OptionsListEmbeddableFactory, OptionsListEmbeddableInput } from '../../options_list'; import { ControlGroupContainer } from '../embeddable/control_group_container'; import { OptionsListEmbeddable } from '../../options_list/embeddable/options_list_embeddable'; - -let reduxEmbeddablePackage: ReduxEmbeddablePackage; +import { mockedReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public/mocks'; const controlGroupInput = { chainingSystem: 'NONE', panels: {} } as ControlGroupInput; const deleteControlAction = new DeleteControlAction(); -beforeAll(async () => { - reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); -}); - test('Action is incompatible with Error Embeddables', async () => { const editControlAction = new EditControlAction(deleteControlAction); const errorEmbeddable = new ErrorEmbeddable('Wow what an awful error', { id: ' 404' }); @@ -44,7 +35,7 @@ test('Action is incompatible with embeddables that are not editable', async () = pluginServices.getServices().embeddable.getEmbeddableFactory = mockGetFactory; const editControlAction = new EditControlAction(deleteControlAction); - const emptyContainer = new ControlGroupContainer(reduxEmbeddablePackage, controlGroupInput); + const emptyContainer = new ControlGroupContainer(mockedReduxEmbeddablePackage, controlGroupInput); await emptyContainer.untilInitialized(); await emptyContainer.addTimeSliderControl(); @@ -62,7 +53,7 @@ test('Action is compatible with embeddables that are editable', async () => { pluginServices.getServices().embeddable.getEmbeddableFactory = mockGetFactory; const editControlAction = new EditControlAction(deleteControlAction); - const emptyContainer = new ControlGroupContainer(reduxEmbeddablePackage, controlGroupInput); + const emptyContainer = new ControlGroupContainer(mockedReduxEmbeddablePackage, controlGroupInput); await emptyContainer.untilInitialized(); await emptyContainer.addOptionsListControl({ dataViewId: 'test-data-view', @@ -82,7 +73,7 @@ test('Action is compatible with embeddables that are editable', async () => { test('Execute throws an error when called with an embeddable not in a parent', async () => { const editControlAction = new EditControlAction(deleteControlAction); const optionsListEmbeddable = new OptionsListEmbeddable( - reduxEmbeddablePackage, + mockedReduxEmbeddablePackage, {} as OptionsListEmbeddableInput, {} as ControlOutput ); @@ -95,7 +86,7 @@ test('Execute should open a flyout', async () => { const spyOn = jest.fn().mockResolvedValue(undefined); pluginServices.getServices().overlays.openFlyout = spyOn; - const emptyContainer = new ControlGroupContainer(reduxEmbeddablePackage, controlGroupInput); + const emptyContainer = new ControlGroupContainer(mockedReduxEmbeddablePackage, controlGroupInput); await emptyContainer.untilInitialized(); await emptyContainer.addOptionsListControl({ dataViewId: 'test-data-view', diff --git a/src/plugins/controls/public/control_group/actions/edit_control_action.tsx b/src/plugins/controls/public/control_group/actions/edit_control_action.tsx index 9500640332446..fef0b7cd2a5bb 100644 --- a/src/plugins/controls/public/control_group/actions/edit_control_action.tsx +++ b/src/plugins/controls/public/control_group/actions/edit_control_action.tsx @@ -19,7 +19,7 @@ import { DeleteControlAction } from './delete_control_action'; import { ControlGroupStrings } from '../control_group_strings'; import { ACTION_EDIT_CONTROL, ControlGroupContainer } from '..'; import { ControlEmbeddable, DataControlInput } from '../../types'; -import { setFlyoutRef } from '../embeddable/control_group_container'; +import { ControlGroupContainerContext, setFlyoutRef } from '../embeddable/control_group_container'; import { isControlGroup } from '../embeddable/control_group_helpers'; export interface EditControlActionContext { @@ -91,11 +91,10 @@ export class EditControlAction implements Action { throw new IncompatibleActionError(); } const controlGroup = embeddable.parent as ControlGroupContainer; - const ReduxWrapper = controlGroup.getReduxEmbeddableTools().Wrapper; const flyoutInstance = this.openFlyout( toMountPoint( - + this.deleteControlAction.execute({ embeddable })} @@ -104,7 +103,7 @@ export class EditControlAction implements Action { flyoutInstance.close(); }} /> - , + , { theme$: this.theme$ } ), diff --git a/src/plugins/controls/public/control_group/actions/edit_control_flyout.tsx b/src/plugins/controls/public/control_group/actions/edit_control_flyout.tsx index c5f3dda8f5f8c..c182a61ff1567 100644 --- a/src/plugins/controls/public/control_group/actions/edit_control_flyout.tsx +++ b/src/plugins/controls/public/control_group/actions/edit_control_flyout.tsx @@ -14,7 +14,7 @@ import { EmbeddableFactoryNotFoundError } from '@kbn/embeddable-plugin/public'; import { DataControlInput, ControlEmbeddable, IEditableControlFactory } from '../../types'; import { pluginServices } from '../../services'; import { ControlGroupStrings } from '../control_group_strings'; -import { useControlGroupContainerContext } from '../control_group_renderer'; +import { useControlGroupContainer } from '../embeddable/control_group_container'; import { ControlEditor } from '../editor/control_editor'; export const EditControlFlyout = ({ @@ -32,17 +32,10 @@ export const EditControlFlyout = ({ controls: { getControlFactory }, } = pluginServices.getServices(); // Redux embeddable container Context - const reduxContext = useControlGroupContainerContext(); - const { - embeddableInstance: controlGroup, - actions: { setControlWidth, setControlGrow }, - useEmbeddableSelector, - useEmbeddableDispatch, - } = reduxContext; - const dispatch = useEmbeddableDispatch(); + const controlGroup = useControlGroupContainer(); // current state - const panels = useEmbeddableSelector((state) => state.explicitInput.panels); + const panels = controlGroup.select((state) => state.explicitInput.panels); const panel = panels[embeddable.id]; const [currentGrow, setCurrentGrow] = useState(panel.grow); @@ -86,9 +79,9 @@ export const EditControlFlyout = ({ } if (currentWidth !== panel.width) - dispatch(setControlWidth({ width: currentWidth, embeddableId: embeddable.id })); + controlGroup.dispatch.setControlWidth({ width: currentWidth, embeddableId: embeddable.id }); if (currentGrow !== panel.grow) - dispatch(setControlGrow({ grow: currentGrow, embeddableId: embeddable.id })); + controlGroup.dispatch.setControlGrow({ grow: currentGrow, embeddableId: embeddable.id }); closeFlyout(); await controlGroup.replaceEmbeddable(embeddable.id, inputToReturn, type); diff --git a/src/plugins/controls/public/control_group/component/control_frame_component.tsx b/src/plugins/controls/public/control_group/component/control_frame_component.tsx index 2697e941ab234..49282b05d76f9 100644 --- a/src/plugins/controls/public/control_group/component/control_frame_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_frame_component.tsx @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import React, { useEffect, useMemo, useState } from 'react'; import classNames from 'classnames'; +import React, { useEffect, useMemo, useState } from 'react'; + import { EuiButtonEmpty, EuiFormControlLayout, @@ -17,15 +18,16 @@ import { EuiPopover, EuiToolTip, } from '@elastic/eui'; - import { FormattedMessage } from '@kbn/i18n-react'; import { Markdown } from '@kbn/kibana-react-plugin/public'; -import { useReduxEmbeddableContext, FloatingActions } from '@kbn/presentation-util-plugin/public'; -import { ControlGroupReduxState } from '../types'; +import { FloatingActions } from '@kbn/presentation-util-plugin/public'; + +import { + controlGroupSelector, + useControlGroupContainer, +} from '../embeddable/control_group_container'; import { ControlGroupStrings } from '../control_group_strings'; import { useChildEmbeddable } from '../../hooks/use_child_embeddable'; -import { controlGroupReducers } from '../state/control_group_reducers'; -import { ControlGroupContainer } from '..'; interface ControlFrameErrorProps { error: Error; @@ -82,16 +84,11 @@ export const ControlFrame = ({ const embeddableRoot: React.RefObject = useMemo(() => React.createRef(), []); const [fatalError, setFatalError] = useState(); - const { useEmbeddableSelector: select, embeddableInstance: controlGroup } = - useReduxEmbeddableContext< - ControlGroupReduxState, - typeof controlGroupReducers, - ControlGroupContainer - >(); + const controlGroup = useControlGroupContainer(); - const viewMode = select((state) => state.explicitInput.viewMode); - const controlStyle = select((state) => state.explicitInput.controlStyle); - const disabledActions = select((state) => state.explicitInput.disabledActions); + const controlStyle = controlGroupSelector((state) => state.explicitInput.controlStyle); + const viewMode = controlGroupSelector((state) => state.explicitInput.viewMode); + const disabledActions = controlGroupSelector((state) => state.explicitInput.disabledActions); const embeddable = useChildEmbeddable({ untilEmbeddableLoaded: controlGroup.untilEmbeddableLoaded.bind(controlGroup), 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 a92ee31e83053..875cb48b5a8f4 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,9 +8,6 @@ import '../control_group.scss'; -import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; -import React, { useMemo, useState } from 'react'; -import classNames from 'classnames'; import { arrayMove, SortableContext, @@ -28,28 +25,28 @@ import { useSensors, LayoutMeasuringStrategy, } from '@dnd-kit/core'; +import classNames from 'classnames'; +import React, { useMemo, useState } from 'react'; +import { TypedUseSelectorHook, useSelector } from 'react-redux'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { ControlClone, SortableControl } from './control_group_sortable_item'; -import { useControlGroupContainerContext } from '../control_group_renderer'; + +import { ControlGroupReduxState } from '../types'; import { ControlGroupStrings } from '../control_group_strings'; +import { ControlClone, SortableControl } from './control_group_sortable_item'; +import { useControlGroupContainer } from '../embeddable/control_group_container'; + +const contextSelect = useSelector as TypedUseSelectorHook; export const ControlGroup = () => { - // Redux embeddable container Context - const reduxContext = useControlGroupContainerContext(); - const { - embeddableInstance: controlGroup, - actions: { setControlOrders }, - useEmbeddableSelector: select, - useEmbeddableDispatch, - } = reduxContext; - const dispatch = useEmbeddableDispatch(); + const controlGroup = useControlGroupContainer(); // current state - const panels = select((state) => state.explicitInput.panels); - const viewMode = select((state) => state.explicitInput.viewMode); - const controlStyle = select((state) => state.explicitInput.controlStyle); - const showAddButton = select((state) => state.componentState.showAddButton); + const panels = contextSelect((state) => state.explicitInput.panels); + const viewMode = contextSelect((state) => state.explicitInput.viewMode); + const controlStyle = contextSelect((state) => state.explicitInput.controlStyle); + const showAddButton = contextSelect((state) => state.componentState.showAddButton); const isEditable = viewMode === ViewMode.EDIT; @@ -80,7 +77,9 @@ export const ControlGroup = () => { const overIndex = idsInOrder.indexOf(over.id); if (draggingIndex !== overIndex) { const newIndex = overIndex; - dispatch(setControlOrders({ ids: arrayMove([...idsInOrder], draggingIndex, newIndex) })); + controlGroup.dispatch.setControlOrders({ + ids: arrayMove([...idsInOrder], draggingIndex, newIndex), + }); } } setDraggingId(null); diff --git a/src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx b/src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx index 9ef7f9ef4a47d..414565851afce 100644 --- a/src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx +++ b/src/plugins/controls/public/control_group/component/control_group_sortable_item.tsx @@ -6,16 +6,15 @@ * Side Public License, v 1. */ -import { EuiFlexItem, EuiFormLabel, EuiIcon, EuiFlexGroup } from '@elastic/eui'; -import React, { forwardRef, HTMLAttributes } from 'react'; -import { useSortable } from '@dnd-kit/sortable'; -import { CSS } from '@dnd-kit/utilities'; import classNames from 'classnames'; +import { CSS } from '@dnd-kit/utilities'; +import { useSortable } from '@dnd-kit/sortable'; +import React, { forwardRef, HTMLAttributes } from 'react'; +import { EuiFlexItem, EuiFormLabel, EuiIcon, EuiFlexGroup } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { ControlFrame, ControlFrameProps } from './control_frame_component'; -import { ControlGroupReduxState } from '../types'; import { ControlGroupStrings } from '../control_group_strings'; +import { ControlFrame, ControlFrameProps } from './control_frame_component'; +import { controlGroupSelector } from '../embeddable/control_group_container'; interface DragInfo { isOver?: boolean; @@ -70,8 +69,7 @@ const SortableControlInner = forwardRef< dragHandleRef ) => { const { isOver, isDragging, draggingIndex, index } = dragInfo; - const { useEmbeddableSelector } = useReduxEmbeddableContext(); - const panels = useEmbeddableSelector((state) => state.explicitInput.panels); + const panels = controlGroupSelector((state) => state.explicitInput.panels); const grow = panels[embeddableId].grow; const width = panels[embeddableId].width; @@ -122,9 +120,8 @@ const SortableControlInner = forwardRef< * can be quite cumbersome. */ export const ControlClone = ({ draggingId }: { draggingId: string }) => { - const { useEmbeddableSelector: select } = useReduxEmbeddableContext(); - const panels = select((state) => state.explicitInput.panels); - const controlStyle = select((state) => state.explicitInput.controlStyle); + const panels = controlGroupSelector((state) => state.explicitInput.panels); + const controlStyle = controlGroupSelector((state) => state.explicitInput.controlStyle); const width = panels[draggingId].width; const title = panels[draggingId].explicitInput.title; diff --git a/src/plugins/controls/public/control_group/control_group_renderer.tsx b/src/plugins/controls/public/control_group/control_group_renderer.tsx deleted file mode 100644 index 88b270349c42b..0000000000000 --- a/src/plugins/controls/public/control_group/control_group_renderer.tsx +++ /dev/null @@ -1,128 +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 { v4 as uuidv4 } from 'uuid'; -import { isEqual } from 'lodash'; -import useLifecycles from 'react-use/lib/useLifecycles'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; - -import { compareFilters } from '@kbn/es-query'; -import type { Filter, TimeRange, Query } from '@kbn/es-query'; -import { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; - -import { - ControlGroupCreationOptions, - ControlGroupInput, - ControlGroupOutput, - ControlGroupReduxState, - CONTROL_GROUP_TYPE, -} from './types'; -import { pluginServices } from '../services'; -import { getDefaultControlGroupInput } from '../../common'; -import { controlGroupReducers } from './state/control_group_reducers'; -import { controlGroupInputBuilder } from './control_group_input_builder'; -import { ControlGroupContainer } from './embeddable/control_group_container'; -import { ControlGroupContainerFactory } from './embeddable/control_group_container_factory'; - -export interface ControlGroupRendererProps { - filters?: Filter[]; - getCreationOptions: ( - initialInput: Partial, - builder: typeof controlGroupInputBuilder - ) => Promise; - onLoadComplete?: (controlGroup: ControlGroupContainer) => void; - timeRange?: TimeRange; - query?: Query; -} - -export const ControlGroupRenderer = ({ - onLoadComplete, - getCreationOptions, - filters, - timeRange, - query, -}: ControlGroupRendererProps) => { - const controlGroupRef = useRef(null); - const [controlGroup, setControlGroup] = useState(); - const id = useMemo(() => uuidv4(), []); - /** - * Use Lifecycles to load initial control group container - */ - useLifecycles( - // onMount - () => { - const { embeddable } = pluginServices.getServices(); - (async () => { - const factory = embeddable.getEmbeddableFactory(CONTROL_GROUP_TYPE) as EmbeddableFactory< - ControlGroupInput, - ControlGroupOutput, - ControlGroupContainer - > & { - create: ControlGroupContainerFactory['create']; - }; - const { initialInput, settings } = await getCreationOptions( - getDefaultControlGroupInput(), - controlGroupInputBuilder - ); - const newControlGroup = (await factory?.create( - { - id, - ...getDefaultControlGroupInput(), - ...initialInput, - }, - undefined, - settings - )) as ControlGroupContainer; - - if (controlGroupRef.current) { - newControlGroup.render(controlGroupRef.current); - } - setControlGroup(newControlGroup); - if (onLoadComplete) { - onLoadComplete(newControlGroup); - } - })(); - }, - // onUnmount - () => { - controlGroup?.destroy(); - } - ); - - useEffect(() => { - if (!controlGroup) { - return; - } - - if ( - (timeRange && !isEqual(controlGroup.getInput().timeRange, timeRange)) || - !compareFilters(controlGroup.getInput().filters ?? [], filters ?? []) || - !isEqual(controlGroup.getInput().query, query) - ) { - controlGroup.updateInput({ - timeRange, - query, - filters, - }); - } - }, [query, filters, controlGroup, timeRange]); - - return
; -}; - -export const useControlGroupContainerContext = () => - useReduxEmbeddableContext< - ControlGroupReduxState, - typeof controlGroupReducers, - ControlGroupContainer - >(); - -// required for dynamic import using React.lazy() -// eslint-disable-next-line import/no-default-export -export default ControlGroupRenderer; diff --git a/src/plugins/controls/public/control_group/editor/control_editor.tsx b/src/plugins/controls/public/control_group/editor/control_editor.tsx index 5cc94acaebcf1..8bf021b8c4b8a 100644 --- a/src/plugins/controls/public/control_group/editor/control_editor.tsx +++ b/src/plugins/controls/public/control_group/editor/control_editor.tsx @@ -53,7 +53,8 @@ import { import { CONTROL_WIDTH_OPTIONS } from './editor_constants'; import { pluginServices } from '../../services'; import { getDataControlFieldRegistry } from './data_control_editor_tools'; -import { useControlGroupContainerContext } from '../control_group_renderer'; +import { useControlGroupContainer } from '../embeddable/control_group_container'; + interface EditControlProps { embeddable?: ControlEmbeddable; isCreate: boolean; @@ -95,8 +96,11 @@ export const ControlEditor = ({ controls: { getControlFactory }, } = pluginServices.getServices(); - const { useEmbeddableSelector: select } = useControlGroupContainerContext(); - const editorConfig = select((state) => state.componentState.editorConfig); + const controlGroup = useControlGroupContainer(); + const editorConfig = controlGroup.select((state) => state.componentState.editorConfig); + const customFilterPredicate = controlGroup.select( + (state) => state.componentState.fieldFilterPredicate + ); const [defaultTitle, setDefaultTitle] = useState(); const [currentTitle, setCurrentTitle] = useState(title ?? ''); @@ -195,7 +199,10 @@ export const ControlEditor = ({ )} Boolean(fieldRegistry?.[field.name])} + filterPredicate={(field: DataViewField) => { + const customPredicate = customFilterPredicate ? customFilterPredicate(field) : true; + return Boolean(fieldRegistry?.[field.name]) && customPredicate; + }} selectedFieldName={selectedField} dataView={selectedDataView} onSelectField={(field) => { diff --git a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx index 0b44c09fbaabf..695eaa42e064d 100644 --- a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx +++ b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx @@ -8,30 +8,42 @@ import React from 'react'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; + +import { + ControlGroupContainer, + ControlGroupContainerContext, + setFlyoutRef, +} from '../embeddable/control_group_container'; import type { AddDataControlProps, AddOptionsListControlProps, AddRangeSliderControlProps, -} from '../control_group_input_builder'; -import { ControlGroupStrings } from '../control_group_strings'; -import { ControlGroupContainer, setFlyoutRef } from '../embeddable/control_group_container'; -import { pluginServices } from '../../services'; -import { ControlEditor } from './control_editor'; -import { DataControlInput, OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '../..'; -import { IEditableControlFactory } from '../../types'; +} from '../external_api/control_group_input_builder'; import { DEFAULT_CONTROL_GROW, DEFAULT_CONTROL_WIDTH, } from '../../../common/control_group/control_group_constants'; +import { pluginServices } from '../../services'; +import { ControlEditor } from './control_editor'; +import { IEditableControlFactory } from '../../types'; +import { ControlInputTransform } from '../../../common/types'; +import { ControlGroupStrings } from '../control_group_strings'; +import { DataControlInput, OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '../..'; -export function openAddDataControlFlyout(this: ControlGroupContainer) { +export function openAddDataControlFlyout( + this: ControlGroupContainer, + options?: { + controlInputTransform?: ControlInputTransform; + onSave?: (id: string) => void; + } +) { + const { controlInputTransform, onSave } = options || {}; const { overlays: { openFlyout, openConfirm }, controls: { getControlFactory }, theme: { theme$ }, } = pluginServices.getServices(); - const ControlsServicesProvider = pluginServices.getContextProvider(); - const ReduxWrapper = this.getReduxEmbeddableTools().Wrapper; let controlInput: Partial = {}; const onCancel = () => { @@ -54,47 +66,60 @@ export function openAddDataControlFlyout(this: ControlGroupContainer) { const flyoutInstance = openFlyout( toMountPoint( - - - this.setLastUsedDataViewId(newId)} - getRelevantDataViewId={this.getMostRelevantDataViewId} - isCreate={true} - width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH} - grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW} - updateTitle={(newTitle) => (controlInput.title = newTitle)} - updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })} - updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })} - onSave={(type) => { - this.closeAllFlyouts(); - if (!type) { - return; - } + + this.setLastUsedDataViewId(newId)} + getRelevantDataViewId={this.getMostRelevantDataViewId} + isCreate={true} + width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH} + grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW} + updateTitle={(newTitle) => (controlInput.title = newTitle)} + updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })} + updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })} + onSave={async (type) => { + this.closeAllFlyouts(); + if (!type) { + return; + } - const factory = getControlFactory(type) as IEditableControlFactory; - if (factory.presaveTransformFunction) { - controlInput = factory.presaveTransformFunction(controlInput); - } + const factory = getControlFactory(type) as IEditableControlFactory; + if (factory.presaveTransformFunction) { + controlInput = factory.presaveTransformFunction(controlInput); + } + + if (controlInputTransform) { + controlInput = controlInputTransform({ ...controlInput }, type); + } - if (type === OPTIONS_LIST_CONTROL) { - this.addOptionsListControl(controlInput as AddOptionsListControlProps); - return; - } + let newControl; - if (type === RANGE_SLIDER_CONTROL) { - this.addRangeSliderControl(controlInput as AddRangeSliderControlProps); - return; - } + switch (type) { + case OPTIONS_LIST_CONTROL: + newControl = await this.addOptionsListControl( + controlInput as AddOptionsListControlProps + ); + break; + case RANGE_SLIDER_CONTROL: + newControl = await this.addRangeSliderControl( + controlInput as AddRangeSliderControlProps + ); + break; + default: + newControl = await this.addDataControlFromField( + controlInput as AddDataControlProps + ); + } - this.addDataControlFromField(controlInput as AddDataControlProps); - }} - onCancel={onCancel} - onTypeEditorChange={(partialInput) => - (controlInput = { ...controlInput, ...partialInput }) + if (onSave && !isErrorEmbeddable(newControl)) { + onSave(newControl.id); } - /> - - , + }} + onCancel={onCancel} + onTypeEditorChange={(partialInput) => + (controlInput = { ...controlInput, ...partialInput }) + } + /> + , { theme$ } ), { diff --git a/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx b/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx index 156ab377792dd..8708830d532e9 100644 --- a/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx +++ b/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx @@ -13,14 +13,17 @@ import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { pluginServices } from '../../services'; import { ControlGroupEditor } from './control_group_editor'; import { ControlGroupStrings } from '../control_group_strings'; -import { ControlGroupContainer, setFlyoutRef } from '../embeddable/control_group_container'; +import { + ControlGroupContainer, + ControlGroupContainerContext, + setFlyoutRef, +} from '../embeddable/control_group_container'; export function openEditControlGroupFlyout(this: ControlGroupContainer) { const { overlays: { openFlyout, openConfirm }, theme: { theme$ }, } = pluginServices.getServices(); - const ReduxWrapper = this.getReduxEmbeddableTools().Wrapper; const onDeleteAll = (ref: OverlayRef) => { openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), { @@ -37,7 +40,7 @@ export function openEditControlGroupFlyout(this: ControlGroupContainer) { const flyoutInstance = openFlyout( toMountPoint( - + this.updateInput(changes)} @@ -45,7 +48,7 @@ export function openEditControlGroupFlyout(this: ControlGroupContainer) { onDeleteAll={() => onDeleteAll(flyoutInstance)} onClose={() => flyoutInstance.close()} /> - , + , { theme$ } ), { diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_chaining_system.ts b/src/plugins/controls/public/control_group/embeddable/control_group_chaining_system.ts index b5e703afefd19..1857bd8a8df07 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_chaining_system.ts +++ b/src/plugins/controls/public/control_group/embeddable/control_group_chaining_system.ts @@ -35,7 +35,7 @@ interface OnChildChangedProps { interface ChainingSystem { getContainerSettings: ( initialInput: ControlGroupInput - ) => EmbeddableContainerSettings | undefined; + ) => EmbeddableContainerSettings | undefined; getPrecedingFilters: ( props: GetPrecedingFiltersProps ) => { filters: Filter[]; timeslice?: [number, number] } | undefined; 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 533f8ffa443fe..220967b8ef23e 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 @@ -5,18 +5,19 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import { skip, debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import React from 'react'; +import { isEqual } from 'lodash'; import ReactDOM from 'react-dom'; -import { compareFilters, COMPARE_ALL_OPTIONS, Filter, uniqFilters } from '@kbn/es-query'; +import { Provider, TypedUseSelectorHook, useSelector } from 'react-redux'; +import React, { createContext, useContext } from 'react'; import { BehaviorSubject, merge, Subject, Subscription } from 'rxjs'; -import _ from 'lodash'; +import { skip, debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { compareFilters, COMPARE_ALL_OPTIONS, Filter, uniqFilters } from '@kbn/es-query'; -import { ReduxEmbeddablePackage, ReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public'; import { OverlayRef } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { Container, EmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { ReduxToolsPackage, ReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public'; + import { ControlGroupInput, ControlGroupOutput, @@ -31,23 +32,21 @@ import { ControlGroupChainingSystems, controlOrdersAreEqual, } from './control_group_chaining_system'; -import { pluginServices } from '../../services'; -import { openAddDataControlFlyout } from '../editor/open_add_data_control_flyout'; -import { ControlGroup } from '../component/control_group_component'; -import { controlGroupReducers } from '../state/control_group_reducers'; -import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; -import { getNextPanelOrder } from './control_group_helpers'; -import type { - AddDataControlProps, - AddOptionsListControlProps, - AddRangeSliderControlProps, -} from '../control_group_input_builder'; import { + type AddDataControlProps, + type AddOptionsListControlProps, + type AddRangeSliderControlProps, getDataControlPanelState, getOptionsListPanelState, getRangeSliderPanelState, getTimeSliderPanelState, -} from '../control_group_input_builder'; +} from '../external_api/control_group_input_builder'; +import { pluginServices } from '../../services'; +import { getNextPanelOrder } from './control_group_helpers'; +import { ControlGroup } from '../component/control_group_component'; +import { controlGroupReducers } from '../state/control_group_reducers'; +import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; +import { openAddDataControlFlyout } from '../editor/open_add_data_control_flyout'; import { openEditControlGroupFlyout } from '../editor/open_edit_control_group_flyout'; let flyoutRef: OverlayRef | undefined; @@ -55,6 +54,21 @@ export const setFlyoutRef = (newRef: OverlayRef | undefined) => { flyoutRef = newRef; }; +export const ControlGroupContainerContext = createContext(null); +export const controlGroupSelector = useSelector as TypedUseSelectorHook; +export const useControlGroupContainer = (): ControlGroupContainer => { + const controlGroup = useContext(ControlGroupContainerContext); + if (controlGroup == null) { + throw new Error('useControlGroupContainer must be used inside ControlGroupContainerContext.'); + } + return controlGroup!; +}; + +type ControlGroupReduxEmbeddableTools = ReduxEmbeddableTools< + ControlGroupReduxState, + typeof controlGroupReducers +>; + export class ControlGroupContainer extends Container< ControlInput, ControlGroupInput, @@ -71,63 +85,21 @@ export class ControlGroupContainer extends Container< private relevantDataViewId?: string; private lastUsedDataViewId?: string; - private reduxEmbeddableTools: ReduxEmbeddableTools< - ControlGroupReduxState, - typeof controlGroupReducers - >; + // state management + public select: ControlGroupReduxEmbeddableTools['select']; + public getState: ControlGroupReduxEmbeddableTools['getState']; + public dispatch: ControlGroupReduxEmbeddableTools['dispatch']; + public onStateChange: ControlGroupReduxEmbeddableTools['onStateChange']; - public onFiltersPublished$: Subject; - public onControlRemoved$: Subject; + private store: ControlGroupReduxEmbeddableTools['store']; - public setLastUsedDataViewId = (lastUsedDataViewId: string) => { - this.lastUsedDataViewId = lastUsedDataViewId; - }; + private cleanupStateTools: () => void; - public setRelevantDataViewId = (newRelevantDataViewId: string) => { - this.relevantDataViewId = newRelevantDataViewId; - }; - - public getMostRelevantDataViewId = () => { - const staticDataViewId = - this.getReduxEmbeddableTools().getState().componentState.staticDataViewId; - return staticDataViewId ?? this.lastUsedDataViewId ?? this.relevantDataViewId; - }; - - public getReduxEmbeddableTools = () => { - return this.reduxEmbeddableTools; - }; - - public closeAllFlyouts() { - flyoutRef?.close(); - flyoutRef = undefined; - } - - public async addDataControlFromField(controlProps: AddDataControlProps) { - const panelState = await getDataControlPanelState(this.getInput(), controlProps); - return this.createAndSaveEmbeddable(panelState.type, panelState); - } - - public addOptionsListControl(controlProps: AddOptionsListControlProps) { - const panelState = getOptionsListPanelState(this.getInput(), controlProps); - return this.createAndSaveEmbeddable(panelState.type, panelState); - } - - public addRangeSliderControl(controlProps: AddRangeSliderControlProps) { - const panelState = getRangeSliderPanelState(this.getInput(), controlProps); - return this.createAndSaveEmbeddable(panelState.type, panelState); - } - - public addTimeSliderControl() { - const panelState = getTimeSliderPanelState(this.getInput()); - return this.createAndSaveEmbeddable(panelState.type, panelState); - } - - public openAddDataControlFlyout = openAddDataControlFlyout; - - public openEditControlGroupFlyout = openEditControlGroupFlyout; + public onFiltersPublished$: Subject; + public onControlRemoved$: Subject; constructor( - reduxEmbeddablePackage: ReduxEmbeddablePackage, + reduxToolsPackage: ReduxToolsPackage, initialInput: ControlGroupInput, parent?: Container, settings?: ControlGroupSettings @@ -145,7 +117,7 @@ export class ControlGroupContainer extends Container< this.onControlRemoved$ = new Subject(); // build redux embeddable tools - this.reduxEmbeddableTools = reduxEmbeddablePackage.createTools< + const reduxEmbeddableTools = reduxToolsPackage.createReduxEmbeddableTools< ControlGroupReduxState, typeof controlGroupReducers >({ @@ -154,6 +126,14 @@ export class ControlGroupContainer extends Container< initialComponentState: settings, }); + this.select = reduxEmbeddableTools.select; + this.getState = reduxEmbeddableTools.getState; + this.dispatch = reduxEmbeddableTools.dispatch; + this.cleanupStateTools = reduxEmbeddableTools.cleanup; + this.onStateChange = reduxEmbeddableTools.onStateChange; + + this.store = reduxEmbeddableTools.store; + // when all children are ready setup subscriptions this.untilAllChildrenReady().then(() => { this.recalculateDataViews(); @@ -204,6 +184,47 @@ export class ControlGroupContainer extends Container< ); }; + public setLastUsedDataViewId = (lastUsedDataViewId: string) => { + this.lastUsedDataViewId = lastUsedDataViewId; + }; + + public setRelevantDataViewId = (newRelevantDataViewId: string) => { + this.relevantDataViewId = newRelevantDataViewId; + }; + + public getMostRelevantDataViewId = () => { + return this.lastUsedDataViewId ?? this.relevantDataViewId; + }; + + public closeAllFlyouts() { + flyoutRef?.close(); + flyoutRef = undefined; + } + + public async addDataControlFromField(controlProps: AddDataControlProps) { + const panelState = await getDataControlPanelState(this.getInput(), controlProps); + return this.createAndSaveEmbeddable(panelState.type, panelState); + } + + public addOptionsListControl(controlProps: AddOptionsListControlProps) { + const panelState = getOptionsListPanelState(this.getInput(), controlProps); + return this.createAndSaveEmbeddable(panelState.type, panelState); + } + + public addRangeSliderControl(controlProps: AddRangeSliderControlProps) { + const panelState = getRangeSliderPanelState(this.getInput(), controlProps); + return this.createAndSaveEmbeddable(panelState.type, panelState); + } + + public addTimeSliderControl() { + const panelState = getTimeSliderPanelState(this.getInput()); + return this.createAndSaveEmbeddable(panelState.type, panelState); + } + + public openAddDataControlFlyout = openAddDataControlFlyout; + + public openEditControlGroupFlyout = openEditControlGroupFlyout; + public getPanelCount = () => { return Object.keys(this.getInput().panels).length; }; @@ -225,7 +246,7 @@ export class ControlGroupContainer extends Container< // if filters are different, publish them if ( !compareFilters(this.output.filters ?? [], allFilters ?? [], COMPARE_ALL_OPTIONS) || - !_.isEqual(this.output.timeslice, timeslice) + !isEqual(this.output.timeslice, timeslice) ) { this.updateOutput({ filters: uniqFilters(allFilters), timeslice }); this.onFiltersPublished$.next(allFilters); @@ -337,15 +358,13 @@ export class ControlGroupContainer extends Container< ReactDOM.unmountComponentAtNode(this.domNode); } this.domNode = dom; - const ControlsServicesProvider = pluginServices.getContextProvider(); - const { Wrapper: ControlGroupReduxWrapper } = this.reduxEmbeddableTools; ReactDOM.render( - - + + - - + + , dom ); @@ -355,7 +374,7 @@ export class ControlGroupContainer extends Container< super.destroy(); this.closeAllFlyouts(); this.subscriptions.unsubscribe(); - this.reduxEmbeddableTools.cleanup(); + this.cleanupStateTools(); if (this.domNode) ReactDOM.unmountComponentAtNode(this.domNode); } } diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts b/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts index b4fe9285d3bb1..d14ceb3679a0b 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container_factory.ts @@ -16,7 +16,7 @@ import { i18n } from '@kbn/i18n'; import { Container, EmbeddableFactoryDefinition } from '@kbn/embeddable-plugin/public'; -import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { lazyLoadReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common'; import { ControlGroupInput, ControlGroupSettings, CONTROL_GROUP_TYPE } from '../types'; @@ -54,7 +54,7 @@ export class ControlGroupContainerFactory implements EmbeddableFactoryDefinition parent?: Container, settings?: ControlGroupSettings ) => { - const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); + const reduxEmbeddablePackage = await lazyLoadReduxToolsPackage(); const { ControlGroupContainer } = await import('./control_group_container'); return new ControlGroupContainer(reduxEmbeddablePackage, initialInput, parent, settings); }; diff --git a/src/plugins/controls/public/control_group/external_api/control_group_api.ts b/src/plugins/controls/public/control_group/external_api/control_group_api.ts new file mode 100644 index 0000000000000..d269eb61c0fab --- /dev/null +++ b/src/plugins/controls/public/control_group/external_api/control_group_api.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 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 { ControlGroupContainer } from '..'; + +// TODO lock down ControlGroupAPI +export type ControlGroupAPI = ControlGroupContainer; +export type AwaitingControlGroupAPI = ControlGroupAPI | null; + +export const buildApiFromControlGroupContainer = (container?: ControlGroupContainer) => + container ?? null; diff --git a/src/plugins/controls/public/control_group/control_group_input_builder.ts b/src/plugins/controls/public/control_group/external_api/control_group_input_builder.ts similarity index 89% rename from src/plugins/controls/public/control_group/control_group_input_builder.ts rename to src/plugins/controls/public/control_group/external_api/control_group_input_builder.ts index e340294cb2b15..fafbd33da893e 100644 --- a/src/plugins/controls/public/control_group/control_group_input_builder.ts +++ b/src/plugins/controls/public/control_group/external_api/control_group_input_builder.ts @@ -8,22 +8,22 @@ import { i18n } from '@kbn/i18n'; import { v4 as uuidv4 } from 'uuid'; -import { ControlPanelState, OptionsListEmbeddableInput } from '../../common'; -import { - DEFAULT_CONTROL_GROW, - DEFAULT_CONTROL_WIDTH, -} from '../../common/control_group/control_group_constants'; -import { RangeValue } from '../../common/range_slider/types'; + import { - ControlInput, + ControlPanelState, ControlWidth, - DataControlInput, + OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL, - RANGE_SLIDER_CONTROL, TIME_SLIDER_CONTROL, -} from '..'; -import { ControlGroupInput } from './types'; -import { getCompatibleControlType, getNextPanelOrder } from './embeddable/control_group_helpers'; +} from '../../../common'; +import { + DEFAULT_CONTROL_GROW, + DEFAULT_CONTROL_WIDTH, +} from '../../../common/control_group/control_group_constants'; +import { ControlGroupInput } from '../types'; +import { ControlInput, DataControlInput } from '../../types'; +import { RangeValue, RANGE_SLIDER_CONTROL } from '../../../common/range_slider/types'; +import { getCompatibleControlType, getNextPanelOrder } from '../embeddable/control_group_helpers'; export interface AddDataControlProps { controlId?: string; @@ -40,6 +40,8 @@ export type AddRangeSliderControlProps = AddDataControlProps & { value?: RangeValue; }; +export type ControlGroupInputBuilder = typeof controlGroupInputBuilder; + export const controlGroupInputBuilder = { addDataControlFromField: async ( initialInput: Partial, diff --git a/src/plugins/controls/public/control_group/external_api/control_group_renderer.test.tsx b/src/plugins/controls/public/control_group/external_api/control_group_renderer.test.tsx new file mode 100644 index 0000000000000..7349cff7594f8 --- /dev/null +++ b/src/plugins/controls/public/control_group/external_api/control_group_renderer.test.tsx @@ -0,0 +1,129 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; + +import { + ControlGroupContainer, + ControlGroupContainerFactory, + ControlGroupRenderer, + CONTROL_GROUP_TYPE, +} from '..'; +import { pluginServices } from '../../services/plugin_services'; +import { ReactWrapper } from 'enzyme'; +import { Filter } from '@kbn/es-query'; + +describe('control group renderer', () => { + let mockControlGroupFactory: ControlGroupContainerFactory; + let mockControlGroupContainer: ControlGroupContainer; + + beforeEach(() => { + mockControlGroupContainer = { + destroy: jest.fn(), + render: jest.fn(), + updateInput: jest.fn(), + getInput: jest.fn().mockReturnValue({}), + } as unknown as ControlGroupContainer; + mockControlGroupFactory = { + create: jest.fn().mockReturnValue(mockControlGroupContainer), + } as unknown as ControlGroupContainerFactory; + pluginServices.getServices().embeddable.getEmbeddableFactory = jest + .fn() + .mockReturnValue(mockControlGroupFactory); + }); + + test('calls create method on the Control Group embeddable factory with returned initial input', async () => { + await act(async () => { + mountWithIntl( + Promise.resolve({ initialInput: { controlStyle: 'twoLine' } })} + /> + ); + }); + expect(pluginServices.getServices().embeddable.getEmbeddableFactory).toHaveBeenCalledWith( + CONTROL_GROUP_TYPE + ); + expect(mockControlGroupFactory.create).toHaveBeenCalledWith( + expect.objectContaining({ controlStyle: 'twoLine' }), + undefined, + undefined + ); + }); + + test('destroys control group container on unmount', async () => { + let wrapper: ReactWrapper; + await act(async () => { + wrapper = await mountWithIntl(); + }); + wrapper!.unmount(); + expect(mockControlGroupContainer.destroy).toHaveBeenCalledTimes(1); + }); + + test('filter changes are dispatched to control group if they are different', async () => { + const initialFilters: Filter[] = [ + { meta: { alias: 'test', disabled: false, negate: false, index: 'test' } }, + ]; + const updatedFilters: Filter[] = [ + { meta: { alias: 'test', disabled: false, negate: true, index: 'test' } }, + ]; + let wrapper: ReactWrapper; + await act(async () => { + wrapper = mountWithIntl( + Promise.resolve({ initialInput: { filters: initialFilters } })} + /> + ); + }); + await act(async () => { + await wrapper.setProps({ filters: updatedFilters }); + }); + expect(mockControlGroupContainer.updateInput).toHaveBeenCalledWith( + expect.objectContaining({ filters: updatedFilters }) + ); + }); + + test('query changes are dispatched to control group if they are different', async () => { + const initialQuery = { language: 'kql', query: 'query' }; + const updatedQuery = { language: 'kql', query: 'super query' }; + let wrapper: ReactWrapper; + await act(async () => { + wrapper = mountWithIntl( + Promise.resolve({ initialInput: { query: initialQuery } })} + /> + ); + }); + await act(async () => { + await wrapper.setProps({ query: updatedQuery }); + }); + expect(mockControlGroupContainer.updateInput).toHaveBeenCalledWith( + expect.objectContaining({ query: updatedQuery }) + ); + }); + + test('time range changes are dispatched to control group if they are different', async () => { + const initialTime = { from: new Date().toISOString(), to: new Date().toISOString() }; + const updatedTime = { from: new Date().toISOString() + 10, to: new Date().toISOString() + 20 }; + let wrapper: ReactWrapper; + await act(async () => { + wrapper = mountWithIntl( + Promise.resolve({ initialInput: { timeRange: initialTime } })} + /> + ); + }); + await act(async () => { + await wrapper.setProps({ timeRange: updatedTime }); + }); + expect(mockControlGroupContainer.updateInput).toHaveBeenCalledWith( + expect.objectContaining({ timeRange: updatedTime }) + ); + }); +}); diff --git a/src/plugins/controls/public/control_group/external_api/control_group_renderer.tsx b/src/plugins/controls/public/control_group/external_api/control_group_renderer.tsx new file mode 100644 index 0000000000000..e8968ab5b4a7e --- /dev/null +++ b/src/plugins/controls/public/control_group/external_api/control_group_renderer.tsx @@ -0,0 +1,130 @@ +/* + * 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, { + forwardRef, + useEffect, + useImperativeHandle, + useMemo, + useRef, + useState, +} from 'react'; +import { isEqual } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; + +import { compareFilters } from '@kbn/es-query'; +import type { Filter, TimeRange, Query } from '@kbn/es-query'; +import { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; + +import { + ControlGroupInput, + CONTROL_GROUP_TYPE, + ControlGroupOutput, + ControlGroupCreationOptions, +} from '../types'; +import { + ControlGroupAPI, + AwaitingControlGroupAPI, + buildApiFromControlGroupContainer, +} from './control_group_api'; +import { getDefaultControlGroupInput } from '../../../common'; +import { controlGroupInputBuilder, ControlGroupInputBuilder } from './control_group_input_builder'; +import { ControlGroupContainer } from '../embeddable/control_group_container'; +import { ControlGroupContainerFactory } from '../embeddable/control_group_container_factory'; + +export interface ControlGroupRendererProps { + filters?: Filter[]; + getCreationOptions?: ( + initialInput: Partial, + builder: ControlGroupInputBuilder + ) => Promise; + timeRange?: TimeRange; + query?: Query; +} + +export const ControlGroupRenderer = forwardRef( + ({ getCreationOptions, filters, timeRange, query }, ref) => { + const [controlGroup, setControlGroup] = useState(); + + useImperativeHandle( + ref, + () => buildApiFromControlGroupContainer(controlGroup) as ControlGroupAPI, + [controlGroup] + ); + + const controlGroupDomRef = useRef(null); + const id = useMemo(() => uuidv4(), []); + + // onMount + useEffect(() => { + let canceled = false; + let destroyControlGroup: () => void; + + (async () => { + // Lazy loading all services is required in this component because it is exported and contributes to the bundle size. + const { pluginServices } = await import('../../services/plugin_services'); + const { embeddable } = pluginServices.getServices(); + + const factory = embeddable.getEmbeddableFactory(CONTROL_GROUP_TYPE) as EmbeddableFactory< + ControlGroupInput, + ControlGroupOutput, + ControlGroupContainer + > & { + create: ControlGroupContainerFactory['create']; + }; + const { initialInput, settings } = + (await getCreationOptions?.(getDefaultControlGroupInput(), controlGroupInputBuilder)) ?? + {}; + const newControlGroup = (await factory?.create( + { + id, + ...getDefaultControlGroupInput(), + ...initialInput, + }, + undefined, + settings + )) as ControlGroupContainer; + + if (canceled) { + newControlGroup.destroy(); + controlGroup?.destroy(); + return; + } + + if (controlGroupDomRef.current) { + newControlGroup.render(controlGroupDomRef.current); + } + setControlGroup(newControlGroup); + destroyControlGroup = () => newControlGroup.destroy(); + })(); + return () => { + canceled = true; + destroyControlGroup?.(); + }; + // exhaustive deps disabled because we want the control group to be created only on first render. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (!controlGroup) return; + if ( + (timeRange && !isEqual(controlGroup.getInput().timeRange, timeRange)) || + !compareFilters(controlGroup.getInput().filters ?? [], filters ?? []) || + !isEqual(controlGroup.getInput().query, query) + ) { + controlGroup.updateInput({ + timeRange, + query, + filters, + }); + } + }, [query, filters, controlGroup, timeRange]); + + return
; + } +); diff --git a/src/plugins/controls/public/control_group/index.ts b/src/plugins/controls/public/control_group/index.ts index 745e41ec474b1..812733a71077a 100644 --- a/src/plugins/controls/public/control_group/index.ts +++ b/src/plugins/controls/public/control_group/index.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import React from 'react'; - export type { ControlGroupContainer } from './embeddable/control_group_container'; export type { ControlGroupInput, ControlGroupOutput } from './types'; @@ -19,11 +17,14 @@ export { ACTION_EDIT_CONTROL, ACTION_DELETE_CONTROL } from './actions'; export { type AddDataControlProps, type AddOptionsListControlProps, + type ControlGroupInputBuilder, + type AddRangeSliderControlProps, controlGroupInputBuilder, -} from './control_group_input_builder'; +} from './external_api/control_group_input_builder'; + +export type { ControlGroupAPI, AwaitingControlGroupAPI } from './external_api/control_group_api'; export { type ControlGroupRendererProps, - useControlGroupContainerContext, -} from './control_group_renderer'; -export const LazyControlGroupRenderer = React.lazy(() => import('./control_group_renderer')); + ControlGroupRenderer, +} from './external_api/control_group_renderer'; 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 ff1b2e501dabe..a81784351a631 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 @@ -19,6 +19,12 @@ export const controlGroupReducers = { ) => { state.explicitInput.controlStyle = action.payload; }, + setChainingSystem: ( + state: WritableDraft, + action: PayloadAction + ) => { + state.explicitInput.chainingSystem = action.payload; + }, setDefaultControlWidth: ( state: WritableDraft, action: PayloadAction diff --git a/src/plugins/controls/public/control_group/types.ts b/src/plugins/controls/public/control_group/types.ts index 98455b5aab527..5fea9dcfb9628 100644 --- a/src/plugins/controls/public/control_group/types.ts +++ b/src/plugins/controls/public/control_group/types.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { DataViewField } from '@kbn/data-views-plugin/common'; import { ContainerOutput } from '@kbn/embeddable-plugin/public'; import { ReduxEmbeddableState } from '@kbn/presentation-util-plugin/public'; import { ControlGroupInput } from '../../common/control_group/types'; @@ -34,6 +35,7 @@ export interface ControlGroupSettings { hideWidthSettings?: boolean; hideAdditionalSettings?: boolean; }; + fieldFilterPredicate?: (field: DataViewField) => boolean; } export { diff --git a/src/plugins/controls/public/index.ts b/src/plugins/controls/public/index.ts index 1e9df544bd1c9..555b9d4284e89 100644 --- a/src/plugins/controls/public/index.ts +++ b/src/plugins/controls/public/index.ts @@ -35,11 +35,15 @@ export { export { type AddDataControlProps, type AddOptionsListControlProps, + type AddRangeSliderControlProps, type ControlGroupContainer, ControlGroupContainerFactory, type ControlGroupInput, - controlGroupInputBuilder, + type ControlGroupInputBuilder, + type ControlGroupAPI, + type AwaitingControlGroupAPI, type ControlGroupOutput, + controlGroupInputBuilder, } from './control_group'; export { @@ -55,11 +59,10 @@ export { } from './range_slider'; export { - LazyControlGroupRenderer, - useControlGroupContainerContext, - type ControlGroupRendererProps, - ACTION_DELETE_CONTROL, ACTION_EDIT_CONTROL, + ACTION_DELETE_CONTROL, + ControlGroupRenderer, + type ControlGroupRendererProps, } from './control_group'; export function plugin() { diff --git a/src/plugins/controls/public/options_list/components/options_list_control.test.tsx b/src/plugins/controls/public/options_list/components/options_list_control.test.tsx index 7fe1cd2f7aa78..5bfe3edd16c57 100644 --- a/src/plugins/controls/public/options_list/components/options_list_control.test.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_control.test.tsx @@ -11,9 +11,10 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; +import { OptionsListEmbeddableContext } from '../embeddable/options_list_embeddable'; import { OptionsListComponentState, OptionsListReduxState } from '../types'; import { ControlOutput, OptionsListEmbeddableInput } from '../..'; -import { mockOptionsListReduxEmbeddableTools } from '../../../common/mocks'; +import { mockOptionsListEmbeddable } from '../../../common/mocks'; import { OptionsListControl } from './options_list_control'; import { BehaviorSubject } from 'rxjs'; @@ -30,16 +31,16 @@ describe('Options list control', () => { } async function mountComponent(options?: Partial) { - const mockReduxEmbeddableTools = await mockOptionsListReduxEmbeddableTools({ + const optionsListEmbeddable = await mockOptionsListEmbeddable({ componentState: options?.componentState ?? {}, explicitInput: options?.explicitInput ?? {}, output: options?.output ?? {}, } as Partial); return mountWithIntl( - + - + ); } diff --git a/src/plugins/controls/public/options_list/components/options_list_control.tsx b/src/plugins/controls/public/options_list/components/options_list_control.tsx index 6d1e18c32b3e2..7d899f8a90e31 100644 --- a/src/plugins/controls/public/options_list/components/options_list_control.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_control.tsx @@ -12,12 +12,11 @@ import { debounce, isEmpty } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; import { EuiFilterButton, EuiFilterGroup, EuiPopover, useResizeObserver } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; +import { MAX_OPTIONS_LIST_REQUEST_SIZE } from '../types'; import { OptionsListStrings } from './options_list_strings'; import { OptionsListPopover } from './options_list_popover'; -import { optionsListReducers } from '../options_list_reducers'; -import { MAX_OPTIONS_LIST_REQUEST_SIZE, OptionsListReduxState } from '../types'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; import './options_list.scss'; @@ -29,38 +28,29 @@ export const OptionsListControl = ({ loadMoreSubject: Subject; }) => { const resizeRef = useRef(null); + const optionsList = useOptionsList(); const dimensions = useResizeObserver(resizeRef.current); - // Redux embeddable Context - const { - useEmbeddableDispatch, - actions: { replaceSelection, setSearchString, setPopoverOpen }, - useEmbeddableSelector: select, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); + const isPopoverOpen = optionsList.select((state) => state.componentState.popoverOpen); + const validSelections = optionsList.select((state) => state.componentState.validSelections); + const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); - // Select current state from Redux using multiple selectors to avoid rerenders. - const invalidSelections = select((state) => state.componentState.invalidSelections); - const validSelections = select((state) => state.componentState.validSelections); - const isPopoverOpen = select((state) => state.componentState.popoverOpen); + const id = optionsList.select((state) => state.explicitInput.id); + const exclude = optionsList.select((state) => state.explicitInput.exclude); + const fieldName = optionsList.select((state) => state.explicitInput.fieldName); + const placeholder = optionsList.select((state) => state.explicitInput.placeholder); + const controlStyle = optionsList.select((state) => state.explicitInput.controlStyle); + const singleSelect = optionsList.select((state) => state.explicitInput.singleSelect); + const existsSelected = optionsList.select((state) => state.explicitInput.existsSelected); + const selectedOptions = optionsList.select((state) => state.explicitInput.selectedOptions); - const selectedOptions = select((state) => state.explicitInput.selectedOptions); - const existsSelected = select((state) => state.explicitInput.existsSelected); - const controlStyle = select((state) => state.explicitInput.controlStyle); - const singleSelect = select((state) => state.explicitInput.singleSelect); - const fieldName = select((state) => state.explicitInput.fieldName); - const exclude = select((state) => state.explicitInput.exclude); - const id = select((state) => state.explicitInput.id); - - const placeholder = select((state) => state.explicitInput.placeholder); - - const loading = select((state) => state.output.loading); + const loading = optionsList.select((state) => state.output.loading); useEffect(() => { return () => { - dispatch(setPopoverOpen(false)); // on unmount, close the popover + optionsList.dispatch.setPopoverOpen(false); // on unmount, close the popover }; - }, [dispatch, setPopoverOpen]); + }, [optionsList]); // debounce loading state so loading doesn't flash when user types const [debouncedLoading, setDebouncedLoading] = useState(true); @@ -76,16 +66,16 @@ export const OptionsListControl = ({ // remove all other selections if this control is single select useEffect(() => { if (singleSelect && selectedOptions && selectedOptions?.length > 1) { - dispatch(replaceSelection(selectedOptions[0])); + optionsList.dispatch.replaceSelection(selectedOptions[0]); } - }, [selectedOptions, singleSelect, dispatch, replaceSelection]); + }, [selectedOptions, singleSelect, optionsList.dispatch]); const updateSearchString = useCallback( (newSearchString: string) => { typeaheadSubject.next(newSearchString); - dispatch(setSearchString(newSearchString)); + optionsList.dispatch.setSearchString(newSearchString); }, - [typeaheadSubject, dispatch, setSearchString] + [typeaheadSubject, optionsList.dispatch] ); const loadMoreSuggestions = useCallback( @@ -141,7 +131,7 @@ export const OptionsListControl = ({ 'optionsList--filterBtnPlaceholder': !hasSelections, })} data-test-subj={`optionsList-control-${id}`} - onClick={() => dispatch(setPopoverOpen(!isPopoverOpen))} + onClick={() => optionsList.dispatch.setPopoverOpen(!isPopoverOpen)} isSelected={isPopoverOpen} numActiveFilters={validSelectionsCount} hasActiveFilters={Boolean(validSelectionsCount)} @@ -168,7 +158,7 @@ export const OptionsListControl = ({ anchorPosition="downCenter" initialFocus={'[data-test-subj=optionsList-control-search-input]'} className="optionsList__popoverOverride" - closePopover={() => dispatch(setPopoverOpen(false))} + closePopover={() => optionsList.dispatch.setPopoverOpen(false)} anchorClassName="optionsList__anchorOverride" aria-label={OptionsListStrings.popover.getAriaLabel(fieldName)} > diff --git a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx index 4b40414974b67..ffdb1045cad88 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx @@ -13,10 +13,12 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { FieldSpec } from '@kbn/data-views-plugin/common'; -import { OptionsListPopover, OptionsListPopoverProps } from './options_list_popover'; -import { OptionsListComponentState, OptionsListReduxState } from '../types'; -import { mockOptionsListReduxEmbeddableTools } from '../../../common/mocks'; +import { mockOptionsListEmbeddable } from '../../../common/mocks'; import { ControlOutput, OptionsListEmbeddableInput } from '../..'; +import { OptionsListComponentState, OptionsListReduxState } from '../types'; +import { OptionsListEmbeddableContext } from '../embeddable/options_list_embeddable'; +import { OptionsListPopover, OptionsListPopoverProps } from './options_list_popover'; +import { pluginServices } from '../../services'; describe('Options list popover', () => { const defaultProps = { @@ -35,16 +37,16 @@ describe('Options list popover', () => { async function mountComponent(options?: Partial) { const compProps = { ...defaultProps, ...(options?.popoverProps ?? {}) }; - const mockReduxEmbeddableTools = await mockOptionsListReduxEmbeddableTools({ + const optionsListEmbeddable = await mockOptionsListEmbeddable({ componentState: options?.componentState ?? {}, explicitInput: options?.explicitInput ?? {}, output: options?.output ?? {}, } as Partial); return mountWithIntl( - + - + ); } @@ -290,6 +292,9 @@ describe('Options list popover', () => { }); test('ensure warning icon shows up when testAllowExpensiveQueries = false', async () => { + pluginServices.getServices().optionsList.getAllowExpensiveQueries = jest.fn(() => + Promise.resolve(false) + ); const popover = await mountComponent({ componentState: { field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, diff --git a/src/plugins/controls/public/options_list/components/options_list_popover.tsx b/src/plugins/controls/public/options_list/components/options_list_popover.tsx index df5f3d00730d0..79e20925ed369 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover.tsx @@ -9,16 +9,13 @@ import React, { useState } from 'react'; import { isEmpty } from 'lodash'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; - -import { OptionsListReduxState } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; import { OptionsListPopoverTitle } from './options_list_popover_title'; import { OptionsListPopoverFooter } from './options_list_popover_footer'; import { OptionsListPopoverActionBar } from './options_list_popover_action_bar'; import { OptionsListPopoverSuggestions } from './options_list_popover_suggestions'; import { OptionsListPopoverInvalidSelections } from './options_list_popover_invalid_selections'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; export interface OptionsListPopoverProps { width: number; @@ -33,21 +30,16 @@ export const OptionsListPopover = ({ updateSearchString, loadMoreSuggestions, }: OptionsListPopoverProps) => { - // Redux embeddable container Context - const { useEmbeddableSelector: select } = useReduxEmbeddableContext< - OptionsListReduxState, - typeof optionsListReducers - >(); + const optionsList = useOptionsList(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const invalidSelections = select((state) => state.componentState.invalidSelections); - const availableOptions = select((state) => state.componentState.availableOptions); - const field = select((state) => state.componentState.field); + const field = optionsList.select((state) => state.componentState.field); + const availableOptions = optionsList.select((state) => state.componentState.availableOptions); + const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); - const hideActionBar = select((state) => state.explicitInput.hideActionBar); - const hideExclude = select((state) => state.explicitInput.hideExclude); - const fieldName = select((state) => state.explicitInput.fieldName); - const id = select((state) => state.explicitInput.id); + const id = optionsList.select((state) => state.explicitInput.id); + const fieldName = optionsList.select((state) => state.explicitInput.fieldName); + const hideExclude = optionsList.select((state) => state.explicitInput.hideExclude); + const hideActionBar = optionsList.select((state) => state.explicitInput.hideActionBar); const [showOnlySelected, setShowOnlySelected] = useState(false); diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx index d0354525c51a2..ecf70a5b38d6e 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx @@ -17,11 +17,9 @@ import { EuiToolTip, EuiText, } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { OptionsListReduxState } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; import { OptionsListPopoverSortingButton } from './options_list_popover_sorting_button'; interface OptionsListPopoverProps { @@ -35,20 +33,18 @@ export const OptionsListPopoverActionBar = ({ updateSearchString, setShowOnlySelected, }: OptionsListPopoverProps) => { - // Redux embeddable container Context - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { clearSelections }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); + const optionsList = useOptionsList(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const allowExpensiveQueries = select((state) => state.componentState.allowExpensiveQueries); - const invalidSelections = select((state) => state.componentState.invalidSelections); - const totalCardinality = select((state) => state.componentState.totalCardinality) ?? 0; - const searchString = select((state) => state.componentState.searchString); - const hideSort = select((state) => state.explicitInput.hideSort); + const totalCardinality = + optionsList.select((state) => state.componentState.totalCardinality) ?? 0; + const searchString = optionsList.select((state) => state.componentState.searchString); + const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); + + const allowExpensiveQueries = optionsList.select( + (state) => state.componentState.allowExpensiveQueries + ); + + const hideSort = optionsList.select((state) => state.explicitInput.hideSort); return (
@@ -142,7 +138,7 @@ export const OptionsListPopoverActionBar = ({ size="xs" color="danger" iconType="eraser" - onClick={() => dispatch(clearSelections({}))} + onClick={() => optionsList.dispatch.clearSelections({})} data-test-subj="optionsList-control-clear-all-selections" aria-label={OptionsListStrings.popover.getClearAllSelectionsButtonTitle()} /> diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_footer.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_footer.tsx index 966cc56d98044..bd71ad3a9f7d2 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_footer.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_footer.tsx @@ -7,19 +7,18 @@ */ import React from 'react'; + import { - useEuiBackgroundColor, - useEuiPaddingSize, - EuiPopoverFooter, - EuiButtonGroup, EuiProgress, + EuiButtonGroup, + EuiPopoverFooter, + useEuiPaddingSize, + useEuiBackgroundColor, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { OptionsListReduxState } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; const aggregationToggleButtons = [ { @@ -33,16 +32,9 @@ const aggregationToggleButtons = [ ]; export const OptionsListPopoverFooter = ({ isLoading }: { isLoading: boolean }) => { - // Redux embeddable container Context - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setExclude }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); + const optionsList = useOptionsList(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const exclude = select((state) => state.explicitInput.exclude); + const exclude = optionsList.select((state) => state.explicitInput.exclude); return ( <> @@ -71,7 +63,7 @@ export const OptionsListPopoverFooter = ({ isLoading }: { isLoading: boolean }) options={aggregationToggleButtons} idSelected={exclude ? 'optionsList__excludeResults' : 'optionsList__includeResults'} onChange={(optionId) => - dispatch(setExclude(optionId === 'optionsList__excludeResults')) + optionsList.dispatch.setExclude(optionId === 'optionsList__excludeResults') } buttonSize="compressed" data-test-subj="optionsList__includeExcludeButtonGroup" diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx index 424ae37da4bcb..980f861e00b26 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx @@ -15,24 +15,15 @@ import { EuiTitle, EuiScreenReaderOnly, } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { OptionsListReduxState } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; export const OptionsListPopoverInvalidSelections = () => { - // Redux embeddable container Context - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { deselectOption }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); + const optionsList = useOptionsList(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const invalidSelections = select((state) => state.componentState.invalidSelections); - const fieldName = select((state) => state.explicitInput.fieldName); + const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); + const fieldName = optionsList.select((state) => state.explicitInput.fieldName); const [selectableOptions, setSelectableOptions] = useState([]); // will be set in following useEffect useEffect(() => { @@ -80,7 +71,7 @@ export const OptionsListPopoverInvalidSelections = () => { listProps={{ onFocusBadge: false, isVirtualized: false }} onChange={(newSuggestions, _, changedOption) => { setSelectableOptions(newSuggestions); - dispatch(deselectOption(changedOption.label)); + optionsList.dispatch.deselectOption(changedOption.label); }} > {(list) => list} diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx index 9132fee3dc5dc..3867ce4a569cc 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx @@ -21,16 +21,14 @@ import { Direction, EuiToolTip, } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; import { getCompatibleSortingTypes, OPTIONS_LIST_DEFAULT_SORT, OptionsListSortBy, } from '../../../common/options_list/suggestions_sorting'; -import { OptionsListReduxState } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; interface OptionsListSortingPopoverProps { showOnlySelected: boolean; @@ -42,17 +40,10 @@ type SortByItem = EuiSelectableOption & { export const OptionsListPopoverSortingButton = ({ showOnlySelected, }: OptionsListSortingPopoverProps) => { - // Redux embeddable container Context - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setSort }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); + const optionsList = useOptionsList(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const field = select((state) => state.componentState.field); - const sort = select((state) => state.explicitInput.sort ?? OPTIONS_LIST_DEFAULT_SORT); + const field = optionsList.select((state) => state.componentState.field); + const sort = optionsList.select((state) => state.explicitInput.sort ?? OPTIONS_LIST_DEFAULT_SORT); const [isSortingPopoverOpen, setIsSortingPopoverOpen] = useState(false); @@ -87,7 +78,7 @@ export const OptionsListPopoverSortingButton = ({ setSortByOptions(updatedOptions); const selectedOption = updatedOptions.find(({ checked }) => checked === 'on'); if (selectedOption) { - dispatch(setSort({ by: selectedOption.data.sortBy })); + optionsList.dispatch.setSort({ by: selectedOption.data.sortBy }); } }; @@ -135,7 +126,9 @@ export const OptionsListPopoverSortingButton = ({ options={sortOrderOptions} idSelected={sort.direction} legend={OptionsListStrings.editorAndPopover.getSortDirectionLegend()} - onChange={(value) => dispatch(setSort({ direction: value as Direction }))} + onChange={(value) => + optionsList.dispatch.setSort({ direction: value as Direction }) + } /> diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx index fd0e7668c82fa..8d727bed55e20 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx @@ -10,12 +10,11 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { euiThemeVars } from '@kbn/ui-theme'; import { EuiSelectable } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; import { EuiSelectableOption } from '@elastic/eui/src/components/selectable/selectable_option'; +import { MAX_OPTIONS_LIST_REQUEST_SIZE } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; -import { MAX_OPTIONS_LIST_REQUEST_SIZE, OptionsListReduxState } from '../types'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; import { OptionsListPopoverEmptyMessage } from './options_list_popover_empty_message'; import { OptionsListPopoverSuggestionBadge } from './options_list_popover_suggestion_badge'; @@ -28,27 +27,21 @@ export const OptionsListPopoverSuggestions = ({ showOnlySelected, loadMoreSuggestions, }: OptionsListPopoverSuggestionsProps) => { - // Redux embeddable container Context - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { replaceSelection, deselectOption, selectOption, selectExists }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); - - // Select current state from Redux using multiple selectors to avoid rerenders. - const invalidSelections = select((state) => state.componentState.invalidSelections); - const availableOptions = select((state) => state.componentState.availableOptions); - const totalCardinality = select((state) => state.componentState.totalCardinality); - const searchString = select((state) => state.componentState.searchString); - - const selectedOptions = select((state) => state.explicitInput.selectedOptions); - const existsSelected = select((state) => state.explicitInput.existsSelected); - const singleSelect = select((state) => state.explicitInput.singleSelect); - const hideExists = select((state) => state.explicitInput.hideExists); - const isLoading = select((state) => state.output.loading) ?? false; - const fieldName = select((state) => state.explicitInput.fieldName); - const sort = select((state) => state.explicitInput.sort); + const optionsList = useOptionsList(); + + const searchString = optionsList.select((state) => state.componentState.searchString); + const availableOptions = optionsList.select((state) => state.componentState.availableOptions); + const totalCardinality = optionsList.select((state) => state.componentState.totalCardinality); + const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); + + const sort = optionsList.select((state) => state.explicitInput.sort); + const fieldName = optionsList.select((state) => state.explicitInput.fieldName); + const hideExists = optionsList.select((state) => state.explicitInput.hideExists); + const singleSelect = optionsList.select((state) => state.explicitInput.singleSelect); + const existsSelected = optionsList.select((state) => state.explicitInput.existsSelected); + const selectedOptions = optionsList.select((state) => state.explicitInput.selectedOptions); + + const isLoading = optionsList.select((state) => state.output.loading) ?? false; const listRef = useRef(null); @@ -173,13 +166,13 @@ export const OptionsListPopoverSuggestions = ({ setSelectableOptions(newSuggestions); // the order of these checks matters, so be careful if rearranging them if (key === 'exists-option') { - dispatch(selectExists(!Boolean(existsSelected))); + optionsList.dispatch.selectExists(!Boolean(existsSelected)); } else if (showOnlySelected || selectedOptionsSet.has(key)) { - dispatch(deselectOption(key)); + optionsList.dispatch.deselectOption(key); } else if (singleSelect) { - dispatch(replaceSelection(key)); + optionsList.dispatch.replaceSelection(key); } else { - dispatch(selectOption(key)); + optionsList.dispatch.selectOption(key); } }} > diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_title.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_title.tsx index 08aee48f61e28..9801a7bf2474c 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_title.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_title.tsx @@ -9,22 +9,17 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPopoverTitle, EuiIconTip } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { OptionsListReduxState } from '../types'; import { OptionsListStrings } from './options_list_strings'; -import { optionsListReducers } from '../options_list_reducers'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; export const OptionsListPopoverTitle = () => { - // Redux embeddable container Context - const { useEmbeddableSelector: select } = useReduxEmbeddableContext< - OptionsListReduxState, - typeof optionsListReducers - >(); + const optionsList = useOptionsList(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const allowExpensiveQueries = select((state) => state.componentState.allowExpensiveQueries); - const title = select((state) => state.explicitInput.title); + const allowExpensiveQueries = optionsList.select( + (state) => state.componentState.allowExpensiveQueries + ); + const title = optionsList.select((state) => state.explicitInput.title); return ( diff --git a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx index 2a7f7e116c40f..e2503b4e530e8 100644 --- a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx +++ b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx @@ -6,15 +6,14 @@ * Side Public License, v 1. */ -import React from 'react'; import ReactDOM from 'react-dom'; import { batch } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { isEmpty, isEqual } from 'lodash'; import { merge, Subject, Subscription } from 'rxjs'; +import React, { createContext, useContext } from 'react'; import { debounceTime, map, distinctUntilChanged, skip } from 'rxjs/operators'; -import { i18n } from '@kbn/i18n'; import { Filter, compareFilters, @@ -23,23 +22,24 @@ import { COMPARE_ALL_OPTIONS, buildExistsFilter, } from '@kbn/es-query'; -import { ReduxEmbeddableTools, ReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { i18n } from '@kbn/i18n'; import { DataView, FieldSpec } from '@kbn/data-views-plugin/public'; import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; -import { MIN_OPTIONS_LIST_REQUEST_SIZE, OptionsListReduxState } from '../types'; -import { pluginServices } from '../../services'; import { ControlInput, ControlOutput, - OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL, + OptionsListEmbeddableInput, } from '../..'; -import { getDefaultComponentState, optionsListReducers } from '../options_list_reducers'; +import { pluginServices } from '../../services'; +import { MIN_OPTIONS_LIST_REQUEST_SIZE, OptionsListReduxState } from '../types'; import { OptionsListControl } from '../components/options_list_control'; import { ControlsDataViewsService } from '../../services/data_views/types'; import { ControlsOptionsListService } from '../../services/options_list/types'; +import { getDefaultComponentState, optionsListReducers } from '../options_list_reducers'; const diffDataFetchProps = ( last?: OptionsListDataFetchProps, @@ -62,6 +62,20 @@ interface OptionsListDataFetchProps { filters?: ControlInput['filters']; } +export const OptionsListEmbeddableContext = createContext(null); +export const useOptionsList = (): OptionsListEmbeddable => { + const optionsList = useContext(OptionsListEmbeddableContext); + if (optionsList == null) { + throw new Error('useOptionsList must be used inside OptionsListEmbeddableContext.'); + } + return optionsList!; +}; + +type OptionsListReduxEmbeddableTools = ReduxEmbeddableTools< + OptionsListReduxState, + typeof optionsListReducers +>; + export class OptionsListEmbeddable extends Embeddable { public readonly type = OPTIONS_LIST_CONTROL; public deferEmbeddableLoad = true; @@ -80,13 +94,16 @@ export class OptionsListEmbeddable extends Embeddable; + // state management + public select: OptionsListReduxEmbeddableTools['select']; + public getState: OptionsListReduxEmbeddableTools['getState']; + public dispatch: OptionsListReduxEmbeddableTools['dispatch']; + public onStateChange: OptionsListReduxEmbeddableTools['onStateChange']; + + private cleanupStateTools: () => void; constructor( - reduxEmbeddablePackage: ReduxEmbeddablePackage, + reduxToolsPackage: ReduxToolsPackage, input: OptionsListEmbeddableInput, output: ControlOutput, parent?: IContainer @@ -101,7 +118,7 @@ export class OptionsListEmbeddable extends Embeddable(); // build redux embeddable tools - this.reduxEmbeddableTools = reduxEmbeddablePackage.createTools< + const reduxEmbeddableTools = reduxToolsPackage.createReduxEmbeddableTools< OptionsListReduxState, typeof optionsListReducers >({ @@ -110,6 +127,12 @@ export class OptionsListEmbeddable extends Embeddable { if (initialSelectedOptions) { @@ -183,19 +204,10 @@ export class OptionsListEmbeddable extends Embeddable { - const { - actions: { - clearValidAndInvalidSelections, - setValidAndInvalidSelections, - publishFilters, - }, - dispatch, - } = this.reduxEmbeddableTools; - if (!newSelectedOptions || isEmpty(newSelectedOptions)) { - dispatch(clearValidAndInvalidSelections({})); + this.dispatch.clearValidAndInvalidSelections({}); } else { - const { invalidSelections } = this.reduxEmbeddableTools.getState().componentState ?? {}; + const { invalidSelections } = this.getState().componentState ?? {}; const newValidSelections: string[] = []; const newInvalidSelections: string[] = []; for (const selectedOption of newSelectedOptions) { @@ -205,15 +217,13 @@ export class OptionsListEmbeddable extends Embeddable => { - const { - dispatch, - getState, - actions: { setField, setDataViewId }, - } = this.reduxEmbeddableTools; - const { explicitInput: { dataViewId, fieldName }, - } = getState(); + } = this.getState(); if (!this.dataView || this.dataView.id !== dataViewId) { try { @@ -246,7 +250,7 @@ export class OptionsListEmbeddable extends Embeddable { - const { - dispatch, - getState, - actions: { setLoading, publishFilters, setSearchString, updateQueryResults }, - } = this.reduxEmbeddableTools; const previousFieldName = this.field?.name; const { dataView, field } = await this.getCurrentDataViewAndField(); if (!dataView || !field) return; if (previousFieldName && field.name !== previousFieldName) { - dispatch(setSearchString('')); + this.dispatch.setSearchString(''); } const { componentState: { searchString, allowExpensiveQueries }, explicitInput: { selectedOptions, runPastTimeout, existsSelected, sort }, - } = getState(); - dispatch(setLoading(true)); + } = this.getState(); + this.dispatch.setLoading(true); if (searchString.valid) { // need to get filters, query, ignoreParentSettings, and timeRange from input for inheritance const { @@ -342,14 +341,12 @@ export class OptionsListEmbeddable extends Embeddable { - dispatch(setLoading(false)); - dispatch(publishFilters(newFilters)); + this.dispatch.setLoading(false); + this.dispatch.publishFilters(newFilters); }); } else { batch(() => { - dispatch( - updateQueryResults({ - availableOptions: {}, - }) - ); - dispatch(setLoading(false)); + this.dispatch.updateQueryResults({ + availableOptions: {}, + }); + this.dispatch.setLoading(false); }); } }; private buildFilter = async () => { - const { getState } = this.reduxEmbeddableTools; - const { validSelections } = getState().componentState ?? {}; - const { existsSelected } = getState().explicitInput ?? {}; + const { validSelections } = this.getState().componentState ?? {}; + const { existsSelected } = this.getState().explicitInput ?? {}; const { exclude } = this.getInput(); if ((!validSelections || isEmpty(validSelections)) && !existsSelected) { @@ -421,22 +413,18 @@ export class OptionsListEmbeddable extends Embeddable { - const { - dispatch, - actions: { setPopoverOpen, setLoading }, - } = this.reduxEmbeddableTools; batch(() => { - dispatch(setLoading(false)); - dispatch(setPopoverOpen(false)); + this.dispatch.setLoading(false); + this.dispatch.setPopoverOpen(false); }); super.onFatalError(e); }; public destroy = () => { super.destroy(); + this.cleanupStateTools(); this.abortController?.abort(); this.subscriptions.unsubscribe(); - this.reduxEmbeddableTools.cleanup(); if (this.node) ReactDOM.unmountComponentAtNode(this.node); }; @@ -444,16 +432,15 @@ export class OptionsListEmbeddable extends Embeddable - + - + , node ); diff --git a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx index d970c309bb6a8..cb2b1b4a82de3 100644 --- a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx +++ b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx @@ -10,7 +10,7 @@ import deepEqual from 'fast-deep-equal'; import { i18n } from '@kbn/i18n'; import { DataViewField } from '@kbn/data-views-plugin/common'; -import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { lazyLoadReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; import { @@ -33,7 +33,7 @@ export class OptionsListEmbeddableFactory constructor() {} public async create(initialInput: OptionsListEmbeddableInput, parent?: IContainer) { - const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); + const reduxEmbeddablePackage = await lazyLoadReduxToolsPackage(); const { OptionsListEmbeddable } = await import('./options_list_embeddable'); return Promise.resolve( new OptionsListEmbeddable(reduxEmbeddablePackage, initialInput, {}, parent) diff --git a/src/plugins/controls/public/range_slider/components/range_slider_control.tsx b/src/plugins/controls/public/range_slider/components/range_slider_control.tsx index 5e5bd512ab355..c96637b55f1d7 100644 --- a/src/plugins/controls/public/range_slider/components/range_slider_control.tsx +++ b/src/plugins/controls/public/range_slider/components/range_slider_control.tsx @@ -16,10 +16,8 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { rangeSliderReducers } from '../range_slider_reducers'; -import { RangeSliderReduxState } from '../types'; +import { useRangeSlider } from '../embeddable/range_slider_embeddable'; import { RangeSliderPopover, EuiDualRangeRef } from './range_slider_popover'; import './range_slider.scss'; @@ -30,21 +28,14 @@ export const RangeSliderControl: FC = () => { const rangeRef = useRef(null); const [isPopoverOpen, setIsPopoverOpen] = useState(false); - // Controls Services Context - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setSelectedRange }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); + const rangeSlider = useRangeSlider(); - // Select current state from Redux using multiple selectors to avoid rerenders. - const min = select((state) => state.componentState.min); - const max = select((state) => state.componentState.max); - const isInvalid = select((state) => state.componentState.isInvalid); - const id = select((state) => state.explicitInput.id); - const value = select((state) => state.explicitInput.value) ?? ['', '']; - const isLoading = select((state) => state.output.loading); + const min = rangeSlider.select((state) => state.componentState.min); + const max = rangeSlider.select((state) => state.componentState.max); + const isInvalid = rangeSlider.select((state) => state.componentState.isInvalid); + const id = rangeSlider.select((state) => state.explicitInput.id); + const value = rangeSlider.select((state) => state.explicitInput.value) ?? ['', '']; + const isLoading = rangeSlider.select((state) => state.output.loading); const hasAvailableRange = min !== '' && max !== ''; @@ -76,12 +67,10 @@ export const RangeSliderControl: FC = () => { }`} value={hasLowerBoundSelection ? lowerBoundValue : ''} onChange={(event) => { - dispatch( - setSelectedRange([ - event.target.value, - isNaN(upperBoundValue) ? '' : String(upperBoundValue), - ]) - ); + rangeSlider.dispatch.setSelectedRange([ + event.target.value, + isNaN(upperBoundValue) ? '' : String(upperBoundValue), + ]); }} disabled={isLoading} placeholder={`${hasAvailableRange ? roundedMin : ''}`} @@ -103,12 +92,10 @@ export const RangeSliderControl: FC = () => { }`} value={hasUpperBoundSelection ? upperBoundValue : ''} onChange={(event) => { - dispatch( - setSelectedRange([ - isNaN(lowerBoundValue) ? '' : String(lowerBoundValue), - event.target.value, - ]) - ); + rangeSlider.dispatch.setSelectedRange([ + isNaN(lowerBoundValue) ? '' : String(lowerBoundValue), + event.target.value, + ]); }} disabled={isLoading} placeholder={`${hasAvailableRange ? roundedMax : ''}`} diff --git a/src/plugins/controls/public/range_slider/components/range_slider_popover.tsx b/src/plugins/controls/public/range_slider/components/range_slider_popover.tsx index bd2fee564e9cb..c3b2ccbe676f4 100644 --- a/src/plugins/controls/public/range_slider/components/range_slider_popover.tsx +++ b/src/plugins/controls/public/range_slider/components/range_slider_popover.tsx @@ -19,13 +19,11 @@ import { EuiButtonIcon, } from '@elastic/eui'; import type { EuiDualRangeClass } from '@elastic/eui/src/components/form/range/dual_range'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { RangeValue } from '../../../common/range_slider/types'; import { pluginServices } from '../../services'; -import { rangeSliderReducers } from '../range_slider_reducers'; -import { RangeSliderReduxState } from '../types'; import { RangeSliderStrings } from './range_slider_strings'; +import { RangeValue } from '../../../common/range_slider/types'; +import { useRangeSlider } from '../embeddable/range_slider_embeddable'; // Unfortunately, wrapping EuiDualRange in `withEuiTheme` has created this annoying/verbose typing export type EuiDualRangeRef = EuiDualRangeClass & ComponentProps; @@ -37,23 +35,17 @@ export const RangeSliderPopover: FC<{ rangeRef?: Ref }> = ({ ra const { dataViews: { get: getDataViewById }, } = pluginServices.getServices(); - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setSelectedRange }, - } = useReduxEmbeddableContext(); - - const dispatch = useEmbeddableDispatch(); + const rangeSlider = useRangeSlider(); // Select current state from Redux using multiple selectors to avoid rerenders. - const dataViewId = select((state) => state.output.dataViewId); - const fieldSpec = select((state) => state.componentState.field); - const id = select((state) => state.explicitInput.id); - const isInvalid = select((state) => state.componentState.isInvalid); - const max = select((state) => state.componentState.max); - const min = select((state) => state.componentState.min); - const title = select((state) => state.explicitInput.title); - const value = select((state) => state.explicitInput.value) ?? ['', '']; + const dataViewId = rangeSlider.select((state) => state.output.dataViewId); + const fieldSpec = rangeSlider.select((state) => state.componentState.field); + const id = rangeSlider.select((state) => state.explicitInput.id); + const isInvalid = rangeSlider.select((state) => state.componentState.isInvalid); + const max = rangeSlider.select((state) => state.componentState.max); + const min = rangeSlider.select((state) => state.componentState.min); + const title = rangeSlider.select((state) => state.explicitInput.title); + const value = rangeSlider.select((state) => state.explicitInput.value) ?? ['', '']; const hasAvailableRange = min !== '' && max !== ''; const hasLowerBoundSelection = value[0] !== ''; @@ -154,7 +146,7 @@ export const RangeSliderPopover: FC<{ rangeRef?: Ref }> = ({ ra const updatedUpperBound = typeof newUpperBound === 'number' ? String(newUpperBound) : value[1]; - dispatch(setSelectedRange([updatedLowerBound, updatedUpperBound])); + rangeSlider.dispatch.setSelectedRange([updatedLowerBound, updatedUpperBound]); }} value={displayedValue} ticks={hasAvailableRange ? ticks : undefined} @@ -179,7 +171,7 @@ export const RangeSliderPopover: FC<{ rangeRef?: Ref }> = ({ ra iconType="eraser" color="danger" onClick={() => { - dispatch(setSelectedRange(['', ''])); + rangeSlider.dispatch.setSelectedRange(['', '']); }} aria-label={RangeSliderStrings.popover.getClearRangeButtonTitle()} data-test-subj="rangeSlider__clearRangeButton" diff --git a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx index 3ae81b267e9ea..62809be8cc39c 100644 --- a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; import { isEmpty } from 'lodash'; import { batch } from 'react-redux'; @@ -15,8 +15,6 @@ import deepEqual from 'fast-deep-equal'; import { Subscription, lastValueFrom } from 'rxjs'; import { debounceTime, distinctUntilChanged, skip, map } from 'rxjs/operators'; -import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; import { compareFilters, buildRangeFilter, @@ -27,7 +25,9 @@ import { } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { ReduxEmbeddableTools, ReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import { ControlInput, @@ -36,11 +36,11 @@ import { RANGE_SLIDER_CONTROL, } from '../..'; import { pluginServices } from '../../services'; -import { RangeSliderControl } from '../components/range_slider_control'; -import { getDefaultComponentState, rangeSliderReducers } from '../range_slider_reducers'; import { RangeSliderReduxState } from '../types'; import { ControlsDataService } from '../../services/data/types'; +import { RangeSliderControl } from '../components/range_slider_control'; import { ControlsDataViewsService } from '../../services/data_views/types'; +import { getDefaultComponentState, rangeSliderReducers } from '../range_slider_reducers'; const diffDataFetchProps = ( current?: RangeSliderDataFetchProps, @@ -65,6 +65,20 @@ interface RangeSliderDataFetchProps { const fieldMissingError = (fieldName: string) => new Error(`field ${fieldName} not found in index pattern`); +export const RangeSliderControlContext = createContext(null); +export const useRangeSlider = (): RangeSliderEmbeddable => { + const rangeSlider = useContext(RangeSliderControlContext); + if (rangeSlider == null) { + throw new Error('useRangeSlider must be used inside RangeSliderControlContext.'); + } + return rangeSlider!; +}; + +type RangeSliderReduxEmbeddableTools = ReduxEmbeddableTools< + RangeSliderReduxState, + typeof rangeSliderReducers +>; + export class RangeSliderEmbeddable extends Embeddable { public readonly type = RANGE_SLIDER_CONTROL; public deferEmbeddableLoad = true; @@ -80,13 +94,16 @@ export class RangeSliderEmbeddable extends Embeddable; + // state management + public select: RangeSliderReduxEmbeddableTools['select']; + public getState: RangeSliderReduxEmbeddableTools['getState']; + public dispatch: RangeSliderReduxEmbeddableTools['dispatch']; + public onStateChange: RangeSliderReduxEmbeddableTools['onStateChange']; + + private cleanupStateTools: () => void; constructor( - reduxEmbeddablePackage: ReduxEmbeddablePackage, + reduxToolsPackage: ReduxToolsPackage, input: RangeSliderEmbeddableInput, output: ControlOutput, parent?: IContainer @@ -96,7 +113,7 @@ export class RangeSliderEmbeddable extends Embeddable({ @@ -104,6 +121,11 @@ export class RangeSliderEmbeddable extends Embeddable => { - const { - getState, - dispatch, - actions: { setField, setDataViewId }, - } = this.reduxEmbeddableTools; const { explicitInput: { dataViewId, fieldName }, - } = getState(); + } = this.getState(); if (!this.dataView || this.dataView.id !== dataViewId) { try { @@ -179,7 +196,7 @@ export class RangeSliderEmbeddable extends Embeddable { - const { - dispatch, - actions: { setLoading, publishFilters, setMinMax }, - } = this.reduxEmbeddableTools; - - dispatch(setLoading(true)); + this.dispatch.setLoading(true); const { dataView, field } = await this.getCurrentDataViewAndField(); if (!dataView || !field) return; @@ -226,8 +238,8 @@ export class RangeSliderEmbeddable extends Embeddable { - dispatch(setLoading(false)); - dispatch(publishFilters([])); + this.dispatch.setLoading(false); + this.dispatch.publishFilters([]); }); throw fieldMissingError(fieldName); } @@ -258,12 +270,10 @@ export class RangeSliderEmbeddable extends Embeddable { - const { - dispatch, - getState, - actions: { setLoading, setIsInvalid, setDataViewId, publishFilters }, - } = this.reduxEmbeddableTools; const { componentState: { min: availableMin, max: availableMax }, explicitInput: { @@ -337,7 +342,7 @@ export class RangeSliderEmbeddable extends Embeddable { - dispatch(setLoading(false)); - dispatch(setIsInvalid(!ignoreParentSettings?.ignoreValidations && hasEitherSelection)); - dispatch(setDataViewId(dataView.id)); - dispatch(publishFilters([])); + this.dispatch.setLoading(false); + this.dispatch.setIsInvalid(!ignoreParentSettings?.ignoreValidations && hasEitherSelection); + this.dispatch.setDataViewId(dataView.id); + this.dispatch.publishFilters([]); }); return; } @@ -404,20 +409,20 @@ export class RangeSliderEmbeddable extends Embeddable { - dispatch(setLoading(false)); - dispatch(setIsInvalid(true)); - dispatch(setDataViewId(dataView.id)); - dispatch(publishFilters([])); + this.dispatch.setLoading(false); + this.dispatch.setIsInvalid(true); + this.dispatch.setDataViewId(dataView.id); + this.dispatch.publishFilters([]); }); return; } } batch(() => { - dispatch(setLoading(false)); - dispatch(setIsInvalid(false)); - dispatch(setDataViewId(dataView.id)); - dispatch(publishFilters([rangeFilter])); + this.dispatch.setLoading(false); + this.dispatch.setIsInvalid(false); + this.dispatch.setDataViewId(dataView.id); + this.dispatch.publishFilters([rangeFilter]); }); }; @@ -427,23 +432,22 @@ export class RangeSliderEmbeddable extends Embeddable { super.destroy(); + this.cleanupStateTools(); this.subscriptions.unsubscribe(); - this.reduxEmbeddableTools.cleanup(); }; public render = (node: HTMLElement) => { if (this.node) { ReactDOM.unmountComponentAtNode(this.node); } - const { Wrapper: RangeSliderReduxWrapper } = this.reduxEmbeddableTools; this.node = node; const ControlsServicesProvider = pluginServices.getContextProvider(); ReactDOM.render( - + - + , node diff --git a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx index b8cd0d9536b1e..4103be5ff59b3 100644 --- a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx @@ -6,12 +6,12 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import deepEqual from 'fast-deep-equal'; -import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; import { DataViewField } from '@kbn/data-views-plugin/common'; -import { i18n } from '@kbn/i18n'; +import { lazyLoadReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; import { createRangeSliderExtract, @@ -45,7 +45,7 @@ export class RangeSliderEmbeddableFactory public isEditable = () => Promise.resolve(true); public async create(initialInput: RangeSliderEmbeddableInput, parent?: IContainer) { - const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); + const reduxEmbeddablePackage = await lazyLoadReduxToolsPackage(); const { RangeSliderEmbeddable } = await import('./range_slider_embeddable'); return Promise.resolve( diff --git a/src/plugins/controls/public/services/options_list/options_list_service.ts b/src/plugins/controls/public/services/options_list/options_list_service.ts index 7152d190c997d..4ad0ecddb80a8 100644 --- a/src/plugins/controls/public/services/options_list/options_list_service.ts +++ b/src/plugins/controls/public/services/options_list/options_list_service.ts @@ -99,7 +99,7 @@ class OptionsListService implements ControlsOptionsListService { private cachedAllowExpensiveQueries = memoize(async () => { const { allowExpensiveQueries } = await this.http.get<{ allowExpensiveQueries: boolean; - }>('/api/kibana/controls/optionsList/getClusterSettings'); + }>('/api/kibana/controls/optionsList/getExpensiveQueriesSetting'); return allowExpensiveQueries; }); diff --git a/src/plugins/controls/public/time_slider/components/time_slider.tsx b/src/plugins/controls/public/time_slider/components/time_slider.tsx index f8ae06677cd16..5c0fb97f21304 100644 --- a/src/plugins/controls/public/time_slider/components/time_slider.tsx +++ b/src/plugins/controls/public/time_slider/components/time_slider.tsx @@ -8,14 +8,12 @@ import React, { FC, useRef } from 'react'; import { EuiInputPopover } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { timeSliderReducers } from '../time_slider_reducers'; -import { TimeSliderReduxState } from '../types'; -import { TimeSliderPopoverButton } from './time_slider_popover_button'; -import { TimeSliderPopoverContent } from './time_slider_popover_content'; -import { EuiDualRangeRef } from './time_slider_sliding_window_range'; import { FROM_INDEX, TO_INDEX } from '../time_utils'; +import { EuiDualRangeRef } from './time_slider_sliding_window_range'; import { getRoundedTimeRangeBounds } from '../time_slider_selectors'; +import { useTimeSlider } from '../embeddable/time_slider_embeddable'; +import { TimeSliderPopoverButton } from './time_slider_popover_button'; +import { TimeSliderPopoverContent } from './time_slider_popover_content'; import './index.scss'; @@ -25,25 +23,21 @@ interface Props { } export const TimeSlider: FC = (props: Props) => { - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); - const stepSize = select((state) => { + const timeSlider = useTimeSlider(); + + const stepSize = timeSlider.select((state) => { return state.componentState.stepSize; }); - const ticks = select((state) => { + const ticks = timeSlider.select((state) => { return state.componentState.ticks; }); - const timeRangeBounds = select(getRoundedTimeRangeBounds); + const timeRangeBounds = timeSlider.select(getRoundedTimeRangeBounds); const timeRangeMin = timeRangeBounds[FROM_INDEX]; const timeRangeMax = timeRangeBounds[TO_INDEX]; - const value = select((state) => { + const value = timeSlider.select((state) => { return state.componentState.value; }); - const isOpen = select((state) => { + const isOpen = timeSlider.select((state) => { return state.componentState.isOpen; }); @@ -64,7 +58,7 @@ export const TimeSlider: FC = (props: Props) => { input={ { - dispatch(actions.setIsOpen({ isOpen: !isOpen })); + timeSlider.dispatch.setIsOpen({ isOpen: !isOpen }); }} formatDate={props.formatDate} from={from} @@ -72,7 +66,7 @@ export const TimeSlider: FC = (props: Props) => { /> } isOpen={isOpen} - closePopover={() => dispatch(actions.setIsOpen({ isOpen: false }))} + closePopover={() => timeSlider.dispatch.setIsOpen({ isOpen: false })} panelPaddingSize="s" anchorPosition="downCenter" disableFocusTrap diff --git a/src/plugins/controls/public/time_slider/components/time_slider_popover_content.tsx b/src/plugins/controls/public/time_slider/components/time_slider_popover_content.tsx index 68201aa4debe6..400b93efbbbfe 100644 --- a/src/plugins/controls/public/time_slider/components/time_slider_popover_content.tsx +++ b/src/plugins/controls/public/time_slider/components/time_slider_popover_content.tsx @@ -8,13 +8,12 @@ import React, { Ref } from 'react'; import { EuiButtonIcon, EuiRangeTick, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; -import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { TimeSliderAnchoredRange } from './time_slider_anchored_range'; -import { EuiDualRangeRef, TimeSliderSlidingWindowRange } from './time_slider_sliding_window_range'; -import { timeSliderReducers } from '../time_slider_reducers'; -import { TimeSliderReduxState } from '../types'; + import { getIsAnchored } from '../time_slider_selectors'; import { TimeSliderStrings } from './time_slider_strings'; +import { useTimeSlider } from '../embeddable/time_slider_embeddable'; +import { TimeSliderAnchoredRange } from './time_slider_anchored_range'; +import { EuiDualRangeRef, TimeSliderSlidingWindowRange } from './time_slider_sliding_window_range'; interface Props { value: [number, number]; @@ -41,13 +40,8 @@ export function TimeSliderPopoverContent(props: Props) { }; }); - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setIsAnchored }, - } = useReduxEmbeddableContext(); - const dispatch = useEmbeddableDispatch(); - const isAnchored = select(getIsAnchored); + const timeSlider = useTimeSlider(); + const isAnchored = timeSlider.select(getIsAnchored); const rangeInput = isAnchored ? ( void; @@ -22,11 +20,7 @@ interface Props { } export const TimeSliderPrepend: FC = (props: Props) => { - const { useEmbeddableDispatch, actions } = useReduxEmbeddableContext< - TimeSliderReduxState, - typeof timeSliderReducers - >(); - const dispatch = useEmbeddableDispatch(); + const timeSlider = useTimeSlider(); const [isPaused, setIsPaused] = useState(true); const [timeoutId, setTimeoutId] = useState(undefined); @@ -51,13 +45,13 @@ export const TimeSliderPrepend: FC = (props: Props) => { }; const onPlay = () => { - dispatch(actions.setIsOpen({ isOpen: true })); + timeSlider.dispatch.setIsOpen({ isOpen: true }); setIsPaused(false); playNextFrame(); }; const onPause = () => { - dispatch(actions.setIsOpen({ isOpen: true })); + timeSlider.dispatch.setIsOpen({ isOpen: true }); setIsPaused(true); if (subscription) { subscription.unsubscribe(); diff --git a/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx b/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx index 424d2d953c965..b9baa4084fb55 100644 --- a/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx +++ b/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx @@ -10,10 +10,10 @@ import _ from 'lodash'; import { debounceTime, first, map } from 'rxjs/operators'; import moment from 'moment-timezone'; import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; -import { ReduxEmbeddableTools, ReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import type { TimeRange } from '@kbn/es-query'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import React from 'react'; +import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; import { Subscription } from 'rxjs'; import { TIME_SLIDER_CONTROL } from '../..'; @@ -37,6 +37,20 @@ import { } from '../time_utils'; import { getIsAnchored, getRoundedTimeRangeBounds } from '../time_slider_selectors'; +export const TimeSliderControlContext = createContext(null); +export const useTimeSlider = (): TimeSliderControlEmbeddable => { + const timeSlider = useContext(TimeSliderControlContext); + if (timeSlider == null) { + throw new Error('useTimeSlider must be used inside TimeSliderControlContext.'); + } + return timeSlider!; +}; + +type TimeSliderReduxEmbeddableTools = ReduxEmbeddableTools< + TimeSliderReduxState, + typeof timeSliderReducers +>; + export class TimeSliderControlEmbeddable extends Embeddable< TimeSliderControlEmbeddableInput, ControlOutput @@ -47,6 +61,14 @@ export class TimeSliderControlEmbeddable extends Embeddable< private inputSubscription: Subscription; private node?: HTMLElement; + // state management + public select: TimeSliderReduxEmbeddableTools['select']; + public getState: TimeSliderReduxEmbeddableTools['getState']; + public dispatch: TimeSliderReduxEmbeddableTools['dispatch']; + public onStateChange: TimeSliderReduxEmbeddableTools['onStateChange']; + + private cleanupStateTools: () => void; + private getTimezone: ControlsSettingsService['getTimezone']; private timefilter: ControlsDataService['timefilter']; private prevTimeRange: TimeRange | undefined; @@ -56,13 +78,8 @@ export class TimeSliderControlEmbeddable extends Embeddable< }; private readonly waitForControlOutputConsumersToLoad$; - private reduxEmbeddableTools: ReduxEmbeddableTools< - TimeSliderReduxState, - typeof timeSliderReducers - >; - constructor( - reduxEmbeddablePackage: ReduxEmbeddablePackage, + reduxToolsPackage: ReduxToolsPackage, input: TimeSliderControlEmbeddableInput, output: ControlOutput, parent?: IContainer @@ -85,7 +102,7 @@ export class TimeSliderControlEmbeddable extends Embeddable< timeRangeBounds[TO_INDEX], this.getTimezone() ); - this.reduxEmbeddableTools = reduxEmbeddablePackage.createTools< + const reduxEmbeddableTools = reduxToolsPackage.createReduxEmbeddableTools< TimeSliderReduxState, typeof timeSliderReducers >({ @@ -99,6 +116,12 @@ export class TimeSliderControlEmbeddable extends Embeddable< }, }); + this.select = reduxEmbeddableTools.select; + this.getState = reduxEmbeddableTools.getState; + this.dispatch = reduxEmbeddableTools.dispatch; + this.onStateChange = reduxEmbeddableTools.onStateChange; + this.cleanupStateTools = reduxEmbeddableTools.cleanup; + this.inputSubscription = this.getInput$().subscribe(() => this.onInputChange()); this.waitForControlOutputConsumersToLoad$ = @@ -125,7 +148,7 @@ export class TimeSliderControlEmbeddable extends Embeddable< public destroy = () => { super.destroy(); - this.reduxEmbeddableTools.cleanup(); + this.cleanupStateTools(); if (this.inputSubscription) { this.inputSubscription.unsubscribe(); } @@ -136,7 +159,6 @@ export class TimeSliderControlEmbeddable extends Embeddable< const { timesliceStartAsPercentageOfTimeRange, timesliceEndAsPercentageOfTimeRange } = this.prevTimesliceAsPercentage ?? {}; - const { actions, dispatch } = this.reduxEmbeddableTools; if ( timesliceStartAsPercentageOfTimeRange !== input.timesliceStartAsPercentageOfTimeRange || timesliceEndAsPercentageOfTimeRange !== input.timesliceEndAsPercentageOfTimeRange @@ -149,8 +171,8 @@ export class TimeSliderControlEmbeddable extends Embeddable< ) { // If no selections have been saved into the timeslider, then both `timesliceStartAsPercentageOfTimeRange` // and `timesliceEndAsPercentageOfTimeRange` will be undefined - so, need to reset component state to match - dispatch(actions.publishValue({ value: undefined })); - dispatch(actions.setValue({ value: undefined })); + this.dispatch.publishValue({ value: undefined }); + this.dispatch.setValue({ value: undefined }); } else { // Otherwise, need to call `syncWithTimeRange` so that the component state value can be calculated and set this.syncWithTimeRange(); @@ -158,31 +180,26 @@ export class TimeSliderControlEmbeddable extends Embeddable< } else if (input.timeRange && !_.isEqual(input.timeRange, this.prevTimeRange)) { const nextBounds = this.timeRangeToBounds(input.timeRange); const ticks = getTicks(nextBounds[FROM_INDEX], nextBounds[TO_INDEX], this.getTimezone()); - dispatch( - actions.setTimeRangeBounds({ - ...getStepSize(ticks), - ticks, - timeRangeBounds: nextBounds, - }) - ); + this.dispatch.setTimeRangeBounds({ + ...getStepSize(ticks), + ticks, + timeRangeBounds: nextBounds, + }); this.syncWithTimeRange(); } } private syncWithTimeRange() { this.prevTimeRange = this.getInput().timeRange; - const { actions, dispatch, getState } = this.reduxEmbeddableTools; - const stepSize = getState().componentState.stepSize; - const timesliceStartAsPercentageOfTimeRange = - getState().explicitInput.timesliceStartAsPercentageOfTimeRange; - const timesliceEndAsPercentageOfTimeRange = - getState().explicitInput.timesliceEndAsPercentageOfTimeRange; + const stepSize = this.getState().componentState.stepSize; + const { timesliceStartAsPercentageOfTimeRange, timesliceEndAsPercentageOfTimeRange } = + this.getState().explicitInput; if ( timesliceStartAsPercentageOfTimeRange !== undefined && timesliceEndAsPercentageOfTimeRange !== undefined ) { - const timeRangeBounds = getState().componentState.timeRangeBounds; + const timeRangeBounds = this.getState().componentState.timeRangeBounds; const timeRange = timeRangeBounds[TO_INDEX] - timeRangeBounds[FROM_INDEX]; const from = timeRangeBounds[FROM_INDEX] + timesliceStartAsPercentageOfTimeRange * timeRange; const to = timeRangeBounds[FROM_INDEX] + timesliceEndAsPercentageOfTimeRange * timeRange; @@ -190,8 +207,8 @@ export class TimeSliderControlEmbeddable extends Embeddable< roundDownToNextStepSizeFactor(from, stepSize), roundUpToNextStepSizeFactor(to, stepSize), ] as [number, number]; - dispatch(actions.publishValue({ value })); - dispatch(actions.setValue({ value })); + this.dispatch.publishValue({ value }); + this.dispatch.setValue({ value }); this.onRangeChange(value[TO_INDEX] - value[FROM_INDEX]); } } @@ -208,16 +225,14 @@ export class TimeSliderControlEmbeddable extends Embeddable< } private debouncedPublishChange = _.debounce((value?: [number, number]) => { - const { actions, dispatch } = this.reduxEmbeddableTools; - dispatch(actions.publishValue({ value })); + this.dispatch.publishValue({ value }); }, 500); private getTimeSliceAsPercentageOfTimeRange(value?: [number, number]) { - const { getState } = this.reduxEmbeddableTools; let timesliceStartAsPercentageOfTimeRange: number | undefined; let timesliceEndAsPercentageOfTimeRange: number | undefined; if (value) { - const timeRangeBounds = getState().componentState.timeRangeBounds; + const timeRangeBounds = this.getState().componentState.timeRangeBounds; const timeRange = timeRangeBounds[TO_INDEX] - timeRangeBounds[FROM_INDEX]; timesliceStartAsPercentageOfTimeRange = (value[FROM_INDEX] - timeRangeBounds[FROM_INDEX]) / timeRange; @@ -232,39 +247,29 @@ export class TimeSliderControlEmbeddable extends Embeddable< } private onTimesliceChange = (value?: [number, number]) => { - const { actions, dispatch } = this.reduxEmbeddableTools; - const { timesliceStartAsPercentageOfTimeRange, timesliceEndAsPercentageOfTimeRange } = this.getTimeSliceAsPercentageOfTimeRange(value); - dispatch( - actions.setValueAsPercentageOfTimeRange({ - timesliceStartAsPercentageOfTimeRange, - timesliceEndAsPercentageOfTimeRange, - }) - ); - dispatch(actions.setValue({ value })); + this.dispatch.setValueAsPercentageOfTimeRange({ + timesliceStartAsPercentageOfTimeRange, + timesliceEndAsPercentageOfTimeRange, + }); + this.dispatch.setValue({ value }); this.debouncedPublishChange(value); }; private onRangeChange = (range?: number) => { - const { actions, dispatch, getState } = this.reduxEmbeddableTools; - const timeRangeBounds = getState().componentState.timeRangeBounds; + const timeRangeBounds = this.getState().componentState.timeRangeBounds; const timeRange = timeRangeBounds[TO_INDEX] - timeRangeBounds[FROM_INDEX]; - dispatch( - actions.setRange({ - range: range !== undefined && range < timeRange ? range : undefined, - }) - ); + this.dispatch.setRange({ + range: range !== undefined && range < timeRange ? range : undefined, + }); }; private onNext = () => { - const { getState } = this.reduxEmbeddableTools; - const value = getState().componentState.value; - const range = getState().componentState.range; - const ticks = getState().componentState.ticks; - const isAnchored = getIsAnchored(getState()); + const { value, range, ticks } = this.getState().componentState; + const isAnchored = getIsAnchored(this.getState()); const tickRange = ticks[1].value - ticks[0].value; - const timeRangeBounds = getRoundedTimeRangeBounds(getState()); + const timeRangeBounds = getRoundedTimeRangeBounds(this.getState()); if (isAnchored) { if (value === undefined || value[TO_INDEX] >= timeRangeBounds[TO_INDEX]) { @@ -304,13 +309,10 @@ export class TimeSliderControlEmbeddable extends Embeddable< }; private onPrevious = () => { - const { getState } = this.reduxEmbeddableTools; - const value = getState().componentState.value; - const range = getState().componentState.range; - const ticks = getState().componentState.ticks; - const isAnchored = getIsAnchored(getState()); + const { value, range, ticks } = this.getState().componentState; + const isAnchored = getIsAnchored(this.getState()); const tickRange = ticks[1].value - ticks[0].value; - const timeRangeBounds = getRoundedTimeRangeBounds(getState()); + const timeRangeBounds = getRoundedTimeRangeBounds(this.getState()); if (isAnchored) { const prevTick = value @@ -347,10 +349,9 @@ export class TimeSliderControlEmbeddable extends Embeddable< }; private formatDate = (epoch: number) => { - const { getState } = this.reduxEmbeddableTools; return moment .tz(epoch, getMomentTimezone(this.getTimezone())) - .format(getState().componentState.format); + .format(this.getState().componentState.format); }; public render = (node: HTMLElement) => { @@ -358,12 +359,9 @@ export class TimeSliderControlEmbeddable extends Embeddable< ReactDOM.unmountComponentAtNode(this.node); } this.node = node; - - const { Wrapper: TimeSliderControlReduxWrapper } = this.reduxEmbeddableTools; - ReactDOM.render( - + { @@ -372,22 +370,21 @@ export class TimeSliderControlEmbeddable extends Embeddable< this.onRangeChange(range); }} /> - + , node ); }; public renderPrepend() { - const { Wrapper: TimeSliderControlReduxWrapper } = this.reduxEmbeddableTools; return ( - + - + ); } diff --git a/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable_factory.tsx b/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable_factory.tsx index d3f3418104328..51aa173bef2bf 100644 --- a/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable_factory.tsx +++ b/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable_factory.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { lazyLoadReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import { createTimeSliderExtract, createTimeSliderInject, @@ -24,7 +24,7 @@ export class TimeSliderEmbeddableFactory constructor() {} public async create(initialInput: any, parent?: IContainer) { - const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); + const reduxEmbeddablePackage = await lazyLoadReduxToolsPackage(); const { TimeSliderControlEmbeddable } = await import('./time_slider_embeddable'); return Promise.resolve( diff --git a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts index f3b4ea598b886..55390de758b8b 100644 --- a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts +++ b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts @@ -14,12 +14,16 @@ export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { const router = http.createRouter(); router.get( { - path: '/api/kibana/controls/optionsList/getClusterSettings', + path: '/api/kibana/controls/optionsList/getExpensiveQueriesSetting', validate: false, }, async (context, _, response) => { try { - const esClient = (await context.core).elasticsearch.client.asCurrentUser; + /** + * using internal user here because in many cases the logged in user will not have the monitor permission required + * to check cluster settings. This endpoint does not take a query, params, or a body, so there is no chance of leaking info. + */ + const esClient = (await context.core).elasticsearch.client.asInternalUser; const settings = await esClient.cluster.getSettings({ include_defaults: true, filter_path: '**.allow_expensive_queries', diff --git a/src/plugins/dashboard/common/dashboard_container/type_guards.ts b/src/plugins/dashboard/common/dashboard_container/type_guards.ts deleted file mode 100644 index 1c5468609c3af..0000000000000 --- a/src/plugins/dashboard/common/dashboard_container/type_guards.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 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 type { - DashboardContainerByReferenceInput, - DashboardContainerByValueInput, - DashboardContainerInput, -} from './types'; - -export const dashboardContainerInputIsByValue = ( - containerInput: DashboardContainerInput -): containerInput is DashboardContainerByValueInput => { - if ( - (containerInput as DashboardContainerByValueInput).panels && - !(containerInput as DashboardContainerByReferenceInput).savedObjectId - ) { - return true; - } - return false; -}; diff --git a/src/plugins/dashboard/common/dashboard_container/types.ts b/src/plugins/dashboard/common/dashboard_container/types.ts index df71eb4565442..2af75ccbf7fc4 100644 --- a/src/plugins/dashboard/common/dashboard_container/types.ts +++ b/src/plugins/dashboard/common/dashboard_container/types.ts @@ -30,13 +30,9 @@ export interface DashboardPanelState< panelRefName?: string; } -export type DashboardContainerInput = - | DashboardContainerByReferenceInput - | DashboardContainerByValueInput; +export type DashboardContainerByReferenceInput = SavedObjectEmbeddableInput; -export type DashboardContainerByReferenceInput = SavedObjectEmbeddableInput & { panels: never }; - -export interface DashboardContainerByValueInput extends EmbeddableInput { +export interface DashboardContainerInput extends EmbeddableInput { // filter context to be passed to children query: Query; filters: Filter[]; diff --git a/src/plugins/dashboard/common/index.ts b/src/plugins/dashboard/common/index.ts index b997db4d7696c..612314da5245d 100644 --- a/src/plugins/dashboard/common/index.ts +++ b/src/plugins/dashboard/common/index.ts @@ -17,7 +17,6 @@ export type { DashboardPanelMap, DashboardPanelState, DashboardContainerInput, - DashboardContainerByValueInput, DashboardContainerByReferenceInput, } from './dashboard_container/types'; diff --git a/src/plugins/dashboard/common/types.ts b/src/plugins/dashboard/common/types.ts index 4a1abbd49b2db..bf7cedbadec45 100644 --- a/src/plugins/dashboard/common/types.ts +++ b/src/plugins/dashboard/common/types.ts @@ -10,7 +10,7 @@ import { EmbeddableInput, EmbeddableStateWithType } from '@kbn/embeddable-plugin import { PersistableControlGroupInput } from '@kbn/controls-plugin/common'; import { SavedDashboardPanel } from './dashboard_saved_object/types'; -import { DashboardContainerByValueInput, DashboardPanelState } from './dashboard_container/types'; +import { DashboardContainerInput, DashboardPanelState } from './dashboard_container/types'; export interface DashboardOptions { hidePanelTitles: boolean; @@ -32,7 +32,7 @@ export interface DashboardCapabilities { * For BWC reasons, dashboard state is stored with panels as an array instead of a map */ export type SharedDashboardState = Partial< - Omit & { panels: SavedDashboardPanel[] } + Omit & { panels: SavedDashboardPanel[] } >; /** diff --git a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx index 1e9f0e7f02b97..f7c1c2a30f68c 100644 --- a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx @@ -22,7 +22,7 @@ import { } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { getSampleDashboardInput } from '../mocks'; +import { buildMockDashboard } from '../mocks'; import { pluginServices } from '../services/plugin_services'; import { AddToLibraryAction } from './add_to_library_action'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; @@ -48,8 +48,7 @@ Object.defineProperty(pluginServices.getServices().application, 'capabilities', beforeEach(async () => { pluginServices.getServices().application.capabilities = defaultCapabilities; - container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + container = buildMockDashboard(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.test.tsx index d59c2047eeb28..6c1e8bc6d680e 100644 --- a/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.test.tsx @@ -21,7 +21,7 @@ import { ErrorEmbeddable, IContainer, isErrorEmbeddable } from '@kbn/embeddable- import { DashboardPanelState } from '../../common'; import { ClonePanelAction } from './clone_panel_action'; import { pluginServices } from '../services/plugin_services'; -import { getSampleDashboardInput, getSampleDashboardPanel } from '../mocks'; +import { buildMockDashboard, getSampleDashboardPanel } from '../mocks'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; let container: DashboardContainer; @@ -37,7 +37,12 @@ beforeEach(async () => { create: jest.fn().mockImplementation(() => ({ id: 'brandNewSavedObject' })), }; - const input = getSampleDashboardInput({ + const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); + + pluginServices.getServices().embeddable.getEmbeddableFactory = jest + .fn() + .mockReturnValue(mockEmbeddableFactory); + container = buildMockDashboard({ panels: { '123': getSampleDashboardPanel({ explicitInput: { firstName: 'Kibanana', id: '123' }, @@ -45,13 +50,6 @@ beforeEach(async () => { }), }, }); - const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); - - pluginServices.getServices().embeddable.getEmbeddableFactory = jest - .fn() - .mockReturnValue(mockEmbeddableFactory); - container = new DashboardContainer(input); - await container.untilInitialized(); const refOrValContactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx b/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx index 482553e2f002f..228db7138fd54 100644 --- a/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx @@ -95,6 +95,7 @@ export class ClonePanelAction implements Action { height: panelToClone.gridData.h, currentPanels: dashboard.getInput().panels, placeBesideId: panelToClone.explicitInput.id, + scrollToPanel: true, } as IPanelPlacementBesideArgs ); } diff --git a/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.test.tsx index 44a1b2d4828df..877488c6d8041 100644 --- a/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.test.tsx @@ -7,7 +7,7 @@ */ import { ExpandPanelAction } from './expand_panel_action'; -import { getSampleDashboardInput, getSampleDashboardPanel } from '../mocks'; +import { buildMockDashboard, getSampleDashboardPanel } from '../mocks'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; @@ -30,7 +30,7 @@ pluginServices.getServices().embeddable.getEmbeddableFactory = jest .mockReturnValue(mockEmbeddableFactory); beforeEach(async () => { - const input = getSampleDashboardInput({ + container = buildMockDashboard({ panels: { '123': getSampleDashboardPanel({ explicitInput: { firstName: 'Sam', id: '123' }, @@ -39,9 +39,6 @@ beforeEach(async () => { }, }); - container = new DashboardContainer(input); - await container.untilInitialized(); - const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, ContactCardEmbeddableOutput, diff --git a/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx b/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx index 0d3dd592dcc34..4e98a6dd31024 100644 --- a/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/expand_panel_action.tsx @@ -64,5 +64,9 @@ export class ExpandPanelAction implements Action { } const newValue = isExpanded(embeddable) ? undefined : embeddable.id; (embeddable.parent as DashboardContainer).setExpandedPanelId(newValue); + + if (!newValue) { + (embeddable.parent as DashboardContainer).setScrollToPanelId(embeddable.id); + } } } diff --git a/src/plugins/dashboard/public/dashboard_actions/export_csv_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/export_csv_action.test.tsx index 265d34992689c..350db8fad40b1 100644 --- a/src/plugins/dashboard/public/dashboard_actions/export_csv_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/export_csv_action.test.tsx @@ -20,7 +20,7 @@ import { isErrorEmbeddable, IContainer, ErrorEmbeddable } from '@kbn/embeddable- import { ExportCSVAction } from './export_csv_action'; import { pluginServices } from '../services/plugin_services'; -import { getSampleDashboardInput, getSampleDashboardPanel } from '../mocks'; +import { buildMockDashboard, getSampleDashboardPanel } from '../mocks'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; describe('Export CSV action', () => { @@ -45,7 +45,7 @@ describe('Export CSV action', () => { create: jest.fn().mockImplementation(() => ({ id: 'brandNewSavedObject' })), }; - const input = getSampleDashboardInput({ + container = buildMockDashboard({ panels: { '123': getSampleDashboardPanel({ explicitInput: { firstName: 'Kibanana', id: '123' }, @@ -53,8 +53,6 @@ describe('Export CSV action', () => { }), }, }); - container = new DashboardContainer(input); - await container.untilInitialized(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.test.tsx index e864e35d5ad3a..3bec1276fb4c7 100644 --- a/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/filters_notification_action.test.tsx @@ -17,9 +17,8 @@ import { import { type Query, type AggregateQuery, Filter } from '@kbn/es-query'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { getSampleDashboardInput } from '../mocks'; +import { buildMockDashboard } from '../mocks'; import { pluginServices } from '../services/plugin_services'; -import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; import { FiltersNotificationAction } from './filters_notification_action'; const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); @@ -51,8 +50,7 @@ const getMockPhraseFilter = (key: string, value: string) => { }; const buildEmbeddable = async (input?: Partial) => { - const container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + const container = buildMockDashboard(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, ContactCardEmbeddableOutput, diff --git a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx index 7a3b98633fa67..eb7aa344a8311 100644 --- a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx @@ -13,7 +13,7 @@ import { FilterableEmbeddable, isErrorEmbeddable, ViewMode } from '@kbn/embeddab import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { getSampleDashboardInput } from '../mocks'; +import { buildMockDashboard } from '../mocks'; import { EuiPopover } from '@elastic/eui'; import { FiltersNotificationPopover, @@ -40,8 +40,7 @@ describe('filters notification popover', () => { let defaultProps: FiltersNotificationProps; beforeEach(async () => { - container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + container = buildMockDashboard(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, ContactCardEmbeddableOutput, diff --git a/src/plugins/dashboard/public/dashboard_actions/library_notification_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/library_notification_action.test.tsx index ed7f501f83296..9fc05d575cbc3 100644 --- a/src/plugins/dashboard/public/dashboard_actions/library_notification_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/library_notification_action.test.tsx @@ -22,11 +22,11 @@ import { } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { getSampleDashboardInput } from '../mocks'; import { pluginServices } from '../services/plugin_services'; import { UnlinkFromLibraryAction } from './unlink_from_library_action'; import { LibraryNotificationAction } from './library_notification_action'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; +import { buildMockDashboard } from '../mocks'; const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); pluginServices.getServices().embeddable.getEmbeddableFactory = jest @@ -43,8 +43,7 @@ beforeEach(async () => { execute: jest.fn(), } as unknown as UnlinkFromLibraryAction; - container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + container = buildMockDashboard(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_actions/library_notification_popover.test.tsx b/src/plugins/dashboard/public/dashboard_actions/library_notification_popover.test.tsx index eb390b37a88f3..7fd0385f5ef1b 100644 --- a/src/plugins/dashboard/public/dashboard_actions/library_notification_popover.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/library_notification_popover.test.tsx @@ -24,7 +24,7 @@ import { LibraryNotificationPopover, LibraryNotificationProps, } from './library_notification_popover'; -import { getSampleDashboardInput } from '../mocks'; +import { buildMockDashboard } from '../mocks'; import { pluginServices } from '../services/plugin_services'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; @@ -38,8 +38,7 @@ describe('LibraryNotificationPopover', () => { let defaultProps: LibraryNotificationProps; beforeEach(async () => { - container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + container = buildMockDashboard(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_actions/replace_panel_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/replace_panel_action.test.tsx index c5695b55072d2..0829f89424ede 100644 --- a/src/plugins/dashboard/public/dashboard_actions/replace_panel_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/replace_panel_action.test.tsx @@ -17,7 +17,7 @@ import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { ReplacePanelAction } from './replace_panel_action'; import { pluginServices } from '../services/plugin_services'; -import { getSampleDashboardInput, getSampleDashboardPanel } from '../mocks'; +import { buildMockDashboard, getSampleDashboardPanel } from '../mocks'; import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_container'; const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); @@ -28,7 +28,7 @@ pluginServices.getServices().embeddable.getEmbeddableFactory = jest let container: DashboardContainer; let embeddable: ContactCardEmbeddable; beforeEach(async () => { - const input = getSampleDashboardInput({ + container = buildMockDashboard({ panels: { '123': getSampleDashboardPanel({ explicitInput: { firstName: 'Sam', id: '123' }, @@ -36,8 +36,6 @@ beforeEach(async () => { }), }, }); - container = new DashboardContainer(input); - await container.untilInitialized(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, @@ -54,7 +52,7 @@ beforeEach(async () => { } }); -test('Executes the replace panel action', async () => { +test('Executes the replace panel action', () => { let SavedObjectFinder: any; const action = new ReplacePanelAction(SavedObjectFinder); action.execute({ embeddable }); @@ -82,13 +80,13 @@ test('Execute throws an error when called with an embeddable not in a parent', a await expect(check()).rejects.toThrow(Error); }); -test('Returns title', async () => { +test('Returns title', () => { let SavedObjectFinder: any; const action = new ReplacePanelAction(SavedObjectFinder); expect(action.getDisplayName({ embeddable })).toBeDefined(); }); -test('Returns an icon', async () => { +test('Returns an icon', () => { let SavedObjectFinder: any; const action = new ReplacePanelAction(SavedObjectFinder); expect(action.getIconType({ embeddable })).toBeDefined(); diff --git a/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx b/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx index d5d5c439a4745..14067f0b6aa68 100644 --- a/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx @@ -21,6 +21,7 @@ import { Toast } from '@kbn/core/public'; import { DashboardPanelState } from '../../common'; import { pluginServices } from '../services/plugin_services'; import { dashboardReplacePanelActionStrings } from './_dashboard_actions_strings'; +import { DashboardContainer } from '../dashboard_container'; interface Props { container: IContainer; @@ -81,8 +82,8 @@ export class ReplacePanelFlyout extends React.Component { } as DashboardPanelState, }, }); - container.reload(); + (container as DashboardContainer).setHighlightPanelId(id); this.showToast(name); this.props.onClose(); }; diff --git a/src/plugins/dashboard/public/dashboard_actions/unlink_from_library_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/unlink_from_library_action.test.tsx index a0e557ce107a0..1bcb273ea2511 100644 --- a/src/plugins/dashboard/public/dashboard_actions/unlink_from_library_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/unlink_from_library_action.test.tsx @@ -23,7 +23,7 @@ import { } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; -import { getSampleDashboardInput } from '../mocks'; +import { buildMockDashboard } from '../mocks'; import { DashboardPanelState } from '../../common'; import { pluginServices } from '../services/plugin_services'; import { UnlinkFromLibraryAction } from './unlink_from_library_action'; @@ -38,8 +38,7 @@ pluginServices.getServices().embeddable.getEmbeddableFactory = jest .mockReturnValue(mockEmbeddableFactory); beforeEach(async () => { - container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + container = buildMockDashboard(); const contactCardEmbeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts index e87a74d428f9a..dfc5a648f50aa 100644 --- a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts +++ b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts @@ -182,6 +182,14 @@ export const topNavStrings = { defaultMessage: 'Save as a new dashboard', }), }, + resetChanges: { + label: i18n.translate('dashboard.topNave.resetChangesButtonAriaLabel', { + defaultMessage: 'Reset', + }), + description: i18n.translate('dashboard.topNave.resetChangesConfigDescription', { + defaultMessage: 'Reset changes to dashboard', + }), + }, switchToViewMode: { label: i18n.translate('dashboard.topNave.cancelButtonAriaLabel', { defaultMessage: 'Switch to view mode', diff --git a/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx b/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx index b78ae7a4806f4..8fc546a41ea65 100644 --- a/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx +++ b/src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx @@ -8,7 +8,8 @@ import { History } from 'history'; import useMount from 'react-use/lib/useMount'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; @@ -28,14 +29,14 @@ import { removeSearchSessionIdFromURL, createSessionRestorationDataProvider, } from './url/search_sessions_integration'; +import { DashboardAPI, DashboardRenderer } from '..'; import { DASHBOARD_APP_ID } from '../dashboard_constants'; import { pluginServices } from '../services/plugin_services'; import { DashboardTopNav } from './top_nav/dashboard_top_nav'; -import type { DashboardContainer } from '../dashboard_container'; +import { AwaitingDashboardAPI } from '../dashboard_container'; import { type DashboardEmbedSettings, DashboardRedirect } from './types'; import { useDashboardMountContext } from './hooks/dashboard_mount_context'; import { useDashboardOutcomeValidation } from './hooks/use_dashboard_outcome_validation'; -import DashboardContainerRenderer from '../dashboard_container/dashboard_container_renderer'; import { loadDashboardHistoryLocationState } from './locator/load_dashboard_history_location_state'; import type { DashboardCreationOptions } from '../dashboard_container/embeddable/dashboard_container_factory'; @@ -46,6 +47,16 @@ export interface DashboardAppProps { embedSettings?: DashboardEmbedSettings; } +export const DashboardAPIContext = createContext(null); + +export const useDashboardAPI = (): DashboardAPI => { + const api = useContext(DashboardAPIContext); + if (api == null) { + throw new Error('useDashboardAPI must be used inside DashboardAPIContext'); + } + return api!; +}; + export function DashboardApp({ savedDashboardId, embedSettings, @@ -57,10 +68,7 @@ export function DashboardApp({ useMount(() => { (async () => setShowNoDataPage(await isDashboardAppInNoDataState()))(); }); - - const [dashboardContainer, setDashboardContainer] = useState( - undefined - ); + const [dashboardAPI, setDashboardAPI] = useState(null); /** * Unpack & set up dashboard services @@ -72,7 +80,9 @@ export function DashboardApp({ notifications: { toasts }, settings: { uiSettings }, data: { search }, + customBranding, } = pluginServices.getServices(); + const showPlainSpinner = useObservable(customBranding.hasCustomBranding$, false); const incomingEmbeddable = getStateTransfer().getIncomingEmbeddablePackage( DASHBOARD_APP_ID, @@ -140,7 +150,7 @@ export function DashboardApp({ }, // Override all state with URL + Locator input - overrideInput: { + initialInput: { // State loaded from the dashboard app URL and from the locator overrides all other dashboard state. ...initialUrlState, ...stateFromLocator, @@ -163,26 +173,17 @@ export function DashboardApp({ getScreenshotContext, ]); - /** - * Get the redux wrapper from the dashboard container. This is used to wrap the top nav so it can interact with the - * dashboard's redux state. - */ - const DashboardReduxWrapper = useMemo( - () => dashboardContainer?.getReduxEmbeddableTools().Wrapper, - [dashboardContainer] - ); - /** * When the dashboard container is created, or re-created, start syncing dashboard state with the URL */ useEffect(() => { - if (!dashboardContainer) return; + if (!dashboardAPI) return; const { stopWatchingAppStateInUrl } = startSyncingDashboardUrlState({ kbnUrlStateStorage, - dashboardContainer, + dashboardAPI, }); return () => stopWatchingAppStateInUrl(); - }, [dashboardContainer, kbnUrlStateStorage]); + }, [dashboardAPI, kbnUrlStateStorage]); return (
@@ -191,18 +192,19 @@ export function DashboardApp({ )} {!showNoDataPage && ( <> - {DashboardReduxWrapper && ( - + {dashboardAPI && ( + - + )} {getLegacyConflictWarning?.()} - setDashboardContainer(container)} /> )} diff --git a/src/plugins/dashboard/public/dashboard_app/locator/load_dashboard_history_location_state.ts b/src/plugins/dashboard/public/dashboard_app/locator/load_dashboard_history_location_state.ts index c187ab4cdaeed..4234269ca7758 100644 --- a/src/plugins/dashboard/public/dashboard_app/locator/load_dashboard_history_location_state.ts +++ b/src/plugins/dashboard/public/dashboard_app/locator/load_dashboard_history_location_state.ts @@ -8,11 +8,11 @@ import { ScopedHistory } from '@kbn/core-application-browser'; import { ForwardedDashboardState } from './locator'; -import { convertSavedPanelsToPanelMap, DashboardContainerByValueInput } from '../../../common'; +import { convertSavedPanelsToPanelMap, DashboardContainerInput } from '../../../common'; export const loadDashboardHistoryLocationState = ( getScopedHistory: () => ScopedHistory -): Partial => { +): Partial => { const state = getScopedHistory().location.state as undefined | ForwardedDashboardState; if (!state) { diff --git a/src/plugins/dashboard/public/dashboard_app/locator/locator.ts b/src/plugins/dashboard/public/dashboard_app/locator/locator.ts index 7d03bc1bc65c8..073105f38cddf 100644 --- a/src/plugins/dashboard/public/dashboard_app/locator/locator.ts +++ b/src/plugins/dashboard/public/dashboard_app/locator/locator.ts @@ -16,7 +16,7 @@ import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public' import type { GlobalQueryStateFromUrl } from '@kbn/data-plugin/public'; import { DASHBOARD_APP_ID, SEARCH_SESSION_ID } from '../../dashboard_constants'; -import type { DashboardContainerByValueInput, SavedDashboardPanel } from '../../../common'; +import type { DashboardContainerInput, SavedDashboardPanel } from '../../../common'; /** * Useful for ensuring that we don't pass any non-serializable values to history.push (for example, functions). @@ -36,7 +36,7 @@ export const DASHBOARD_APP_LOCATOR = 'DASHBOARD_APP_LOCATOR'; export type DashboardAppLocatorParams = Partial< Omit< - DashboardContainerByValueInput, + DashboardContainerInput, 'panels' | 'controlGroupInput' | 'executionContext' | 'isEmbeddedExternally' > > & { diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx index 6cef7e858b165..e7c7daa2bcc27 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; import { ControlGroupContainer } from '@kbn/controls-plugin/public'; import { getAddControlButtonTitle } from '../../_dashboard_app_strings'; +import { useDashboardAPI } from '../../dashboard_app'; interface Props { closePopover: () => void; @@ -17,6 +18,11 @@ interface Props { } export const AddDataControlButton = ({ closePopover, controlGroup, ...rest }: Props) => { + const dashboard = useDashboardAPI(); + const onSave = () => { + dashboard.scrollToTop(); + }; + return ( { - controlGroup.openAddDataControlFlyout(); + controlGroup.openAddDataControlFlyout({ onSave }); closePopover(); }} > diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx index 8283144e1c155..cbd514be8ba13 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx @@ -13,6 +13,7 @@ import { getAddTimeSliderControlButtonTitle, getOnlyOneTimeSliderControlMsg, } from '../../_dashboard_app_strings'; +import { useDashboardAPI } from '../../dashboard_app'; interface Props { closePopover: () => void; @@ -21,6 +22,7 @@ interface Props { export const AddTimeSliderControlButton = ({ closePopover, controlGroup, ...rest }: Props) => { const [hasTimeSliderControl, setHasTimeSliderControl] = useState(false); + const dashboard = useDashboardAPI(); useEffect(() => { const subscription = controlGroup.getInput$().subscribe(() => { @@ -42,8 +44,9 @@ export const AddTimeSliderControlButton = ({ closePopover, controlGroup, ...rest { - controlGroup.addTimeSliderControl(); + onClick={async () => { + await controlGroup.addTimeSliderControl(); + dashboard.scrollToTop(); closePopover(); }} data-test-subj="controls-create-timeslider-button" diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx index f3b67afbac91d..708af176d785d 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx @@ -6,25 +6,31 @@ * Side Public License, v 1. */ +import { css } from '@emotion/react'; +import React, { useCallback } from 'react'; import { METRIC_TYPE } from '@kbn/analytics'; +import { IconType, useEuiTheme } from '@elastic/eui'; + +import { + AddFromLibraryButton, + IconButton, + IconButtonGroup, + Toolbar, + ToolbarButton, +} from '@kbn/shared-ux-button-toolbar'; import { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { AddFromLibraryButton, Toolbar, ToolbarButton } from '@kbn/shared-ux-button-toolbar'; -import { IconButton, IconButtonGroup } from '@kbn/shared-ux-button-toolbar'; import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public'; -import React from 'react'; -import { useCallback } from 'react'; -import { IconType, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { dashboardReplacePanelActionStrings } from '../../dashboard_actions/_dashboard_actions_strings'; -import { DASHBOARD_APP_ID, DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants'; -import { useDashboardContainerContext } from '../../dashboard_container/dashboard_container_context'; -import { pluginServices } from '../../services/plugin_services'; + import { getCreateVisualizationButtonTitle, getQuickCreateButtonGroupLegend, } from '../_dashboard_app_strings'; import { EditorMenu } from './editor_menu'; +import { useDashboardAPI } from '../dashboard_app'; +import { pluginServices } from '../../services/plugin_services'; import { ControlsToolbarButton } from './controls_toolbar_button'; +import { DASHBOARD_APP_ID, DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants'; +import { dashboardReplacePanelActionStrings } from '../../dashboard_actions/_dashboard_actions_strings'; export function DashboardEditingToolbar() { const { @@ -36,7 +42,7 @@ export function DashboardEditingToolbar() { } = pluginServices.getServices(); const { euiTheme } = useEuiTheme(); - const { embeddableInstance: dashboardContainer } = useDashboardContainerContext(); + const dashboard = useDashboardAPI(); const stateTransferService = getStateTransfer(); @@ -101,19 +107,18 @@ export function DashboardEditingToolbar() { return; } - const newEmbeddable = await dashboardContainer.addNewEmbeddable( - embeddableFactory.type, - explicitInput - ); + const newEmbeddable = await dashboard.addNewEmbeddable(embeddableFactory.type, explicitInput); if (newEmbeddable) { + dashboard.setScrollToPanelId(newEmbeddable.id); + dashboard.setHighlightPanelId(newEmbeddable.id); toasts.addSuccess({ title: dashboardReplacePanelActionStrings.getSuccessMessage(newEmbeddable.getTitle()), 'data-test-subj': 'addEmbeddableToDashboardSuccess', }); } }, - [trackUiMetric, dashboardContainer, toasts] + [trackUiMetric, dashboard, toasts] ); const getVisTypeQuickButton = ( @@ -170,12 +175,12 @@ export function DashboardEditingToolbar() { const extraButtons = [ , dashboardContainer.addFromLibrary()} + onClick={() => dashboard.addFromLibrary()} data-test-subj="dashboardAddPanelButton" />, ]; - if (dashboardContainer.controlGroup) { - extraButtons.push(); + if (dashboard.controlGroup) { + extraButtons.push(); } return ( diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx index c2febc4750185..9ae7f5759e6cc 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx @@ -26,13 +26,13 @@ import { unsavedChangesBadgeStrings, } from '../_dashboard_app_strings'; import { UI_SETTINGS } from '../../../common'; +import { useDashboardAPI } from '../dashboard_app'; import { pluginServices } from '../../services/plugin_services'; import { useDashboardMenuItems } from './use_dashboard_menu_items'; import { DashboardEmbedSettings, DashboardRedirect } from '../types'; import { DashboardEditingToolbar } from './dashboard_editing_toolbar'; import { useDashboardMountContext } from '../hooks/dashboard_mount_context'; import { getFullEditPath, LEGACY_DASHBOARD_APP_ID } from '../../dashboard_constants'; -import { useDashboardContainerContext } from '../../dashboard_container/dashboard_container_context'; import './_dashboard_top_nav.scss'; export interface DashboardTopNavProps { @@ -69,36 +69,26 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr } = pluginServices.getServices(); const isLabsEnabled = uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI); const { setHeaderActionMenu, onAppLeave } = useDashboardMountContext(); - /** - * Unpack dashboard state from redux - */ - const { - useEmbeddableDispatch, - actions: { setSavedQueryId }, - useEmbeddableSelector: select, - embeddableInstance: dashboardContainer, - } = useDashboardContainerContext(); - const dispatch = useEmbeddableDispatch(); + + const dashboard = useDashboardAPI(); const PresentationUtilContextProvider = getPresentationUtilContextProvider(); - const hasUnsavedChanges = select((state) => state.componentState.hasUnsavedChanges); - const fullScreenMode = select((state) => state.componentState.fullScreenMode); - const savedQueryId = select((state) => state.componentState.savedQueryId); - const lastSavedId = select((state) => state.componentState.lastSavedId); - const viewMode = select((state) => state.explicitInput.viewMode); - const query = select((state) => state.explicitInput.query); - const title = select((state) => state.explicitInput.title); + const hasUnsavedChanges = dashboard.select((state) => state.componentState.hasUnsavedChanges); + const fullScreenMode = dashboard.select((state) => state.componentState.fullScreenMode); + const savedQueryId = dashboard.select((state) => state.componentState.savedQueryId); + const lastSavedId = dashboard.select((state) => state.componentState.lastSavedId); + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); + const query = dashboard.select((state) => state.explicitInput.query); + const title = dashboard.select((state) => state.explicitInput.title); // store data views in state & subscribe to dashboard data view changes. - const [allDataViews, setAllDataViews] = useState( - dashboardContainer.getAllDataViews() - ); + const [allDataViews, setAllDataViews] = useState(dashboard.getAllDataViews()); useEffect(() => { - const subscription = dashboardContainer.onDataViewsUpdate$.subscribe((dataViews) => + const subscription = dashboard.onDataViewsUpdate$.subscribe((dataViews) => setAllDataViews(dataViews) ); return () => subscription.unsubscribe(); - }, [dashboardContainer]); + }, [dashboard]); const dashboardTitle = useMemo(() => { return getDashboardTitle(title, viewMode, !lastSavedId); @@ -212,7 +202,7 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr }, [embedSettings, filterManager, fullScreenMode, isChromeVisible, viewMode]); UseUnmount(() => { - dashboardContainer.clearOverlays(); + dashboard.clearOverlays(); }); return ( @@ -264,12 +254,12 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr } onQuerySubmit={(_payload, isUpdate) => { if (isUpdate === false) { - dashboardContainer.forceRefresh(); + dashboard.forceRefresh(); } }} - onSavedQueryIdChange={(newId: string | undefined) => { - dispatch(setSavedQueryId(newId)); - }} + onSavedQueryIdChange={(newId: string | undefined) => + dashboard.dispatch.setSavedQueryId(newId) + } /> {viewMode !== ViewMode.PRINT && isLabsEnabled && isLabsShown ? ( diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx index f2603110ebaa1..8beba68e51da2 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/share/show_share_modal.test.tsx @@ -7,7 +7,7 @@ */ import { Capabilities } from '@kbn/core/public'; -import { convertPanelMapToSavedPanels, DashboardContainerByValueInput } from '../../../../common'; +import { convertPanelMapToSavedPanels, DashboardContainerInput } from '../../../../common'; import { DashboardAppLocatorParams } from '../../..'; import { pluginServices } from '../../../services/plugin_services'; @@ -68,7 +68,7 @@ describe('ShowShareModal', () => { }); const getPropsAndShare = ( - unsavedState?: Partial + unsavedState?: Partial ): ShowShareModalProps => { pluginServices.getServices().dashboardSessionStorage.getState = jest .fn() @@ -94,7 +94,7 @@ describe('ShowShareModal', () => { }); it('locatorParams unsaved state is properly propagated to locator', () => { - const unsavedDashboardState: DashboardContainerByValueInput = { + const unsavedDashboardState: DashboardContainerInput = { panels: { panel_1: { type: 'panel_type', @@ -121,7 +121,7 @@ describe('ShowShareModal', () => { }, ], query: { query: 'bye', language: 'kuery' }, - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; const showModalProps = getPropsAndShare(unsavedDashboardState); ShowShareModal(showModalProps); expect(toggleShareMenuSpy).toHaveBeenCalledTimes(1); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx index b548d2e39afc5..8ac32563d3e19 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx @@ -14,13 +14,13 @@ import { TopNavMenuData } from '@kbn/navigation-plugin/public'; import { DashboardRedirect } from '../types'; import { UI_SETTINGS } from '../../../common'; +import { useDashboardAPI } from '../dashboard_app'; import { topNavStrings } from '../_dashboard_app_strings'; import { ShowShareModal } from './share/show_share_modal'; import { pluginServices } from '../../services/plugin_services'; import { CHANGE_CHECK_DEBOUNCE } from '../../dashboard_constants'; import { SaveDashboardReturn } from '../../services/dashboard_saved_object/types'; import { confirmDiscardUnsavedChanges } from '../../dashboard_listing/confirm_overlays'; -import { useDashboardContainerContext } from '../../dashboard_container/dashboard_container_context'; export const useDashboardMenuItems = ({ redirectTo, @@ -46,18 +46,13 @@ export const useDashboardMenuItems = ({ /** * Unpack dashboard state from redux */ - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - embeddableInstance: dashboardContainer, - actions: { setViewMode, setFullScreenMode }, - } = useDashboardContainerContext(); - const dispatch = useEmbeddableDispatch(); + const dashboard = useDashboardAPI(); - const hasUnsavedChanges = select((state) => state.componentState.hasUnsavedChanges); - const hasOverlays = select((state) => state.componentState.hasOverlays); - const lastSavedId = select((state) => state.componentState.lastSavedId); - const dashboardTitle = select((state) => state.explicitInput.title); + const hasUnsavedChanges = dashboard.select((state) => state.componentState.hasUnsavedChanges); + const hasOverlays = dashboard.select((state) => state.componentState.hasOverlays); + const lastSavedId = dashboard.select((state) => state.componentState.lastSavedId); + const dashboardTitle = dashboard.select((state) => state.explicitInput.title); + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); /** * Show the Dashboard app's share menu @@ -95,41 +90,46 @@ export const useDashboardMenuItems = ({ */ const quickSaveDashboard = useCallback(() => { setIsSaveInProgress(true); - dashboardContainer + dashboard .runQuickSave() .then(() => setTimeout(() => setIsSaveInProgress(false), CHANGE_CHECK_DEBOUNCE)); - }, [dashboardContainer]); + }, [dashboard]); /** * Show the dashboard's save modal */ const saveDashboardAs = useCallback(() => { - dashboardContainer.runSaveAs().then((result) => maybeRedirect(result)); - }, [maybeRedirect, dashboardContainer]); + dashboard.runSaveAs().then((result) => maybeRedirect(result)); + }, [maybeRedirect, dashboard]); /** * Clone the dashboard */ const clone = useCallback(() => { - dashboardContainer.runClone().then((result) => maybeRedirect(result)); - }, [maybeRedirect, dashboardContainer]); + dashboard.runClone().then((result) => maybeRedirect(result)); + }, [maybeRedirect, dashboard]); /** - * Returns to view mode. If the dashboard has unsaved changes shows a warning and resets to last saved state. + * Show the dashboard's "Confirm reset changes" modal. If confirmed: + * (1) reset the dashboard to the last saved state, and + * (2) if `switchToViewMode` is `true`, set the dashboard to view mode. */ - const returnToViewMode = useCallback(() => { - dashboardContainer.clearOverlays(); - if (hasUnsavedChanges) { - confirmDiscardUnsavedChanges(() => { - batch(() => { - dashboardContainer.resetToLastSavedState(); - dispatch(setViewMode(ViewMode.VIEW)); - }); - }); - return; - } - dispatch(setViewMode(ViewMode.VIEW)); - }, [dashboardContainer, dispatch, hasUnsavedChanges, setViewMode]); + const resetChanges = useCallback( + (switchToViewMode: boolean = false) => { + dashboard.clearOverlays(); + if (hasUnsavedChanges) { + confirmDiscardUnsavedChanges(() => { + batch(() => { + dashboard.resetToLastSavedState(); + if (switchToViewMode) dashboard.dispatch.setViewMode(ViewMode.VIEW); + }); + }, viewMode); + } else { + if (switchToViewMode) dashboard.dispatch.setViewMode(ViewMode.VIEW); + } + }, + [dashboard, hasUnsavedChanges, viewMode] + ); /** * Register all of the top nav configs that can be used by dashboard. @@ -140,7 +140,7 @@ export const useDashboardMenuItems = ({ ...topNavStrings.fullScreen, id: 'full-screen', testId: 'dashboardFullScreenMode', - run: () => dispatch(setFullScreenMode(true)), + run: () => dashboard.dispatch.setFullScreenMode(true), } as TopNavMenuData, labs: { @@ -158,8 +158,8 @@ export const useDashboardMenuItems = ({ testId: 'dashboardEditMode', className: 'eui-hideFor--s eui-hideFor--xs', // hide for small screens - editing doesn't work in mobile mode. run: () => { - dashboardContainer.clearOverlays(); - dispatch(setViewMode(ViewMode.EDIT)); + dashboard.dispatch.setViewMode(ViewMode.EDIT); + dashboard.clearOverlays(); }, } as TopNavMenuData, @@ -190,7 +190,7 @@ export const useDashboardMenuItems = ({ id: 'cancel', disableButton: isSaveInProgress || !lastSavedId || hasOverlays, testId: 'dashboardViewOnlyMode', - run: () => returnToViewMode(), + run: () => resetChanges(true), } as TopNavMenuData, share: { @@ -206,7 +206,7 @@ export const useDashboardMenuItems = ({ id: 'settings', testId: 'dashboardSettingsButton', disableButton: isSaveInProgress || hasOverlays, - run: () => dashboardContainer.showSettings(), + run: () => dashboard.showSettings(), } as TopNavMenuData, clone: { @@ -219,43 +219,66 @@ export const useDashboardMenuItems = ({ }; }, [ quickSaveDashboard, - dashboardContainer, hasUnsavedChanges, - hasOverlays, - setFullScreenMode, isSaveInProgress, - returnToViewMode, saveDashboardAs, setIsLabsShown, + resetChanges, + hasOverlays, lastSavedId, - setViewMode, isLabsShown, showShare, - dispatch, + dashboard, clone, ]); + const resetChangesMenuItem = useMemo(() => { + return { + ...topNavStrings.resetChanges, + id: 'reset', + testId: 'dashboardDiscardChangesMenuItem', + disableButton: + !hasUnsavedChanges || + hasOverlays || + (viewMode === ViewMode.EDIT && (isSaveInProgress || !lastSavedId)), + run: () => resetChanges(), + }; + }, [hasOverlays, lastSavedId, resetChanges, viewMode, isSaveInProgress, hasUnsavedChanges]); + /** * Build ordered menus for view and edit mode. */ const viewModeTopNavConfig = useMemo(() => { const labsMenuItem = isLabsEnabled ? [menuItems.labs] : []; const shareMenuItem = share ? [menuItems.share] : []; - const writePermissionsMenuItems = showWriteControls ? [menuItems.clone, menuItems.edit] : []; - return [...labsMenuItem, menuItems.fullScreen, ...shareMenuItem, ...writePermissionsMenuItems]; - }, [menuItems, share, showWriteControls, isLabsEnabled]); + const cloneMenuItem = showWriteControls ? [menuItems.clone] : []; + const editMenuItem = showWriteControls ? [menuItems.edit] : []; + return [ + ...labsMenuItem, + menuItems.fullScreen, + ...shareMenuItem, + ...cloneMenuItem, + resetChangesMenuItem, + ...editMenuItem, + ]; + }, [menuItems, share, showWriteControls, resetChangesMenuItem, isLabsEnabled]); const editModeTopNavConfig = useMemo(() => { const labsMenuItem = isLabsEnabled ? [menuItems.labs] : []; const shareMenuItem = share ? [menuItems.share] : []; const editModeItems: TopNavMenuData[] = []; if (lastSavedId) { - editModeItems.push(menuItems.saveAs, menuItems.switchToViewMode, menuItems.quickSave); + editModeItems.push( + menuItems.saveAs, + menuItems.switchToViewMode, + resetChangesMenuItem, + menuItems.quickSave + ); } else { editModeItems.push(menuItems.switchToViewMode, menuItems.saveAs); } return [...labsMenuItem, menuItems.settings, ...shareMenuItem, ...editModeItems]; - }, [lastSavedId, menuItems, share, isLabsEnabled]); + }, [lastSavedId, menuItems, share, resetChangesMenuItem, isLabsEnabled]); return { viewModeTopNavConfig, editModeTopNavConfig }; }; diff --git a/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts b/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts index 79e6742a793c1..2cf31c0ec0e03 100644 --- a/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts +++ b/src/plugins/dashboard/public/dashboard_app/url/search_sessions_integration.ts @@ -83,7 +83,7 @@ function getLocatorParams({ const { componentState: { lastSavedId }, explicitInput: { panels, query, viewMode }, - } = container.getReduxEmbeddableTools().getState(); + } = container.getState(); return { viewMode, diff --git a/src/plugins/dashboard/public/dashboard_app/url/sync_dashboard_url_state.ts b/src/plugins/dashboard/public/dashboard_app/url/sync_dashboard_url_state.ts index cbfc7271aef41..8be9b1a35f71c 100644 --- a/src/plugins/dashboard/public/dashboard_app/url/sync_dashboard_url_state.ts +++ b/src/plugins/dashboard/public/dashboard_app/url/sync_dashboard_url_state.ts @@ -18,9 +18,9 @@ import { SavedDashboardPanel, SharedDashboardState, convertSavedPanelsToPanelMap, - DashboardContainerByValueInput, + DashboardContainerInput, } from '../../../common'; -import { DashboardContainer } from '../../dashboard_container'; +import { DashboardAPI } from '../../dashboard_container'; import { pluginServices } from '../../services/plugin_services'; import { getPanelTooOldErrorString } from '../_dashboard_app_strings'; import { DASHBOARD_STATE_STORAGE_KEY } from '../../dashboard_constants'; @@ -59,7 +59,7 @@ function getPanelsMap(appStateInUrl: SharedDashboardState): DashboardPanelMap | */ export const loadAndRemoveDashboardState = ( kbnUrlStateStorage: IKbnUrlStateStorage -): Partial => { +): Partial => { const rawAppStateInUrl = kbnUrlStateStorage.get( DASHBOARD_STATE_STORAGE_KEY ); @@ -72,7 +72,7 @@ export const loadAndRemoveDashboardState = ( return hashQuery; }); kbnUrlStateStorage.kbnUrlControls.update(nextUrl, true); - const partialState: Partial = { + const partialState: Partial = { ..._.omit(rawAppStateInUrl, ['panels', 'query']), ...(panelsMap ? { panels: panelsMap } : {}), ...(rawAppStateInUrl.query ? { query: migrateLegacyQuery(rawAppStateInUrl.query) } : {}), @@ -83,10 +83,10 @@ export const loadAndRemoveDashboardState = ( export const startSyncingDashboardUrlState = ({ kbnUrlStateStorage, - dashboardContainer, + dashboardAPI, }: { kbnUrlStateStorage: IKbnUrlStateStorage; - dashboardContainer: DashboardContainer; + dashboardAPI: DashboardAPI; }) => { const appStateSubscription = kbnUrlStateStorage .change$(DASHBOARD_STATE_STORAGE_KEY) @@ -94,7 +94,7 @@ export const startSyncingDashboardUrlState = ({ .subscribe(() => { const stateFromUrl = loadAndRemoveDashboardState(kbnUrlStateStorage); if (Object.keys(stateFromUrl).length === 0) return; - dashboardContainer.updateInput(stateFromUrl); + dashboardAPI.updateInput(stateFromUrl); }); const stopWatchingAppStateInUrl = () => appStateSubscription.unsubscribe(); diff --git a/src/plugins/dashboard/public/dashboard_constants.ts b/src/plugins/dashboard/public/dashboard_constants.ts index 7a6f7bb68cc53..0d8c72395f300 100644 --- a/src/plugins/dashboard/public/dashboard_constants.ts +++ b/src/plugins/dashboard/public/dashboard_constants.ts @@ -7,7 +7,7 @@ */ import { ViewMode } from '@kbn/embeddable-plugin/common'; -import type { DashboardContainerByValueInput } from '../common'; +import type { DashboardContainerInput } from '../common'; // ------------------------------------------------------------------ // URL Constants @@ -69,7 +69,7 @@ export const CHANGE_CHECK_DEBOUNCE = 100; // ------------------------------------------------------------------ // Default State // ------------------------------------------------------------------ -export const DEFAULT_DASHBOARD_INPUT: Omit = { +export const DEFAULT_DASHBOARD_INPUT: Omit = { viewMode: ViewMode.EDIT, // new dashboards start in edit mode. timeRestore: false, query: { query: '', language: 'kuery' }, diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/_dashboard_grid.scss b/src/plugins/dashboard/public/dashboard_container/component/grid/_dashboard_grid.scss index 7e9529a90be8b..cc96c816ce8b7 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/_dashboard_grid.scss +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/_dashboard_grid.scss @@ -36,10 +36,13 @@ } /** - * When a single panel is expanded, all the other panels are hidden in the grid. + * When a single panel is expanded, all the other panels moved offscreen. + * Shifting the rendered panels offscreen prevents a quick flash when redrawing the panels on minimize */ .dshDashboardGrid__item--hidden { - display: none; + position: absolute; + top: -9999px; + left: -9999px; } /** @@ -53,11 +56,12 @@ * 1. We need to mark this as important because react grid layout sets the width and height of the panels inline. */ .dshDashboardGrid__item--expanded { + position: absolute; height: 100% !important; /* 1 */ width: 100% !important; /* 1 */ top: 0 !important; /* 1 */ left: 0 !important; /* 1 */ - transform: translate(0, 0) !important; /* 1 */ + transform: none !important; padding: $euiSizeS; // Altered panel styles can be found in ../panel diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx index c6b128efe5570..be21a9ba6645e 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx @@ -6,17 +6,15 @@ * Side Public License, v 1. */ -// @ts-ignore import React from 'react'; -import { CONTACT_CARD_EMBEDDABLE } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { CONTACT_CARD_EMBEDDABLE } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; -import { pluginServices } from '../../../services/plugin_services'; import { DashboardGrid } from './dashboard_grid'; -import { DashboardContainer } from '../../embeddable/dashboard_container'; -import { getSampleDashboardInput } from '../../../mocks'; +import { buildMockDashboard } from '../../../mocks'; import type { Props as DashboardGridItemProps } from './dashboard_grid_item'; +import { DashboardContainerContext } from '../../embeddable/dashboard_container'; jest.mock('./dashboard_grid_item', () => { return { @@ -39,10 +37,8 @@ jest.mock('./dashboard_grid_item', () => { }; }); -const DashboardServicesProvider = pluginServices.getContextProvider(); - -async function getDashboardContainer() { - const initialInput = getSampleDashboardInput({ +const createAndMountDashboardGrid = () => { + const dashboardContainer = buildMockDashboard({ panels: { '1': { gridData: { x: 0, y: 0, w: 6, h: 6, i: '1' }, @@ -56,55 +52,29 @@ async function getDashboardContainer() { }, }, }); - const dashboardContainer = new DashboardContainer(initialInput); - await dashboardContainer.untilInitialized(); - return dashboardContainer; -} - -test('renders DashboardGrid', async () => { - const dashboardContainer = await getDashboardContainer(); - const { Wrapper: DashboardReduxWrapper } = dashboardContainer.getReduxEmbeddableTools(); - const component = mountWithIntl( - - - - - + + + ); + return { dashboardContainer, component }; +}; + +test('renders DashboardGrid', async () => { + const { component } = createAndMountDashboardGrid(); const panelElements = component.find('GridItem'); expect(panelElements.length).toBe(2); }); test('renders DashboardGrid with no visualizations', async () => { - const dashboardContainer = await getDashboardContainer(); - const { Wrapper: DashboardReduxWrapper } = dashboardContainer.getReduxEmbeddableTools(); - - const component = mountWithIntl( - - - - - - ); - + const { dashboardContainer, component } = createAndMountDashboardGrid(); dashboardContainer.updateInput({ panels: {} }); component.update(); expect(component.find('GridItem').length).toBe(0); }); test('DashboardGrid removes panel when removed from container', async () => { - const dashboardContainer = await getDashboardContainer(); - const { Wrapper: DashboardReduxWrapper } = dashboardContainer.getReduxEmbeddableTools(); - - const component = mountWithIntl( - - - - - - ); - + const { dashboardContainer, component } = createAndMountDashboardGrid(); const originalPanels = dashboardContainer.getInput().panels; const filteredPanels = { ...originalPanels }; delete filteredPanels['1']; @@ -115,17 +85,7 @@ test('DashboardGrid removes panel when removed from container', async () => { }); test('DashboardGrid renders expanded panel', async () => { - const dashboardContainer = await getDashboardContainer(); - const { Wrapper: DashboardReduxWrapper } = dashboardContainer.getReduxEmbeddableTools(); - - const component = mountWithIntl( - - - - - - ); - + const { dashboardContainer, component } = createAndMountDashboardGrid(); dashboardContainer.setExpandedPanelId('1'); component.update(); // Both panels should still exist in the dom, so nothing needs to be re-fetched once minimized. diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx index e888fb1dce4f2..0055e24685b89 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx @@ -12,30 +12,25 @@ import 'react-grid-layout/css/styles.css'; import { pick } from 'lodash'; import classNames from 'classnames'; import { useEffectOnce } from 'react-use/lib'; -import React, { useState, useMemo, useCallback } from 'react'; +import React, { useState, useMemo, useCallback, useEffect } from 'react'; import { Layout, Responsive as ResponsiveReactGridLayout } from 'react-grid-layout'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { DashboardPanelState } from '../../../../common'; import { DashboardGridItem } from './dashboard_grid_item'; -import { DASHBOARD_GRID_HEIGHT, DASHBOARD_MARGIN_SIZE } from '../../../dashboard_constants'; import { useDashboardGridSettings } from './use_dashboard_grid_settings'; -import { useDashboardContainerContext } from '../../dashboard_container_context'; +import { useDashboardContainer } from '../../embeddable/dashboard_container'; import { useDashboardPerformanceTracker } from './use_dashboard_performance_tracker'; -import { getPanelLayoutsAreEqual } from '../../embeddable/integrations/diff_state/dashboard_diffing_utils'; +import { getPanelLayoutsAreEqual } from '../../state/diffing/dashboard_diffing_utils'; +import { DASHBOARD_GRID_HEIGHT, DASHBOARD_MARGIN_SIZE } from '../../../dashboard_constants'; export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => { - const { - useEmbeddableSelector: select, - actions: { setPanels }, - useEmbeddableDispatch, - } = useDashboardContainerContext(); - const dispatch = useEmbeddableDispatch(); - const panels = select((state) => state.explicitInput.panels); - const viewMode = select((state) => state.explicitInput.viewMode); - const useMargins = select((state) => state.explicitInput.useMargins); - const expandedPanelId = select((state) => state.componentState.expandedPanelId); + const dashboard = useDashboardContainer(); + const panels = dashboard.select((state) => state.explicitInput.panels); + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); + const useMargins = dashboard.select((state) => state.explicitInput.useMargins); + const expandedPanelId = dashboard.select((state) => state.componentState.expandedPanelId); // turn off panel transform animations for the first 500ms so that the dashboard doesn't animate on its first render. const [animatePanelTransforms, setAnimatePanelTransforms] = useState(false); @@ -43,6 +38,15 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => { setTimeout(() => setAnimatePanelTransforms(true), 500); }); + useEffect(() => { + if (expandedPanelId) { + setAnimatePanelTransforms(false); + } else { + // delaying enabling CSS transforms to the next tick prevents a panel slide animation on minimize + setTimeout(() => setAnimatePanelTransforms(true), 0); + } + }, [expandedPanelId]); + const { onPanelStatusChange } = useDashboardPerformanceTracker({ panelCount: Object.keys(panels).length, }); @@ -93,17 +97,17 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => { {} as { [key: string]: DashboardPanelState } ); if (!getPanelLayoutsAreEqual(panels, updatedPanels)) { - dispatch(setPanels(updatedPanels)); + dashboard.dispatch.setPanels(updatedPanels); } }, - [dispatch, panels, setPanels] + [dashboard, panels] ); const classes = classNames({ 'dshLayout-withoutMargins': !useMargins, 'dshLayout--viewing': viewMode === ViewMode.VIEW, 'dshLayout--editing': viewMode !== ViewMode.VIEW, - 'dshLayout--noAnimation': !animatePanelTransforms, + 'dshLayout--noAnimation': !animatePanelTransforms || expandedPanelId, 'dshLayout-isMaximizedPanel': expandedPanelId !== undefined, }); diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx index edea6b0e68dc1..39ff6ebc48418 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useEffect, useLayoutEffect } from 'react'; import { EuiLoadingChart } from '@elastic/eui'; import classNames from 'classnames'; @@ -18,7 +18,7 @@ import { import { DashboardPanelState } from '../../../../common'; import { pluginServices } from '../../../services/plugin_services'; -import { useDashboardContainerContext } from '../../dashboard_container_context'; +import { useDashboardContainer } from '../../embeddable/dashboard_container'; type DivProps = Pick, 'className' | 'style' | 'children'>; @@ -55,7 +55,9 @@ const Item = React.forwardRef( const { embeddable: { EmbeddablePanel: PanelComponent }, } = pluginServices.getServices(); - const { embeddableInstance: container } = useDashboardContainerContext(); + const container = useDashboardContainer(); + const scrollToPanelId = container.select((state) => state.componentState.scrollToPanelId); + const highlightPanelId = container.select((state) => state.componentState.highlightPanelId); const expandPanel = expandedPanelId !== undefined && expandedPanelId === id; const hidePanel = expandedPanelId !== undefined && expandedPanelId !== id; @@ -66,11 +68,23 @@ const Item = React.forwardRef( printViewport__vis: container.getInput().viewMode === ViewMode.PRINT, }); + useLayoutEffect(() => { + if (typeof ref !== 'function' && ref?.current) { + if (scrollToPanelId === id) { + container.scrollToPanel(ref.current); + } + if (highlightPanelId === id) { + container.highlightPanel(ref.current); + } + } + }, [id, container, scrollToPanelId, highlightPanelId, ref]); + return (
@@ -134,9 +148,9 @@ export const DashboardGridItem = React.forwardRef((props, settings: { isProjectEnabledInLabs }, } = pluginServices.getServices(); - const { useEmbeddableSelector: select } = useDashboardContainerContext(); + const dashboard = useDashboardContainer(); - const isPrintMode = select((state) => state.explicitInput.viewMode) === ViewMode.PRINT; + const isPrintMode = dashboard.select((state) => state.explicitInput.viewMode) === ViewMode.PRINT; const isEnabled = !isPrintMode && isProjectEnabledInLabs('labs:dashboard:deferBelowFold'); return isEnabled ? : ; diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_grid_settings.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_grid_settings.tsx index 1fb684b236b8a..58d0f45c6a312 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_grid_settings.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_grid_settings.tsx @@ -12,14 +12,14 @@ import { useEuiTheme } from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { DASHBOARD_GRID_COLUMN_COUNT } from '../../../dashboard_constants'; -import { useDashboardContainerContext } from '../../dashboard_container_context'; +import { useDashboardContainer } from '../../embeddable/dashboard_container'; export const useDashboardGridSettings = (panelsInOrder: string[]) => { - const { useEmbeddableSelector: select } = useDashboardContainerContext(); + const dashboard = useDashboardContainer(); const { euiTheme } = useEuiTheme(); - const panels = select((state) => state.explicitInput.panels); - const viewMode = select((state) => state.explicitInput.viewMode); + const panels = dashboard.select((state) => state.explicitInput.panels); + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); const layouts = useMemo(() => { return { diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_performance_tracker.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_performance_tracker.tsx index af0312572d7c5..f8b04e70861e9 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_performance_tracker.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/use_dashboard_performance_tracker.tsx @@ -10,7 +10,7 @@ import { useCallback, useRef } from 'react'; import { EmbeddablePhaseEvent } from '@kbn/embeddable-plugin/public'; -import { useDashboardContainerContext } from '../../dashboard_container_context'; +import { useDashboardContainer } from '../../embeddable/dashboard_container'; import { DashboardLoadedEventStatus, DashboardRenderPerformanceStats } from '../../types'; type DashboardRenderPerformanceTracker = DashboardRenderPerformanceStats & { @@ -30,7 +30,7 @@ const getDefaultPerformanceTracker: () => DashboardRenderPerformanceTracker = () }); export const useDashboardPerformanceTracker = ({ panelCount }: { panelCount: number }) => { - const { embeddableInstance: dashboardContainer } = useDashboardContainerContext(); + const dashboard = useDashboardContainer(); // reset performance tracker on each render. const performanceRefs = useRef(getDefaultPerformanceTracker()); @@ -52,11 +52,11 @@ export const useDashboardPerformanceTracker = ({ panelCount }: { panelCount: num performanceRefs.current.doneCount++; if (performanceRefs.current.doneCount === panelCount) { performanceRefs.current.panelsRenderDoneTime = performance.now(); - dashboardContainer.reportPerformanceMetrics(performanceRefs.current); + dashboard.reportPerformanceMetrics(performanceRefs.current); } } }, - [dashboardContainer, panelCount] + [dashboard, panelCount] ); return { onPanelStatusChange }; diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel/_dashboard_panel.scss b/src/plugins/dashboard/public/dashboard_container/component/panel/_dashboard_panel.scss index f04e5e29d960b..f8715220ddf37 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel/_dashboard_panel.scss +++ b/src/plugins/dashboard/public/dashboard_container/component/panel/_dashboard_panel.scss @@ -11,6 +11,10 @@ box-shadow: none; border-radius: 0; } + + .dshDashboardGrid__item--highlighted { + border-radius: 0; + } } // Remove border color unless in editing mode @@ -25,3 +29,24 @@ cursor: default; } } + +@keyframes highlightOutline { + 0% { + outline: solid $euiSizeXS transparentize($euiColorSuccess, 1); + } + 25% { + outline: solid $euiSizeXS transparentize($euiColorSuccess, .5); + } + 100% { + outline: solid $euiSizeXS transparentize($euiColorSuccess, 1); + } +} + +.dshDashboardGrid__item--highlighted { + border-radius: $euiSizeXS; + animation-name: highlightOutline; + animation-duration: 4s; + animation-timing-function: ease-out; + // keeps outline from getting cut off by other panels without margins + z-index: 999 !important; +} diff --git a/src/plugins/dashboard/public/dashboard_container/component/panel/dashboard_panel_placement.ts b/src/plugins/dashboard/public/dashboard_container/component/panel/dashboard_panel_placement.ts index 77b51874319ba..e570e1eadd6ca 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/panel/dashboard_panel_placement.ts +++ b/src/plugins/dashboard/public/dashboard_container/component/panel/dashboard_panel_placement.ts @@ -24,6 +24,7 @@ export interface IPanelPlacementArgs { width: number; height: number; currentPanels: { [key: string]: DashboardPanelState }; + scrollToPanel?: boolean; } export interface IPanelPlacementBesideArgs extends IPanelPlacementArgs { diff --git a/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx b/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx index d7cd61cdd7f7c..881f245ee7733 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx @@ -26,9 +26,9 @@ import { EuiSwitch, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DashboardContainerByValueInput } from '../../../../common'; +import { DashboardContainerInput } from '../../../../common'; import { pluginServices } from '../../../services/plugin_services'; -import { useDashboardContainerContext } from '../../dashboard_container_context'; +import { useDashboardContainer } from '../../embeddable/dashboard_container'; interface DashboardSettingsProps { onClose: () => void; @@ -42,23 +42,18 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => { dashboardSavedObject: { checkForDuplicateDashboardTitle }, } = pluginServices.getServices(); - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setStateFromSettingsFlyout }, - embeddableInstance: dashboardContainer, - } = useDashboardContainerContext(); + const dashboard = useDashboardContainer(); const [dashboardSettingsState, setDashboardSettingsState] = useState({ - ...dashboardContainer.getInputAsValueType(), + ...dashboard.getInput(), }); const [isTitleDuplicate, setIsTitleDuplicate] = useState(false); const [isTitleDuplicateConfirmed, setIsTitleDuplicateConfirmed] = useState(false); const [isApplying, setIsApplying] = useState(false); - const lastSavedId = select((state) => state.componentState.lastSavedId); - const lastSavedTitle = select((state) => state.explicitInput.title); + const lastSavedId = dashboard.select((state) => state.componentState.lastSavedId); + const lastSavedTitle = dashboard.select((state) => state.explicitInput.title); const isMounted = useMountedState(); @@ -83,24 +78,19 @@ export const DashboardSettings = ({ onClose }: DashboardSettingsProps) => { setIsApplying(false); if (validTitle) { - dispatch(setStateFromSettingsFlyout({ lastSavedId, ...dashboardSettingsState })); + dashboard.dispatch.setStateFromSettingsFlyout({ lastSavedId, ...dashboardSettingsState }); onClose(); } }; - const updateDashboardSetting = useCallback( - (newSettings: Partial) => { - setDashboardSettingsState((prevDashboardSettingsState) => { - return { - ...prevDashboardSettingsState, - ...newSettings, - }; - }); - }, - [] - ); - - const dispatch = useEmbeddableDispatch(); + const updateDashboardSetting = useCallback((newSettings: Partial) => { + setDashboardSettingsState((prevDashboardSettingsState) => { + return { + ...prevDashboardSettingsState, + ...newSettings, + }; + }); + }, []); const renderDuplicateTitleCallout = () => { if (!isTitleDuplicate) { diff --git a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx index c5919462765d6..07bb9d111cd07 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx @@ -17,8 +17,8 @@ import { ExitFullScreenButton } from '@kbn/shared-ux-button-exit-full-screen'; import { DashboardGrid } from '../grid'; import { pluginServices } from '../../../services/plugin_services'; +import { useDashboardContainer } from '../../embeddable/dashboard_container'; import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen'; -import { useDashboardContainerContext } from '../../dashboard_container_context'; export const useDebouncedWidthObserver = (wait = 250) => { const [width, setWidth] = useState(0); @@ -38,26 +38,25 @@ export const DashboardViewportComponent = () => { } = pluginServices.getServices(); const controlsRoot = useRef(null); - const { useEmbeddableSelector: select, embeddableInstance: dashboardContainer } = - useDashboardContainerContext(); + const dashboard = useDashboardContainer(); /** * Render Control group */ - const controlGroup = dashboardContainer.controlGroup; + const controlGroup = dashboard.controlGroup; useEffect(() => { if (controlGroup && controlsRoot.current) controlGroup.render(controlsRoot.current); }, [controlGroup]); - const panelCount = Object.keys(select((state) => state.explicitInput.panels)).length; + const panelCount = Object.keys(dashboard.select((state) => state.explicitInput.panels)).length; const controlCount = Object.keys( - select((state) => state.explicitInput.controlGroupInput?.panels) ?? {} + dashboard.select((state) => state.explicitInput.controlGroupInput?.panels) ?? {} ).length; - const viewMode = select((state) => state.explicitInput.viewMode); - const dashboardTitle = select((state) => state.explicitInput.title); - const description = select((state) => state.explicitInput.description); - const expandedPanelId = select((state) => state.componentState.expandedPanelId); + const viewMode = dashboard.select((state) => state.explicitInput.viewMode); + const dashboardTitle = dashboard.select((state) => state.explicitInput.title); + const description = dashboard.select((state) => state.explicitInput.description); + const expandedPanelId = dashboard.select((state) => state.componentState.expandedPanelId); const controlsEnabled = isProjectEnabledInLabs('labs:dashboard:dashboardControls'); const { ref: resizeRef, width: viewportWidth } = useDebouncedWidthObserver(); @@ -98,15 +97,12 @@ export const DashboardViewportComponent = () => { // because ExitFullScreenButton sets isFullscreenMode to false on unmount while rerendering. // This specifically fixed maximizing/minimizing panels without exiting fullscreen mode. const WithFullScreenButton = ({ children }: { children: JSX.Element }) => { - const { - useEmbeddableDispatch, - useEmbeddableSelector: select, - actions: { setFullScreenMode }, - } = useDashboardContainerContext(); - const dispatch = useEmbeddableDispatch(); + const dashboard = useDashboardContainer(); - const isFullScreenMode = select((state) => state.componentState.fullScreenMode); - const isEmbeddedExternally = select((state) => state.componentState.isEmbeddedExternally); + const isFullScreenMode = dashboard.select((state) => state.componentState.fullScreenMode); + const isEmbeddedExternally = dashboard.select( + (state) => state.componentState.isEmbeddedExternally + ); return ( <> @@ -114,7 +110,7 @@ const WithFullScreenButton = ({ children }: { children: JSX.Element }) => { {isFullScreenMode && ( dispatch(setFullScreenMode(false))} + onExit={() => dashboard.dispatch.setFullScreenMode(false)} toggleChrome={!isEmbeddedExternally} /> diff --git a/src/plugins/dashboard/public/dashboard_container/dashboard_container_context.tsx b/src/plugins/dashboard/public/dashboard_container/dashboard_container_context.tsx deleted file mode 100644 index b3e498dba36ef..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/dashboard_container_context.tsx +++ /dev/null @@ -1,19 +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 { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { dashboardContainerReducers } from './state/dashboard_container_reducers'; -import { DashboardReduxState } from './types'; -import { DashboardContainer } from '..'; - -export const useDashboardContainerContext = () => - useReduxEmbeddableContext< - DashboardReduxState, - typeof dashboardContainerReducers, - DashboardContainer - >(); diff --git a/src/plugins/dashboard/public/dashboard_container/dashboard_container_renderer.tsx b/src/plugins/dashboard/public/dashboard_container/dashboard_container_renderer.tsx deleted file mode 100644 index 1c79a1e0977f4..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/dashboard_container_renderer.tsx +++ /dev/null @@ -1,123 +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 './_dashboard_container.scss'; - -import { v4 as uuidv4 } from 'uuid'; -import classNames from 'classnames'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import useObservable from 'react-use/lib/useObservable'; - -import { EuiLoadingElastic, EuiLoadingSpinner } from '@elastic/eui'; - -import { - DashboardContainerFactory, - DashboardContainerFactoryDefinition, - DashboardCreationOptions, -} from './embeddable/dashboard_container_factory'; -import { DASHBOARD_CONTAINER_TYPE } from '..'; -import { pluginServices } from '../services/plugin_services'; -import { DEFAULT_DASHBOARD_INPUT } from '../dashboard_constants'; -import { DashboardContainer } from './embeddable/dashboard_container'; - -export interface DashboardContainerRendererProps { - savedObjectId?: string; - getCreationOptions?: () => Promise; - onDashboardContainerLoaded?: (dashboardContainer: DashboardContainer) => void; -} - -export const DashboardContainerRenderer = ({ - savedObjectId, - getCreationOptions, - onDashboardContainerLoaded, -}: DashboardContainerRendererProps) => { - const { - embeddable, - screenshotMode: { isScreenshotMode }, - customBranding, - } = pluginServices.getServices(); - - const dashboardRoot = useRef(null); - const [dashboardIdToBuild, setDashboardIdToBuild] = useState(savedObjectId); - const [dashboardContainer, setDashboardContainer] = useState(); - const [loading, setLoading] = useState(true); - const showPlainSpinner = useObservable(customBranding.hasCustomBranding$, false); - - useEffect(() => { - // check if dashboard container is expecting id change... if not, update dashboardIdToBuild to force it to rebuild the container. - if (!dashboardContainer) return; - if (!dashboardContainer.isExpectingIdChange()) setDashboardIdToBuild(savedObjectId); - - // Disabling exhaustive deps because this useEffect should only be triggered when the savedObjectId changes. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [savedObjectId]); - - const id = useMemo(() => uuidv4(), []); - - useEffect(() => { - let canceled = false; - let destroyContainer: () => void; - - (async () => { - const creationOptions = await getCreationOptions?.(); - const dashboardFactory = embeddable.getEmbeddableFactory( - DASHBOARD_CONTAINER_TYPE - ) as DashboardContainerFactory & { create: DashboardContainerFactoryDefinition['create'] }; - const container = (await dashboardFactory?.create( - { - id, - ...DEFAULT_DASHBOARD_INPUT, - ...creationOptions?.initialInput, - savedObjectId: dashboardIdToBuild, - }, - undefined, - creationOptions - )) as DashboardContainer; - - await container.untilInitialized(); - if (canceled) { - container.destroy(); - return; - } - - setLoading(false); - if (dashboardRoot.current) { - container.render(dashboardRoot.current); - } - onDashboardContainerLoaded?.(container); - setDashboardContainer(container); - - destroyContainer = () => container.destroy(); - })(); - return () => { - canceled = true; - destroyContainer?.(); - }; - // Disabling exhaustive deps because embeddable should only be created when the dashboardIdToBuild changes. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dashboardIdToBuild]); - - const viewportClasses = classNames( - 'dashboardViewport', - { 'dashboardViewport--screenshotMode': isScreenshotMode() }, - { 'dashboardViewport--loading': loading } - ); - - const loadingSpinner = showPlainSpinner ? ( - - ) : ( - - ); - return ( -
{loading ? loadingSpinner :
}
- ); -}; - -// required for dynamic import using React.lazy() -// eslint-disable-next-line import/no-default-export -export default DashboardContainerRenderer; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/add_panel_from_library.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/add_panel_from_library.ts index ef4f4dc7ea5c9..c708937e3d56e 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/add_panel_from_library.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/add_panel_from_library.ts @@ -41,6 +41,10 @@ export function addFromLibrary(this: DashboardContainer) { notifications, overlays, theme, + onAddPanel: (id: string) => { + this.setScrollToPanelId(id); + this.setHighlightPanelId(id); + }, }) ); } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts index ee57970a93cd4..7b02001a93c6c 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts @@ -86,11 +86,7 @@ export async function replacePanel( }; } - await this.updateInput({ - panels, - lastReloadRequestTime: new Date().getTime(), - }); - + await this.updateInput({ panels }); return panelId; } @@ -132,7 +128,12 @@ export function showPlaceholderUntil newStateComplete) - .then((newPanelState: Partial) => - this.replacePanel(placeholderPanelState, newPanelState) - ); + .then(async (newPanelState: Partial) => { + const panelId = await this.replacePanel(placeholderPanelState, newPanelState); + + if (placementArgs?.scrollToPanel) { + this.setScrollToPanelId(panelId); + this.setHighlightPanelId(panelId); + } + }); } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx index 94f4c8fdfa58e..d3dea0d250036 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx @@ -17,7 +17,7 @@ import { DashboardSaveModal } from './overlays/save_modal'; import { DashboardContainer } from '../dashboard_container'; import { showCloneModal } from './overlays/show_clone_modal'; import { pluginServices } from '../../../services/plugin_services'; -import { DashboardContainerByValueInput } from '../../../../common'; +import { DashboardContainerInput } from '../../../../common'; import { SaveDashboardReturn } from '../../../services/dashboard_saved_object/types'; export function runSaveAs(this: DashboardContainer) { @@ -31,15 +31,10 @@ export function runSaveAs(this: DashboardContainer) { dashboardSavedObject: { checkForDuplicateDashboardTitle, saveDashboardStateToSavedObject }, } = pluginServices.getServices(); - const { - getState, - dispatch, - actions: { setStateFromSaveModal, setLastSavedInput }, - } = this.getReduxEmbeddableTools(); const { explicitInput: currentState, componentState: { lastSavedId }, - } = getState(); + } = this.getState(); return new Promise((resolve) => { const onSave = async ({ @@ -81,7 +76,7 @@ export function runSaveAs(this: DashboardContainer) { // do not save if title is duplicate and is unconfirmed return {}; } - const stateToSave: DashboardContainerByValueInput = { + const stateToSave: DashboardContainerInput = { ...currentState, ...stateFromSaveModal, }; @@ -103,8 +98,8 @@ export function runSaveAs(this: DashboardContainer) { stateFromSaveModal.lastSavedId = saveResult.id; if (saveResult.id) { batch(() => { - dispatch(setStateFromSaveModal(stateFromSaveModal)); - dispatch(setLastSavedInput(stateToSave)); + this.dispatch.setStateFromSaveModal(stateFromSaveModal); + this.dispatch.setLastSavedInput(stateToSave); }); } if (newCopyOnSave || !lastSavedId) this.expectIdChange(); @@ -136,22 +131,17 @@ export async function runQuickSave(this: DashboardContainer) { dashboardSavedObject: { saveDashboardStateToSavedObject }, } = pluginServices.getServices(); - const { - getState, - dispatch, - actions: { setLastSavedInput }, - } = this.getReduxEmbeddableTools(); const { explicitInput: currentState, componentState: { lastSavedId }, - } = getState(); + } = this.getState(); const saveResult = await saveDashboardStateToSavedObject({ lastSavedId, currentState, saveOptions: {}, }); - dispatch(setLastSavedInput(currentState)); + this.dispatch.setLastSavedInput(currentState); return saveResult; } @@ -161,12 +151,7 @@ export async function runClone(this: DashboardContainer) { dashboardSavedObject: { saveDashboardStateToSavedObject, checkForDuplicateDashboardTitle }, } = pluginServices.getServices(); - const { - getState, - dispatch, - actions: { setTitle }, - } = this.getReduxEmbeddableTools(); - const { explicitInput: currentState } = getState(); + const { explicitInput: currentState } = this.getState(); return new Promise((resolve) => { const onClone = async ( @@ -191,7 +176,7 @@ export async function runClone(this: DashboardContainer) { currentState: { ...currentState, title: newTitle }, }); - dispatch(setTitle(newTitle)); + this.dispatch.setTitle(newTitle); resolve(saveResult); this.expectIdChange(); return saveResult.id ? { id: saveResult.id } : { error: saveResult.error }; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/show_settings.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/show_settings.tsx index 266c0a414b5b9..56798f5770dd1 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/show_settings.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/show_settings.tsx @@ -10,9 +10,9 @@ import React from 'react'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { DashboardSettings } from '../../component/settings/settings_flyout'; -import { DashboardContainer } from '../dashboard_container'; import { pluginServices } from '../../../services/plugin_services'; +import { DashboardSettings } from '../../component/settings/settings_flyout'; +import { DashboardContainer, DashboardContainerContext } from '../dashboard_container'; export function showSettings(this: DashboardContainer) { const { @@ -22,26 +22,20 @@ export function showSettings(this: DashboardContainer) { overlays, } = pluginServices.getServices(); - const { - dispatch, - Wrapper: DashboardReduxWrapper, - actions: { setHasOverlays }, - } = this.getReduxEmbeddableTools(); - // TODO Move this action into DashboardContainer.openOverlay - dispatch(setHasOverlays(true)); + this.dispatch.setHasOverlays(true); this.openOverlay( overlays.openFlyout( toMountPoint( - + { - dispatch(setHasOverlays(false)); + this.dispatch.setHasOverlays(false); this.clearOverlays(); }} /> - , + , { theme$ } ), { @@ -49,7 +43,7 @@ export function showSettings(this: DashboardContainer) { 'data-test-subj': 'dashboardSettingsFlyout', onClose: (flyout) => { this.clearOverlays(); - dispatch(setHasOverlays(false)); + this.dispatch.setHasOverlays(false); flyout.close(); }, } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/controls/dashboard_control_group_integration.test.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/controls/dashboard_control_group_integration.test.ts similarity index 95% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/controls/dashboard_control_group_integration.test.ts rename to src/plugins/dashboard/public/dashboard_container/embeddable/create/controls/dashboard_control_group_integration.test.ts index ae8102318e36c..148c409e8d702 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/controls/dashboard_control_group_integration.test.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/controls/dashboard_control_group_integration.test.ts @@ -9,7 +9,7 @@ import { mockControlGroupInput } from '@kbn/controls-plugin/common/mocks'; import { ControlGroupContainer } from '@kbn/controls-plugin/public/control_group/embeddable/control_group_container'; import { Filter } from '@kbn/es-query'; -import { ReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import { combineDashboardFiltersWithControlGroupFilters } from './dashboard_control_group_integration'; jest.mock('@kbn/controls-plugin/public/control_group/embeddable/control_group_container'); @@ -52,7 +52,7 @@ const testFilter3: Filter = { }; const mockControlGroupContainer = new ControlGroupContainer( - { getTools: () => {} } as unknown as ReduxEmbeddablePackage, + { getTools: () => {} } as unknown as ReduxToolsPackage, mockControlGroupInput() ); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/controls/dashboard_control_group_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/controls/dashboard_control_group_integration.ts similarity index 61% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/controls/dashboard_control_group_integration.ts rename to src/plugins/dashboard/public/dashboard_container/embeddable/create/controls/dashboard_control_group_integration.ts index 2f0f8626528b9..4201da0bf6996 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/controls/dashboard_control_group_integration.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/controls/dashboard_control_group_integration.ts @@ -6,24 +6,21 @@ * Side Public License, v 1. */ -import _, { identity, pickBy } from 'lodash'; -import { Observable, Subscription } from 'rxjs'; +import { isEqual } from 'lodash'; +import { Observable } from 'rxjs'; import deepEqual from 'fast-deep-equal'; import { compareFilters, COMPARE_ALL_OPTIONS, type Filter } from '@kbn/es-query'; import { debounceTime, distinctUntilChanged, distinctUntilKeyChanged, skip } from 'rxjs/operators'; import { ControlGroupInput, - CONTROL_GROUP_TYPE, getDefaultControlGroupInput, persistableControlGroupInputIsEqual, } from '@kbn/controls-plugin/common'; -import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; -import { ControlGroupContainer, ControlGroupOutput } from '@kbn/controls-plugin/public'; +import { ControlGroupContainer } from '@kbn/controls-plugin/public'; import { DashboardContainer } from '../../dashboard_container'; -import { pluginServices } from '../../../../services/plugin_services'; -import { DashboardContainerByValueInput } from '../../../../../common'; +import { DashboardContainerInput } from '../../../../../common'; interface DiffChecks { [key: string]: (a?: unknown, b?: unknown) => boolean; @@ -35,61 +32,16 @@ const distinctUntilDiffCheck = (a: T, b: T, diffChecks: DiffChecks .includes(false); type DashboardControlGroupCommonKeys = keyof Pick< - DashboardContainerByValueInput | ControlGroupInput, + DashboardContainerInput | ControlGroupInput, 'filters' | 'lastReloadRequestTime' | 'timeRange' | 'query' >; -export async function startControlGroupIntegration( - this: DashboardContainer, - initialInput: DashboardContainerByValueInput -): Promise { - const { - embeddable: { getEmbeddableFactory }, - } = pluginServices.getServices(); - const controlsGroupFactory = getEmbeddableFactory< - ControlGroupInput, - ControlGroupOutput, - ControlGroupContainer - >(CONTROL_GROUP_TYPE); - const { filters, query, timeRange, viewMode, controlGroupInput, id } = initialInput; - const controlGroup = await controlsGroupFactory?.create({ - id: `control_group_${id ?? 'new_dashboard'}`, - ...getDefaultControlGroupInput(), - ...pickBy(controlGroupInput, identity), // undefined keys in initialInput should not overwrite defaults - timeRange, - viewMode, - filters, - query, - }); - if (!controlGroup || isErrorEmbeddable(controlGroup)) { - return; - } - - this.untilInitialized().then(() => { - const stopSyncingControlGroup = - startSyncingDashboardControlGroup.bind(this)()?.stopSyncingWithControlGroup; - this.onDestroyControlGroup = () => { - stopSyncingControlGroup?.(); - this.controlGroup?.destroy(); - }; - }); - await controlGroup.untilInitialized(); - return controlGroup; -} - -function startSyncingDashboardControlGroup(this: DashboardContainer) { +export function startSyncingDashboardControlGroup(this: DashboardContainer) { if (!this.controlGroup) return; - const subscriptions = new Subscription(); - - const { - actions: { setControlGroupState }, - dispatch, - } = this.getReduxEmbeddableTools(); - const isControlGroupInputEqual = () => persistableControlGroupInputIsEqual( this.controlGroup!.getInput(), - this.getInputAsValueType().controlGroupInput + this.getInput().controlGroupInput ); // Because dashboard container stores control group state, certain control group changes need to be passed up dashboard container @@ -99,7 +51,7 @@ function startSyncingDashboardControlGroup(this: DashboardContainer) { chainingSystem: deepEqual, ignoreParentSettings: deepEqual, }; - subscriptions.add( + this.subscriptions.add( this.controlGroup .getInput$() .pipe( @@ -111,9 +63,12 @@ function startSyncingDashboardControlGroup(this: DashboardContainer) { const { panels, controlStyle, chainingSystem, ignoreParentSettings } = this.controlGroup!.getInput(); if (!isControlGroupInputEqual()) { - dispatch( - setControlGroupState({ panels, controlStyle, chainingSystem, ignoreParentSettings }) - ); + this.dispatch.setControlGroupState({ + panels, + controlStyle, + chainingSystem, + ignoreParentSettings, + }); } }) ); @@ -129,23 +84,20 @@ function startSyncingDashboardControlGroup(this: DashboardContainer) { }; // pass down any pieces of input needed to refetch or force refetch data for the controls - subscriptions.add( - (this.getInput$() as Readonly>) + this.subscriptions.add( + (this.getInput$() as Readonly>) .pipe( distinctUntilChanged((a, b) => - distinctUntilDiffCheck(a, b, dashboardRefetchDiff) + distinctUntilDiffCheck(a, b, dashboardRefetchDiff) ) ) .subscribe(() => { const newInput: { [key: string]: unknown } = {}; (Object.keys(dashboardRefetchDiff) as DashboardControlGroupCommonKeys[]).forEach((key) => { if ( - !dashboardRefetchDiff[key]?.( - this.getInputAsValueType()[key], - this.controlGroup!.getInput()[key] - ) + !dashboardRefetchDiff[key]?.(this.getInput()[key], this.controlGroup!.getInput()[key]) ) { - newInput[key] = this.getInputAsValueType()[key]; + newInput[key] = this.getInput()[key]; } }); if (Object.keys(newInput).length > 0) { @@ -155,24 +107,24 @@ function startSyncingDashboardControlGroup(this: DashboardContainer) { ); // dashboard may reset the control group input when discarding changes. Subscribe to these changes and update accordingly - subscriptions.add( - (this.getInput$() as Readonly>) + this.subscriptions.add( + (this.getInput$() as Readonly>) .pipe(debounceTime(10), distinctUntilKeyChanged('controlGroupInput')) .subscribe(() => { if (!isControlGroupInputEqual()) { - if (!this.getInputAsValueType().controlGroupInput) { + if (!this.getInput().controlGroupInput) { this.controlGroup!.updateInput(getDefaultControlGroupInput()); return; } this.controlGroup!.updateInput({ - ...this.getInputAsValueType().controlGroupInput, + ...this.getInput().controlGroupInput, }); } }) ); // when control group outputs filters, force a refresh! - subscriptions.add( + this.subscriptions.add( this.controlGroup .getOutput$() .pipe( @@ -184,23 +136,23 @@ function startSyncingDashboardControlGroup(this: DashboardContainer) { .subscribe(() => this.forceRefresh(false)) // we should not reload the control group when the control group output changes - otherwise, performance is severely impacted ); - subscriptions.add( + this.subscriptions.add( this.controlGroup .getOutput$() .pipe( distinctUntilChanged(({ timeslice: timesliceA }, { timeslice: timesliceB }) => - _.isEqual(timesliceA, timesliceB) + isEqual(timesliceA, timesliceB) ) ) .subscribe(({ timeslice }) => { - if (!_.isEqual(timeslice, this.getInputAsValueType().timeslice)) { - this.updateInput({ timeslice }); + if (!isEqual(timeslice, this.getInput().timeslice)) { + this.dispatch.setTimeslice(timeslice); } }) ); // the Control Group needs to know when any dashboard children are loading in order to know when to move on to the next time slice when playing. - subscriptions.add( + this.subscriptions.add( this.getAnyChildOutputChange$().subscribe(() => { if (!this.controlGroup) { return; @@ -216,12 +168,6 @@ function startSyncingDashboardControlGroup(this: DashboardContainer) { this.controlGroup.anyControlOutputConsumerLoading$.next(false); }) ); - - return { - stopSyncingWithControlGroup: () => { - subscriptions.unsubscribe(); - }, - }; } export const combineDashboardFiltersWithControlGroupFilters = ( diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts new file mode 100644 index 0000000000000..6f9f40745322b --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.test.ts @@ -0,0 +1,255 @@ +/* + * 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 { + ContactCardEmbeddableInput, + CONTACT_CARD_EMBEDDABLE, +} from '@kbn/embeddable-plugin/public/lib/test_samples'; +import { + ControlGroupInput, + ControlGroupContainer, + ControlGroupContainerFactory, +} from '@kbn/controls-plugin/public'; +import { Filter } from '@kbn/es-query'; +import { EmbeddablePackageState } from '@kbn/embeddable-plugin/public'; +import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; + +import { createDashboard } from './create_dashboard'; +import { getSampleDashboardPanel } from '../../../mocks'; +import { pluginServices } from '../../../services/plugin_services'; +import { DashboardCreationOptions } from '../dashboard_container_factory'; +import { Observable } from 'rxjs'; + +const embeddableId = 'create-dat-dashboard'; + +test('throws error when no data views are available', async () => { + pluginServices.getServices().data.dataViews.getDefaultDataView = jest + .fn() + .mockReturnValue(undefined); + await expect(async () => { + await createDashboard(embeddableId); + }).rejects.toThrow('Dashboard requires at least one data view before it can be initialized.'); + + // reset get default data view + pluginServices.getServices().data.dataViews.getDefaultDataView = jest.fn().mockResolvedValue({}); +}); + +test('throws error when provided validation function returns invalid', async () => { + const creationOptions: DashboardCreationOptions = { + validateLoadedSavedObject: jest.fn().mockImplementation(() => false), + }; + await expect(async () => { + await createDashboard(embeddableId, creationOptions, 0, 'test-id'); + }).rejects.toThrow('Dashboard failed saved object result validation'); +}); + +test('pulls state from dashboard saved object when given a saved object id', async () => { + pluginServices.getServices().dashboardSavedObject.loadDashboardStateFromSavedObject = jest + .fn() + .mockResolvedValue({ dashboardInput: { description: 'wow would you look at that? Wow.' } }); + const dashboard = await createDashboard(embeddableId, {}, 0, 'wow-such-id'); + expect( + pluginServices.getServices().dashboardSavedObject.loadDashboardStateFromSavedObject + ).toHaveBeenCalledWith({ id: 'wow-such-id' }); + expect(dashboard.getState().explicitInput.description).toBe('wow would you look at that? Wow.'); +}); + +test('pulls state from session storage which overrides state from saved object', async () => { + pluginServices.getServices().dashboardSavedObject.loadDashboardStateFromSavedObject = jest + .fn() + .mockResolvedValue({ dashboardInput: { description: 'wow this description is okay' } }); + pluginServices.getServices().dashboardSessionStorage.getState = jest + .fn() + .mockReturnValue({ description: 'wow this description marginally better' }); + const dashboard = await createDashboard( + embeddableId, + { useSessionStorageIntegration: true }, + 0, + 'wow-such-id' + ); + expect(dashboard.getState().explicitInput.description).toBe( + 'wow this description marginally better' + ); +}); + +test('pulls state from creation options initial input which overrides all other state sources', async () => { + pluginServices.getServices().dashboardSavedObject.loadDashboardStateFromSavedObject = jest + .fn() + .mockResolvedValue({ dashboardInput: { description: 'wow this description is okay' } }); + pluginServices.getServices().dashboardSessionStorage.getState = jest + .fn() + .mockReturnValue({ description: 'wow this description marginally better' }); + const dashboard = await createDashboard( + embeddableId, + { + useSessionStorageIntegration: true, + initialInput: { description: 'wow this description is a masterpiece' }, + }, + 0, + 'wow-such-id' + ); + expect(dashboard.getState().explicitInput.description).toBe( + 'wow this description is a masterpiece' + ); +}); + +test('applies filters and query from state to query service', async () => { + const filters: Filter[] = [ + { meta: { alias: 'test', disabled: false, negate: false, index: 'test' } }, + ]; + const query = { language: 'kql', query: 'query' }; + await createDashboard(embeddableId, { + useUnifiedSearchIntegration: true, + unifiedSearchSettings: { + kbnUrlStateStorage: createKbnUrlStateStorage(), + }, + initialInput: { filters, query }, + }); + expect(pluginServices.getServices().data.query.queryString.setQuery).toHaveBeenCalledWith(query); + expect(pluginServices.getServices().data.query.filterManager.setAppFilters).toHaveBeenCalledWith( + filters + ); +}); + +test('applies time range and refresh interval from initial input to query service if time restore is on', async () => { + const timeRange = { from: new Date().toISOString(), to: new Date().toISOString() }; + const refreshInterval = { pause: false, value: 42 }; + await createDashboard(embeddableId, { + useUnifiedSearchIntegration: true, + unifiedSearchSettings: { + kbnUrlStateStorage: createKbnUrlStateStorage(), + }, + initialInput: { timeRange, refreshInterval, timeRestore: true }, + }); + expect( + pluginServices.getServices().data.query.timefilter.timefilter.setTime + ).toHaveBeenCalledWith(timeRange); + expect( + pluginServices.getServices().data.query.timefilter.timefilter.setRefreshInterval + ).toHaveBeenCalledWith(refreshInterval); +}); + +test('applied time range from query service to initial input if time restore is off', async () => { + const timeRange = { from: new Date().toISOString(), to: new Date().toISOString() }; + pluginServices.getServices().data.query.timefilter.timefilter.getTime = jest + .fn() + .mockReturnValue(timeRange); + const dashboard = await createDashboard(embeddableId, { + useUnifiedSearchIntegration: true, + unifiedSearchSettings: { + kbnUrlStateStorage: createKbnUrlStateStorage(), + }, + }); + expect(dashboard.getState().explicitInput.timeRange).toEqual(timeRange); +}); + +test('replaces panel with incoming embeddable if id matches existing panel', async () => { + const incomingEmbeddable: EmbeddablePackageState = { + type: CONTACT_CARD_EMBEDDABLE, + input: { + id: 'i_match', + firstName: 'wow look at this replacement wow', + } as ContactCardEmbeddableInput, + embeddableId: 'i_match', + }; + const dashboard = await createDashboard(embeddableId, { + incomingEmbeddable, + initialInput: { + panels: { + i_match: getSampleDashboardPanel({ + explicitInput: { + id: 'i_match', + firstName: 'oh no, I am about to get replaced', + }, + type: CONTACT_CARD_EMBEDDABLE, + }), + }, + }, + }); + expect(dashboard.getState().explicitInput.panels.i_match.explicitInput).toStrictEqual( + expect.objectContaining({ + id: 'i_match', + firstName: 'wow look at this replacement wow', + }) + ); +}); + +test('creates new embeddable with incoming embeddable if id does not match existing panel', async () => { + const incomingEmbeddable: EmbeddablePackageState = { + type: CONTACT_CARD_EMBEDDABLE, + input: { + id: 'i_match', + firstName: 'wow look at this new panel wow', + } as ContactCardEmbeddableInput, + embeddableId: 'i_match', + }; + const mockContactCardFactory = { + create: jest.fn().mockReturnValue({ destroy: jest.fn() }), + getDefaultInput: jest.fn().mockResolvedValue({}), + }; + pluginServices.getServices().embeddable.getEmbeddableFactory = jest + .fn() + .mockReturnValue(mockContactCardFactory); + + await createDashboard(embeddableId, { + incomingEmbeddable, + initialInput: { + panels: { + i_do_not_match: getSampleDashboardPanel({ + explicitInput: { + id: 'i_do_not_match', + firstName: 'phew... I will not be replaced', + }, + type: CONTACT_CARD_EMBEDDABLE, + }), + }, + }, + }); + // flush promises + await new Promise((r) => setTimeout(r, 1)); + expect(mockContactCardFactory.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'i_match', + firstName: 'wow look at this new panel wow', + }), + expect.any(Object) + ); +}); + +test('creates a control group from the control group factory and waits for it to be initialized', async () => { + const mockControlGroupContainer = { + destroy: jest.fn(), + render: jest.fn(), + updateInput: jest.fn(), + untilInitialized: jest.fn(), + getInput: jest.fn().mockReturnValue({}), + getInput$: jest.fn().mockReturnValue(new Observable()), + getOutput$: jest.fn().mockReturnValue(new Observable()), + } as unknown as ControlGroupContainer; + const mockControlGroupFactory = { + create: jest.fn().mockReturnValue(mockControlGroupContainer), + } as unknown as ControlGroupContainerFactory; + pluginServices.getServices().embeddable.getEmbeddableFactory = jest + .fn() + .mockReturnValue(mockControlGroupFactory); + await createDashboard(embeddableId, { + useControlGroupIntegration: true, + initialInput: { + controlGroupInput: { controlStyle: 'twoLine' } as unknown as ControlGroupInput, + }, + }); + // flush promises + await new Promise((r) => setTimeout(r, 1)); + expect(pluginServices.getServices().embeddable.getEmbeddableFactory).toHaveBeenCalledWith( + 'control_group' + ); + expect(mockControlGroupFactory.create).toHaveBeenCalledWith( + expect.objectContaining({ controlStyle: 'twoLine' }) + ); + expect(mockControlGroupContainer.untilInitialized).toHaveBeenCalled(); +}); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts new file mode 100644 index 0000000000000..f0a20e832e431 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/create_dashboard.ts @@ -0,0 +1,295 @@ +/* + * 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 { Subject } from 'rxjs'; +import { cloneDeep, identity, pickBy } from 'lodash'; + +import { + ControlGroupInput, + CONTROL_GROUP_TYPE, + getDefaultControlGroupInput, +} from '@kbn/controls-plugin/common'; +import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; +import { isErrorEmbeddable, ViewMode } from '@kbn/embeddable-plugin/public'; +import { lazyLoadReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { type ControlGroupContainer, ControlGroupOutput } from '@kbn/controls-plugin/public'; + +import { DashboardContainerInput } from '../../../../common'; +import { DashboardContainer } from '../dashboard_container'; +import { pluginServices } from '../../../services/plugin_services'; +import { DEFAULT_DASHBOARD_INPUT } from '../../../dashboard_constants'; +import { DashboardCreationOptions } from '../dashboard_container_factory'; +import { startSyncingDashboardDataViews } from './data_views/sync_dashboard_data_views'; +import { syncUnifiedSearchState } from './unified_search/sync_dashboard_unified_search_state'; +import { startSyncingDashboardControlGroup } from './controls/dashboard_control_group_integration'; +import { startDashboardSearchSessionIntegration } from './search_sessions/start_dashboard_search_session_integration'; + +/** + * + * @param creationOptions + */ +export const createDashboard = async ( + embeddableId: string, + creationOptions?: DashboardCreationOptions, + dashboardCreationStartTime?: number, + savedObjectId?: string +): Promise => { + // -------------------------------------------------------------------------------------- + // Unpack services & Options + // -------------------------------------------------------------------------------------- + const { + dashboardSessionStorage, + embeddable: { getEmbeddableFactory }, + data: { + dataViews, + query: queryService, + search: { session }, + }, + dashboardSavedObject: { loadDashboardStateFromSavedObject }, + } = pluginServices.getServices(); + + const { + queryString, + filterManager, + timefilter: { timefilter: timefilterService }, + } = queryService; + + const { + searchSessionSettings, + unifiedSearchSettings, + validateLoadedSavedObject, + useControlGroupIntegration, + useUnifiedSearchIntegration, + initialInput: overrideInput, + useSessionStorageIntegration, + } = creationOptions ?? {}; + + // -------------------------------------------------------------------------------------- + // Create method which allows work to be done on the dashboard container when it's ready. + // -------------------------------------------------------------------------------------- + const dashboardContainerReady$ = new Subject(); + const untilDashboardReady = () => + new Promise((resolve) => { + const subscription = dashboardContainerReady$.subscribe((container) => { + subscription.unsubscribe(); + resolve(container); + }); + }); + + // -------------------------------------------------------------------------------------- + // Lazy load required systems and Dashboard saved object. + // -------------------------------------------------------------------------------------- + + const reduxEmbeddablePackagePromise = lazyLoadReduxToolsPackage(); + const defaultDataViewAssignmentPromise = dataViews.getDefaultDataView(); + const dashboardSavedObjectPromise = savedObjectId + ? loadDashboardStateFromSavedObject({ id: savedObjectId }) + : Promise.resolve(undefined); + + const [reduxEmbeddablePackage, savedObjectResult, defaultDataView] = await Promise.all([ + reduxEmbeddablePackagePromise, + dashboardSavedObjectPromise, + defaultDataViewAssignmentPromise, + ]); + + // -------------------------------------------------------------------------------------- + // Run validations. + // -------------------------------------------------------------------------------------- + if (!defaultDataView) { + throw new Error('Dashboard requires at least one data view before it can be initialized.'); + } + + if ( + savedObjectResult && + validateLoadedSavedObject && + !validateLoadedSavedObject(savedObjectResult) + ) { + throw new Error('Dashboard failed saved object result validation'); + } + + // -------------------------------------------------------------------------------------- + // Gather input from session storage if integration is used. + // -------------------------------------------------------------------------------------- + const sessionStorageInput = ((): Partial | undefined => { + if (!useSessionStorageIntegration) return; + return dashboardSessionStorage.getState(savedObjectId); + })(); + + // -------------------------------------------------------------------------------------- + // Combine input from saved object, session storage, & passed input to create initial input. + // -------------------------------------------------------------------------------------- + const initialInput: DashboardContainerInput = cloneDeep({ + ...DEFAULT_DASHBOARD_INPUT, + ...(savedObjectResult?.dashboardInput ?? {}), + ...sessionStorageInput, + ...overrideInput, + id: embeddableId, + }); + + initialInput.executionContext = { + type: 'dashboard', + description: initialInput.title, + }; + + // -------------------------------------------------------------------------------------- + // Set up unified search integration. + // -------------------------------------------------------------------------------------- + if (useUnifiedSearchIntegration && unifiedSearchSettings?.kbnUrlStateStorage) { + const { filters, query, timeRestore, timeRange, refreshInterval } = initialInput; + const { kbnUrlStateStorage } = unifiedSearchSettings; + + // apply filters and query to the query service + filterManager.setAppFilters(cloneDeep(filters ?? [])); + queryString.setQuery(query ?? queryString.getDefaultQuery()); + + /** + * If a global time range is not set explicitly and the time range was saved with the dashboard, apply + * time range and refresh interval to the query service. Otherwise, set the current dashboard time range + * from the query service. The order of the following lines is very important. + */ + if (timeRestore) { + if (timeRange) timefilterService.setTime(timeRange); + if (refreshInterval) timefilterService.setRefreshInterval(refreshInterval); + } + + const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( + queryService, + kbnUrlStateStorage + ); + + if (!timeRestore) { + initialInput.timeRange = timefilterService.getTime(); + } + + untilDashboardReady().then((dashboardContainer) => { + const stopSyncingUnifiedSearchState = + syncUnifiedSearchState.bind(dashboardContainer)(kbnUrlStateStorage); + dashboardContainer.stopSyncingWithUnifiedSearch = () => { + stopSyncingUnifiedSearchState(); + stopSyncingQueryServiceStateWithUrl(); + }; + }); + } + + // -------------------------------------------------------------------------------------- + // Place the incoming embeddable if there is one + // -------------------------------------------------------------------------------------- + const incomingEmbeddable = creationOptions?.incomingEmbeddable; + if (incomingEmbeddable) { + initialInput.viewMode = ViewMode.EDIT; // view mode must always be edit to recieve an embeddable. + + const panelExists = + incomingEmbeddable.embeddableId && + Boolean(initialInput.panels[incomingEmbeddable.embeddableId]); + if (panelExists) { + // this embeddable already exists, we will update the explicit input. + const panelToUpdate = initialInput.panels[incomingEmbeddable.embeddableId as string]; + const sameType = panelToUpdate.type === incomingEmbeddable.type; + + panelToUpdate.type = incomingEmbeddable.type; + panelToUpdate.explicitInput = { + // if the incoming panel is the same type as what was there before we can safely spread the old panel's explicit input + ...(sameType ? panelToUpdate.explicitInput : {}), + + ...incomingEmbeddable.input, + id: incomingEmbeddable.embeddableId as string, + + // maintain hide panel titles setting. + hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles, + }; + } else { + // otherwise this incoming embeddable is brand new and can be added via the default method after the dashboard container is created. + untilDashboardReady().then(async (container) => { + container.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input); + }); + } + + untilDashboardReady().then(async (container) => { + container.setScrollToPanelId(incomingEmbeddable.embeddableId); + container.setHighlightPanelId(incomingEmbeddable.embeddableId); + }); + } + + // -------------------------------------------------------------------------------------- + // Set up search sessions integration. + // -------------------------------------------------------------------------------------- + if (searchSessionSettings) { + const { sessionIdToRestore } = searchSessionSettings; + + // if this incoming embeddable has a session, continue it. + if (incomingEmbeddable?.searchSessionId) { + session.continue(incomingEmbeddable.searchSessionId); + } + if (sessionIdToRestore) { + session.restore(sessionIdToRestore); + } + const existingSession = session.getSessionId(); + + const initialSearchSessionId = + sessionIdToRestore ?? + (existingSession && incomingEmbeddable ? existingSession : session.start()); + + untilDashboardReady().then((container) => { + startDashboardSearchSessionIntegration.bind(container)( + creationOptions?.searchSessionSettings + ); + }); + initialInput.searchSessionId = initialSearchSessionId; + } + + // -------------------------------------------------------------------------------------- + // Start the control group integration. + // -------------------------------------------------------------------------------------- + if (useControlGroupIntegration) { + const controlsGroupFactory = getEmbeddableFactory< + ControlGroupInput, + ControlGroupOutput, + ControlGroupContainer + >(CONTROL_GROUP_TYPE); + const { filters, query, timeRange, viewMode, controlGroupInput, id } = initialInput; + const controlGroup = await controlsGroupFactory?.create({ + id: `control_group_${id ?? 'new_dashboard'}`, + ...getDefaultControlGroupInput(), + ...pickBy(controlGroupInput, identity), // undefined keys in initialInput should not overwrite defaults + timeRange, + viewMode, + filters, + query, + }); + if (!controlGroup || isErrorEmbeddable(controlGroup)) { + throw new Error('Error in control group startup'); + } + + untilDashboardReady().then((dashboardContainer) => { + dashboardContainer.controlGroup = controlGroup; + startSyncingDashboardControlGroup.bind(dashboardContainer)(); + }); + await controlGroup.untilInitialized(); + } + + // -------------------------------------------------------------------------------------- + // Start the data views integration. + // -------------------------------------------------------------------------------------- + untilDashboardReady().then((dashboardContainer) => { + dashboardContainer.subscriptions.add(startSyncingDashboardDataViews.bind(dashboardContainer)()); + }); + + // -------------------------------------------------------------------------------------- + // Build and return the dashboard container. + // -------------------------------------------------------------------------------------- + const dashboardContainer = new DashboardContainer( + initialInput, + reduxEmbeddablePackage, + savedObjectResult?.dashboardInput, + dashboardCreationStartTime, + undefined, + creationOptions, + savedObjectId + ); + dashboardContainerReady$.next(dashboardContainer); + return dashboardContainer; +}; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/data_views/sync_dashboard_data_views.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/data_views/sync_dashboard_data_views.ts similarity index 100% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/data_views/sync_dashboard_data_views.ts rename to src/plugins/dashboard/public/dashboard_container/embeddable/create/data_views/sync_dashboard_data_views.ts diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.ts new file mode 100644 index 0000000000000..506083ab25386 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/search_sessions/start_dashboard_search_session_integration.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 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 { debounceTime, pairwise, skip } from 'rxjs/operators'; + +import { noSearchSessionStorageCapabilityMessage } from '@kbn/data-plugin/public'; + +import { DashboardContainer } from '../../dashboard_container'; +import { DashboardContainerInput } from '../../../../../common'; +import { pluginServices } from '../../../../services/plugin_services'; +import { CHANGE_CHECK_DEBOUNCE } from '../../../../dashboard_constants'; +import { DashboardCreationOptions } from '../../dashboard_container_factory'; +import { getShouldRefresh } from '../../../state/diffing/dashboard_diffing_integration'; + +/** + * Enables dashboard search sessions. + */ +export function startDashboardSearchSessionIntegration( + this: DashboardContainer, + searchSessionSettings: DashboardCreationOptions['searchSessionSettings'] +) { + if (!searchSessionSettings) return; + + const { + data: { + search: { session }, + }, + dashboardCapabilities: { storeSearchSession: canStoreSearchSession }, + } = pluginServices.getServices(); + + const { + sessionIdUrlChangeObservable, + getSearchSessionIdFromURL, + removeSessionIdFromUrl, + createSessionRestorationDataProvider, + } = searchSessionSettings; + + session.enableStorage(createSessionRestorationDataProvider(this), { + isDisabled: () => + canStoreSearchSession + ? { disabled: false } + : { + disabled: true, + reasonText: noSearchSessionStorageCapabilityMessage, + }, + }); + + // force refresh when the session id in the URL changes. This will also fire off the "handle search session change" below. + const searchSessionIdChangeSubscription = sessionIdUrlChangeObservable + ?.pipe(skip(1)) + .subscribe(() => this.forceRefresh()); + + // listen to and compare states to determine when to launch a new session. + this.getInput$() + .pipe(pairwise(), debounceTime(CHANGE_CHECK_DEBOUNCE)) + .subscribe(async (states) => { + const [previous, current] = states as DashboardContainerInput[]; + const shouldRefetch = await getShouldRefresh.bind(this)(previous, current); + if (!shouldRefetch) return; + + const currentSearchSessionId = this.getState().explicitInput.searchSessionId; + + const updatedSearchSessionId: string | undefined = (() => { + // do not update session id if this is irrelevant state change to prevent excessive searches + if (!shouldRefetch) return; + + let searchSessionIdFromURL = getSearchSessionIdFromURL(); + if (searchSessionIdFromURL) { + if (session.isRestore() && session.isCurrentSession(searchSessionIdFromURL)) { + // we had previously been in a restored session but have now changed state so remove the session id from the URL. + removeSessionIdFromUrl(); + searchSessionIdFromURL = undefined; + } else { + session.restore(searchSessionIdFromURL); + } + } + return searchSessionIdFromURL ?? session.start(); + })(); + + if (updatedSearchSessionId && updatedSearchSessionId !== currentSearchSessionId) { + this.dispatch.setSearchSessionId(updatedSearchSessionId); + } + }); + + this.subscriptions.add(searchSessionIdChangeSubscription); +} diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/unified_search/sync_dashboard_unified_search_state.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts similarity index 86% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/unified_search/sync_dashboard_unified_search_state.ts rename to src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts index c3dc5f616330a..02095015f75a8 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/unified_search/sync_dashboard_unified_search_state.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/create/unified_search/sync_dashboard_unified_search_state.ts @@ -31,19 +31,12 @@ export function syncUnifiedSearchState( const { queryString, timefilter } = queryService; const { timefilter: timefilterService } = timefilter; - const { - getState, - dispatch, - onStateChange, - actions: { setFiltersAndQuery, setTimeRange, setRefreshInterval }, - } = this.getReduxEmbeddableTools(); - // get Observable for when the dashboard's saved filters or query change. const OnFiltersChange$ = new Subject<{ filters: Filter[]; query: Query }>(); - const unsubscribeFromSavedFilterChanges = onStateChange(() => { + const unsubscribeFromSavedFilterChanges = this.onStateChange(() => { const { explicitInput: { filters, query }, - } = getState(); + } = this.getState(); OnFiltersChange$.next({ filters: filters ?? [], query: query ?? queryString.getDefaultQuery(), @@ -53,7 +46,7 @@ export function syncUnifiedSearchState( // starts syncing app filters between dashboard state and filterManager const { explicitInput: { filters, query }, - } = getState(); + } = this.getState(); const intermediateFilterState: { filters: Filter[]; query: Query } = { query: query ?? queryString.getDefaultQuery(), filters: filters ?? [], @@ -66,7 +59,7 @@ export function syncUnifiedSearchState( set: ({ filters: newFilters, query: newQuery }) => { intermediateFilterState.filters = cleanFiltersForSerialize(newFilters); intermediateFilterState.query = newQuery; - dispatch(setFiltersAndQuery(intermediateFilterState)); + this.dispatch.setFiltersAndQuery(intermediateFilterState); }, state$: OnFiltersChange$.pipe(distinctUntilChanged()), }, @@ -78,11 +71,11 @@ export function syncUnifiedSearchState( const timeUpdateSubscription = timefilterService .getTimeUpdate$() - .subscribe(() => dispatch(setTimeRange(timefilterService.getTime()))); + .subscribe(() => this.dispatch.setTimeRange(timefilterService.getTime())); const refreshIntervalSubscription = timefilterService .getRefreshIntervalUpdate$() - .subscribe(() => dispatch(setRefreshInterval(timefilterService.getRefreshInterval()))); + .subscribe(() => this.dispatch.setRefreshInterval(timefilterService.getRefreshInterval())); const autoRefreshSubscription = timefilterService .getAutoRefreshFetch$() diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx index a6ac4bc970a00..bdce2754ba0dd 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx @@ -29,8 +29,7 @@ import { applicationServiceMock, coreMock } from '@kbn/core/public/mocks'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { createEditModeActionDefinition } from '@kbn/embeddable-plugin/public/lib/test_samples'; -import { DashboardContainer } from './dashboard_container'; -import { getSampleDashboardInput, getSampleDashboardPanel } from '../../mocks'; +import { buildMockDashboard, getSampleDashboardPanel } from '../../mocks'; import { pluginServices } from '../../services/plugin_services'; import { ApplicationStart } from '@kbn/core-application-browser'; @@ -47,7 +46,7 @@ beforeEach(() => { }); test('DashboardContainer initializes embeddables', (done) => { - const initialInput = getSampleDashboardInput({ + const container = buildMockDashboard({ panels: { '123': getSampleDashboardPanel({ explicitInput: { firstName: 'Sam', id: '123' }, @@ -55,7 +54,6 @@ test('DashboardContainer initializes embeddables', (done) => { }), }, }); - const container = new DashboardContainer(initialInput); const subscription = container.getOutput$().subscribe((output) => { if (container.getOutput().embeddableLoaded['123']) { @@ -76,8 +74,7 @@ test('DashboardContainer initializes embeddables', (done) => { }); test('DashboardContainer.addNewEmbeddable', async () => { - const container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + const container = buildMockDashboard(); const embeddable = await container.addNewEmbeddable( CONTACT_CARD_EMBEDDABLE, { @@ -99,7 +96,8 @@ test('DashboardContainer.addNewEmbeddable', async () => { test('DashboardContainer.replacePanel', (done) => { const ID = '123'; - const initialInput = getSampleDashboardInput({ + + const container = buildMockDashboard({ panels: { [ID]: getSampleDashboardPanel({ explicitInput: { firstName: 'Sam', id: ID }, @@ -107,8 +105,6 @@ test('DashboardContainer.replacePanel', (done) => { }), }, }); - - const container = new DashboardContainer(initialInput); let counter = 0; const subscription = container.getInput$().subscribe( @@ -141,7 +137,7 @@ test('DashboardContainer.replacePanel', (done) => { }); test('Container view mode change propagates to existing children', async () => { - const initialInput = getSampleDashboardInput({ + const container = buildMockDashboard({ panels: { '123': getSampleDashboardPanel({ explicitInput: { firstName: 'Sam', id: '123' }, @@ -149,8 +145,6 @@ test('Container view mode change propagates to existing children', async () => { }), }, }); - const container = new DashboardContainer(initialInput); - await container.untilInitialized(); const embeddable = await container.untilEmbeddableLoaded('123'); expect(embeddable.getInput().viewMode).toBe(ViewMode.VIEW); @@ -159,8 +153,7 @@ test('Container view mode change propagates to existing children', async () => { }); test('Container view mode change propagates to new children', async () => { - const container = new DashboardContainer(getSampleDashboardInput()); - await container.untilInitialized(); + const container = buildMockDashboard(); const embeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, ContactCardEmbeddableOutput, @@ -178,10 +171,7 @@ test('Container view mode change propagates to new children', async () => { test('searchSessionId propagates to children', async () => { const searchSessionId1 = 'searchSessionId1'; - const container = new DashboardContainer( - getSampleDashboardInput({ searchSessionId: searchSessionId1 }) - ); - await container.untilInitialized(); + const container = buildMockDashboard({ searchSessionId: searchSessionId1 }); const embeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, ContactCardEmbeddableOutput, @@ -205,9 +195,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { uiActionsSetup.registerAction(editModeAction); uiActionsSetup.addTriggerAction(CONTEXT_MENU_TRIGGER, editModeAction); - const initialInput = getSampleDashboardInput({ viewMode: ViewMode.VIEW }); - const container = new DashboardContainer(initialInput); - await container.untilInitialized(); + const container = buildMockDashboard({ viewMode: ViewMode.VIEW }); const embeddable = await container.addNewEmbeddable< ContactCardEmbeddableInput, diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index 11e7de65f75ec..d5a5385e779b3 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -6,19 +6,14 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; -import { cloneDeep, omit } from 'lodash'; -import { BehaviorSubject, Subject, Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; -import { - lazyLoadReduxEmbeddablePackage, - ReduxEmbeddableTools, -} from '@kbn/presentation-util-plugin/public'; +import { ReduxToolsPackage, ReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public'; import { ViewMode, Container, - type Embeddable, type IEmbeddable, type EmbeddableInput, type EmbeddableOutput, @@ -44,36 +39,19 @@ import { showPlaceholderUntil, addOrUpdateEmbeddable, } from './api'; -import { - DashboardReduxState, - DashboardContainerOutput, - DashboardRenderPerformanceStats, -} from '../types'; -import { - DashboardPanelState, - DashboardContainerInput, - DashboardContainerByValueInput, -} from '../../../common'; -import { - startDiffingDashboardState, - startControlGroupIntegration, - startUnifiedSearchIntegration, - startSyncingDashboardDataViews, - startDashboardSearchSessionIntegration, - combineDashboardFiltersWithControlGroupFilters, - getUnsavedChanges, - keysNotConsideredUnsavedChanges, -} from './integrations'; + import { DASHBOARD_CONTAINER_TYPE } from '../..'; import { createPanelState } from '../component/panel'; import { pluginServices } from '../../services/plugin_services'; -import { DASHBOARD_LOADED_EVENT } from '../../dashboard_constants'; import { DashboardCreationOptions } from './dashboard_container_factory'; import { DashboardAnalyticsService } from '../../services/analytics/types'; import { DashboardViewport } from '../component/viewport/dashboard_viewport'; +import { DashboardPanelState, DashboardContainerInput } from '../../../common'; +import { DashboardReduxState, DashboardRenderPerformanceStats } from '../types'; import { dashboardContainerReducers } from '../state/dashboard_container_reducers'; -import { DashboardSavedObjectService } from '../../services/dashboard_saved_object/types'; -import { dashboardContainerInputIsByValue } from '../../../common/dashboard_container/type_guards'; +import { startDiffingDashboardState } from '../state/diffing/dashboard_diffing_integration'; +import { DASHBOARD_LOADED_EVENT, DEFAULT_DASHBOARD_INPUT } from '../../dashboard_constants'; +import { combineDashboardFiltersWithControlGroupFilters } from './create/controls/dashboard_control_group_integration'; export interface InheritedChildInput { filters: Filter[]; @@ -91,25 +69,40 @@ export interface InheritedChildInput { executionContext?: KibanaExecutionContext; } +type DashboardReduxEmbeddableTools = ReduxEmbeddableTools< + DashboardReduxState, + typeof dashboardContainerReducers +>; + +export const DashboardContainerContext = createContext(null); +export const useDashboardContainer = (): DashboardContainer => { + const dashboard = useContext(DashboardContainerContext); + if (dashboard == null) { + throw new Error('useDashboardContainer must be used inside DashboardContainerContext.'); + } + return dashboard!; +}; + export class DashboardContainer extends Container { public readonly type = DASHBOARD_CONTAINER_TYPE; + + // state management + public select: DashboardReduxEmbeddableTools['select']; + public getState: DashboardReduxEmbeddableTools['getState']; + public dispatch: DashboardReduxEmbeddableTools['dispatch']; + public onStateChange: DashboardReduxEmbeddableTools['onStateChange']; + + public subscriptions: Subscription = new Subscription(); public controlGroup?: ControlGroupContainer; - // Dashboard State - public onDestroyControlGroup?: () => void; - private subscriptions: Subscription = new Subscription(); + // cleanup + public stopSyncingWithUnifiedSearch?: () => void; + private cleanupStateTools: () => void; - private initialized$: BehaviorSubject = new BehaviorSubject(false); + // performance monitoring private dashboardCreationStartTime?: number; private savedObjectLoadTime?: number; - private initialSavedDashboardId?: string; - - private reduxEmbeddableTools?: ReduxEmbeddableTools< - DashboardReduxState, - typeof dashboardContainerReducers - >; - private domNode?: HTMLElement; private overlayRef?: OverlayRef; private allDataViews: DataView[] = []; @@ -117,19 +110,19 @@ export class DashboardContainer extends Container(); const { embeddable: { getEmbeddableFactory }, } = pluginServices.getServices(); @@ -140,15 +133,11 @@ export class DashboardContainer extends Container { - if (!dashboardContainerInputIsByValue(this.input)) { - throw new Error('cannot get input as value type until after dashboard input is unwrapped.'); - } - return this.getInput() as DashboardContainerByValueInput; - }; - - private async unwrapDashboardContainerInput(): Promise< - DashboardContainerByValueInput | undefined - > { - if (dashboardContainerInputIsByValue(this.input)) { - return this.input; - } - - // if this dashboard is loaded by reference, unwrap it and track the saved object load time. - const savedObjectLoadStartTime = performance.now(); - const unwrapResult = await this.dashboardSavedObjectService.loadDashboardStateFromSavedObject({ - id: this.input.savedObjectId, - }); - this.updateInput({ savedObjectId: undefined }); - this.savedObjectLoadTime = performance.now() - savedObjectLoadStartTime; - if ( - !this.creationOptions?.validateLoadedSavedObject || - this.creationOptions.validateLoadedSavedObject(unwrapResult) - ) { - return unwrapResult.dashboardInput; - } - } - - private async initializeDashboard( - readyToInitializeChildren$: Subject, - creationOptions?: DashboardCreationOptions - ) { - const { - data: { dataViews }, - } = pluginServices.getServices(); - - const reduxEmbeddablePackagePromise = lazyLoadReduxEmbeddablePackage(); - const defaultDataViewAssignmentPromise = dataViews.getDefaultDataView(); - const dashboardStateUnwrapPromise = this.unwrapDashboardContainerInput(); - - const [reduxEmbeddablePackage, inputFromSavedObject, defaultDataView] = await Promise.all([ - reduxEmbeddablePackagePromise, - dashboardStateUnwrapPromise, - defaultDataViewAssignmentPromise, - ]); - - if (!defaultDataView) { - throw new Error('Dashboard requires at least one data view before it can be initialized.'); - } - - // inputFromSavedObject will only be undefined if the provided valiation function returns false. - if (!inputFromSavedObject) { - this.destroy(); - return; - } - - // Gather input from session storage if integration is used - let sessionStorageInput: Partial = {}; - if (creationOptions?.useSessionStorageIntegration) { - const { dashboardSessionStorage } = pluginServices.getServices(); - const sessionInput = dashboardSessionStorage.getState(this.initialSavedDashboardId); - if (sessionInput) sessionStorageInput = sessionInput; - } - - // Combine input from saved object with override input. - const initialInput: DashboardContainerByValueInput = cloneDeep({ - ...inputFromSavedObject, - ...sessionStorageInput, - ...creationOptions?.overrideInput, - }); - - // set up execution context - initialInput.executionContext = { - type: 'dashboard', - description: initialInput.title, - }; - - // set up unified search integration - if ( - creationOptions?.useUnifiedSearchIntegration && - creationOptions.unifiedSearchSettings?.kbnUrlStateStorage - ) { - const { kbnUrlStateStorage } = creationOptions.unifiedSearchSettings; - const initialTimeRange = startUnifiedSearchIntegration.bind(this)({ - initialInput, - kbnUrlStateStorage, - setCleanupFunction: (cleanup) => { - this.stopSyncingWithUnifiedSearch = cleanup; - }, - }); - if (initialTimeRange) initialInput.timeRange = initialTimeRange; - } - - // place the incoming embeddable if there is one - const incomingEmbeddable = creationOptions?.incomingEmbeddable; - if (incomingEmbeddable) { - initialInput.viewMode = ViewMode.EDIT; // view mode must always be edit to recieve an embeddable. - if ( - incomingEmbeddable.embeddableId && - Boolean(initialInput.panels[incomingEmbeddable.embeddableId]) - ) { - // this embeddable already exists, we will update the explicit input. - const panelToUpdate = initialInput.panels[incomingEmbeddable.embeddableId]; - const sameType = panelToUpdate.type === incomingEmbeddable.type; - - panelToUpdate.type = incomingEmbeddable.type; - panelToUpdate.explicitInput = { - // if the incoming panel is the same type as what was there before we can safely spread the old panel's explicit input - ...(sameType ? panelToUpdate.explicitInput : {}), - - ...incomingEmbeddable.input, - id: incomingEmbeddable.embeddableId, - - // maintain hide panel titles setting. - hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles, - }; - } else { - // otherwise this incoming embeddable is brand new and can be added via the default method after the dashboard container is created. - this.untilInitialized().then(() => - this.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input) - ); - } - } - - // start search sessions integration - if (creationOptions?.useSearchSessionsIntegration) { - const { initialSearchSessionId, stopSyncingDashboardSearchSessions } = - startDashboardSearchSessionIntegration.bind(this)( - creationOptions?.searchSessionSettings, - incomingEmbeddable - ); - initialInput.searchSessionId = initialSearchSessionId; - this.stopSyncingDashboardSearchSessions = stopSyncingDashboardSearchSessions; - } - - // update input so the redux embeddable tools get the unwrapped, initial input - this.updateInput({ ...initialInput }); - - // build Control Group - if (creationOptions?.useControlGroupIntegration) { - this.controlGroup = await startControlGroupIntegration.bind(this)(initialInput); - } - - // now that the input with the initial panel state has been set and the control group is ready, we can tell the container class it's time to start loading children. - readyToInitializeChildren$.next(initialInput); // start diffing dashboard state - const diffingMiddleware = startDiffingDashboardState.bind(this)({ - useSessionBackup: creationOptions?.useSessionStorageIntegration, - setCleanupFunction: (cleanup) => { - this.stopDiffingDashboardState = cleanup; - }, - }); - - // set up data views integration - this.dataViewsChangeSubscription = startSyncingDashboardDataViews.bind(this)(); + const diffingMiddleware = startDiffingDashboardState.bind(this)(creationOptions); // build redux embeddable tools - this.reduxEmbeddableTools = reduxEmbeddablePackage.createTools< + const reduxTools = reduxToolsPackage.createReduxEmbeddableTools< DashboardReduxState, typeof dashboardContainerReducers >({ - embeddable: this as Embeddable, // cast to unwrapped state type + embeddable: this, reducers: dashboardContainerReducers, additionalMiddleware: [diffingMiddleware], initialComponentState: { - lastSavedInput: inputFromSavedObject, + lastSavedInput: initialLastSavedInput ?? { + ...DEFAULT_DASHBOARD_INPUT, + id: initialInput.id, + }, hasUnsavedChanges: false, // if there is initial unsaved changes, the initial diff will catch them. - lastSavedId: this.initialSavedDashboardId, + lastSavedId: savedObjectId, }, }); - this.initialized$.next(true); + this.onStateChange = reduxTools.onStateChange; + this.cleanupStateTools = reduxTools.cleanup; + this.getState = reduxTools.getState; + this.dispatch = reduxTools.dispatch; + this.select = reduxTools.select; } - public async untilInitialized() { - if (this.initialized$.value) return Promise.resolve(); - return new Promise((resolve) => { - const subscription = this.initialized$.subscribe((isInitialized) => { - if (isInitialized) { - resolve(); - subscription.unsubscribe(); - } - }); - }); + public getDashboardSavedObjectId() { + return this.getState().componentState.lastSavedId; } public reportPerformanceMetrics(stats: DashboardRenderPerformanceStats) { if (this.analyticsService && this.dashboardCreationStartTime) { - const panelCount = Object.keys( - this.getReduxEmbeddableTools().getState().explicitInput.panels - ).length; + const panelCount = Object.keys(this.getState().explicitInput.panels).length; const totalDuration = stats.panelsRenderDoneTime - this.dashboardCreationStartTime; reportPerformanceMetricEvent(this.analyticsService, { eventName: DASHBOARD_LOADED_EVENT, @@ -393,44 +211,22 @@ export class DashboardContainer extends Container 0 - ); - } - - public getReduxEmbeddableTools() { - if (!this.reduxEmbeddableTools) { - throw new Error('Dashboard must be initialized before accessing redux embeddable tools'); - } - return this.reduxEmbeddableTools; - } - public render(dom: HTMLElement) { - if (!this.reduxEmbeddableTools) { - throw new Error('Dashboard must be initialized before it can be rendered'); - } if (this.domNode) { ReactDOM.unmountComponentAtNode(this.domNode); } this.domNode = dom; - this.domNode.className = 'dashboardContainer'; - const { Wrapper: DashboardReduxWrapper } = this.reduxEmbeddableTools; ReactDOM.render( - + - + , @@ -451,7 +247,7 @@ export class DashboardContainer extends Container void; - private stopSyncingWithUnifiedSearch?: () => void; - private dataViewsChangeSubscription?: Subscription = undefined; - private stopSyncingDashboardSearchSessions: (() => void) | undefined; - public destroy() { super.destroy(); - this.onDestroyControlGroup?.(); + this.cleanupStateTools(); + this.controlGroup?.destroy(); this.subscriptions.unsubscribe(); - this.stopDiffingDashboardState?.(); - this.reduxEmbeddableTools?.cleanup(); this.stopSyncingWithUnifiedSearch?.(); - this.stopSyncingDashboardSearchSessions?.(); - this.dataViewsChangeSubscription?.unsubscribe(); if (this.domNode) ReactDOM.unmountComponentAtNode(this.domNode); } @@ -529,29 +317,20 @@ export class DashboardContainer extends Container(); public resetToLastSavedState() { - const { - dispatch, - getState, - actions: { resetToLastSavedInput }, - } = this.getReduxEmbeddableTools(); - dispatch(resetToLastSavedInput({})); + this.dispatch.resetToLastSavedInput({}); const { explicitInput: { timeRange, refreshInterval }, componentState: { lastSavedInput: { timeRestore: lastSavedTimeRestore }, }, - } = getState(); + } = this.getState(); // if we are using the unified search integration, we need to force reset the time picker. if (this.creationOptions?.useUnifiedSearchIntegration && lastSavedTimeRestore) { @@ -585,8 +364,11 @@ export class DashboardContainer extends Container { - if (!this.reduxEmbeddableTools) throw new Error(); - return this.reduxEmbeddableTools.getState().componentState.expandedPanelId; + return this.getState().componentState.expandedPanelId; + }; + + public setExpandedPanelId = (newId?: string) => { + this.dispatch.setExpandedPanelId(newId); }; public openOverlay = (ref: OverlayRef) => { @@ -599,15 +381,6 @@ export class DashboardContainer extends Container { - if (!this.reduxEmbeddableTools) throw new Error(); - const { - actions: { setExpandedPanelId }, - dispatch, - } = this.reduxEmbeddableTools; - dispatch(setExpandedPanelId(newId)); - }; - public getPanelCount = () => { return Object.keys(this.getInput().panels).length; }; @@ -625,4 +398,41 @@ export class DashboardContainer extends Container { + this.dispatch.setScrollToPanelId(id); + }; + + public scrollToPanel = async (panelRef: HTMLDivElement) => { + const id = this.getState().componentState.scrollToPanelId; + if (!id) return; + + this.untilEmbeddableLoaded(id).then(() => { + this.setScrollToPanelId(undefined); + panelRef.scrollIntoView({ block: 'center' }); + }); + }; + + public scrollToTop = () => { + window.scroll(0, 0); + }; + + public setHighlightPanelId = (id: string | undefined) => { + this.dispatch.setHighlightPanelId(id); + }; + + public highlightPanel = (panelRef: HTMLDivElement) => { + const id = this.getState().componentState.highlightPanelId; + + if (id && panelRef) { + this.untilEmbeddableLoaded(id).then(() => { + panelRef.classList.add('dshDashboardGrid__item--highlighted'); + // Removes the class after the highlight animation finishes + setTimeout(() => { + panelRef.classList.remove('dshDashboardGrid__item--highlighted'); + }, 5000); + }); + } + this.setHighlightPanelId(undefined); + }; } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx index 7c61591d5ff27..2af0d60572cbd 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container_factory.tsx @@ -21,15 +21,10 @@ import { SearchSessionInfoProvider } from '@kbn/data-plugin/public'; import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { EmbeddablePersistableStateService } from '@kbn/embeddable-plugin/common'; -import { - createInject, - createExtract, - DashboardContainerInput, - DashboardContainerByValueInput, -} from '../../../common'; import { DASHBOARD_CONTAINER_TYPE } from '..'; import type { DashboardContainer } from './dashboard_container'; import { DEFAULT_DASHBOARD_INPUT } from '../../dashboard_constants'; +import { createInject, createExtract, DashboardContainerInput } from '../../../common'; import { LoadDashboardFromSavedObjectReturn } from '../../services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object'; export type DashboardContainerFactory = EmbeddableFactory< @@ -40,7 +35,6 @@ export type DashboardContainerFactory = EmbeddableFactory< export interface DashboardCreationOptions { initialInput?: Partial; - overrideInput?: Partial; incomingEmbeddable?: EmbeddablePackageState; @@ -97,19 +91,17 @@ export class DashboardContainerFactoryDefinition public create = async ( initialInput: DashboardContainerInput, parent?: Container, - creationOptions?: DashboardCreationOptions + creationOptions?: DashboardCreationOptions, + savedObjectId?: string ): Promise => { const dashboardCreationStartTime = performance.now(); - const { DashboardContainer: DashboardContainerEmbeddable } = await import( - './dashboard_container' - ); - return Promise.resolve( - new DashboardContainerEmbeddable( - initialInput, - dashboardCreationStartTime, - parent, - creationOptions - ) - ); + const { createDashboard } = await import('./create/create_dashboard'); + try { + return Promise.resolve( + createDashboard(initialInput.id, creationOptions, dashboardCreationStartTime, savedObjectId) + ); + } catch (e) { + return new ErrorEmbeddable(e.text, { id: e.id }); + } }; } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/index.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/index.ts deleted file mode 100644 index f5399991cc8a2..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/index.ts +++ /dev/null @@ -1,26 +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. - */ - -export { - getUnsavedChanges, - startDiffingDashboardState, - keysNotConsideredUnsavedChanges, -} from './diff_state/dashboard_diffing_integration'; - -export { - startUnifiedSearchIntegration, - applySavedFiltersToUnifiedSearch, -} from './unified_search/start_dashboard_unified_search_integration'; - -export { - startControlGroupIntegration, - combineDashboardFiltersWithControlGroupFilters, -} from './controls/dashboard_control_group_integration'; - -export { startSyncingDashboardDataViews } from './data_views/sync_dashboard_data_views'; -export { startDashboardSearchSessionIntegration } from './search_sessions/start_dashboard_search_session_integration'; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/search_sessions/start_dashboard_search_session_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/search_sessions/start_dashboard_search_session_integration.ts deleted file mode 100644 index 986a53a022c6a..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/search_sessions/start_dashboard_search_session_integration.ts +++ /dev/null @@ -1,122 +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 { Subscription } from 'rxjs'; -import { debounceTime, pairwise, skip } from 'rxjs/operators'; -import { noSearchSessionStorageCapabilityMessage } from '@kbn/data-plugin/public'; - -import { DashboardContainer } from '../../dashboard_container'; -import { pluginServices } from '../../../../services/plugin_services'; -import { DashboardContainerByValueInput } from '../../../../../common'; -import { CHANGE_CHECK_DEBOUNCE } from '../../../../dashboard_constants'; -import { DashboardCreationOptions } from '../../dashboard_container_factory'; -import { getShouldRefresh } from '../diff_state/dashboard_diffing_integration'; - -/** - * Enables dashboard search sessions. - */ -export function startDashboardSearchSessionIntegration( - this: DashboardContainer, - searchSessionSettings: DashboardCreationOptions['searchSessionSettings'], - incomingEmbeddable: DashboardCreationOptions['incomingEmbeddable'] -) { - if (!searchSessionSettings) { - throw new Error('Cannot start search sessions integration without settings'); - } - const { - createSessionRestorationDataProvider, - sessionIdUrlChangeObservable, - getSearchSessionIdFromURL, - removeSessionIdFromUrl, - sessionIdToRestore, - } = searchSessionSettings; - - const { - data: { - search: { session }, - }, - dashboardCapabilities: { storeSearchSession: canStoreSearchSession }, - } = pluginServices.getServices(); - - // if this incoming embeddable has a session, continue it. - if (incomingEmbeddable?.searchSessionId) { - session.continue(incomingEmbeddable.searchSessionId); - } - if (sessionIdToRestore) { - session.restore(sessionIdToRestore); - } - const existingSession = session.getSessionId(); - - const initialSearchSessionId = - sessionIdToRestore ?? - (existingSession && incomingEmbeddable ? existingSession : session.start()); - - session.enableStorage(createSessionRestorationDataProvider(this), { - isDisabled: () => - canStoreSearchSession - ? { disabled: false } - : { - disabled: true, - reasonText: noSearchSessionStorageCapabilityMessage, - }, - }); - - let searchSessionIdChangeSubscription: Subscription | undefined; - let checkForSessionChangeSubscription: Subscription | undefined; - this.untilInitialized().then(() => { - // force refresh when the session id in the URL changes. This will also fire off the "handle search session change" below. - searchSessionIdChangeSubscription = sessionIdUrlChangeObservable - ?.pipe(skip(1)) - .subscribe(() => this.forceRefresh()); - - // listen to and compare states to determine when to launch a new session. - this.getInput$() - .pipe(pairwise(), debounceTime(CHANGE_CHECK_DEBOUNCE)) - .subscribe(async (states) => { - const [previous, current] = states as DashboardContainerByValueInput[]; - const shouldRefetch = await getShouldRefresh.bind(this)(previous, current); - if (!shouldRefetch) return; - - const { - getState, - dispatch, - actions: { setSearchSessionId }, - } = this.getReduxEmbeddableTools(); - const currentSearchSessionId = getState().explicitInput.searchSessionId; - - const updatedSearchSessionId: string | undefined = (() => { - // do not update session id if this is irrelevant state change to prevent excessive searches - if (!shouldRefetch) return; - - let searchSessionIdFromURL = getSearchSessionIdFromURL(); - if (searchSessionIdFromURL) { - if (session.isRestore() && session.isCurrentSession(searchSessionIdFromURL)) { - // we had previously been in a restored session but have now changed state so remove the session id from the URL. - removeSessionIdFromUrl(); - searchSessionIdFromURL = undefined; - } else { - session.restore(searchSessionIdFromURL); - } - } - return searchSessionIdFromURL ?? session.start(); - })(); - - if (updatedSearchSessionId && updatedSearchSessionId !== currentSearchSessionId) { - dispatch(setSearchSessionId(updatedSearchSessionId)); - } - }); - }); - - return { - initialSearchSessionId, - stopSyncingDashboardSearchSessions: () => { - checkForSessionChangeSubscription?.unsubscribe(); - searchSessionIdChangeSubscription?.unsubscribe(); - }, - }; -} diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/unified_search/start_dashboard_unified_search_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/unified_search/start_dashboard_unified_search_integration.ts deleted file mode 100644 index 322caf7846b60..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/unified_search/start_dashboard_unified_search_integration.ts +++ /dev/null @@ -1,90 +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 { cloneDeep } from 'lodash'; - -import { syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; -import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; - -import { DashboardContainer } from '../../dashboard_container'; -import { pluginServices } from '../../../../services/plugin_services'; -import { DashboardContainerByValueInput } from '../../../../../common'; -import { syncUnifiedSearchState } from './sync_dashboard_unified_search_state'; - -/** - * Applies initial state to the query service, and the saved dashboard search source - * Sets up syncing and subscriptions between the filter state from the Data plugin - * and the dashboard Redux store. - */ -export function startUnifiedSearchIntegration( - this: DashboardContainer, - { - initialInput, - setCleanupFunction, - kbnUrlStateStorage, - }: { - kbnUrlStateStorage: IKbnUrlStateStorage; - initialInput: DashboardContainerByValueInput; - setCleanupFunction: (cleanupFunction: () => void) => void; - } -) { - const { - data: { query: queryService }, - } = pluginServices.getServices(); - const { timefilter } = queryService; - const { timefilter: timefilterService } = timefilter; - - // apply initial dashboard saved filters, query, and time range to the query bar. - applySavedFiltersToUnifiedSearch.bind(this)(initialInput); - - // starts syncing `_g` portion of url with query services - const { stop: stopSyncingQueryServiceStateWithUrl } = syncGlobalQueryStateWithUrl( - queryService, - kbnUrlStateStorage - ); - - const initialTimeRange = initialInput.timeRestore ? undefined : timefilterService.getTime(); - this.untilInitialized().then(() => { - const stopSyncingUnifiedSearchState = syncUnifiedSearchState.bind(this)(kbnUrlStateStorage); - setCleanupFunction(() => { - stopSyncingQueryServiceStateWithUrl?.(); - stopSyncingUnifiedSearchState?.(); - }); - }); - return initialTimeRange; -} - -export function applySavedFiltersToUnifiedSearch( - this: DashboardContainer, - initialInput?: DashboardContainerByValueInput -) { - const { - data: { - query: { filterManager, queryString, timefilter }, - }, - } = pluginServices.getServices(); - const { timefilter: timefilterService } = timefilter; - - const input = initialInput - ? initialInput - : this.getReduxEmbeddableTools().getState().explicitInput; - const { filters, query, timeRestore, timeRange, refreshInterval } = input; - - // apply filters and query to the query service - filterManager.setAppFilters(cloneDeep(filters ?? [])); - queryString.setQuery(query ?? queryString.getDefaultQuery()); - - /** - * If a global time range is not set explicitly and the time range was saved with the dashboard, apply - * time range and refresh interval to the query service. - */ - if (timeRestore) { - if (timeRange) timefilterService.setTime(timeRange); - if (refreshInterval) timefilterService.setRefreshInterval(refreshInterval); - } -} diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_api.ts b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_api.ts new file mode 100644 index 0000000000000..ff71fdc70ccfb --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_api.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 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 { DashboardContainer } from '../embeddable/dashboard_container'; + +// TODO lock down DashboardAPI +export type DashboardAPI = DashboardContainer; +export type AwaitingDashboardAPI = DashboardAPI | null; + +export const buildApiFromDashboardContainer = (container?: DashboardContainer) => container ?? null; diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx new file mode 100644 index 0000000000000..4497812c4c716 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.test.tsx @@ -0,0 +1,102 @@ +/* + * 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 { ReactWrapper } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; + +import { DashboardContainerFactory } from '..'; +import { DASHBOARD_CONTAINER_TYPE } from '../..'; +import { DashboardRenderer } from './dashboard_renderer'; +import { pluginServices } from '../../services/plugin_services'; +import { DashboardContainer } from '../embeddable/dashboard_container'; +import { DashboardCreationOptions } from '../embeddable/dashboard_container_factory'; + +describe('dashboard renderer', () => { + let mockDashboardContainer: DashboardContainer; + let mockDashboardFactory: DashboardContainerFactory; + + beforeEach(() => { + mockDashboardContainer = { + destroy: jest.fn(), + render: jest.fn(), + isExpectingIdChange: jest.fn().mockReturnValue(false), + } as unknown as DashboardContainer; + mockDashboardFactory = { + create: jest.fn().mockReturnValue(mockDashboardContainer), + } as unknown as DashboardContainerFactory; + pluginServices.getServices().embeddable.getEmbeddableFactory = jest + .fn() + .mockReturnValue(mockDashboardFactory); + }); + + test('calls create method on the Dashboard embeddable factory', async () => { + await act(async () => { + mountWithIntl(); + }); + expect(pluginServices.getServices().embeddable.getEmbeddableFactory).toHaveBeenCalledWith( + DASHBOARD_CONTAINER_TYPE + ); + expect(mockDashboardFactory.create).toHaveBeenCalled(); + }); + + test('saved object id & creation options are passed to dashboard factory', async () => { + const options: DashboardCreationOptions = { + useControlGroupIntegration: true, + useSessionStorageIntegration: true, + useUnifiedSearchIntegration: true, + }; + await act(async () => { + mountWithIntl( + Promise.resolve(options)} + /> + ); + }); + expect(mockDashboardFactory.create).toHaveBeenCalledWith( + expect.any(Object), + undefined, + options, + 'saved_object_kibanana' + ); + }); + + test('destroys dashboard container on unmount', async () => { + let wrapper: ReactWrapper; + await act(async () => { + wrapper = await mountWithIntl(); + }); + wrapper!.unmount(); + expect(mockDashboardContainer.destroy).toHaveBeenCalledTimes(1); + }); + + test('destroys dashboard container on unexpected ID change', async () => { + let wrapper: ReactWrapper; + await act(async () => { + wrapper = await mountWithIntl(); + }); + await act(async () => { + await wrapper.setProps({ savedObjectId: 'saved_object_kibanakiwi' }); + }); + expect(mockDashboardContainer.destroy).toHaveBeenCalledTimes(1); + }); + + test('does not destroy dashboard container on expected ID change', async () => { + let wrapper: ReactWrapper; + await act(async () => { + wrapper = await mountWithIntl(); + }); + mockDashboardContainer.isExpectingIdChange = jest.fn().mockReturnValue(true); + await act(async () => { + await wrapper.setProps({ savedObjectId: 'saved_object_kibanakiwi' }); + }); + expect(mockDashboardContainer.destroy).not.toHaveBeenCalled(); + }); +}); diff --git a/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx new file mode 100644 index 0000000000000..04d85c2f06b9b --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx @@ -0,0 +1,140 @@ +/* + * 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 '../_dashboard_container.scss'; + +import React, { + useRef, + useMemo, + useState, + useEffect, + forwardRef, + useImperativeHandle, +} from 'react'; +import { v4 as uuidv4 } from 'uuid'; +import classNames from 'classnames'; + +import { EuiLoadingElastic, EuiLoadingSpinner } from '@elastic/eui'; + +import { + DashboardAPI, + AwaitingDashboardAPI, + buildApiFromDashboardContainer, +} from './dashboard_api'; +import { + DashboardCreationOptions, + DashboardContainerFactory, + DashboardContainerFactoryDefinition, +} from '../embeddable/dashboard_container_factory'; +import { DASHBOARD_CONTAINER_TYPE } from '..'; +import { DashboardContainerInput } from '../../../common'; +import type { DashboardContainer } from '../embeddable/dashboard_container'; + +export interface DashboardRendererProps { + savedObjectId?: string; + showPlainSpinner?: boolean; + getCreationOptions?: () => Promise; +} + +export const DashboardRenderer = forwardRef( + ({ savedObjectId, getCreationOptions, showPlainSpinner }, ref) => { + const dashboardRoot = useRef(null); + const [loading, setLoading] = useState(true); + const [screenshotMode, setScreenshotMode] = useState(false); + const [dashboardContainer, setDashboardContainer] = useState(); + const [dashboardIdToBuild, setDashboardIdToBuild] = useState(savedObjectId); + + useImperativeHandle( + ref, + () => buildApiFromDashboardContainer(dashboardContainer) as DashboardAPI, + [dashboardContainer] + ); + + useEffect(() => { + (async () => { + // Lazy loading all services is required in this component because it is exported and contributes to the bundle size. + const { pluginServices } = await import('../../services/plugin_services'); + const { + screenshotMode: { isScreenshotMode }, + } = pluginServices.getServices(); + setScreenshotMode(isScreenshotMode()); + })(); + }, []); + + useEffect(() => { + // check if dashboard container is expecting id change... if not, update dashboardIdToBuild to force it to rebuild the container. + if (!dashboardContainer) return; + if (!dashboardContainer.isExpectingIdChange()) setDashboardIdToBuild(savedObjectId); + + // Disabling exhaustive deps because this useEffect should only be triggered when the savedObjectId changes. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [savedObjectId]); + + const id = useMemo(() => uuidv4(), []); + + useEffect(() => { + let canceled = false; + let destroyContainer: () => void; + + (async () => { + const creationOptions = await getCreationOptions?.(); + + // Lazy loading all services is required in this component because it is exported and contributes to the bundle size. + const { pluginServices } = await import('../../services/plugin_services'); + const { embeddable } = pluginServices.getServices(); + + const dashboardFactory = embeddable.getEmbeddableFactory( + DASHBOARD_CONTAINER_TYPE + ) as DashboardContainerFactory & { create: DashboardContainerFactoryDefinition['create'] }; + const container = (await dashboardFactory?.create( + { id } as unknown as DashboardContainerInput, // Input from creationOptions is used instead. + undefined, + creationOptions, + savedObjectId + )) as DashboardContainer; + + if (canceled) { + container.destroy(); + return; + } + + setLoading(false); + if (dashboardRoot.current) { + container.render(dashboardRoot.current); + } + + setDashboardContainer(container); + destroyContainer = () => container.destroy(); + })(); + return () => { + canceled = true; + destroyContainer?.(); + }; + // Disabling exhaustive deps because embeddable should only be created when the dashboardIdToBuild changes. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dashboardIdToBuild]); + + const viewportClasses = classNames( + 'dashboardViewport', + { 'dashboardViewport--screenshotMode': screenshotMode }, + { 'dashboardViewport--loading': loading } + ); + + const loadingSpinner = showPlainSpinner ? ( + + ) : ( + + ); + + return ( +
+ {loading ? loadingSpinner :
} +
+ ); + } +); diff --git a/src/plugins/dashboard/public/dashboard_container/index.ts b/src/plugins/dashboard/public/dashboard_container/index.ts index 8484caad68db2..5aa300a33df28 100644 --- a/src/plugins/dashboard/public/dashboard_container/index.ts +++ b/src/plugins/dashboard/public/dashboard_container/index.ts @@ -6,18 +6,14 @@ * Side Public License, v 1. */ -import React from 'react'; - export const DASHBOARD_CONTAINER_TYPE = 'dashboard'; -export { useDashboardContainerContext } from './dashboard_container_context'; -export const LazyDashboardContainerRenderer = React.lazy( - () => import('./dashboard_container_renderer') -); - export type { DashboardContainer } from './embeddable/dashboard_container'; export { type DashboardContainerFactory, type DashboardCreationOptions, DashboardContainerFactoryDefinition, } from './embeddable/dashboard_container_factory'; + +export { DashboardRenderer } from './external_api/dashboard_renderer'; +export type { DashboardAPI, AwaitingDashboardAPI } from './external_api/dashboard_api'; diff --git a/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts b/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts index 9a5d135b0f61e..86a58bb72f639 100644 --- a/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/dashboard_container_reducers.ts @@ -7,13 +7,14 @@ */ import { PayloadAction } from '@reduxjs/toolkit'; + import { - DashboardPublicState, DashboardReduxState, + DashboardPublicState, DashboardStateFromSaveModal, DashboardStateFromSettingsFlyout, } from '../types'; -import { DashboardContainerByValueInput } from '../../../common'; +import { DashboardContainerInput } from '../../../common'; export const dashboardContainerReducers = { // ------------------------------------------------------------------------------ @@ -21,14 +22,14 @@ export const dashboardContainerReducers = { // ------------------------------------------------------------------------------ setControlGroupState: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.controlGroupInput = action.payload; }, setPanels: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.panels = action.payload; }, @@ -75,35 +76,32 @@ export const dashboardContainerReducers = { setDescription: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.description = action.payload; }, setViewMode: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.viewMode = action.payload; }, - setTags: ( - state: DashboardReduxState, - action: PayloadAction - ) => { + setTags: (state: DashboardReduxState, action: PayloadAction) => { state.explicitInput.tags = action.payload; }, setTitle: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.title = action.payload; }, setSearchSessionId: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.searchSessionId = action.payload; }, @@ -126,7 +124,10 @@ export const dashboardContainerReducers = { }, resetToLastSavedInput: (state: DashboardReduxState) => { - state.explicitInput = state.componentState.lastSavedInput; + state.explicitInput = { + ...state.componentState.lastSavedInput, + viewMode: state.explicitInput.viewMode, // keep current view mode when resetting + }; }, // ------------------------------------------------------------------------------ @@ -134,7 +135,7 @@ export const dashboardContainerReducers = { // ------------------------------------------------------------------------------ setFiltersAndQuery: ( state: DashboardReduxState, - action: PayloadAction> + action: PayloadAction> ) => { state.explicitInput.filters = action.payload.filters; state.explicitInput.query = action.payload.query; @@ -146,14 +147,14 @@ export const dashboardContainerReducers = { setFilters: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.filters = action.payload; }, setQuery: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.query = action.payload; }, @@ -167,28 +168,28 @@ export const dashboardContainerReducers = { setTimeRestore: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.timeRestore = action.payload; }, setTimeRange: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.timeRange = action.payload; }, setRefreshInterval: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.refreshInterval = action.payload; }, setTimeslice: ( state: DashboardReduxState, - action: PayloadAction + action: PayloadAction ) => { state.explicitInput.timeslice = action.payload; }, @@ -208,4 +209,12 @@ export const dashboardContainerReducers = { setHasOverlays: (state: DashboardReduxState, action: PayloadAction) => { state.componentState.hasOverlays = action.payload; }, + + setScrollToPanelId: (state: DashboardReduxState, action: PayloadAction) => { + state.componentState.scrollToPanelId = action.payload; + }, + + setHighlightPanelId: (state: DashboardReduxState, action: PayloadAction) => { + state.componentState.highlightPanelId = action.payload; + }, }; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts similarity index 90% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts rename to src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts index 36fd494bd0816..7f2a55044b527 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_functions.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_functions.ts @@ -17,27 +17,28 @@ import { } from '@kbn/es-query'; import { shouldRefreshFilterCompareOptions } from '@kbn/embeddable-plugin/public'; -import { DashboardContainer } from '../../dashboard_container'; -import { DashboardContainerByValueInput } from '../../../../../common'; +import { DashboardContainer } from '../../embeddable/dashboard_container'; +import { DashboardContainerInput } from '../../../../common'; +import { DashboardContainerInputWithoutId } from '../../types'; import { areTimesEqual, getPanelLayoutsAreEqual } from './dashboard_diffing_utils'; -export interface DiffFunctionProps { - currentValue: DashboardContainerByValueInput[Key]; - lastValue: DashboardContainerByValueInput[Key]; +export interface DiffFunctionProps { + currentValue: DashboardContainerInput[Key]; + lastValue: DashboardContainerInput[Key]; - currentInput: DashboardContainerByValueInput; - lastInput: DashboardContainerByValueInput; + currentInput: DashboardContainerInputWithoutId; + lastInput: DashboardContainerInputWithoutId; container: DashboardContainer; } export type DashboardDiffFunctions = { - [key in keyof Partial]: ( + [key in keyof Partial]: ( props: DiffFunctionProps ) => boolean | Promise; }; export const isKeyEqual = async ( - key: keyof DashboardContainerByValueInput, + key: keyof DashboardContainerInput, diffFunctionProps: DiffFunctionProps, diffingFunctions: DashboardDiffFunctions ) => { diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.test.ts similarity index 81% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts rename to src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.test.ts index 011fd833b6d55..c0953f8bbc98a 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.test.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.test.ts @@ -6,11 +6,13 @@ * Side Public License, v 1. */ -import { buildExistsFilter, disableFilter, pinFilter, toggleFilterNegated } from '@kbn/es-query'; import type { DataViewFieldBase, DataViewBase } from '@kbn/es-query'; +import { buildExistsFilter, disableFilter, pinFilter, toggleFilterNegated } from '@kbn/es-query'; + import { getShouldRefresh } from './dashboard_diffing_integration'; -import { DashboardContainer } from '../../dashboard_container'; -import { DashboardContainerByValueInput } from '../../../../../common'; + +import { DashboardContainerInput } from '../../../../common'; +import { DashboardContainer } from '../../embeddable/dashboard_container'; describe('getShouldRefresh', () => { const dashboardContainerMock = { @@ -30,7 +32,7 @@ describe('getShouldRefresh', () => { test('should return false when filters do not change', async () => { const lastInput = { filters: [existsFilter], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, lastInput)).toBe(false); }); @@ -38,10 +40,10 @@ describe('getShouldRefresh', () => { const pinnedFilter = pinFilter(existsFilter); const lastInput = { filters: [pinnedFilter], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; const input = { filters: [toggleFilterNegated(pinnedFilter)], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); }); @@ -49,20 +51,20 @@ describe('getShouldRefresh', () => { const disabledFilter = disableFilter(existsFilter); const lastInput = { filters: [disabledFilter], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; const input = { filters: [toggleFilterNegated(disabledFilter)], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(false); }); test('should return false when pinned filter changes to unpinned', async () => { const lastInput = { filters: [existsFilter], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; const input = { filters: [pinFilter(existsFilter)], - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(false); }); }); @@ -71,7 +73,7 @@ describe('getShouldRefresh', () => { test('should return false when timeRange does not change', async () => { const lastInput = { timeRange: { from: 'now-15m', to: 'now' }, - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, lastInput)).toBe(false); }); @@ -79,11 +81,11 @@ describe('getShouldRefresh', () => { const lastInput = { timeRange: { from: 'now-15m', to: 'now' }, timeRestore: true, - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; const input = { timeRange: { from: 'now-30m', to: 'now' }, timeRestore: true, - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); }); @@ -91,11 +93,11 @@ describe('getShouldRefresh', () => { const lastInput = { timeRange: { from: 'now-15m', to: 'now' }, timeRestore: false, - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; const input = { timeRange: { from: 'now-30m', to: 'now' }, timeRestore: false, - } as unknown as DashboardContainerByValueInput; + } as unknown as DashboardContainerInput; expect(await getShouldRefresh.bind(dashboardContainerMock)(lastInput, input)).toBe(true); }); }); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts similarity index 64% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts rename to src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts index 3f5f137d829fa..f91cfe51fe739 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_integration.ts @@ -6,27 +6,27 @@ * Side Public License, v 1. */ import { omit } from 'lodash'; -import { AnyAction, Middleware } from '@reduxjs/toolkit'; -import { debounceTime, Observable, Subject, switchMap } from 'rxjs'; +import { AnyAction, Middleware } from 'redux'; +import { debounceTime, Observable, startWith, Subject, switchMap } from 'rxjs'; -import { DashboardContainer } from '../../dashboard_container'; -import { pluginServices } from '../../../../services/plugin_services'; -import { DashboardContainerByValueInput } from '../../../../../common'; -import { CHANGE_CHECK_DEBOUNCE } from '../../../../dashboard_constants'; +import { DashboardContainerInput } from '../../../../common'; import type { DashboardDiffFunctions } from './dashboard_diffing_functions'; import { isKeyEqual, shouldRefreshDiffingFunctions, unsavedChangesDiffingFunctions, } from './dashboard_diffing_functions'; -import { dashboardContainerReducers } from '../../../state/dashboard_container_reducers'; +import { pluginServices } from '../../../services/plugin_services'; +import { DashboardContainer, DashboardCreationOptions } from '../..'; +import { CHANGE_CHECK_DEBOUNCE } from '../../../dashboard_constants'; +import { dashboardContainerReducers } from '../dashboard_container_reducers'; /** * An array of reducers which cannot cause unsaved changes. Unsaved changes only compares the explicit input * and the last saved input, so we can safely ignore any output reducers, and most componentState reducers. * This is only for performance reasons, because the diffing function itself can be quite heavy. */ -const reducersToIgnore: Array = [ +export const reducersToIgnore: Array = [ 'setTimeslice', 'setFullScreenMode', 'setSearchSessionId', @@ -37,11 +37,12 @@ const reducersToIgnore: Array = [ /** * Some keys will often have deviated from their last saved state, but should not persist over reloads */ -const keysToOmitFromSessionStorage: Array = [ +const keysToOmitFromSessionStorage: Array = [ 'lastReloadRequestTime', 'executionContext', 'searchSessionId', 'timeslice', + 'id', 'timeRange', // Current behaviour expects time range not to be backed up. Revisit this? 'refreshInterval', @@ -51,18 +52,19 @@ const keysToOmitFromSessionStorage: Array * Some keys will often have deviated from their last saved state, but should be * ignored when calculating whether or not this dashboard has unsaved changes. */ -export const keysNotConsideredUnsavedChanges: Array = [ +export const keysNotConsideredUnsavedChanges: Array = [ 'lastReloadRequestTime', 'executionContext', 'searchSessionId', 'timeslice', 'viewMode', + 'id', ]; /** * input keys that will cause a new session to be created. */ -const refetchKeys: Array = [ +const refetchKeys: Array = [ 'query', 'filters', 'timeRange', @@ -82,17 +84,35 @@ const refetchKeys: Array = [ */ export function startDiffingDashboardState( this: DashboardContainer, - { - useSessionBackup, - setCleanupFunction, - }: { - useSessionBackup?: boolean; - setCleanupFunction: (cleanupFunction: () => void) => void; - } + creationOptions?: DashboardCreationOptions ) { const checkForUnsavedChangesSubject$ = new Subject(); - - // middleware starts the check for unsaved changes function if the action dispatched could cause them. + this.subscriptions.add( + checkForUnsavedChangesSubject$ + .pipe( + startWith(null), + debounceTime(CHANGE_CHECK_DEBOUNCE), + switchMap(() => { + return new Observable((observer) => { + const { + explicitInput: currentInput, + componentState: { lastSavedInput }, + } = this.getState(); + getUnsavedChanges + .bind(this)(lastSavedInput, currentInput) + .then((unsavedChanges) => { + if (observer.closed) return; + + updateUnsavedChangesState.bind(this)(unsavedChanges); + if (creationOptions?.useSessionStorageIntegration) { + backupUnsavedChanges.bind(this)(unsavedChanges); + } + }); + }); + }) + ) + .subscribe() + ); const diffingMiddleware: Middleware = (store) => (next) => (action) => { const dispatchedActionName = action.type.split('/')?.[1]; if ( @@ -104,81 +124,28 @@ export function startDiffingDashboardState( } next(action); }; - - const getHasUnsavedChangesSubscription = checkForUnsavedChangesSubject$ - .pipe( - debounceTime(CHANGE_CHECK_DEBOUNCE), - switchMap(() => { - return new Observable((observer) => { - const { - explicitInput: currentInput, - componentState: { lastSavedInput }, - } = this.getReduxEmbeddableTools().getState(); - getUnsavedChanges - .bind(this)(lastSavedInput, currentInput) - .then((unsavedChanges) => { - if (observer.closed) return; - - updateUnsavedChangesState.bind(this)(unsavedChanges); - if (useSessionBackup) backupUnsavedChanges.bind(this)(unsavedChanges); - }); - }); - }) - ) - .subscribe(); - - setCleanupFunction(() => getHasUnsavedChangesSubscription.unsubscribe()); - return diffingMiddleware; } -function updateUnsavedChangesState( - this: DashboardContainer, - unsavedChanges: Partial -) { - const { - getState, - dispatch, - actions: { setHasUnsavedChanges }, - } = this.getReduxEmbeddableTools(); - - // dispatch has unsaved changes state - const hasChanges = Object.keys(omit(unsavedChanges, keysNotConsideredUnsavedChanges)).length > 0; - if (getState().componentState.hasUnsavedChanges !== hasChanges) { - dispatch(setHasUnsavedChanges(hasChanges)); - } -} - -function backupUnsavedChanges( - this: DashboardContainer, - unsavedChanges: Partial -) { - const { dashboardSessionStorage } = pluginServices.getServices(); - dashboardSessionStorage.setState( - this.getDashboardSavedObjectId(), - omit(unsavedChanges, keysToOmitFromSessionStorage) - ); -} - /** * Does a shallow diff between @param lastInput and @param input and * @returns an object out of the keys which are different. */ export async function getUnsavedChanges( this: DashboardContainer, - lastInput: DashboardContainerByValueInput, - input: DashboardContainerByValueInput -): Promise> { + lastInput: DashboardContainerInput, + input: DashboardContainerInput +): Promise> { const allKeys = [...new Set([...Object.keys(lastInput), ...Object.keys(input)])] as Array< - keyof DashboardContainerByValueInput + keyof DashboardContainerInput >; return await getInputChanges(this, lastInput, input, allKeys, unsavedChangesDiffingFunctions); } export async function getShouldRefresh( this: DashboardContainer, - lastInput: DashboardContainerByValueInput, - input: DashboardContainerByValueInput + lastInput: DashboardContainerInput, + input: DashboardContainerInput ): Promise { const inputChanges = await getInputChanges( this, @@ -192,15 +159,14 @@ export async function getShouldRefresh( async function getInputChanges( container: DashboardContainer, - lastInput: DashboardContainerByValueInput, - input: DashboardContainerByValueInput, - keys: Array, + lastInput: DashboardContainerInput, + input: DashboardContainerInput, + keys: Array, diffingFunctions: DashboardDiffFunctions -): Promise> { - await container.untilInitialized(); +): Promise> { const keyComparePromises = keys.map( (key) => - new Promise<{ key: keyof DashboardContainerByValueInput; isEqual: boolean }>((resolve) => { + new Promise<{ key: keyof DashboardContainerInput; isEqual: boolean }>((resolve) => { if (input[key] === undefined && lastInput[key] === undefined) { resolve({ key, isEqual: true }); } @@ -226,6 +192,28 @@ async function getInputChanges( if (!isEqual) (changes as { [key: string]: unknown })[key] = input[key]; } return changes; - }, {} as Partial); + }, {} as Partial); return inputChanges; } + +function updateUnsavedChangesState( + this: DashboardContainer, + unsavedChanges: Partial +) { + // dispatch has unsaved changes state + const hasChanges = Object.keys(omit(unsavedChanges, keysNotConsideredUnsavedChanges)).length > 0; + if (this.getState().componentState.hasUnsavedChanges !== hasChanges) { + this.dispatch.setHasUnsavedChanges(hasChanges); + } +} + +function backupUnsavedChanges( + this: DashboardContainer, + unsavedChanges: Partial +) { + const { dashboardSessionStorage } = pluginServices.getServices(); + dashboardSessionStorage.setState( + this.getDashboardSavedObjectId(), + omit(unsavedChanges, keysToOmitFromSessionStorage) + ); +} diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_utils.ts b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_utils.ts similarity index 97% rename from src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_utils.ts rename to src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_utils.ts index cc69acd8762c3..0b6d2db559b5a 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_utils.ts +++ b/src/plugins/dashboard/public/dashboard_container/state/diffing/dashboard_diffing_utils.ts @@ -10,7 +10,7 @@ import { isEmpty, xor } from 'lodash'; import moment, { Moment } from 'moment'; import fastIsEqual from 'fast-deep-equal'; -import { DashboardPanelMap } from '../../../../../common'; +import { DashboardPanelMap } from '../../../../common'; const convertTimeToUTCString = (time?: string | Moment): undefined | string => { if (moment(time).isValid()) { diff --git a/src/plugins/dashboard/public/dashboard_container/types.ts b/src/plugins/dashboard/public/dashboard_container/types.ts index 436a6ab0029d8..544317d9f6bcc 100644 --- a/src/plugins/dashboard/public/dashboard_container/types.ts +++ b/src/plugins/dashboard/public/dashboard_container/types.ts @@ -8,16 +8,16 @@ import type { ContainerOutput } from '@kbn/embeddable-plugin/public'; import type { ReduxEmbeddableState } from '@kbn/presentation-util-plugin/public'; -import type { DashboardContainerByValueInput, DashboardOptions } from '../../common'; +import type { DashboardContainerInput, DashboardOptions } from '../../common'; export type DashboardReduxState = ReduxEmbeddableState< - DashboardContainerByValueInput, + DashboardContainerInput, DashboardContainerOutput, DashboardPublicState >; export type DashboardStateFromSaveModal = Pick< - DashboardContainerByValueInput, + DashboardContainerInput, 'title' | 'description' | 'tags' | 'timeRestore' | 'timeRange' | 'refreshInterval' > & Pick; @@ -25,7 +25,7 @@ export type DashboardStateFromSaveModal = Pick< export type DashboardStateFromSettingsFlyout = DashboardStateFromSaveModal & DashboardOptions; export interface DashboardPublicState { - lastSavedInput: DashboardContainerByValueInput; + lastSavedInput: DashboardContainerInput; isEmbeddedExternally?: boolean; hasUnsavedChanges?: boolean; hasOverlays?: boolean; @@ -33,6 +33,8 @@ export interface DashboardPublicState { fullScreenMode?: boolean; savedQueryId?: string; lastSavedId?: string; + scrollToPanelId?: string; + highlightPanelId?: string; } export interface DashboardRenderPerformanceStats { @@ -41,6 +43,8 @@ export interface DashboardRenderPerformanceStats { panelsRenderStartTime: number; } +export type DashboardContainerInputWithoutId = Omit; + export interface DashboardContainerOutput extends ContainerOutput { usedDataViewIds?: string[]; } diff --git a/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts b/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts index df036a01fcf95..8887272da3d91 100644 --- a/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts +++ b/src/plugins/dashboard/public/dashboard_listing/_dashboard_listing_strings.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; export const dashboardListingErrorStrings = { @@ -91,32 +92,32 @@ export const dashboardUnsavedListingStrings = { defaultMessage: 'Continue editing', }), getDiscardAriaLabel: (title: string) => - i18n.translate('dashboard.listing.unsaved.discardAria', { - defaultMessage: 'Discard changes to {title}', + i18n.translate('dashboard.listing.unsaved.resetAria', { + defaultMessage: 'Reset changes to {title}', values: { title }, }), getDiscardTitle: () => - i18n.translate('dashboard.listing.unsaved.discardTitle', { - defaultMessage: 'Discard changes', + i18n.translate('dashboard.listing.unsaved.resetTitle', { + defaultMessage: 'Reset changes', }), }; -export const discardConfirmStrings = { - getDiscardTitle: () => - i18n.translate('dashboard.discardChangesConfirmModal.discardChangesTitle', { - defaultMessage: 'Discard changes to dashboard?', - }), - getDiscardSubtitle: () => - i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { - defaultMessage: `Once you discard your changes, there's no getting them back.`, - }), - getDiscardConfirmButtonText: () => - i18n.translate('dashboard.discardChangesConfirmModal.confirmButtonLabel', { - defaultMessage: 'Discard changes', - }), - getDiscardCancelButtonText: () => - i18n.translate('dashboard.discardChangesConfirmModal.cancelButtonLabel', { - defaultMessage: 'Cancel', +export const resetConfirmStrings = { + getResetTitle: () => + i18n.translate('dashboard.resetChangesConfirmModal.resetChangesTitle', { + defaultMessage: 'Reset dashboard?', + }), + getResetSubtitle: (viewMode: ViewMode) => + viewMode === ViewMode.EDIT + ? i18n.translate('dashboard.discardChangesConfirmModal.discardChangesDescription', { + defaultMessage: `All unsaved changes will be lost.`, + }) + : i18n.translate('dashboard.resetChangesConfirmModal.resetChangesDescription', { + defaultMessage: `This dashboard will return to its last saved state. You might lose changes to filters and queries.`, + }), + getResetConfirmButtonText: () => + i18n.translate('dashboard.resetChangesConfirmModal.confirmButtonLabel', { + defaultMessage: 'Reset dashboard', }), }; diff --git a/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx index 03027cda242b9..40f4367059eff 100644 --- a/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/confirm_overlays.tsx @@ -20,24 +20,28 @@ import { EuiText, EUI_MODAL_CANCEL_BUTTON, } from '@elastic/eui'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { pluginServices } from '../services/plugin_services'; -import { createConfirmStrings, discardConfirmStrings } from './_dashboard_listing_strings'; +import { createConfirmStrings, resetConfirmStrings } from './_dashboard_listing_strings'; export type DiscardOrKeepSelection = 'cancel' | 'discard' | 'keep'; -export const confirmDiscardUnsavedChanges = (discardCallback: () => void) => { +export const confirmDiscardUnsavedChanges = ( + discardCallback: () => void, + viewMode: ViewMode = ViewMode.EDIT // we want to show the danger modal on the listing page +) => { const { overlays: { openConfirm }, } = pluginServices.getServices(); - openConfirm(discardConfirmStrings.getDiscardSubtitle(), { - confirmButtonText: discardConfirmStrings.getDiscardConfirmButtonText(), - cancelButtonText: discardConfirmStrings.getDiscardCancelButtonText(), - buttonColor: 'danger', + openConfirm(resetConfirmStrings.getResetSubtitle(viewMode), { + confirmButtonText: resetConfirmStrings.getResetConfirmButtonText(), + buttonColor: viewMode === ViewMode.EDIT ? 'danger' : 'primary', + maxWidth: 500, defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON, - title: discardConfirmStrings.getDiscardTitle(), + title: resetConfirmStrings.getResetTitle(), }).then((isConfirmed) => { if (isConfirmed) { discardCallback(); diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 8572356687fd6..9d55fafd94e5b 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -234,7 +234,7 @@ export const DashboardListing = ({ createItem={!showWriteControls ? undefined : createItem} editItem={!showWriteControls ? undefined : editItem} entityNamePlural={getEntityNamePlural()} - tableListTitle={getTableListTitle()} + title={getTableListTitle()} headingId="dashboardListingHeading" initialPageSize={initialPageSize} initialFilter={initialFilter} diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index af7c9a307957d..0bfe5dfae8b4c 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -15,11 +15,11 @@ export { LEGACY_DASHBOARD_APP_ID, } from './dashboard_constants'; export { + type DashboardAPI, + type AwaitingDashboardAPI, + DashboardRenderer, DASHBOARD_CONTAINER_TYPE, - type DashboardContainer, type DashboardCreationOptions, - LazyDashboardContainerRenderer, - useDashboardContainerContext, } from './dashboard_container'; export type { DashboardSetup, DashboardStart, DashboardFeatureFlagConfig } from './plugin'; diff --git a/src/plugins/dashboard/public/mocks.tsx b/src/plugins/dashboard/public/mocks.tsx index c6668630548a0..69ec7efadf1ad 100644 --- a/src/plugins/dashboard/public/mocks.tsx +++ b/src/plugins/dashboard/public/mocks.tsx @@ -6,14 +6,12 @@ * Side Public License, v 1. */ -import { Embeddable, EmbeddableInput, ViewMode } from '@kbn/embeddable-plugin/public'; -import { createReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public/redux_embeddables/create_redux_embeddable_tools'; +import { EmbeddableInput, ViewMode } from '@kbn/embeddable-plugin/public'; +import { mockedReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public/mocks'; import { DashboardStart } from './plugin'; -import { DashboardContainerByValueInput, DashboardPanelState } from '../common'; -import { DashboardContainerOutput, DashboardReduxState } from './dashboard_container/types'; +import { DashboardContainerInput, DashboardPanelState } from '../common'; import { DashboardContainer } from './dashboard_container/embeddable/dashboard_container'; -import { dashboardContainerReducers } from './dashboard_container/state/dashboard_container_reducers'; export type Start = jest.Mocked; @@ -68,28 +66,15 @@ export function setupIntersectionObserverMock({ }); } -export const mockDashboardReduxEmbeddableTools = async ( - partialState?: Partial -) => { - const mockDashboard = new DashboardContainer( - getSampleDashboardInput(partialState?.explicitInput) - ) as Embeddable; - - const mockReduxEmbeddableTools = createReduxEmbeddableTools({ - embeddable: mockDashboard, - reducers: dashboardContainerReducers, - initialComponentState: { lastSavedInput: mockDashboard.getInput() }, - }); - - return { - tools: mockReduxEmbeddableTools, - dashboardContainer: mockDashboard as DashboardContainer, - }; -}; +export function buildMockDashboard(overrides?: Partial) { + const initialInput = getSampleDashboardInput(overrides); + const dashboardContainer = new DashboardContainer(initialInput, mockedReduxEmbeddablePackage); + return dashboardContainer; +} export function getSampleDashboardInput( - overrides?: Partial -): DashboardContainerByValueInput { + overrides?: Partial +): DashboardContainerInput { return { // options useMargins: true, diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts index 15304119e31e9..57e5ea2a21836 100644 --- a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts @@ -20,8 +20,8 @@ import { rawControlGroupAttributesToControlGroupInput } from '@kbn/controls-plug import { parseSearchSourceJSON, injectSearchSourceReferences } from '@kbn/data-plugin/public'; import { - DashboardContainerByValueInput, convertSavedPanelsToPanelMap, + DashboardContainerInput, DashboardAttributes, DashboardOptions, injectReferences, @@ -47,7 +47,7 @@ export interface LoadDashboardFromSavedObjectReturn { dashboardFound: boolean; dashboardId?: string; resolveMeta?: Omit; - dashboardInput: DashboardContainerByValueInput; + dashboardInput: DashboardContainerInput; } export const loadDashboardStateFromSavedObject = async ({ diff --git a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts index fbb897fca68d4..d19799c02bff5 100644 --- a/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts +++ b/src/plugins/dashboard/public/services/dashboard_saved_object/lib/save_dashboard_state_to_saved_object.ts @@ -24,7 +24,7 @@ import { extractReferences, DashboardAttributes, convertPanelMapToSavedPanels, - DashboardContainerByValueInput, + DashboardContainerInput, } from '../../../../common'; import { DashboardSavedObjectRequiredServices } from '../types'; import { DASHBOARD_SAVED_OBJECT_TYPE } from '../../../dashboard_constants'; @@ -34,13 +34,13 @@ export type SavedDashboardSaveOpts = SavedObjectSaveOpts & { saveAsCopy?: boolea export type SaveDashboardProps = DashboardSavedObjectRequiredServices & { savedObjectsClient: SavedObjectsClientContract; - currentState: DashboardContainerByValueInput; + currentState: DashboardContainerInput; saveOptions: SavedDashboardSaveOpts; lastSavedId?: string; }; export const serializeControlGroupInput = ( - controlGroupInput: DashboardContainerByValueInput['controlGroupInput'] + controlGroupInput: DashboardContainerInput['controlGroupInput'] ) => { // only save to saved object if control group is not default if ( diff --git a/src/plugins/dashboard/public/services/dashboard_session_storage/dashboard_session_storage_service.ts b/src/plugins/dashboard/public/services/dashboard_session_storage/dashboard_session_storage_service.ts index f949657f882d4..279d7fefb6d3d 100644 --- a/src/plugins/dashboard/public/services/dashboard_session_storage/dashboard_session_storage_service.ts +++ b/src/plugins/dashboard/public/services/dashboard_session_storage/dashboard_session_storage_service.ts @@ -16,7 +16,7 @@ import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/p import { DashboardSpacesService } from '../spaces/types'; import type { DashboardStartDependencies } from '../../plugin'; import type { DashboardSessionStorageServiceType } from './types'; -import type { DashboardContainerByValueInput } from '../../../common'; +import type { DashboardContainerInput } from '../../../common'; import { DashboardNotificationsService } from '../notifications/types'; import { panelStorageErrorStrings } from '../../dashboard_container/_dashboard_container_strings'; @@ -68,9 +68,7 @@ class DashboardSessionStorageService implements DashboardSessionStorageServiceTy } } - public getState( - id = DASHBOARD_PANELS_UNSAVED_ID - ): Partial | undefined { + public getState(id = DASHBOARD_PANELS_UNSAVED_ID): Partial | undefined { try { return this.sessionStorage.get(DASHBOARD_PANELS_SESSION_KEY)?.[this.activeSpaceId]?.[id]; } catch (e) { @@ -81,10 +79,7 @@ class DashboardSessionStorageService implements DashboardSessionStorageServiceTy } } - public setState( - id = DASHBOARD_PANELS_UNSAVED_ID, - newState: Partial - ) { + public setState(id = DASHBOARD_PANELS_UNSAVED_ID, newState: Partial) { try { const sessionStateStorage = this.sessionStorage.get(DASHBOARD_PANELS_SESSION_KEY) || {}; set(sessionStateStorage, [this.activeSpaceId, id], newState); diff --git a/src/plugins/dashboard/public/services/dashboard_session_storage/types.ts b/src/plugins/dashboard/public/services/dashboard_session_storage/types.ts index bfd0dc7656598..e02d6463e2d3a 100644 --- a/src/plugins/dashboard/public/services/dashboard_session_storage/types.ts +++ b/src/plugins/dashboard/public/services/dashboard_session_storage/types.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import type { DashboardContainerByValueInput } from '../../../common'; +import type { DashboardContainerInput } from '../../../common'; export interface DashboardSessionStorageServiceType { clearState: (id?: string) => void; - getState: (id: string | undefined) => Partial | undefined; - setState: (id: string | undefined, newState: Partial) => void; + getState: (id: string | undefined) => Partial | undefined; + setState: (id: string | undefined, newState: Partial) => void; getDashboardIdsWithUnsavedChanges: () => string[]; dashboardHasUnsavedEdits: (id?: string) => boolean; } diff --git a/src/plugins/dashboard/server/dashboard_saved_object/dashboard_saved_object.ts b/src/plugins/dashboard/server/dashboard_saved_object/dashboard_saved_object.ts index b8474149ca87b..9844d565ee36f 100644 --- a/src/plugins/dashboard/server/dashboard_saved_object/dashboard_saved_object.ts +++ b/src/plugins/dashboard/server/dashboard_saved_object/dashboard_saved_object.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { createDashboardSavedObjectTypeMigrations, @@ -18,6 +19,7 @@ export const createDashboardSavedObjectType = ({ migrationDeps: DashboardSavedObjectTypeMigrationsDeps; }): SavedObjectsType => ({ name: 'dashboard', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts b/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts index 1ca13b4308586..bac38fdbb5c75 100644 --- a/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts +++ b/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts @@ -114,12 +114,14 @@ export function dashboardTaskRunner(logger: Logger, core: CoreSetup, embeddable: return dashboardData; }; - const kibanaIndex = core.savedObjects.getKibanaIndex(); + const dashboardIndex = await core + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects.getIndexForType('dashboard')); const pageSize = 50; const searchParams = { size: pageSize, - index: kibanaIndex, + index: dashboardIndex, ignore_unavailable: true, filter_path: ['hits.hits', '_scroll_id'], body: { query: { bool: { filter: { term: { type: 'dashboard' } } } } }, diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 4bfb899c5301d..ba94e006057ec 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -56,6 +56,7 @@ "@kbn/saved-objects-finder-plugin", "@kbn/saved-objects-management-plugin", "@kbn/shared-ux-button-toolbar", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts b/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts index 6302af57166c4..35a285f2f5f30 100644 --- a/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts +++ b/src/plugins/data/server/kql_telemetry/kql_telemetry_service.ts @@ -28,7 +28,9 @@ export class KqlTelemetryService implements Plugin { if (usageCollection) { try { - makeKQLUsageCollector(usageCollection, savedObjects.getKibanaIndex()); + const getIndexForType = (type: string) => + getStartServices().then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); + makeKQLUsageCollector(usageCollection, getIndexForType); } catch (e) { this.initializerContext.logger .get('kql-telemetry') diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts index 679b2e67158eb..91158343885ab 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts @@ -75,7 +75,7 @@ function setupMockCallCluster( describe('makeKQLUsageCollector', () => { describe('fetch method', () => { beforeEach(() => { - fetch = fetchProvider('.kibana'); + fetch = fetchProvider(() => Promise.resolve('.kibana')); }); it('should return opt in data from the .kibana/kql-telemetry doc', async () => { diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts index 3f0d2129c6b92..90499f1e920bc 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts @@ -18,19 +18,19 @@ export interface Usage { defaultQueryLanguage: string; } -export function fetchProvider(index: string) { +export function fetchProvider(getIndexForType: (type: string) => Promise) { return async ({ esClient }: CollectorFetchContext): Promise => { const [response, config] = await Promise.all([ esClient.get( { - index, + index: await getIndexForType('kql-telemetry'), id: 'kql-telemetry:kql-telemetry', }, { ignore: [404] } ), esClient.search( { - index, + index: await getIndexForType('config'), body: { query: { term: { type: 'config' } } }, }, { ignore: [404] } diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.test.ts b/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.test.ts index 129bf59ca80a9..a96346714c0f9 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.test.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.test.ts @@ -12,6 +12,8 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; describe('makeKQLUsageCollector', () => { let usageCollectionMock: jest.Mocked; + const getIndexForType = () => Promise.resolve('.kibana'); + beforeEach(() => { usageCollectionMock = { makeUsageCollector: jest.fn(), @@ -20,12 +22,12 @@ describe('makeKQLUsageCollector', () => { }); it('should call registerCollector', () => { - makeKQLUsageCollector(usageCollectionMock as UsageCollectionSetup, '.kibana'); + makeKQLUsageCollector(usageCollectionMock as UsageCollectionSetup, getIndexForType); expect(usageCollectionMock.registerCollector).toHaveBeenCalledTimes(1); }); it('should call makeUsageCollector with type = kql', () => { - makeKQLUsageCollector(usageCollectionMock as UsageCollectionSetup, '.kibana'); + makeKQLUsageCollector(usageCollectionMock as UsageCollectionSetup, getIndexForType); expect(usageCollectionMock.makeUsageCollector).toHaveBeenCalledTimes(1); expect(usageCollectionMock.makeUsageCollector.mock.calls[0][0].type).toBe('kql'); }); diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.ts b/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.ts index 885c6b1807e92..6a9190a953fdd 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/make_kql_usage_collector.ts @@ -9,10 +9,13 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { fetchProvider, Usage } from './fetch'; -export function makeKQLUsageCollector(usageCollection: UsageCollectionSetup, kibanaIndex: string) { +export function makeKQLUsageCollector( + usageCollection: UsageCollectionSetup, + getIndexForType: (type: string) => Promise +) { const kqlUsageCollector = usageCollection.makeUsageCollector({ type: 'kql', - fetch: fetchProvider(kibanaIndex), + fetch: fetchProvider(getIndexForType), isReady: () => true, schema: { optInCount: { type: 'long' }, diff --git a/src/plugins/data/server/saved_objects/kql_telemetry.ts b/src/plugins/data/server/saved_objects/kql_telemetry.ts index 51575ad2d3d54..15758d4ba5be9 100644 --- a/src/plugins/data/server/saved_objects/kql_telemetry.ts +++ b/src/plugins/data/server/saved_objects/kql_telemetry.ts @@ -6,11 +6,13 @@ * Side Public License, v 1. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { SCHEMA_KQL_TELEMETRY_V8_8_0 } from './schemas/kql_telemetry'; export const kqlTelemetry: SavedObjectsType = { name: 'kql-telemetry', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, namespaceType: 'agnostic', hidden: false, mappings: { diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts index cf75c28743d4f..f59adbbef8750 100644 --- a/src/plugins/data/server/saved_objects/query.ts +++ b/src/plugins/data/server/saved_objects/query.ts @@ -6,12 +6,14 @@ * Side Public License, v 1. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { savedQueryMigrations } from './migrations/query'; import { SCHEMA_QUERY_V8_8_0 } from './schemas/query'; export const querySavedObjectType: SavedObjectsType = { name: 'query', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/src/plugins/data/server/saved_objects/search_telemetry.ts b/src/plugins/data/server/saved_objects/search_telemetry.ts index 2f72712801648..581e719a9183c 100644 --- a/src/plugins/data/server/saved_objects/search_telemetry.ts +++ b/src/plugins/data/server/saved_objects/search_telemetry.ts @@ -6,12 +6,14 @@ * Side Public License, v 1. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { migrate712 } from './migrations/to_v7_12_0'; import { SCHEMA_SEARCH_TELEMETRY_V8_8_0 } from './schemas/search_telemetry'; export const searchTelemetry: SavedObjectsType = { name: 'search-telemetry', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, namespaceType: 'agnostic', hidden: false, mappings: { diff --git a/src/plugins/data/server/search/collectors/search/fetch.ts b/src/plugins/data/server/search/collectors/search/fetch.ts index 08f365d0a6073..5fcecd1e60a65 100644 --- a/src/plugins/data/server/search/collectors/search/fetch.ts +++ b/src/plugins/data/server/search/collectors/search/fetch.ts @@ -13,11 +13,12 @@ interface SearchTelemetry { 'search-telemetry': CollectedUsage; } -export function fetchProvider(kibanaIndex: string) { +export function fetchProvider(getIndexForType: (type: string) => Promise) { return async ({ esClient }: CollectorFetchContext): Promise => { + const searchIndex = await getIndexForType('search-telemetry'); const esResponse = await esClient.search( { - index: kibanaIndex, + index: searchIndex, body: { query: { term: { type: { value: 'search-telemetry' } } }, }, diff --git a/src/plugins/data/server/search/collectors/search/register.ts b/src/plugins/data/server/search/collectors/search/register.ts index 927c5274e8128..f335e8c5de7c5 100644 --- a/src/plugins/data/server/search/collectors/search/register.ts +++ b/src/plugins/data/server/search/collectors/search/register.ts @@ -21,12 +21,15 @@ export interface ReportedUsage { averageDuration: number | null; } -export function registerUsageCollector(usageCollection: UsageCollectionSetup, kibanaIndex: string) { +export function registerUsageCollector( + usageCollection: UsageCollectionSetup, + getIndexForType: (type: string) => Promise +) { try { const collector = usageCollection.makeUsageCollector({ type: 'search', isReady: () => true, - fetch: fetchProvider(kibanaIndex), + fetch: fetchProvider(getIndexForType), schema: { successCount: { type: 'long' }, errorCount: { type: 'long' }, diff --git a/src/plugins/data/server/search/collectors/search_session/fetch.test.ts b/src/plugins/data/server/search/collectors/search_session/fetch.test.ts index 1290b06b94ee0..e0fc723de44d1 100644 --- a/src/plugins/data/server/search/collectors/search_session/fetch.test.ts +++ b/src/plugins/data/server/search/collectors/search_session/fetch.test.ts @@ -17,12 +17,13 @@ describe('fetchProvider', () => { beforeEach(async () => { const kibanaIndex = '123'; + const getIndexForType = () => Promise.resolve(kibanaIndex); mockLogger = { warn: jest.fn(), debug: jest.fn(), } as any; esClient = elasticsearchServiceMock.createElasticsearchClient(); - fetchFn = fetchProvider(kibanaIndex, mockLogger); + fetchFn = fetchProvider(getIndexForType, mockLogger); }); test('returns when ES returns no results', async () => { diff --git a/src/plugins/data/server/search/collectors/search_session/fetch.ts b/src/plugins/data/server/search/collectors/search_session/fetch.ts index 536ca4b168188..08660f4adb136 100644 --- a/src/plugins/data/server/search/collectors/search_session/fetch.ts +++ b/src/plugins/data/server/search/collectors/search_session/fetch.ts @@ -17,11 +17,12 @@ interface SessionPersistedTermsBucket { doc_count: number; } -export function fetchProvider(kibanaIndex: string, logger: Logger) { +export function fetchProvider(getIndexForType: (type: string) => Promise, logger: Logger) { return async ({ esClient }: CollectorFetchContext): Promise => { try { + const searchSessionIndex = await getIndexForType(SEARCH_SESSION_TYPE); const esResponse = await esClient.search({ - index: kibanaIndex, + index: searchSessionIndex, body: { size: 0, aggs: { diff --git a/src/plugins/data/server/search/collectors/search_session/register.ts b/src/plugins/data/server/search/collectors/search_session/register.ts index c6ea85a2b53d3..b30b1fb888797 100644 --- a/src/plugins/data/server/search/collectors/search_session/register.ts +++ b/src/plugins/data/server/search/collectors/search_session/register.ts @@ -18,14 +18,14 @@ export interface ReportedUsage { export function registerUsageCollector( usageCollection: UsageCollectionSetup, - kibanaIndex: string, + getIndexForType: (type: string) => Promise, logger: Logger ) { try { const collector = usageCollection.makeUsageCollector({ type: 'search-session', isReady: () => true, - fetch: fetchProvider(kibanaIndex, logger), + fetch: fetchProvider(getIndexForType, logger), schema: { transientCount: { type: 'long' }, persistedCount: { type: 'long' }, diff --git a/src/plugins/data/server/search/saved_objects/search_session.ts b/src/plugins/data/server/search/saved_objects/search_session.ts index 598229075df42..67f3acb819b98 100644 --- a/src/plugins/data/server/search/saved_objects/search_session.ts +++ b/src/plugins/data/server/search/saved_objects/search_session.ts @@ -7,12 +7,14 @@ */ import { schema } from '@kbn/config-schema'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { SEARCH_SESSION_TYPE } from '../../../common'; import { searchSessionSavedObjectMigrations } from './search_session_migration'; export const searchSessionSavedObjectType: SavedObjectsType = { name: SEARCH_SESSION_TYPE, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, namespaceType: 'single', hidden: true, mappings: { diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index eba7ea22acf4f..8189177c4b58b 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -213,12 +213,10 @@ export class SearchService implements Plugin { core.savedObjects.registerType(searchTelemetry); if (usageCollection) { - registerSearchUsageCollector(usageCollection, core.savedObjects.getKibanaIndex()); - registerSearchSessionUsageCollector( - usageCollection, - core.savedObjects.getKibanaIndex(), - this.logger - ); + const getIndexForType = (type: string) => + core.getStartServices().then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); + registerSearchUsageCollector(usageCollection, getIndexForType); + registerSearchSessionUsageCollector(usageCollection, getIndexForType, this.logger); } expressions.registerFunction(getEsaggs({ getStartServices: core.getStartServices })); diff --git a/src/plugins/data/tsconfig.json b/src/plugins/data/tsconfig.json index dc9b57dca02df..9d7fb6227448a 100644 --- a/src/plugins/data/tsconfig.json +++ b/src/plugins/data/tsconfig.json @@ -47,6 +47,7 @@ "@kbn/config", "@kbn/config-schema", "@kbn/core-application-browser", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index e356c76ddb989..e76cf6960cc23 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -149,7 +149,7 @@ export const WithFieldEditorDependencies = }; const mergedDependencies = merge({}, dependencies, overridingDependencies); - const previewController = new PreviewController({ dataView, search }); + const previewController = new PreviewController({ dataView, search, fieldFormats }); return ( diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx index fddf14c864743..077471b44c237 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx @@ -24,7 +24,7 @@ import { } from '../../../shared_imports'; import type { RuntimeFieldPainlessError } from '../../../types'; import { painlessErrorToMonacoMarker } from '../../../lib'; -import { useFieldPreviewContext, Context } from '../../preview'; +import { useFieldPreviewContext } from '../../preview'; import { schema } from '../form_schema'; import type { FieldFormInternal } from '../field_editor'; import { useStateSelector } from '../../../state_utils'; @@ -57,6 +57,7 @@ const mapReturnTypeToPainlessContext = (runtimeType: RuntimeType): PainlessConte const currentDocumentSelector = (state: PreviewState) => state.documents[state.currentIdx]; const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadingDocuments; +const currentErrorSelector = (state: PreviewState) => state.previewResponse?.error; const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => { const { @@ -66,14 +67,15 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr const editorValidationSubscription = useRef(); const fieldCurrentValue = useRef(''); - const { error, isLoadingPreview, isPreviewAvailable, controller } = useFieldPreviewContext(); + const { isLoadingPreview, isPreviewAvailable, controller } = useFieldPreviewContext(); + const error = useStateSelector(controller.state$, currentErrorSelector); const currentDocument = useStateSelector(controller.state$, currentDocumentSelector); const isFetchingDoc = useStateSelector(controller.state$, currentDocumentIsLoadingSelector); const [validationData$, nextValidationData$] = useBehaviorSubject< | { isFetchingDoc: boolean; isLoadingPreview: boolean; - error: Context['error']; + error: PreviewState['previewResponse']['error']; } | undefined >(undefined); diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts b/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts index 391f54581f258..45ecac570dd99 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts @@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiComboBoxOptionOption } from '@elastic/eui'; import { fieldValidators, FieldConfig, RuntimeType, ValidationFunc } from '../../shared_imports'; -import type { Context } from '../preview'; import { RUNTIME_FIELD_OPTIONS } from './constants'; +import type { PreviewState } from '../preview/types'; const { containsCharsField, emptyField, numberGreaterThanField } = fieldValidators; const i18nTexts = { @@ -25,7 +25,7 @@ const i18nTexts = { // Validate the painless **script** const painlessScriptValidator: ValidationFunc = async ({ customData: { provider } }) => { - const previewError = (await provider()) as Context['error']; + const previewError = (await provider()) as PreviewState['previewResponse']['error']; if (previewError && previewError.code === 'PAINLESS_SCRIPT_ERROR') { return { diff --git a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx index 60903fae03ea1..c763d08ae470b 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -85,7 +85,7 @@ export const FieldEditorFlyoutContentContainer = ({ fieldFormats, uiSettings, }: Props) => { - const [controller] = useState(() => new PreviewController({ dataView, search })); + const [controller] = useState(() => new PreviewController({ dataView, search, fieldFormats })); const [isSaving, setIsSaving] = useState(false); const { fields } = dataView; diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx index 672f0a747991d..005a2c634cf84 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx @@ -16,6 +16,7 @@ import { DocumentsNavPreview } from './documents_nav_preview'; import { FieldPreviewError } from './field_preview_error'; import { PreviewListItem } from './field_list/field_list_item'; import { PreviewFieldList } from './field_list/field_list'; +import { useStateSelector } from '../../state_utils'; import './field_preview.scss'; @@ -28,12 +29,12 @@ export const FieldPreview = () => { value: { name, script, format }, }, isLoadingPreview, - fields, - error, documents: { fetchDocError }, reset, isPreviewAvailable, + controller, } = useFieldPreviewContext(); + const { fields, error } = useStateSelector(controller.state$, (state) => state.previewResponse); // To show the preview we at least need a name to be defined, the script or the format // and an first response from the _execute API diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index f554025ce9f4b..c4cdfad7d4fec 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -20,7 +20,6 @@ import { renderToString } from 'react-dom/server'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { BehaviorSubject } from 'rxjs'; import { RuntimePrimitiveTypes } from '../../shared_imports'; import { useStateSelector } from '../../state_utils'; @@ -32,7 +31,6 @@ import type { Context, Params, EsDocument, - ScriptErrorCodes, FetchDocError, FieldPreview, PreviewState, @@ -101,17 +99,11 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro notifications, api: { getFieldPreview }, }, - fieldFormats, fieldName$, } = useFieldEditorContext(); const fieldPreview$ = useRef(new BehaviorSubject(undefined)); - /** Response from the Painless _execute API */ - const [previewResponse, setPreviewResponse] = useState<{ - fields: Context['fields']; - error: Context['error']; - }>({ fields: [], error: null }); const [initialPreviewComplete, setInitialPreviewComplete] = useState(false); /** Possible error while fetching sample documents */ @@ -169,45 +161,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro ); }, [type, script, currentDocId]); - const setPreviewError = useCallback((error: Context['error']) => { - setPreviewResponse((prev) => ({ - ...prev, - error, - })); - }, []); - - const clearPreviewError = useCallback((errorCode: ScriptErrorCodes) => { - setPreviewResponse((prev) => { - const error = prev.error === null || prev.error?.code === errorCode ? null : prev.error; - return { - ...prev, - error, - }; - }); - }, []); - - const valueFormatter = useCallback( - (value: unknown) => { - if (format?.id) { - const formatter = fieldFormats.getInstance(format.id, format.params); - if (formatter) { - return formatter.getConverterFor('html')(value) ?? JSON.stringify(value); - } - } - - if (type) { - const fieldType = castEsToKbnFieldTypeName(type); - const defaultFormatterForType = fieldFormats.getDefaultInstance(fieldType); - if (defaultFormatterForType) { - return defaultFormatterForType.getConverterFor('html')(value) ?? JSON.stringify(value); - } - } - - return defaultValueFormatter(value); - }, - [format, type, fieldFormats] - ); - const fetchSampleDocuments = useCallback( async (limit: number = 50) => { if (typeof limit !== 'number') { @@ -217,7 +170,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro lastExecutePainlessRequestParams.current.documentId = undefined; setIsFetchingDocument(true); - setPreviewResponse({ fields: [], error: null }); + controller.setPreviewResponse({ fields: [], error: null }); const [response, searchError] = await search .search({ @@ -335,14 +288,14 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const updateSingleFieldPreview = useCallback( (fieldName: string, values: unknown[]) => { const [value] = values; - const formattedValue = valueFormatter(value); + const formattedValue = controller.valueFormatter({ value, type, format }); - setPreviewResponse({ + controller.setPreviewResponse({ fields: [{ key: fieldName, value, formattedValue }], error: null, }); }, - [valueFormatter] + [controller, type, format] ); const updateCompositeFieldPreview = useCallback( @@ -359,7 +312,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro updatedFieldsInScript.push(fieldName); const [value] = values; - const formattedValue = valueFormatter(value); + const formattedValue = controller.valueFormatter({ value, type, format }); return { key: parentName @@ -375,12 +328,12 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro .sort((a, b) => a.key.localeCompare(b.key)); fieldPreview$.current.next(fields); - setPreviewResponse({ + controller.setPreviewResponse({ fields, error: null, }); }, - [valueFormatter, parentName, name, fieldPreview$, fieldName$] + [parentName, name, fieldPreview$, fieldName$, controller, type, format] ); const updatePreview = useCallback(async () => { @@ -437,7 +390,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const { values, error } = response.data; if (error) { - setPreviewResponse({ + controller.setPreviewResponse({ fields: [ { key: name ?? '', @@ -474,6 +427,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro updateSingleFieldPreview, updateCompositeFieldPreview, currentDocIndex, + controller, ]); const reset = useCallback(() => { @@ -482,7 +436,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro previewCount.current = 0; controller.setDocuments([]); - setPreviewResponse({ fields: [], error: null }); + controller.setPreviewResponse({ fields: [], error: null }); setIsLoadingPreview(false); setIsFetchingDocument(false); }, [controller]); @@ -490,8 +444,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const ctx = useMemo( () => ({ controller, - fields: previewResponse.fields, - error: previewResponse.error, fieldPreview$: fieldPreview$.current, isPreviewAvailable, isLoadingPreview, @@ -521,7 +473,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro [ controller, currentIdx, - previewResponse, fieldPreview$, fetchDocError, params, @@ -583,74 +534,70 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro * Whenever the name or the format changes we immediately update the preview */ useEffect(() => { - setPreviewResponse((prev) => { - const { fields } = prev; - - let updatedFields: Context['fields'] = fields.map((field) => { - let key = name ?? ''; - - if (type === 'composite') { - // restore initial key segement (the parent name), which was not returned - const { 1: fieldName } = field.key.split('.'); - key = `${name ?? ''}.${fieldName}`; - } + const { previewResponse: prev } = controller.state$.getValue(); + const { fields } = prev; - return { - ...field, - key, - }; - }); + let updatedFields: PreviewState['previewResponse']['fields'] = fields.map((field) => { + let key = name ?? ''; - // If the user has entered a name but not yet any script we will display - // the field in the preview with just the name - if (updatedFields.length === 0 && name !== null) { - updatedFields = [ - { key: name, value: undefined, formattedValue: undefined, type: undefined }, - ]; + if (type === 'composite') { + // restore initial key segement (the parent name), which was not returned + const { 1: fieldName } = field.key.split('.'); + key = `${name ?? ''}.${fieldName}`; } return { - ...prev, - fields: updatedFields, + ...field, + key, }; }); - }, [name, type, parentName]); + + // If the user has entered a name but not yet any script we will display + // the field in the preview with just the name + if (updatedFields.length === 0 && name !== null) { + updatedFields = [{ key: name, value: undefined, formattedValue: undefined, type: undefined }]; + } + + controller.setPreviewResponse({ + ...prev, + fields: updatedFields, + }); + }, [name, type, parentName, controller]); /** * Whenever the format changes we immediately update the preview */ useEffect(() => { - setPreviewResponse((prev) => { - const { fields } = prev; + const { previewResponse: prev } = controller.state$.getValue(); + const { fields } = prev; - return { - ...prev, - fields: fields.map((field) => { - const nextValue = - script === null && Boolean(document) - ? get(document?._source, name ?? '') ?? get(document?.fields, name ?? '') // When there is no script we try to read the value from _source/fields - : field?.value; + controller.setPreviewResponse({ + ...prev, + fields: fields.map((field) => { + const nextValue = + script === null && Boolean(document) + ? get(document?._source, name ?? '') ?? get(document?.fields, name ?? '') // When there is no script we try to read the value from _source/fields + : field?.value; - const formattedValue = valueFormatter(nextValue); + const formattedValue = controller.valueFormatter({ value: nextValue, type, format }); - return { - ...field, - value: nextValue, - formattedValue, - }; - }), - }; + return { + ...field, + value: nextValue, + formattedValue, + }; + }), }); - }, [name, script, document, valueFormatter]); + }, [name, script, document, controller, type, format]); useEffect(() => { if (script?.source === undefined) { // Whenever the source is not defined ("Set value" is toggled off or the // script is empty) we clear the error and update the params cache. lastExecutePainlessRequestParams.current.script = undefined; - setPreviewError(null); + controller.setPreviewError(null); } - }, [script?.source, setPreviewError]); + }, [script?.source, controller]); // Handle the validation state coming from the Painless DiagnosticAdapter // (see @kbn-monaco/src/painless/diagnostics_adapter.ts) @@ -677,16 +624,16 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro }), }, }; - setPreviewError(error); + controller.setPreviewError(error); // Make sure to update the lastExecutePainlessRequestParams cache so when the user updates // the script and fixes the syntax the "updatePreview()" will run lastExecutePainlessRequestParams.current.script = script?.source; } else { // Clear possible previous syntax error - clearPreviewError('PAINLESS_SYNTAX_ERROR'); + controller.clearPreviewError('PAINLESS_SYNTAX_ERROR'); } - }, [scriptEditorValidation, script?.source, setPreviewError, clearPreviewError]); + }, [scriptEditorValidation, script?.source, controller]); /** * Whenever updatePreview() changes (meaning whenever a param changes) diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx similarity index 60% rename from src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts rename to src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx index b572827eac06d..8e9f6156c7d7b 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx @@ -9,13 +9,23 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { ISearchStart } from '@kbn/data-plugin/public'; import { BehaviorSubject } from 'rxjs'; +import { castEsToKbnFieldTypeName } from '@kbn/field-types'; +import { renderToString } from 'react-dom/server'; +import React from 'react'; import { PreviewState } from './types'; import { BehaviorObservable } from '../../state_utils'; -import { EsDocument } from './types'; +import { EsDocument, ScriptErrorCodes, Params } from './types'; +import type { FieldFormatsStart } from '../../shared_imports'; + +export const defaultValueFormatter = (value: unknown) => { + const content = typeof value === 'object' ? JSON.stringify(value) : String(value) ?? '-'; + return renderToString(<>{content}); +}; interface PreviewControllerDependencies { dataView: DataView; search: ISearchStart; + fieldFormats: FieldFormatsStart; } const previewStateDefault: PreviewState = { @@ -30,12 +40,14 @@ const previewStateDefault: PreviewState = { documentSource: 'cluster', /** Keep track if the script painless syntax is being validated and if it is valid */ scriptEditorValidation: { isValidating: false, isValid: true, message: null }, + previewResponse: { fields: [], error: null }, }; export class PreviewController { - constructor({ dataView, search }: PreviewControllerDependencies) { + constructor({ dataView, search, fieldFormats }: PreviewControllerDependencies) { this.dataView = dataView; this.search = search; + this.fieldFormats = fieldFormats; this.internalState$ = new BehaviorSubject({ ...previewStateDefault, @@ -44,10 +56,13 @@ export class PreviewController { this.state$ = this.internalState$ as BehaviorObservable; } + // dependencies // @ts-ignore private dataView: DataView; // @ts-ignore private search: ISearchStart; + private fieldFormats: FieldFormatsStart; + private internalState$: BehaviorSubject; state$: BehaviorObservable; @@ -104,4 +119,52 @@ export class PreviewController { setCustomId = (customId?: string) => { this.updateState({ customId }); }; + + setPreviewError = (error: PreviewState['previewResponse']['error']) => { + this.updateState({ + previewResponse: { ...this.internalState$.getValue().previewResponse, error }, + }); + }; + + setPreviewResponse = (previewResponse: PreviewState['previewResponse']) => { + this.updateState({ previewResponse }); + }; + + clearPreviewError = (errorCode: ScriptErrorCodes) => { + const { previewResponse: prev } = this.internalState$.getValue(); + const error = prev.error === null || prev.error?.code === errorCode ? null : prev.error; + this.updateState({ + previewResponse: { + ...prev, + error, + }, + }); + }; + + valueFormatter = ({ + value, + format, + type, + }: { + value: unknown; + format: Params['format']; + type: Params['type']; + }) => { + if (format?.id) { + const formatter = this.fieldFormats.getInstance(format.id, format.params); + if (formatter) { + return formatter.getConverterFor('html')(value) ?? JSON.stringify(value); + } + } + + if (type) { + const fieldType = castEsToKbnFieldTypeName(type); + const defaultFormatterForType = this.fieldFormats.getDefaultInstance(fieldType); + if (defaultFormatterForType) { + return defaultFormatterForType.getConverterFor('html')(value) ?? JSON.stringify(value); + } + } + + return defaultValueFormatter(value); + }; } diff --git a/src/plugins/data_view_field_editor/public/components/preview/types.ts b/src/plugins/data_view_field_editor/public/components/preview/types.ts index 347e0a709cf28..b4280f54786ac 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/types.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/types.ts @@ -55,6 +55,11 @@ export interface PreviewState { isValid: boolean; message: string | null; }; + /** Response from the Painless _execute API */ + previewResponse: { + fields: FieldPreview[]; + error: PreviewError | null; + }; } export interface FetchDocError { @@ -108,9 +113,7 @@ export type ChangeSet = Record; export interface Context { controller: PreviewController; - fields: FieldPreview[]; fieldPreview$: BehaviorSubject; - error: PreviewError | null; fieldTypeInfo?: FieldTypeInfo[]; initialPreviewComplete: boolean; params: { diff --git a/src/plugins/data_views/server/saved_objects/data_views.ts b/src/plugins/data_views/server/saved_objects/data_views.ts index 4b8318b3d065f..2dbc4584e2aed 100644 --- a/src/plugins/data_views/server/saved_objects/data_views.ts +++ b/src/plugins/data_views/server/saved_objects/data_views.ts @@ -7,11 +7,13 @@ */ import type { SavedObjectsType } from '@kbn/core/server'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../../common'; export const dataViewSavedObjectType: SavedObjectsType = { name: DATA_VIEW_SAVED_OBJECT_TYPE, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/src/plugins/data_views/tsconfig.json b/src/plugins/data_views/tsconfig.json index 94fe86cb31321..dae61e82637c6 100644 --- a/src/plugins/data_views/tsconfig.json +++ b/src/plugins/data_views/tsconfig.json @@ -28,6 +28,7 @@ "@kbn/utility-types-jest", "@kbn/safer-lodash-set", "@kbn/core-http-server", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 9804a093113a7..77d6e36715dd7 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -9,11 +9,13 @@ import React, { memo, useCallback, useMemo } from 'react'; import { EuiFlexItem, EuiLoadingSpinner, + EuiProgress, EuiScreenReaderOnly, EuiSpacer, EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; import { DataView } from '@kbn/data-views-plugin/public'; import { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import { DataTableRecord } from '../../../../types'; @@ -42,6 +44,14 @@ import { getRawRecordType } from '../../utils/get_raw_record_type'; import { DiscoverGridFlyout } from '../../../../components/discover_grid/discover_grid_flyout'; import { DocViewer } from '../../../../services/doc_views/components/doc_viewer'; +const containerStyles = css` + position: relative; +`; + +const progressStyle = css` + z-index: 2; +`; + const DocTableInfiniteMemoized = React.memo(DocTableInfinite); const DataGridMemoized = React.memo(DiscoverGrid); @@ -182,7 +192,7 @@ function DiscoverDocumentsComponent({ } return ( - +

@@ -253,6 +263,9 @@ function DiscoverDocumentsComponent({

)} + {isDataLoading && ( + + )} ); } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index b162d53564e91..74118cf674dad 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import './discover_layout.scss'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { EuiButtonIcon, EuiFlexGroup, @@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import classNames from 'classnames'; import { generateFilters } from '@kbn/data-plugin/public'; +import { DragContext } from '@kbn/dom-drag-drop'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; import { VIEW_MODE } from '../../../../../common/constants'; import { useInternalStateSelector } from '../../services/discover_internal_state_container'; @@ -207,6 +208,17 @@ export function DiscoverLayout({ const resizeRef = useRef(null); + const dragDropContext = useContext(DragContext); + const draggingFieldName = dragDropContext.dragging?.id; + + const onDropFieldToTable = useMemo(() => { + if (!draggingFieldName || currentColumns.includes(draggingFieldName)) { + return undefined; + } + + return () => onAddColumn(draggingFieldName); + }, [onAddColumn, draggingFieldName, currentColumns]); + const mainDisplay = useMemo(() => { if (resultState === 'none') { const globalQueryState = data.query.getState(); @@ -245,6 +257,7 @@ export function DiscoverLayout({ onFieldEdited={onFieldEdited} resizeRef={resizeRef} inspectorAdapters={inspectorAdapters} + onDropFieldToTable={onDropFieldToTable} /> {resultState === 'loading' && } @@ -265,7 +278,9 @@ export function DiscoverLayout({ savedSearch, stateContainer, viewMode, + onDropFieldToTable, ]); + return (

Promise; + onDropFieldToTable?: () => void; columns: string[]; } @@ -45,6 +60,7 @@ export const DiscoverMainContent = ({ columns, stateContainer, savedSearch, + onDropFieldToTable, }: DiscoverMainContentProps) => { const { trackUiMetric } = useDiscoverServices(); @@ -64,49 +80,65 @@ export const DiscoverMainContent = ({ ); const dataState = useDataState(stateContainer.dataState.data$.main$); + const isDropAllowed = Boolean(onDropFieldToTable); return ( - - - - {!isPlainRecord && ( - - )} - - {dataState.error && ( - - )} - {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( - - ) : ( - - )} - + + + + + {!isPlainRecord && ( + + )} + + {dataState.error && ( + + )} + + {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( + + ) : ( + + )} + + + ); }; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx index 31919d1e2de20..778de3c3e8f94 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx @@ -7,7 +7,7 @@ */ import { act } from 'react-dom/test-utils'; -import { EuiPopover, EuiProgress, EuiButtonIcon } from '@elastic/eui'; +import { EuiButtonIcon, EuiPopover, EuiProgress } from '@elastic/eui'; import React from 'react'; import { BehaviorSubject } from 'rxjs'; import { findTestSubject } from '@elastic/eui/lib/test'; @@ -23,6 +23,7 @@ import { DataDocuments$ } from '../../services/discover_data_state_container'; import { getDataTableRecords } from '../../../../__fixtures__/real_hits'; import * as DetailsUtil from './deprecated_stats/get_details'; import { createDiscoverServicesMock } from '../../../../__mocks__/services'; +import { FieldItemButton } from '@kbn/unified-field-list-plugin/public'; jest.spyOn(DetailsUtil, 'getDetails'); @@ -97,7 +98,10 @@ async function getComponent({ onEditField: jest.fn(), onRemoveField: jest.fn(), showFieldStats, - selected, + isSelected: selected, + isEmpty: false, + groupIndex: 1, + itemIndex: 0, contextualFields: [], }; const services = { @@ -206,7 +210,7 @@ describe('discover sidebar field', function () { findTestSubject(comp, 'field-bytes-showDetails').simulate('click'); expect(DetailsUtil.getDetails).toHaveBeenCalledTimes(1); }); - it('should not return the popover if onAddFilter is not provided', async function () { + it('should not enable the popover if onAddFilter is not provided', async function () { const field = new DataViewField({ name: '_source', type: '_source', @@ -220,8 +224,8 @@ describe('discover sidebar field', function () { field, onAddFilterExists: false, }); - const popover = findTestSubject(comp, 'discoverFieldListPanelPopover'); - expect(popover.length).toBe(0); + + expect(comp.find(FieldItemButton).prop('onClick')).toBeUndefined(); }); it('should request field stats', async function () { const field = new DataViewField({ diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx index aa5fafe691fd8..01b503e9a8ca2 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx @@ -8,162 +8,69 @@ import './discover_field.scss'; -import React, { useState, useCallback, memo, useMemo } from 'react'; -import { - EuiButtonIcon, - EuiToolTip, - EuiTitle, - EuiIcon, - EuiSpacer, - EuiHighlight, -} from '@elastic/eui'; +import React, { memo, useCallback, useMemo, useState } from 'react'; +import { EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { UiCounterMetricType } from '@kbn/analytics'; -import classNames from 'classnames'; -import { FieldButton } from '@kbn/react-field'; -import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; +import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { - FieldIcon, + FieldItemButton, + type FieldItemButtonProps, FieldPopover, FieldPopoverHeader, FieldPopoverHeaderProps, - FieldPopoverVisualize, - getFieldIconProps, + FieldPopoverFooter, } from '@kbn/unified-field-list-plugin/public'; +import { DragDrop } from '@kbn/dom-drag-drop'; import { DiscoverFieldStats } from './discover_field_stats'; import { DiscoverFieldDetails } from './deprecated_stats/discover_field_details'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { SHOW_LEGACY_FIELD_TOP_VALUES, PLUGIN_ID } from '../../../../../common'; +import { PLUGIN_ID, SHOW_LEGACY_FIELD_TOP_VALUES } from '../../../../../common'; import { getUiActions } from '../../../../kibana_services'; import { type DataDocuments$ } from '../../services/discover_data_state_container'; -const FieldInfoIcon: React.FC = memo(() => ( - - - -)); - -const DiscoverFieldTypeIcon: React.FC<{ field: DataViewField }> = memo(({ field }) => { - return ; -}); - -const FieldName: React.FC<{ field: DataViewField; highlight?: string }> = memo( - ({ field, highlight }) => { - const title = - field.displayName !== field.name - ? i18n.translate('discover.field.title', { - defaultMessage: '{fieldName} ({fieldDisplayName})', - values: { - fieldName: field.name, - fieldDisplayName: field.displayName, - }, - }) - : field.displayName; - - return ( - - {field.displayName} - - ); - } -); - -interface ActionButtonProps { +interface GetCommonFieldItemButtonPropsParams { field: DataViewField; - isSelected?: boolean; - alwaysShow: boolean; + isSelected: boolean; toggleDisplay: (field: DataViewField, isSelected?: boolean) => void; } -const ActionButton: React.FC = memo( - ({ field, isSelected, alwaysShow, toggleDisplay }) => { - const actionBtnClassName = classNames('dscSidebarItem__action', { - ['dscSidebarItem__mobile']: alwaysShow, - }); - if (field.name === '_source') { - return null; - } - if (!isSelected) { - return ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); - toggleDisplay(field, isSelected); - }} - data-test-subj={`fieldToggle-${field.name}`} - aria-label={i18n.translate('discover.fieldChooser.discoverField.addButtonAriaLabel', { - defaultMessage: 'Add {field} to table', - values: { field: field.name }, - })} - /> - - ); - } else { - return ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); - toggleDisplay(field, isSelected); - }} - data-test-subj={`fieldToggle-${field.name}`} - aria-label={i18n.translate( - 'discover.fieldChooser.discoverField.removeButtonAriaLabel', - { - defaultMessage: 'Remove {field} from table', - values: { field: field.name }, - } - )} - /> - - ); - } - } -); +function getCommonFieldItemButtonProps({ + field, + isSelected, + toggleDisplay, +}: GetCommonFieldItemButtonPropsParams): { + field: FieldItemButtonProps['field']; + isSelected: FieldItemButtonProps['isSelected']; + dataTestSubj: FieldItemButtonProps['dataTestSubj']; + buttonAddFieldToWorkspaceProps: FieldItemButtonProps['buttonAddFieldToWorkspaceProps']; + buttonRemoveFieldFromWorkspaceProps: FieldItemButtonProps['buttonRemoveFieldFromWorkspaceProps']; + onAddFieldToWorkspace: FieldItemButtonProps['onAddFieldToWorkspace']; + onRemoveFieldFromWorkspace: FieldItemButtonProps['onRemoveFieldFromWorkspace']; +} { + const dataTestSubj = `fieldToggle-${field.name}`; + const handler = + field.name === '_source' ? undefined : (f: DataViewField) => toggleDisplay(f, isSelected); + return { + field, + isSelected, + dataTestSubj: `field-${field.name}-showDetails`, + buttonAddFieldToWorkspaceProps: { + 'aria-label': i18n.translate('discover.fieldChooser.discoverField.addFieldTooltip', { + defaultMessage: 'Add field as column', + }), + 'data-test-subj': dataTestSubj, + }, + buttonRemoveFieldFromWorkspaceProps: { + 'aria-label': i18n.translate('discover.fieldChooser.discoverField.removeFieldTooltip', { + defaultMessage: 'Remove field from table', + }), + 'data-test-subj': dataTestSubj, + }, + onAddFieldToWorkspace: handler, + onRemoveFieldFromWorkspace: handler, + }; +} interface MultiFieldsProps { multiFields: NonNullable; @@ -183,22 +90,20 @@ const MultiFields: React.FC = memo( {multiFields.map((entry) => ( - } - fieldAction={ - - } - fieldName={} - key={entry.field.name} + shouldAlwaysShowAction={alwaysShowActionButton} + onClick={undefined} + {...getCommonFieldItemButtonProps({ + field: entry.field, + isSelected: entry.isSelected, + toggleDisplay, + })} /> ))} @@ -235,10 +140,14 @@ export interface DiscoverFieldProps { * @param fieldName */ onRemoveField: (fieldName: string) => void; + /** + * Determines whether the field is empty + */ + isEmpty: boolean; /** * Determines whether the field is selected */ - selected?: boolean; + isSelected: boolean; /** * Metric tracking function * @param metricType @@ -273,6 +182,16 @@ export interface DiscoverFieldProps { * Search by field name */ highlight?: string; + + /** + * Group index in the field list + */ + groupIndex: number; + + /** + * Item index in the field list + */ + itemIndex: number; } function DiscoverFieldComponent({ @@ -284,13 +203,16 @@ function DiscoverFieldComponent({ onAddField, onRemoveField, onAddFilter, - selected, + isEmpty, + isSelected, trackUiMetric, multiFields, onEditField, onDeleteField, showFieldStats, contextualFields, + groupIndex, + itemIndex, }: DiscoverFieldProps) { const services = useDiscoverServices(); const [infoIsOpen, setOpen] = useState(false); @@ -315,7 +237,7 @@ function DiscoverFieldComponent({ setOpen(false); }, [setOpen]); - const toggleDisplay: ActionButtonProps['toggleDisplay'] = useCallback( + const toggleDisplay: GetCommonFieldItemButtonPropsParams['toggleDisplay'] = useCallback( (f, isCurrentlySelected) => { closePopover(); if (isCurrentlySelected) { @@ -349,51 +271,6 @@ function DiscoverFieldComponent({ [field.name] ); - if (field.type === '_source') { - return ( - } - fieldAction={ - - } - fieldName={} - /> - ); - } - - const button = ( - } - fieldAction={ - - } - fieldName={} - fieldInfoIcon={field.type === 'conflict' && } - /> - ); - - if (!isDocumentRecord) { - return button; - } - const renderPopover = () => { const showLegacyFieldStats = services.uiSettings.get(SHOW_LEGACY_FIELD_TOP_VALUES); @@ -430,7 +307,7 @@ function DiscoverFieldComponent({ )} - closePopover()} /> ); }; + const value = useMemo( + () => ({ + id: field.name, + humanData: { + label: field.displayName, + position: itemIndex + 1, + }, + }), + [field, itemIndex] + ); + const order = useMemo(() => [0, groupIndex, itemIndex], [groupIndex, itemIndex]); + return ( + + + } closePopover={closePopover} data-test-subj="discoverFieldListPanelPopover" renderHeader={() => ( )} - renderContent={renderPopover} + renderContent={isDocumentRecord ? renderPopover : undefined} /> ); } diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss index 806893269da34..d8e320822f5f7 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.scss @@ -38,29 +38,3 @@ .dscSidebar__flyoutHeader { align-items: center; } - -.dscSidebarItem { - &:hover, - &:focus-within, - &[class*='-isActive'] { - .dscSidebarItem__action { - opacity: 1; - } - } -} - -/** - * 1. Only visually hide the action, so that it's still accessible to screen readers. - * 2. When tabbed to, this element needs to be visible for keyboard accessibility. - */ -.dscSidebarItem__action { - opacity: 0; /* 1 */ - - &.dscSidebarItem__mobile { - opacity: 1; - } - - &:focus { - opacity: 1; /* 2 */ - } -} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index 2bea887c21a62..b335054ec9960 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -223,7 +223,7 @@ export function DiscoverSidebarComponent({ }); const renderFieldItem: FieldListGroupedProps['renderFieldItem'] = useCallback( - ({ field, groupName, fieldSearchHighlight }) => ( + ({ field, groupName, groupIndex, itemIndex, fieldSearchHighlight }) => (
  • - + + + ); } diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index d571de95f074d..2537bc6be16ee 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -20,7 +20,6 @@ import { EuiLoadingSpinner, EuiIcon, EuiDataGridRefProps, - EuiLink, } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { SortOrder } from '@kbn/saved-search-plugin/public'; @@ -43,7 +42,6 @@ import { GRID_STYLE, toolbarVisibility as toolbarVisibilityDefaults } from './co import { getDisplayedColumns } from '../../utils/columns'; import { DOC_HIDE_TIME_COLUMN_SETTING, - SAMPLE_SIZE_SETTING, MAX_DOC_FIELDS_DISPLAYED, SHOW_MULTIFIELDS, } from '../../../common'; @@ -613,23 +611,10 @@ export const DiscoverGrid = ({ {showDisclaimer && (

    - - - ), }} />

    diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx index ef5ca408d2bd8..78a7ab5f44ff8 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx @@ -47,10 +47,10 @@ export function TableHeader({ return (
  • - {displayedColumns.map((col) => { + {displayedColumns.map((col, index) => { return ( ); } else { - columns.forEach(function (column: string) { + columns.forEach(function (column: string, index) { + const cellKey = `${column}-${index}`; if (useNewFieldsApi && !mapping(column) && row.raw.fields && !row.raw.fields[column]) { const innerColumns = Object.fromEntries( Object.entries(row.raw.fields).filter(([key]) => { @@ -163,7 +164,7 @@ export const TableRow = ({ rowCells.push( { ); return ( - - - - {shouldShowLimitedResultsWarning && ( - - - - - - )} - {props.totalHitCount !== 0 && ( - - - - )} - - - - - - - - {showPagination && ( - + + + + ) : undefined + } + append={ + showPagination ? ( { onPageClick={onPageChange} onPageSizeChange={onPageSizeChange} /> - - )} - + ) : undefined + } + > + + ); }; diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.test.ts b/src/plugins/discover/public/embeddable/saved_search_embeddable.test.ts index 449c83b77966c..a797a78ba6875 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.test.ts +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.test.ts @@ -132,13 +132,16 @@ describe('saved search embeddable', () => { jest.spyOn(embeddable, 'updateOutput'); embeddable.render(mountpoint); + expect(render).toHaveBeenCalledTimes(1); await waitOneTick(); + expect(render).toHaveBeenCalledTimes(2); const searchProps = discoverComponent.find(SavedSearchEmbeddableComponent).prop('searchProps'); searchProps.onAddColumn!('bytes'); await waitOneTick(); expect(searchProps.columns).toEqual(['message', 'extension', 'bytes']); + expect(render).toHaveBeenCalledTimes(4); // twice per an update to show and then hide a loading indicator searchProps.onRemoveColumn!('bytes'); await waitOneTick(); diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index d89b98de0c93a..0e315d3b348e5 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -198,6 +198,8 @@ export class SavedSearchEmbeddable this.searchProps!.isLoading = true; + const wasAlreadyRendered = this.getOutput().rendered; + this.updateOutput({ ...this.getOutput(), loading: true, @@ -205,6 +207,11 @@ export class SavedSearchEmbeddable error: undefined, }); + if (wasAlreadyRendered && this.node) { + // to show a loading indicator during a refetch, we need to rerender here + this.render(this.node); + } + const parentContext = this.input.executionContext; const child: KibanaExecutionContext = { type: this.type, diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx new file mode 100644 index 0000000000000..17785570b9487 --- /dev/null +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable_base.tsx @@ -0,0 +1,67 @@ +/* + * 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 { css } from '@emotion/react'; +import { EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; +import { TotalDocuments } from '../application/main/components/total_documents/total_documents'; + +const containerStyles = css` + width: 100%; + position: relative; +`; + +export interface SavedSearchEmbeddableBaseProps { + isLoading: boolean; + totalHitCount: number; + prepend?: React.ReactElement; + append?: React.ReactElement; + dataTestSubj?: string; +} + +export const SavedSearchEmbeddableBase: React.FC = ({ + isLoading, + totalHitCount, + prepend, + append, + dataTestSubj, + children, +}) => { + return ( + + {isLoading && } + + + {Boolean(prepend) && {prepend}} + + {Boolean(totalHitCount) && ( + + + + )} + + + + {children} + + {Boolean(append) && {append}} + + ); +}; diff --git a/src/plugins/discover/public/embeddable/saved_search_grid.tsx b/src/plugins/discover/public/embeddable/saved_search_grid.tsx index a1e75e17dca5f..21514a61c87a9 100644 --- a/src/plugins/discover/public/embeddable/saved_search_grid.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_grid.tsx @@ -6,12 +6,11 @@ * Side Public License, v 1. */ import React, { useState, memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { DataTableRecord } from '../types'; import { DiscoverGrid, DiscoverGridProps } from '../components/discover_grid/discover_grid'; -import { TotalDocuments } from '../application/main/components/total_documents/total_documents'; import './saved_search_grid.scss'; import { DiscoverGridFlyout } from '../components/discover_grid/discover_grid_flyout'; +import { SavedSearchEmbeddableBase } from './saved_search_embeddable_base'; export interface DiscoverGridEmbeddableProps extends DiscoverGridProps { totalHitCount: number; @@ -21,27 +20,19 @@ export const DataGridMemoized = memo(DiscoverGrid); export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { const [expandedDoc, setExpandedDoc] = useState(undefined); + return ( - - {Boolean(props.totalHitCount) && props.totalHitCount !== 0 && ( - - - - )} - - - - + + ); } diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index 3c0fe05348324..d6985033cdb1d 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -137,6 +137,12 @@ export const getUiSettings: (docLinks: DocLinksServiceSetup) => Record { +test('redirects to app using state transfer', async () => { applicationMock.currentAppId$ = of('superCoolCurrentApp'); const testPath = '/test-path'; const action = new EditPanelAction( @@ -78,32 +78,6 @@ test('redirects to app using state transfer with by value mode', async () => { }); }); -test('redirects to app using state transfer without by value mode', async () => { - applicationMock.currentAppId$ = of('superCoolCurrentApp'); - const testPath = '/test-path'; - const action = new EditPanelAction( - getFactory, - applicationMock, - stateTransferMock, - () => testPath - ); - const embeddable = new EditableEmbeddable( - { id: '123', viewMode: ViewMode.EDIT, savedObjectId: '1234' } as SavedObjectEmbeddableInput, - true - ); - embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); - await action.execute({ embeddable }); - expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { - path: '/123', - state: { - originatingApp: 'superCoolCurrentApp', - embeddableId: '123', - valueInput: undefined, - originatingPath: testPath, - }, - }); -}); - test('getHref returns the edit urls', async () => { const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); expect(action.getHref).toBeDefined(); diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts index 1dcecf4ac894d..ba59d92cbef60 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts @@ -17,7 +17,6 @@ import { IEmbeddable, EmbeddableEditorState, EmbeddableStateTransfer, - SavedObjectEmbeddableInput, EmbeddableInput, Container, } from '../..'; @@ -124,13 +123,11 @@ export class EditPanelAction implements Action { if (app && path) { if (this.currentAppId) { - const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId; - const originatingPath = this.getOriginatingPath?.(); const state: EmbeddableEditorState = { originatingApp: this.currentAppId, - valueInput: byValueMode ? this.getExplicitInput({ embeddable }) : undefined, + valueInput: this.getExplicitInput({ embeddable }), embeddableId: embeddable.id, searchSessionId: embeddable.getInput().searchSessionId, originatingPath, diff --git a/src/plugins/embeddable/public/lib/containers/container.ts b/src/plugins/embeddable/public/lib/containers/container.ts index 847d1fa5cd5dc..a53cfb8725fac 100644 --- a/src/plugins/embeddable/public/lib/containers/container.ts +++ b/src/plugins/embeddable/public/lib/containers/container.ts @@ -64,7 +64,7 @@ export abstract class Container< output: TContainerOutput, protected readonly getFactory: EmbeddableStart['getEmbeddableFactory'], parent?: IContainer, - settings?: EmbeddableContainerSettings + settings?: EmbeddableContainerSettings ) { super(input, output, parent); this.getFactory = getFactory; // Currently required for using in storybook due to https://github.com/storybookjs/storybook/issues/13834 @@ -74,11 +74,7 @@ export abstract class Container< settings?.initializeSequentially || settings?.childIdInitializeOrder ); - const initSource = settings?.readyToInitializeChildren$ - ? settings?.readyToInitializeChildren$ - : this.getInput$(); - - const init$ = initSource.pipe( + const init$ = this.getInput$().pipe( take(1), mergeMap(async (currentInput) => { const initPromise = this.initializeChildEmbeddables(currentInput, settings); @@ -372,7 +368,7 @@ export abstract class Container< private async initializeChildEmbeddables( initialInput: TContainerInput, - initializeSettings?: EmbeddableContainerSettings + initializeSettings?: EmbeddableContainerSettings ) { let initializeOrder = Object.keys(initialInput.panels); diff --git a/src/plugins/embeddable/public/lib/containers/i_container.ts b/src/plugins/embeddable/public/lib/containers/i_container.ts index f7ae3d64c6a25..5539f854b24d9 100644 --- a/src/plugins/embeddable/public/lib/containers/i_container.ts +++ b/src/plugins/embeddable/public/lib/containers/i_container.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import { Observable } from 'rxjs'; - import { Embeddable, EmbeddableInput, @@ -30,7 +28,7 @@ export interface ContainerInput extends EmbeddableInput }; } -export interface EmbeddableContainerSettings { +export interface EmbeddableContainerSettings { /** * If true, the container will wait for each embeddable to load after creation before loading the next embeddable. */ @@ -39,10 +37,6 @@ export interface EmbeddableContainerSettings { * Initialise children in the order specified. If an ID does not match it will be skipped and if a child is not included it will be initialized in the default order after the list of provided IDs. */ childIdInitializeOrder?: string[]; - /** - * - */ - readyToInitializeChildren$?: Observable; } export interface IContainer< diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx index dcaa3880678ab..ea7c150bf38b8 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx @@ -30,6 +30,7 @@ interface Props { SavedObjectFinder: React.ComponentType; showCreateNewMenu?: boolean; reportUiCounter?: UsageCollectionStart['reportUiCounter']; + onAddPanel?: (id: string) => void; } interface State { @@ -101,7 +102,7 @@ export class AddPanelFlyout extends React.Component { throw new EmbeddableFactoryNotFoundError(savedObjectType); } - this.props.container.addNewEmbeddable( + const embeddable = await this.props.container.addNewEmbeddable( factoryForSavedObjectType.type, { savedObjectId } ); @@ -109,6 +110,9 @@ export class AddPanelFlyout extends React.Component { this.doTelemetryForAddEvent(this.props.container.type, factoryForSavedObjectType, so); this.showToast(name); + if (this.props.onAddPanel) { + this.props.onAddPanel(embeddable.id); + } }; private doTelemetryForAddEvent( diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx index 4cc5a7ccb6e11..eb2722dcf9869 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx @@ -24,6 +24,7 @@ export function openAddPanelFlyout(options: { showCreateNewMenu?: boolean; reportUiCounter?: UsageCollectionStart['reportUiCounter']; theme: ThemeServiceStart; + onAddPanel?: (id: string) => void; }): OverlayRef { const { embeddable, @@ -35,11 +36,13 @@ export function openAddPanelFlyout(options: { showCreateNewMenu, reportUiCounter, theme, + onAddPanel, } = options; const flyoutSession = overlays.openFlyout( toMountPoint( { if (flyoutSession) { flyoutSession.close(); diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx index ec6c2011ca53d..fc7a30efbe256 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_panel/customize_panel_action.tsx @@ -129,6 +129,10 @@ export class CustomizePanelAction implements Action { size: 's', 'data-test-subj': 'customizePanel', + onClose: (overlayRef) => { + if (overlayTracker) overlayTracker.clearOverlays(); + overlayRef.close(); + }, } ); overlayTracker?.openOverlay(handle); diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx index 0758103b6f634..18dc9778bc3eb 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx @@ -42,9 +42,7 @@ export class HelloWorldContainer extends Container, private readonly options: HelloWorldContainerOptions, - initializeSettings?: EmbeddableContainerSettings< - ContainerInput<{ firstName: string; lastName: string }> - > + initializeSettings?: EmbeddableContainerSettings ) { super( input, diff --git a/src/plugins/embeddable/public/tests/container.test.ts b/src/plugins/embeddable/public/tests/container.test.ts index 177b1a618282c..c6088e95069cb 100644 --- a/src/plugins/embeddable/public/tests/container.test.ts +++ b/src/plugins/embeddable/public/tests/container.test.ts @@ -45,7 +45,7 @@ import { EmbeddableContainerSettings } from '../lib/containers/i_container'; async function createHelloWorldContainerAndEmbeddable( containerInput: ContainerInput = { id: 'hello', panels: {} }, embeddableInput = {}, - settings?: EmbeddableContainerSettings> + settings?: EmbeddableContainerSettings ) { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index e360699ed5938..2347d98cf6381 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -25,6 +25,7 @@ "@kbn/saved-objects-tagging-plugin", "@kbn/presentation-util-plugin", "@kbn/content-management-table-list", + "@kbn/visualizations-plugin" ], "exclude": [ "target/**/*", diff --git a/src/plugins/files/public/plugin.ts b/src/plugins/files/public/plugin.ts index 54646e9199f9a..13828d0ee366c 100644 --- a/src/plugins/files/public/plugin.ts +++ b/src/plugins/files/public/plugin.ts @@ -35,7 +35,10 @@ export interface FilesSetup { registerFileKind(fileKind: FileKindBrowser): void; } -export type FilesStart = Pick; +export type FilesStart = Pick & { + getFileKindDefinition: (id: string) => FileKindBrowser; + getAllFindKindDefinitions: () => FileKindBrowser[]; +}; /** * Bringing files to Kibana @@ -77,6 +80,12 @@ export class FilesPlugin implements Plugin { start(core: CoreStart): FilesStart { return { filesClientFactory: this.filesClientFactory!, + getFileKindDefinition: (id: string): FileKindBrowser => { + return this.registry.get(id); + }, + getAllFindKindDefinitions: (): FileKindBrowser[] => { + return this.registry.getAll(); + }, }; } } diff --git a/src/plugins/files/server/file_client/file_metadata_client/adapters/query_filters.ts b/src/plugins/files/server/file_client/file_metadata_client/adapters/query_filters.ts index 0f453a1b81e6a..014e57b41d2b1 100644 --- a/src/plugins/files/server/file_client/file_metadata_client/adapters/query_filters.ts +++ b/src/plugins/files/server/file_client/file_metadata_client/adapters/query_filters.ts @@ -24,6 +24,7 @@ export function filterArgsToKuery({ extension, mimeType, kind, + kindToExclude, meta, name, status, @@ -50,12 +51,27 @@ export function filterArgsToKuery({ } }; + const addExcludeFilters = (fieldName: keyof FileMetadata | string, values: string[] = []) => { + if (values.length) { + const andExpressions = values + .filter(Boolean) + .map((value) => + nodeTypes.function.buildNode( + 'not', + nodeBuilder.is(`${attrPrefix}.${fieldName}`, escapeKuery(value)) + ) + ); + kueryExpressions.push(nodeBuilder.and(andExpressions)); + } + }; + addFilters('name', name, true); addFilters('FileKind', kind); addFilters('Status', status); addFilters('extension', extension); addFilters('mime_type', mimeType); addFilters('user.id', user); + addExcludeFilters('FileKind', kindToExclude); if (meta) { const addMetaFilters = pipe( diff --git a/src/plugins/files/server/file_service/file_action_types.ts b/src/plugins/files/server/file_service/file_action_types.ts index 4247f567802ed..96795ac93b387 100644 --- a/src/plugins/files/server/file_service/file_action_types.ts +++ b/src/plugins/files/server/file_service/file_action_types.ts @@ -82,6 +82,10 @@ export interface FindFileArgs extends Pagination { * File kind(s), see {@link FileKind}. */ kind?: string[]; + /** + * File kind(s) to exclude from search, see {@link FileKind}. + */ + kindToExclude?: string[]; /** * File name(s). */ diff --git a/src/plugins/files/server/integration_tests/file_service.test.ts b/src/plugins/files/server/integration_tests/file_service.test.ts index 25d7f463de03a..3492eb8e5f12c 100644 --- a/src/plugins/files/server/integration_tests/file_service.test.ts +++ b/src/plugins/files/server/integration_tests/file_service.test.ts @@ -157,26 +157,39 @@ describe('FileService', () => { createDisposableFile({ fileKind, name: 'foo-2' }), createDisposableFile({ fileKind, name: 'foo-3' }), createDisposableFile({ fileKind, name: 'test-3' }), + createDisposableFile({ fileKind: fileKindNonDefault, name: 'foo-1' }), ]); { const { files, total } = await fileService.find({ - kind: [fileKind], + kind: [fileKind, fileKindNonDefault], name: ['foo*'], perPage: 2, page: 1, }); expect(files.length).toBe(2); - expect(total).toBe(3); + expect(total).toBe(4); } { const { files, total } = await fileService.find({ - kind: [fileKind], + kind: [fileKind, fileKindNonDefault], name: ['foo*'], perPage: 2, page: 2, }); - expect(files.length).toBe(1); + expect(files.length).toBe(2); + expect(total).toBe(4); + } + + // Filter out fileKind + { + const { files, total } = await fileService.find({ + kindToExclude: [fileKindNonDefault], + name: ['foo*'], + perPage: 10, + page: 1, + }); + expect(files.length).toBe(3); // foo-1 from fileKindNonDefault not returned expect(total).toBe(3); } }); diff --git a/src/plugins/files/server/routes/find.ts b/src/plugins/files/server/routes/find.ts index a81a9d2ea5220..6749e06254100 100644 --- a/src/plugins/files/server/routes/find.ts +++ b/src/plugins/files/server/routes/find.ts @@ -30,6 +30,7 @@ export function toArrayOrUndefined(val?: string | string[]): undefined | string[ const rt = { body: schema.object({ kind: schema.maybe(stringOrArrayOfStrings), + kindToExclude: schema.maybe(stringOrArrayOfStrings), status: schema.maybe(stringOrArrayOfStrings), extension: schema.maybe(stringOrArrayOfStrings), name: schema.maybe(nameStringOrArrayOfNameStrings), @@ -50,12 +51,13 @@ export type Endpoint = CreateRouteDefinition< const handler: CreateHandler = async ({ files }, req, res) => { const { fileService } = await files; const { - body: { meta, extension, kind, name, status }, + body: { meta, extension, kind, name, status, kindToExclude }, query, } = req; const { files: results, total } = await fileService.asCurrentUser().find({ kind: toArrayOrUndefined(kind), + kindToExclude: toArrayOrUndefined(kindToExclude), name: toArrayOrUndefined(name), status: toArrayOrUndefined(status), extension: toArrayOrUndefined(extension), diff --git a/src/plugins/files_management/public/app.tsx b/src/plugins/files_management/public/app.tsx index becdd05fa0e2c..3ee4e5f52720c 100644 --- a/src/plugins/files_management/public/app.tsx +++ b/src/plugins/files_management/public/app.tsx @@ -12,20 +12,33 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; import numeral from '@elastic/numeral'; import type { FileJSON } from '@kbn/files-plugin/common'; + import { useFilesManagementContext } from './context'; import { i18nTexts } from './i18n_texts'; import { EmptyPrompt, DiagnosticsFlyout, FileFlyout } from './components'; -type FilesUserContentSchema = UserContentCommonSchema; +type FilesUserContentSchema = Omit & { + attributes: { + title: string; + description?: string; + fileKind: string; + }; +}; function naivelyFuzzify(query: string): string { return query.includes('*') ? query : `*${query}*`; } export const App: FunctionComponent = () => { - const { filesClient } = useFilesManagementContext(); + const { filesClient, getFileKindDefinition, getAllFindKindDefinitions } = + useFilesManagementContext(); const [showDiagnosticsFlyout, setShowDiagnosticsFlyout] = useState(false); const [selectedFile, setSelectedFile] = useState(undefined); + + const kindToExcludeFromSearch = getAllFindKindDefinitions() + .filter(({ managementUiActions }) => managementUiActions?.list?.enabled === false) + .map(({ id }) => id); + return (
    @@ -37,7 +50,10 @@ export const App: FunctionComponent = () => { entityNamePlural={i18nTexts.entityNamePlural} findItems={(searchQuery) => filesClient - .find({ name: searchQuery ? naivelyFuzzify(searchQuery) : undefined }) + .find({ + name: searchQuery ? naivelyFuzzify(searchQuery) : undefined, + kindToExclude: kindToExcludeFromSearch, + }) .then(({ files, total }) => ({ hits: files.map((file) => ({ id: file.id, @@ -71,6 +87,12 @@ export const App: FunctionComponent = () => { {i18nTexts.diagnosticsFlyoutTitle} , ]} + rowItemActions={({ attributes }) => { + const definition = getFileKindDefinition(attributes.fileKind); + return { + delete: definition?.managementUiActions?.delete, + }; + }} /> {showDiagnosticsFlyout && ( setShowDiagnosticsFlyout(false)} /> diff --git a/src/plugins/files_management/public/context.tsx b/src/plugins/files_management/public/context.tsx index 18f031b84e5c1..0688c5a7edecb 100644 --- a/src/plugins/files_management/public/context.tsx +++ b/src/plugins/files_management/public/context.tsx @@ -12,9 +12,16 @@ import type { AppContext } from './types'; const FilesManagementAppContext = createContext(null as unknown as AppContext); -export const FilesManagementAppContextProvider: FC = ({ children, filesClient }) => { +export const FilesManagementAppContextProvider: FC = ({ + children, + filesClient, + getFileKindDefinition, + getAllFindKindDefinitions, +}) => { return ( - + {children} ); diff --git a/src/plugins/files_management/public/i18n_texts.ts b/src/plugins/files_management/public/i18n_texts.ts index c5f4956af372f..d430038dcdddc 100644 --- a/src/plugins/files_management/public/i18n_texts.ts +++ b/src/plugins/files_management/public/i18n_texts.ts @@ -101,4 +101,7 @@ export const i18nTexts = { defaultMessage: 'Upload error', }), } as Record, + rowCheckboxDisabled: i18n.translate('filesManagement.table.checkBoxDisabledLabel', { + defaultMessage: 'This file cannot be deleted.', + }), }; diff --git a/src/plugins/files_management/public/mount_management_section.tsx b/src/plugins/files_management/public/mount_management_section.tsx index 7dce1986237a7..9c7091516d46e 100755 --- a/src/plugins/files_management/public/mount_management_section.tsx +++ b/src/plugins/files_management/public/mount_management_section.tsx @@ -30,6 +30,10 @@ export const mountManagementSection = ( startDeps: StartDependencies, { element, history }: ManagementAppMountParams ) => { + const { + files: { filesClientFactory, getAllFindKindDefinitions, getFileKindDefinition }, + } = startDeps; + ReactDOM.render( @@ -41,7 +45,9 @@ export const mountManagementSection = ( }} > diff --git a/src/plugins/files_management/public/types.ts b/src/plugins/files_management/public/types.ts index 2a73b69bea017..303d5e1c5d1a7 100755 --- a/src/plugins/files_management/public/types.ts +++ b/src/plugins/files_management/public/types.ts @@ -11,6 +11,8 @@ import { ManagementSetup } from '@kbn/management-plugin/public'; export interface AppContext { filesClient: FilesClient; + getFileKindDefinition: FilesStart['getFileKindDefinition']; + getAllFindKindDefinitions: FilesStart['getAllFindKindDefinitions']; } export interface SetupDependencies { diff --git a/src/plugins/guided_onboarding/README.md b/src/plugins/guided_onboarding/README.md index 7dbf443f7f86e..1d1db6d13098a 100755 --- a/src/plugins/guided_onboarding/README.md +++ b/src/plugins/guided_onboarding/README.md @@ -45,18 +45,18 @@ When starting Kibana with `yarn start --run-examples` the `guided_onboarding_exa The guided onboarding plugin exposes an API service from its start contract that is intended to be used by other plugins. The API service allows consumers to access the current state of the guided onboarding process and manipulate it. To use the API service in your plugin, declare the guided onboarding plugin as a dependency in the file `kibana.json` of your plugin. Add the API service to your plugin's start dependencies to rely on the provided TypeScript interface: -``` +```js export interface AppPluginStartDependencies { guidedOnboarding: GuidedOnboardingPluginStart; } ``` The API service is now available to your plugin in the setup lifecycle function of your plugin -``` +```js // startDependencies is of type AppPluginStartDependencies const [coreStart, startDependencies] = await core.getStartServices(); ``` or in the start lifecycle function of your plugin. -``` +```js public start(core: CoreStart, startDependencies: AppPluginStartDependencies) { ... } @@ -67,7 +67,7 @@ public start(core: CoreStart, startDependencies: AppPluginStartDependencies) { The API service exposes an Observable that contains a boolean value for the state of a specific guide step. For example, if your plugin needs to check if the "Add data" step of the SIEM guide is currently active, you could use the following code snippet. -``` +```js const { guidedOnboardingApi } = guidedOnboarding; const isDataStepActive = useObservable(guidedOnboardingApi!.isGuideStepActive$('siem', 'add_data')); useEffect(() => { @@ -76,7 +76,7 @@ useEffect(() => { ``` Alternatively, you can subscribe to the Observable directly. -``` +```js useEffect(() => { const subscription = guidedOnboardingApi?.isGuideStepActive$('siem', 'add_data').subscribe((isDataStepACtive) => { // do some logic depending on the step state @@ -89,7 +89,7 @@ useEffect(() => { Similar to `isGuideStepActive$`, the observable `isGuideStepReadyToComplete$` can be used to track the state of a step that is configured for manual completion. The observable broadcasts `true` when the manual completion popover is displayed and the user can mark the step "done". In this state the step is not in progress anymore but is not yet fully completed. -### completeGuideStep(guideId: GuideId, stepId: GuideStepIds): Promise\<{ pluginState: PluginState } | undefined\> +### completeGuideStep(guideId: GuideId, stepId: GuideStepIds, params?: GuideParams): Promise\<{ pluginState: PluginState } | undefined\> The API service exposes an async function to mark a guide step as completed. If the specified guide step is not currently active, the function is a noop. In that case the return value is `undefined`, otherwise an updated `PluginState` is returned. @@ -98,8 +98,20 @@ otherwise an updated `PluginState` is returned. await guidedOnboardingApi?.completeGuideStep('siem', 'add_data'); ``` +The function also accepts an optional argument `params` that will be saved in the state and later used for step URLs with dynamic parameters. For example, step 2 of the guide has a dynamic parameter `indexID` in its location path: +```js +const step2Config = { + id: 'step2', + description: 'Step with dynamic url', + location: { + appID: 'test', path: 'testPath/{indexID}' + } +}; +``` +The value of the parameter `indexID` needs to be passed to the API service when completing step 1: `completeGuideStep('testGuide', 'step1', { indexID: 'testIndex' })` + ## Guides config -To use the API service, you need to know a guide ID (currently one of `search`, `kubernetes`, `siem`) and a step ID (for example, `add_data`, `search_experience`, `rules` etc). The consumers of guided onboarding register their guide configs themselves and have therefore full control over the guide ID and step IDs used for their guide. For more details on registering a guide config, see below. +To use the API service, you need to know a guide ID (currently one of `appSearch`, `websiteSearch`, `databaseSearch`, `kubernetes`, `siem`) and a step ID (for example, `add_data`, `search_experience`, `rules` etc). The consumers of guided onboarding register their guide configs themselves and have therefore full control over the guide ID and step IDs used for their guide. For more details on registering a guide config, see below. ## Server side: register a guide config The guided onboarding exposes a function `registerGuideConfig(guideId: GuideId, guideConfig: GuideConfig)` function in its setup contract. This function allows consumers to register a guide config for a specified guide ID. The function throws an error if a config already exists for the guide ID. See code examples in following plugins: diff --git a/src/plugins/guided_onboarding/common/constants.ts b/src/plugins/guided_onboarding/common/constants.ts index e0c5961c1f6aa..1ab45d3659d73 100755 --- a/src/plugins/guided_onboarding/common/constants.ts +++ b/src/plugins/guided_onboarding/common/constants.ts @@ -9,4 +9,4 @@ export const PLUGIN_ID = 'guidedOnboarding'; export const PLUGIN_NAME = 'guidedOnboarding'; -export const API_BASE_PATH = '/api/guided_onboarding'; +export const API_BASE_PATH = '/internal/guided_onboarding'; diff --git a/src/plugins/guided_onboarding/public/components/get_step_location.test.ts b/src/plugins/guided_onboarding/public/components/get_step_location.test.ts new file mode 100644 index 0000000000000..98c72fb3ae0b2 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/get_step_location.test.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 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 type { PluginState } from '../../common'; +import { testGuideStep4ActiveState } from '../services/api.mocks'; +import { getStepLocationPath } from './get_step_location'; + +describe('getStepLocationPath', () => { + let result: string | undefined; + const pathWithParams = 'testPath/{param1}/{param2}'; + const pathWithoutParams = 'testPath'; + const pluginStateWithoutParams: PluginState = { + status: 'in_progress', + isActivePeriod: true, + activeGuide: testGuideStep4ActiveState, + }; + + it('returns initial location path if no params passed', () => { + result = getStepLocationPath(pathWithParams, pluginStateWithoutParams); + expect(result).toBe(pathWithParams); + }); + + it('returns dynamic location path if params passed', () => { + const pluginStateWithParams: PluginState = { + status: 'in_progress', + isActivePeriod: true, + activeGuide: { ...testGuideStep4ActiveState, params: { param1: 'test1', param2: 'test2' } }, + }; + result = getStepLocationPath(pathWithParams, pluginStateWithParams); + expect(result).toBe(`testPath/test1/test2`); + }); + + it('returns initial location path if params passed but no params are used in the location', () => { + const pluginStateWithParams: PluginState = { + status: 'in_progress', + isActivePeriod: true, + activeGuide: { ...testGuideStep4ActiveState, params: { indexName: 'test1234' } }, + }; + result = getStepLocationPath(pathWithoutParams, pluginStateWithParams); + expect(result).toBe(`testPath`); + }); +}); diff --git a/src/plugins/guided_onboarding/public/components/get_step_location.ts b/src/plugins/guided_onboarding/public/components/get_step_location.ts new file mode 100644 index 0000000000000..3e68b3af47eb5 --- /dev/null +++ b/src/plugins/guided_onboarding/public/components/get_step_location.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 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 { PluginState } from '../../common'; + +// regex matches everything between an opening and a closing curly braces +// without matching the braces themselves +const paramsBetweenCurlyBraces = /(?<=\{)[^\{\}]+(?=\})/g; +export const getStepLocationPath = (path: string, pluginState: PluginState): string | undefined => { + if (pluginState.activeGuide?.params) { + let dynamicPath = path; + const matchedParams = path.match(paramsBetweenCurlyBraces); + if (matchedParams) { + for (const param of matchedParams) { + dynamicPath = dynamicPath.replace(`{${param}}`, pluginState.activeGuide?.params[param]); + } + return dynamicPath; + } + } + return path; +}; diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx index 233d9c348c436..42440cd2587b2 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.test.tsx @@ -457,7 +457,7 @@ describe('Guided setup', () => { expect( find('guidePanelStepDescription') - .last() + .first() .containsMatchingElement(

    {testGuideConfig.steps[2].description}

    ) ).toBe(true); }); diff --git a/src/plugins/guided_onboarding/public/components/guide_panel.tsx b/src/plugins/guided_onboarding/public/components/guide_panel.tsx index 2d3ef51489081..a87b006eb55eb 100644 --- a/src/plugins/guided_onboarding/public/components/guide_panel.tsx +++ b/src/plugins/guided_onboarding/public/components/guide_panel.tsx @@ -24,6 +24,7 @@ import { getGuidePanelStyles } from './guide_panel.styles'; import { GuideButton } from './guide_button'; import { GuidePanelFlyout } from './guide_panel_flyout'; +import { getStepLocationPath } from './get_step_location'; interface GuidePanelProps { api: GuidedOnboardingApi; @@ -76,7 +77,7 @@ export const GuidePanel = ({ api, application, notifications, uiSettings }: Guid if (stepConfig.location) { await application.navigateToApp(stepConfig.location.appID, { - path: stepConfig.location.path, + path: getStepLocationPath(stepConfig.location.path, pluginState), }); if (stepConfig.manualCompletion?.readyToCompleteOnNavigation) { diff --git a/src/plugins/guided_onboarding/public/services/api.mocks.ts b/src/plugins/guided_onboarding/public/services/api.mocks.ts index 1d5dfc16a9145..3899eb61ff4bf 100644 --- a/src/plugins/guided_onboarding/public/services/api.mocks.ts +++ b/src/plugins/guided_onboarding/public/services/api.mocks.ts @@ -12,7 +12,7 @@ import { PluginState } from '../../common'; export const testGuideFirstStep: GuideStepIds = 'step1'; export const testGuideManualCompletionStep = 'step2'; -export const testGuideLastStep: GuideStepIds = 'step3'; +export const testGuideLastStep: GuideStepIds = 'step4'; export const testIntegration = 'testIntegration'; export const wrongIntegration = 'notTestIntegration'; @@ -33,6 +33,10 @@ export const testGuideStep1ActiveState: GuideState = { id: 'step3', status: 'inactive', }, + { + id: 'step4', + status: 'inactive', + }, ], }; @@ -45,6 +49,7 @@ export const testGuideStep1InProgressState: GuideState = { }, testGuideStep1ActiveState.steps[1], testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -60,6 +65,7 @@ export const testGuideStep2ActiveState: GuideState = { status: 'active', }, testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -75,6 +81,7 @@ export const testGuideStep2InProgressState: GuideState = { status: 'in_progress', }, testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -90,6 +97,7 @@ export const testGuideStep2ReadyToCompleteState: GuideState = { status: 'ready_to_complete', }, testGuideStep1ActiveState.steps[2], + testGuideStep1ActiveState.steps[3], ], }; @@ -108,6 +116,29 @@ export const testGuideStep3ActiveState: GuideState = { id: testGuideStep1ActiveState.steps[2].id, status: 'active', }, + testGuideStep1ActiveState.steps[3], + ], +}; + +export const testGuideStep4ActiveState: GuideState = { + ...testGuideStep1ActiveState, + steps: [ + { + ...testGuideStep1ActiveState.steps[0], + status: 'complete', + }, + { + id: testGuideStep1ActiveState.steps[1].id, + status: 'complete', + }, + { + id: testGuideStep1ActiveState.steps[2].id, + status: 'complete', + }, + { + id: testGuideStep1ActiveState.steps[3].id, + status: 'active', + }, ], }; @@ -126,6 +157,10 @@ export const readyToCompleteGuideState: GuideState = { ...testGuideStep1ActiveState.steps[2], status: 'complete', }, + { + ...testGuideStep1ActiveState.steps[3], + status: 'complete', + }, ], }; @@ -144,3 +179,8 @@ export const mockPluginStateInProgress: PluginState = { isActivePeriod: true, activeGuide: testGuideStep1ActiveState, }; + +export const testGuideParams = { + param1: 'test1', + param2: 'test2', +}; diff --git a/src/plugins/guided_onboarding/public/services/api.test.ts b/src/plugins/guided_onboarding/public/services/api.service.test.ts similarity index 95% rename from src/plugins/guided_onboarding/public/services/api.test.ts rename to src/plugins/guided_onboarding/public/services/api.service.test.ts index 41c6a93faf27e..e6ce000bde594 100644 --- a/src/plugins/guided_onboarding/public/services/api.test.ts +++ b/src/plugins/guided_onboarding/public/services/api.service.test.ts @@ -16,7 +16,6 @@ import { API_BASE_PATH } from '../../common'; import { ApiService } from './api.service'; import { testGuideFirstStep, - testGuideLastStep, testGuideManualCompletionStep, testGuideStep1ActiveState, testGuideStep1InProgressState, @@ -30,6 +29,7 @@ import { mockPluginStateNotStarted, testGuideStep3ActiveState, testGuideStep2ReadyToCompleteState, + testGuideParams, } from './api.mocks'; describe('GuidedOnboarding ApiService', () => { @@ -395,6 +395,21 @@ describe('GuidedOnboarding ApiService', () => { }); }); + it(`saves the params if present`, async () => { + httpClient.get.mockResolvedValue({ + pluginState: { ...mockPluginStateInProgress, activeGuide: testGuideStep1InProgressState }, + }); + apiService.setup(httpClient, true); + + await apiService.completeGuideStep(testGuideId, testGuideFirstStep, testGuideParams); + + expect(httpClient.put).toHaveBeenCalledTimes(1); + // Verify the params were sent to the endpoint + expect(httpClient.put).toHaveBeenLastCalledWith(`${API_BASE_PATH}/state`, { + body: JSON.stringify({ guide: { ...testGuideStep2ActiveState, params: testGuideParams } }), + }); + }); + it(`marks the step as 'ready_to_complete' if it's configured for manual completion`, async () => { httpClient.get.mockResolvedValueOnce({ pluginState: { ...mockPluginStateInProgress, activeGuide: testGuideStep2InProgressState }, @@ -416,6 +431,7 @@ describe('GuidedOnboarding ApiService', () => { testGuideStep2InProgressState.steps[0], { ...testGuideStep2InProgressState.steps[1], status: 'ready_to_complete' }, testGuideStep2InProgressState.steps[2], + testGuideStep2InProgressState.steps[3], ], }, }), @@ -436,11 +452,21 @@ describe('GuidedOnboarding ApiService', () => { }, }); httpClient.get.mockResolvedValueOnce({ - config: testGuideConfig, + config: { + ...testGuideConfig, + steps: [ + // remove step4 for this test to make step3 the last in the guide + testGuideConfig.steps[0], + testGuideConfig.steps[1], + testGuideConfig.steps[2], + ], + }, }); apiService.setup(httpClient, true); - await apiService.completeGuideStep(testGuideId, testGuideLastStep); + // for this test step3 is the last step + const lastStepId = testGuideConfig.steps[2].id; + await apiService.completeGuideStep(testGuideId, lastStepId); expect(httpClient.put).toHaveBeenCalledTimes(1); // Verify the guide now has a "ready_to_complete" status and the last step is "complete" @@ -479,6 +505,7 @@ describe('GuidedOnboarding ApiService', () => { testGuideStep2ActiveState.steps[0], { ...testGuideStep2ActiveState.steps[1], status: 'active' }, testGuideStep2ActiveState.steps[2], + testGuideStep2ActiveState.steps[3], ], }, }), diff --git a/src/plugins/guided_onboarding/public/services/api.service.ts b/src/plugins/guided_onboarding/public/services/api.service.ts index 949002b19ea6a..d3d20143f600d 100644 --- a/src/plugins/guided_onboarding/public/services/api.service.ts +++ b/src/plugins/guided_onboarding/public/services/api.service.ts @@ -23,6 +23,7 @@ import type { GuideStep, GuideStepIds, GuideConfig, + GuideParams, } from '@kbn/guided-onboarding'; import { API_BASE_PATH } from '../../common'; @@ -360,7 +361,8 @@ export class ApiService implements GuidedOnboardingApi { */ public async completeGuideStep( guideId: GuideId, - stepId: GuideStepIds + stepId: GuideStepIds, + params?: GuideParams ): Promise<{ pluginState: PluginState } | undefined> { const pluginState = await firstValueFrom(this.fetchPluginState$()); // For now, returning undefined if consumer attempts to complete a step for a guide that isn't active @@ -395,6 +397,7 @@ export class ApiService implements GuidedOnboardingApi { isActive: true, status, steps: updatedSteps, + params, }; return await this.updatePluginState( diff --git a/src/plugins/guided_onboarding/public/types.ts b/src/plugins/guided_onboarding/public/types.ts index 1b0ccc7d925b3..1103c2ee350da 100755 --- a/src/plugins/guided_onboarding/public/types.ts +++ b/src/plugins/guided_onboarding/public/types.ts @@ -8,7 +8,13 @@ import { Observable } from 'rxjs'; import { HttpSetup } from '@kbn/core/public'; -import type { GuideState, GuideId, GuideStepIds, GuideConfig } from '@kbn/guided-onboarding'; +import type { + GuideState, + GuideId, + GuideStepIds, + GuideConfig, + GuideParams, +} from '@kbn/guided-onboarding'; import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { PluginStatus, PluginState } from '../common'; @@ -45,7 +51,8 @@ export interface GuidedOnboardingApi { ) => Promise<{ pluginState: PluginState } | undefined>; completeGuideStep: ( guideId: GuideId, - stepId: GuideStepIds + stepId: GuideStepIds, + params?: GuideParams ) => Promise<{ pluginState: PluginState } | undefined>; isGuidedOnboardingActiveForIntegration$: (integration?: string) => Observable; completeGuidedOnboardingForIntegration: ( diff --git a/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts b/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts index 997303c095098..dcd46984b166f 100644 --- a/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts +++ b/src/plugins/guided_onboarding/server/routes/plugin_state_routes.ts @@ -53,6 +53,8 @@ export const registerPutPluginStateRoute = (router: IRouter) => { id: schema.string(), }) ), + // params are dynamic values + params: schema.maybe(schema.object({}, { unknowns: 'allow' })), }) ), }), diff --git a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts index ba8f3c31fc742..687372ed0a559 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/saved_objects.ts @@ -28,6 +28,7 @@ export const getSavedObjects = (): SavedObject[] => [ id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.11.0', + managed: false, references: [], type: 'index-pattern', updated_at: '2021-08-05T12:23:57.577Z', @@ -50,6 +51,7 @@ export const getSavedObjects = (): SavedObject[] => [ id: 'b80e6540-b891-11e8-a6d9-e546fe2bba5f', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.14.0', + managed: false, references: [ { id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', diff --git a/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts index afc9033efa493..7aa32a5c5584c 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/flights/saved_objects.ts @@ -19,6 +19,7 @@ export const getSavedObjects = (): SavedObject[] => [ version: '1', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.9.3', + managed: false, attributes: { title: i18n.translate('home.sampleData.flightsSpec.flightLogTitle', { defaultMessage: '[Flights] Flight Log', @@ -78,6 +79,7 @@ export const getSavedObjects = (): SavedObject[] => [ version: '1', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.14.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.flightsSpec.departuresCountMapTitle', { defaultMessage: '[Flights] Departures Count Map', @@ -290,5 +292,6 @@ export const getSavedObjects = (): SavedObject[] => [ ], coreMigrationVersion: '8.8.0', typeMigrationVersion: '8.7.0', + managed: false, }, ]; diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts index 6d8a2b46f4e72..c0140bd89283d 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts @@ -18,6 +18,7 @@ export const getSavedObjects = (): SavedObject[] => [ version: '1', coreMigrationVersion: '8.8.0', typeMigrationVersion: '8.0.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsSpec.visitorsMapTitle', { defaultMessage: '[Logs] Visitors Map', @@ -47,6 +48,7 @@ export const getSavedObjects = (): SavedObject[] => [ version: '1', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.14.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsSpec.heatmapTitle', { defaultMessage: '[Logs] Unique Destination Heatmap', @@ -89,6 +91,7 @@ export const getSavedObjects = (): SavedObject[] => [ version: '1', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.14.0', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsSpec.bytesDistributionTitle', { defaultMessage: '[Logs] Bytes distribution', @@ -399,6 +402,7 @@ export const getSavedObjects = (): SavedObject[] => [ ], coreMigrationVersion: '8.8.0', typeMigrationVersion: '8.7.0', + managed: false, }, { id: '2f360f30-ea74-11eb-b4c6-3d2afc1cb389', @@ -407,6 +411,7 @@ export const getSavedObjects = (): SavedObject[] => [ version: '1', coreMigrationVersion: '8.8.0', typeMigrationVersion: '7.9.3', + managed: false, attributes: { title: i18n.translate('home.sampleData.logsSpec.discoverTitle', { defaultMessage: '[Logs] Visits', diff --git a/src/plugins/home/server/services/sample_data/sample_data_registry.ts b/src/plugins/home/server/services/sample_data/sample_data_registry.ts index a5cf89f65cb21..47ddc122f82cb 100644 --- a/src/plugins/home/server/services/sample_data/sample_data_registry.ts +++ b/src/plugins/home/server/services/sample_data/sample_data_registry.ts @@ -31,6 +31,7 @@ import { registerSampleDatasetWithIntegration } from './lib/register_with_integr export class SampleDataRegistry { constructor(private readonly initContext: PluginInitializerContext) {} + private readonly sampleDatasets: SampleDatasetSchema[] = []; private readonly appLinksMap = new Map(); @@ -68,8 +69,9 @@ export class SampleDataRegistry { isDevMode?: boolean ) { if (usageCollections) { - const kibanaIndex = core.savedObjects.getKibanaIndex(); - makeSampleDataUsageCollector(usageCollections, kibanaIndex); + const getIndexForType = (type: string) => + core.getStartServices().then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); + makeSampleDataUsageCollector(usageCollections, getIndexForType); } const usageTracker = usage( core.getStartServices().then(([coreStart]) => coreStart.savedObjects), @@ -176,6 +178,7 @@ export class SampleDataRegistry { return {}; } } + /** @public */ export type SampleDataRegistrySetup = ReturnType; diff --git a/src/plugins/home/server/services/sample_data/usage/collector.ts b/src/plugins/home/server/services/sample_data/usage/collector.ts index 5f32d4c79369c..716e630b55215 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector.ts @@ -11,11 +11,11 @@ import { fetchProvider, TelemetryResponse } from './collector_fetch'; export function makeSampleDataUsageCollector( usageCollection: UsageCollectionSetup, - kibanaIndex: string + getIndexForType: (type: string) => Promise ) { const collector = usageCollection.makeUsageCollector({ type: 'sample-data', - fetch: fetchProvider(kibanaIndex), + fetch: fetchProvider(getIndexForType), isReady: () => true, schema: { installed: { type: 'array', items: { type: 'keyword' } }, diff --git a/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts b/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts index 54df964e4bdec..fcf49eb7f744d 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts @@ -19,8 +19,10 @@ const getMockFetchClients = (hits?: unknown[]) => { describe('Sample Data Fetch', () => { let collectorFetchContext: CollectorFetchContext; + const getIndexForType = (index: string) => (type: string) => Promise.resolve(index); + test('uninitialized .kibana', async () => { - const fetch = fetchProvider('index'); + const fetch = fetchProvider(getIndexForType('index')); collectorFetchContext = getMockFetchClients(); const telemetry = await fetch(collectorFetchContext); @@ -28,7 +30,7 @@ describe('Sample Data Fetch', () => { }); test('installed data set', async () => { - const fetch = fetchProvider('index'); + const fetch = fetchProvider(getIndexForType('index')); collectorFetchContext = getMockFetchClients([ { _id: 'sample-data-telemetry:test1', @@ -55,7 +57,7 @@ Object { }); test('multiple installed data sets', async () => { - const fetch = fetchProvider('index'); + const fetch = fetchProvider(getIndexForType('index')); collectorFetchContext = getMockFetchClients([ { _id: 'sample-data-telemetry:test1', @@ -90,7 +92,7 @@ Object { }); test('installed data set, missing counts', async () => { - const fetch = fetchProvider('index'); + const fetch = fetchProvider(getIndexForType('index')); collectorFetchContext = getMockFetchClients([ { _id: 'sample-data-telemetry:test1', @@ -112,7 +114,7 @@ Object { }); test('installed and uninstalled data sets', async () => { - const fetch = fetchProvider('index'); + const fetch = fetchProvider(getIndexForType('index')); collectorFetchContext = getMockFetchClients([ { _id: 'sample-data-telemetry:test0', diff --git a/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts b/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts index a5673fdc12a2f..28e753cb46b60 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts @@ -33,8 +33,9 @@ export interface TelemetryResponse { type ESResponse = SearchResponse; -export function fetchProvider(index: string) { +export function fetchProvider(getIndexForType: (type: string) => Promise) { return async ({ esClient }: CollectorFetchContext) => { + const index = await getIndexForType('sample-data-telemetry'); const response = await esClient.search( { index, diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts index cdf2ca35d6ecc..7d6e600ded94b 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts @@ -27,8 +27,9 @@ describe('kibana_usage', () => { }); const kibanaIndex = '.kibana-tests'; + const getIndicesForTypes = () => Promise.resolve([kibanaIndex]); - beforeAll(() => registerKibanaUsageCollector(usageCollectionMock, kibanaIndex)); + beforeAll(() => registerKibanaUsageCollector(usageCollectionMock, getIndicesForTypes)); afterAll(() => jest.clearAllTimers()); afterEach(() => getSavedObjectsCountsMock.mockReset()); diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts index 9a128888f05d5..8451b3eadeb00 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts @@ -43,7 +43,7 @@ export async function getKibanaSavedObjectCounts( export function registerKibanaUsageCollector( usageCollection: UsageCollectionSetup, - kibanaIndex: string + getIndicesForTypes: (types: string[]) => Promise ) { usageCollection.registerCollector( usageCollection.makeUsageCollector({ @@ -80,8 +80,9 @@ export function registerKibanaUsageCollector( }, }, async fetch({ soClient }) { + const indices = await getIndicesForTypes(['dashboard', 'visualization', 'search']); return { - index: kibanaIndex, + index: indices[0], ...(await getKibanaSavedObjectCounts(soClient)), }; }, diff --git a/src/plugins/kibana_usage_collection/server/plugin.ts b/src/plugins/kibana_usage_collection/server/plugin.ts index 8787d085c692e..5c807e01c0ce0 100644 --- a/src/plugins/kibana_usage_collection/server/plugin.ts +++ b/src/plugins/kibana_usage_collection/server/plugin.ts @@ -123,7 +123,6 @@ export class KibanaUsageCollectionPlugin implements Plugin { pluginStop$: Subject, registerType: SavedObjectsRegisterType ) { - const kibanaIndex = coreSetup.savedObjects.getKibanaIndex(); const getSavedObjectsClient = () => this.savedObjectsClient; const getUiSettingsClient = () => this.uiSettingsClient; const getCoreUsageDataService = () => this.coreUsageData!; @@ -138,7 +137,12 @@ export class KibanaUsageCollectionPlugin implements Plugin { registerUsageCountersUsageCollector(usageCollection); registerOpsStatsCollector(usageCollection, metric$); - registerKibanaUsageCollector(usageCollection, kibanaIndex); + + const getIndicesForTypes = (types: string[]) => + coreSetup + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects.getIndicesForTypes(types)); + registerKibanaUsageCollector(usageCollection, getIndicesForTypes); const coreStartPromise = coreSetup.getStartServices().then(([coreStart]) => coreStart); const getAllSavedObjectTypes = async () => { diff --git a/src/plugins/newsfeed/public/components/flyout_list.tsx b/src/plugins/newsfeed/public/components/flyout_list.tsx index 23be44230e220..c09524d384e86 100644 --- a/src/plugins/newsfeed/public/components/flyout_list.tsx +++ b/src/plugins/newsfeed/public/components/flyout_list.tsx @@ -32,11 +32,11 @@ import { NewsLoadingPrompt } from './loading_news'; export const NewsfeedFlyout = (props: Partial & { showPlainSpinner: boolean }) => { const { newsFetchResult, setFlyoutVisible } = useContext(NewsfeedContext); const closeFlyout = useCallback(() => setFlyoutVisible(false), [setFlyoutVisible]); - + const { showPlainSpinner, ...rest } = props; return ( ## Redux Embeddables -The Redux Embeddables system allows embeddable authors to interact with their embeddables in a standardized way using Redux toolkit. This wrapper abstracts away store and slice creation, embeddable input sync, and context creation. To use this system, a developer can wrap their components in the ReduxEmbeddableWrapper, supplying only an object of reducers. +The Redux Embeddables system allows embeddable authors to interact with their embeddables in a standardized way using Redux toolkit. This wrapper abstracts away store and slice creation, and embeddable input sync. To use this system, a developer can use CreateReduxEmbeddableTools in the constructor of their embeddable, supplying a collection of reducers. ### Reducers The reducer object expected by the ReduxEmbeddableWrapper is the same type as the reducers expected by [Redux Toolkit's CreateSlice](https://redux-toolkit.js.org/api/createslice). @@ -233,74 +233,20 @@ export const myEmbeddableReducers = { ``` -### Lazy Component and Types -Because the ReduxEmbeddableWrapper is a lazy component, it also must be unwrapped with the `withSuspense` component from Presentation Util. When you await this component, you must also pass in the type information so that the redux store and actions are properly typed. - - - ```ts - // my_embeddable.tsx - - import { - withSuspense, - LazyReduxEmbeddableWrapper, - ReduxEmbeddableWrapperPropsWithChildren, - } from '../../../../presentation_util/public'; - - export interface MyEmbeddableInput { - specialBoolean: boolean - } - - const MyEmbeddableReduxWrapper = withSuspense< - ReduxEmbeddableWrapperPropsWithChildren - >(LazyReduxEmbeddableWrapper); - - ``` - - -The ReduxEmbeddableWrapper should be used inside of embeddable classes, and should wrap all components under the embeddable in the render function. - - - ```ts - // my_embeddable.tsx - - public render(dom: HTMLElement) { - if (this.domNode) ReactDOM.unmountComponentAtNode(this.domNode); - this.domNode = dom; - ReactDOM.render( - - - , - dom - ); - } - ``` - - ### Accessing Actions and State -From components under the embeddable, actions, containerActions, and the current state of the redux store are accessed via the ReduxEmbeddableContext. This context requires the input type and the type of the reducers, and will return the appropriately typed actions, a hook for dispatching actions, a selector to get the current redux state, and a suite of `containerActions` if the embeddable is a Container. +From components under the embeddable, actions, containerActions, and the current state of the redux store are accessed via the embeddable instance. You can pass the embeddable instance down as a prop, or use a context. ```ts // my_embeddable_component.tsx - import { useReduxEmbeddableContext } from '../../../../presentation_util/public'; + const MyEmbeddableComponent = ({ embeddableInstance }: { embeddableInstance: IEmbeddable }) => { + // current state + const specialBoolean = embeddableInstance.select((state) => state.specialBoolean); - const { - useEmbeddableSelector, - useEmbeddableDispatch, - actions: { setSpecialBoolean }, - } = useReduxEmbeddableContext< - MyEmbeddableInput, - typeof myEmbeddableReducers - >(); + // change specialBoolean after 5 seconds + setTimeout(() => embeddableInstance.dispatch.setSpecialBoolean(false), 5000); - const dispatch = useEmbeddableDispatch(); - - // current state - const { specialBoolean } = useEmbeddableSelector((state) => state); - - // change specialBoolean after 5 seconds - setTimeout(() => dispatch(setSpecialBoolean(false)), 5000); + } ``` - diff --git a/src/plugins/presentation_util/public/components/floating_actions/floating_actions.tsx b/src/plugins/presentation_util/public/components/floating_actions/floating_actions.tsx index 35c92e83035a0..be0e6ef1054f9 100644 --- a/src/plugins/presentation_util/public/components/floating_actions/floating_actions.tsx +++ b/src/plugins/presentation_util/public/components/floating_actions/floating_actions.tsx @@ -8,12 +8,17 @@ import React, { FC, ReactElement, useEffect, useState } from 'react'; import classNames from 'classnames'; -import { IEmbeddable, panelHoverTrigger, PANEL_HOVER_TRIGGER } from '@kbn/embeddable-plugin/public'; +import { + type ViewMode, + type IEmbeddable, + type EmbeddableInput, + panelHoverTrigger, + PANEL_HOVER_TRIGGER, +} from '@kbn/embeddable-plugin/public'; import { Action } from '@kbn/ui-actions-plugin/public'; import { pluginServices } from '../../services'; import './floating_actions.scss'; -import { ReduxEmbeddableState } from '../../redux_embeddables'; export interface FloatingActionsProps { children: ReactElement; @@ -21,8 +26,8 @@ export interface FloatingActionsProps { className?: string; isEnabled?: boolean; embeddable?: IEmbeddable; - viewMode?: ReduxEmbeddableState['explicitInput']['viewMode']; - disabledActions?: ReduxEmbeddableState['explicitInput']['disabledActions']; + viewMode?: ViewMode; + disabledActions?: EmbeddableInput['disabledActions']; } export const FloatingActions: FC = ({ diff --git a/src/plugins/presentation_util/public/index.ts b/src/plugins/presentation_util/public/index.ts index 51f8a2c902541..f5994b3da82e2 100644 --- a/src/plugins/presentation_util/public/index.ts +++ b/src/plugins/presentation_util/public/index.ts @@ -40,13 +40,13 @@ export { } from './components'; export { - useReduxEmbeddableContext, - lazyLoadReduxEmbeddablePackage, + lazyLoadReduxToolsPackage, cleanFiltersForSerialize, type ReduxEmbeddableState, type ReduxEmbeddableTools, - type ReduxEmbeddablePackage, -} from './redux_embeddables'; + type ReduxTools, + type ReduxToolsPackage, +} from './redux_tools'; export type { ExpressionInputEditorRef, diff --git a/src/plugins/presentation_util/public/mocks.ts b/src/plugins/presentation_util/public/mocks.ts index 2a3bdba1a0a7e..2c59d3ad55aad 100644 --- a/src/plugins/presentation_util/public/mocks.ts +++ b/src/plugins/presentation_util/public/mocks.ts @@ -10,7 +10,9 @@ import { CoreStart } from '@kbn/core/public'; import { PresentationUtilPluginStart } from './types'; import { pluginServices } from './services'; import { registry } from './services/plugin_services'; -import { registerExpressionsLanguage } from '.'; +import { ReduxToolsPackage, registerExpressionsLanguage } from '.'; +import { createReduxEmbeddableTools } from './redux_tools/redux_embeddables/create_redux_embeddable_tools'; +import { createReduxTools } from './redux_tools/create_redux_tools'; const createStartContract = (coreStart: CoreStart): PresentationUtilPluginStart => { pluginServices.setRegistry( @@ -29,4 +31,12 @@ export const presentationUtilPluginMock = { createStartContract, }; +/** + * A non async-imported version of the real redux embeddable tools package for mocking purposes. + */ +export const mockedReduxEmbeddablePackage: ReduxToolsPackage = { + createReduxEmbeddableTools, + createReduxTools, +}; + export * from './__stories__/fixtures/flights'; diff --git a/src/plugins/presentation_util/public/redux_embeddables/index.ts b/src/plugins/presentation_util/public/redux_embeddables/index.ts deleted file mode 100644 index d18715cc814df..0000000000000 --- a/src/plugins/presentation_util/public/redux_embeddables/index.ts +++ /dev/null @@ -1,21 +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 { ReduxEmbeddablePackage } from './types'; - -export { useReduxEmbeddableContext } from './use_redux_embeddable_context'; - -export type { ReduxEmbeddableState, ReduxEmbeddableTools, ReduxEmbeddablePackage } from './types'; -export { cleanFiltersForSerialize } from './clean_redux_embeddable_state'; - -export const lazyLoadReduxEmbeddablePackage = async (): Promise => { - const { createReduxEmbeddableTools } = await import('./create_redux_embeddable_tools'); - return { - createTools: createReduxEmbeddableTools, - }; -}; diff --git a/src/plugins/presentation_util/public/redux_embeddables/types.ts b/src/plugins/presentation_util/public/redux_embeddables/types.ts deleted file mode 100644 index 84e088328b7f9..0000000000000 --- a/src/plugins/presentation_util/public/redux_embeddables/types.ts +++ /dev/null @@ -1,115 +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 { - Dispatch, - AnyAction, - CaseReducer, - PayloadAction, - ActionCreatorWithPayload, - EnhancedStore, -} from '@reduxjs/toolkit'; -import { TypedUseSelectorHook } from 'react-redux'; -import { EmbeddableInput, EmbeddableOutput, Embeddable } from '@kbn/embeddable-plugin/public'; -import { PropsWithChildren } from 'react'; - -export interface ReduxEmbeddableSyncSettings< - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState -> { - disableSync: boolean; - isInputEqual?: ( - a: Partial, - b: Partial - ) => boolean; - isOutputEqual?: ( - a: Partial, - b: Partial - ) => boolean; -} - -/** - * The package type is lazily exported from presentation_util and should contain all methods needed to use the redux embeddable tools. - */ -export interface ReduxEmbeddablePackage { - createTools: typeof import('./create_redux_embeddable_tools')['createReduxEmbeddableTools']; -} - -/** - * The return type from setupReduxEmbeddable. Contains a wrapper which comes with the store provider and provides the context to react components, - * but also returns the context object to allow the embeddable class to interact with the redux store. - */ -export interface ReduxEmbeddableTools< - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, - ReducerType extends EmbeddableReducers = EmbeddableReducers -> { - cleanup: () => void; - Wrapper: React.FC>; - dispatch: EnhancedStore['dispatch']; - getState: EnhancedStore['getState']; - onStateChange: EnhancedStore['subscribe']; - actions: ReduxEmbeddableContext['actions']; -} - -/** - * The Embeddable Redux store should contain Input, Output and State. Input is serialized and used to create the embeddable, - * Output is used as a communication layer for state that the Embeddable creates, and State is used to store ephemeral state which needs - * to be communicated between an embeddable and its inner React components. - */ -export interface ReduxEmbeddableState< - InputType extends EmbeddableInput = EmbeddableInput, - OutputType extends EmbeddableOutput = EmbeddableOutput, - StateType extends unknown = unknown -> { - explicitInput: InputType; - output: OutputType; - componentState: StateType; -} - -/** - * The Embeddable Reducers are the shape of the Raw reducers which will be passed into createSlice. These will be used to populate the actions - * object which will be returned in the ReduxEmbeddableContext. - */ -export interface EmbeddableReducers< - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState -> { - /** - * PayloadAction of type any is strategic here because we want to allow payloads of any shape in generic reducers. - * This type will be overridden to remove any and be type safe when returned by setupReduxEmbeddable. - */ - [key: string]: CaseReducer>; -} - -/** - * This context type contains the actions, selector, and dispatch that embeddables need to interact with their state. This - * should be passed down from the embeddable class, to its react components by wrapping the embeddable's render output in ReduxEmbeddableContext. - */ -export interface ReduxEmbeddableContext< - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, - ReducerType extends EmbeddableReducers = EmbeddableReducers, - EmbeddableType extends Embeddable< - ReduxEmbeddableStateType['explicitInput'], - ReduxEmbeddableStateType['output'] - > = Embeddable -> { - actions: { - [Property in keyof ReducerType]: ActionCreatorWithPayload< - Parameters[1]['payload'] - >; - } & { - // Generic reducers to interact with embeddable Input and Output. - updateEmbeddableReduxInput: ActionCreatorWithPayload< - Partial - >; - updateEmbeddableReduxOutput: ActionCreatorWithPayload< - Partial - >; - }; - embeddableInstance: EmbeddableType; - useEmbeddableSelector: TypedUseSelectorHook; - useEmbeddableDispatch: () => Dispatch; -} diff --git a/src/plugins/presentation_util/public/redux_embeddables/use_redux_embeddable_context.ts b/src/plugins/presentation_util/public/redux_embeddables/use_redux_embeddable_context.ts deleted file mode 100644 index 6463ea306d3f9..0000000000000 --- a/src/plugins/presentation_util/public/redux_embeddables/use_redux_embeddable_context.ts +++ /dev/null @@ -1,48 +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 { Embeddable } from '@kbn/embeddable-plugin/public'; -import { createContext, useContext } from 'react'; - -import type { ReduxEmbeddableState, ReduxEmbeddableContext, EmbeddableReducers } from './types'; - -/** - * When creating the context, a generic EmbeddableInput as placeholder is used. This will later be cast to - * the type passed in by the useReduxEmbeddableContext hook - **/ -export const EmbeddableReduxContext = - createContext | null>(null); - -/** - * A typed use context hook for embeddables that are not containers. it @returns an - * ReduxEmbeddableContextServices object typed to the generic inputTypes and ReducerTypes you pass in. - * Note that the reducer type is optional, but will be required to correctly infer the keys and payload - * types of your reducers. use `typeof MyReducers` here to retain them. - */ -export const useReduxEmbeddableContext = < - ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, - ReducerType extends EmbeddableReducers = EmbeddableReducers, - EmbeddableType extends Embeddable = Embeddable< - ReduxEmbeddableStateType['explicitInput'], - ReduxEmbeddableStateType['output'] - > ->(): ReduxEmbeddableContext => { - const context = useContext< - ReduxEmbeddableContext - >( - EmbeddableReduxContext as unknown as React.Context< - ReduxEmbeddableContext - > - ); - if (context == null) { - throw new Error( - 'useReduxEmbeddableContext must be used inside the ReduxEmbeddableWrapper from build_redux_embeddable_context.' - ); - } - - return context!; -}; diff --git a/src/plugins/presentation_util/public/redux_tools/create_redux_tools.ts b/src/plugins/presentation_util/public/redux_tools/create_redux_tools.ts new file mode 100644 index 0000000000000..2cbd4be93d6fb --- /dev/null +++ b/src/plugins/presentation_util/public/redux_tools/create_redux_tools.ts @@ -0,0 +1,83 @@ +/* + * 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 { + AnyAction, + Middleware, + createSlice, + configureStore, + SliceCaseReducers, + CaseReducerActions, +} from '@reduxjs/toolkit'; +import { v4 as uuidv4 } from 'uuid'; +import { createContext } from 'react'; +import { createSelectorHook } from 'react-redux'; + +import { ReduxTools, ReduxToolsReducers, ReduxToolsSetters } from './types'; + +export const createReduxTools = < + ReduxStateType extends unknown, + ReducerType extends ReduxToolsReducers = ReduxToolsReducers +>({ + reducers, + additionalMiddleware, + initialState, +}: { + additionalMiddleware?: Array>; + initialState: ReduxStateType; + reducers: ReducerType; +}): ReduxTools => { + const id = uuidv4(); + + /** + * Create slice out of reducers and embeddable initial state. + */ + const slice = createSlice>({ + initialState, + name: id, + reducers, + }); + + const store = configureStore({ + reducer: slice.reducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(...(additionalMiddleware ?? [])), + }); + + /** + * Create an object of setter functions by looping through the reducers, and creating a method that dispatches the related + * action to the appropriate store. + */ + const dispatch: ReduxToolsSetters = Object.keys(reducers).reduce( + (acc, key: keyof ReducerType) => { + const sliceAction = + slice.actions[key as keyof CaseReducerActions>]; + acc[key] = (payload) => store.dispatch(sliceAction(payload)); + return acc; + }, + {} as ReduxToolsSetters + ); + + /** + * Create a selector which can be used by react components to get the latest state values and to re-render when state changes. + */ + const select = createSelectorHook( + createContext({ + store, + storeState: store.getState(), + }) + ); + + return { + store, + select, + dispatch, + getState: store.getState, + onStateChange: store.subscribe, + }; +}; diff --git a/src/plugins/presentation_util/public/redux_tools/index.ts b/src/plugins/presentation_util/public/redux_tools/index.ts new file mode 100644 index 0000000000000..2b14c0da6b1a2 --- /dev/null +++ b/src/plugins/presentation_util/public/redux_tools/index.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 { ReduxToolsPackage } from './types'; + +export type { ReduxEmbeddableState, ReduxEmbeddableTools } from './redux_embeddables/types'; +export { cleanFiltersForSerialize } from './redux_embeddables/clean_redux_embeddable_state'; +export type { ReduxToolsPackage, ReduxTools } from './types'; + +export const lazyLoadReduxToolsPackage = async (): Promise => { + const { createReduxTools } = await import('./create_redux_tools'); + const { createReduxEmbeddableTools } = await import( + './redux_embeddables/create_redux_embeddable_tools' + ); + return { + createReduxTools, + createReduxEmbeddableTools, + }; +}; diff --git a/src/plugins/presentation_util/public/redux_embeddables/clean_redux_embeddable_state.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.ts similarity index 94% rename from src/plugins/presentation_util/public/redux_embeddables/clean_redux_embeddable_state.ts rename to src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.ts index ebb282285f173..654c3b0bf8b33 100644 --- a/src/plugins/presentation_util/public/redux_embeddables/clean_redux_embeddable_state.ts +++ b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/clean_redux_embeddable_state.ts @@ -21,7 +21,8 @@ export const stateContainsFilters = ( return false; }; -export const cleanFiltersForSerialize = (filters: Filter[]): Filter[] => { +export const cleanFiltersForSerialize = (filters?: Filter[]): Filter[] => { + if (!filters) return []; return filters.map((filter) => { if (filter.meta.value) delete filter.meta.value; return filter; diff --git a/src/plugins/presentation_util/public/redux_embeddables/create_redux_embeddable_tools.tsx b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/create_redux_embeddable_tools.ts similarity index 50% rename from src/plugins/presentation_util/public/redux_embeddables/create_redux_embeddable_tools.tsx rename to src/plugins/presentation_util/public/redux_tools/redux_embeddables/create_redux_embeddable_tools.ts index ed7938a0760b4..bdcf4ee2e3185 100644 --- a/src/plugins/presentation_util/public/redux_embeddables/create_redux_embeddable_tools.tsx +++ b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/create_redux_embeddable_tools.ts @@ -6,34 +6,19 @@ * Side Public License, v 1. */ -import { - AnyAction, - configureStore, - createSlice, - Draft, - Middleware, - PayloadAction, - SliceCaseReducers, -} from '@reduxjs/toolkit'; -import React, { ReactNode, PropsWithChildren } from 'react'; -import { Provider, TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; +import { Draft, AnyAction, Middleware, PayloadAction } from '@reduxjs/toolkit'; import { Embeddable } from '@kbn/embeddable-plugin/public'; -import { - EmbeddableReducers, - ReduxEmbeddableTools, - ReduxEmbeddableContext, - ReduxEmbeddableState, - ReduxEmbeddableSyncSettings, -} from './types'; +import { ReduxToolsReducers } from '../types'; +import { createReduxTools } from '../create_redux_tools'; import { syncReduxEmbeddable } from './sync_redux_embeddable'; -import { EmbeddableReduxContext } from './use_redux_embeddable_context'; import { cleanStateForRedux } from './clean_redux_embeddable_state'; +import { ReduxEmbeddableTools, ReduxEmbeddableState, ReduxEmbeddableSyncSettings } from './types'; export const createReduxEmbeddableTools = < ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, - ReducerType extends EmbeddableReducers = EmbeddableReducers + ReducerType extends ReduxToolsReducers = ReduxToolsReducers >({ reducers, embeddable, @@ -50,7 +35,9 @@ export const createReduxEmbeddableTools = < syncSettings?: ReduxEmbeddableSyncSettings; reducers: ReducerType; }): ReduxEmbeddableTools => { - // Additional generic reducers to aid in embeddable syncing + /** + * Build additional generic reducers to aid in embeddable syncing. + */ const genericReducers = { replaceEmbeddableReduxInput: ( state: Draft, @@ -65,8 +52,11 @@ export const createReduxEmbeddableTools = < state.output = action.payload; }, }; + const allReducers = { ...reducers, ...genericReducers }; - // create initial state from Embeddable + /** + * Create initial state from Embeddable. + */ let initialState: ReduxEmbeddableStateType = { output: embeddable.getOutput(), componentState: initialComponentState ?? {}, @@ -75,51 +65,33 @@ export const createReduxEmbeddableTools = < initialState = cleanStateForRedux(initialState); - // create slice out of reducers and embeddable initial state. - const slice = createSlice>({ + const { dispatch, store, select, getState, onStateChange } = createReduxTools< + ReduxEmbeddableStateType, + typeof allReducers + >({ + reducers: allReducers, + additionalMiddleware, initialState, - name: `${embeddable.type}_${embeddable.id}`, - reducers: { ...reducers, ...genericReducers }, }); - const store = configureStore({ - reducer: slice.reducer, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(...(additionalMiddleware ?? [])), - }); - - // create the context which will wrap this embeddable's react components to allow access to update and read from the store. - const context = { - embeddableInstance: embeddable, - - actions: slice.actions as ReduxEmbeddableContext< - ReduxEmbeddableStateType, - typeof reducers - >['actions'], - useEmbeddableDispatch: () => useDispatch(), - useEmbeddableSelector: useSelector as TypedUseSelectorHook, - }; - - const Wrapper: React.FC> = ({ children }: { children?: ReactNode }) => ( - - {children} - - ); - + /** + * Sync redux state with embeddable input and output observables. Eventually we can replace the input and output observables + * with redux and remove this sync. + */ const stopReduxEmbeddableSync = syncReduxEmbeddable({ - actions: context.actions, + replaceEmbeddableReduxInput: dispatch.replaceEmbeddableReduxInput, + replaceEmbeddableReduxOutput: dispatch.replaceEmbeddableReduxOutput, settings: syncSettings, embeddable, store, }); - // return redux tools for the embeddable class to use. return { - Wrapper, - actions: context.actions, - dispatch: store.dispatch, - getState: store.getState, - onStateChange: store.subscribe, + store, + select, + dispatch, + getState, + onStateChange, cleanup: () => stopReduxEmbeddableSync?.(), }; }; diff --git a/src/plugins/presentation_util/public/redux_embeddables/sync_redux_embeddable.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/sync_redux_embeddable.ts similarity index 89% rename from src/plugins/presentation_util/public/redux_embeddables/sync_redux_embeddable.ts rename to src/plugins/presentation_util/public/redux_tools/redux_embeddables/sync_redux_embeddable.ts index 37aadd1e9fe47..ac8f7765d7270 100644 --- a/src/plugins/presentation_util/public/redux_embeddables/sync_redux_embeddable.ts +++ b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/sync_redux_embeddable.ts @@ -7,11 +7,12 @@ */ import deepEqual from 'fast-deep-equal'; +import { EnhancedStore } from '@reduxjs/toolkit'; import { IEmbeddable } from '@kbn/embeddable-plugin/public'; -import { EnhancedStore } from '@reduxjs/toolkit'; -import { ReduxEmbeddableContext, ReduxEmbeddableState, ReduxEmbeddableSyncSettings } from './types'; + import { cleanInputForRedux } from './clean_redux_embeddable_state'; +import { ReduxEmbeddableState, ReduxEmbeddableSyncSettings } from './types'; type Writeable = { -readonly [P in keyof T]: T[P] }; @@ -19,17 +20,19 @@ export const syncReduxEmbeddable = < ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState >({ store, - actions, settings, embeddable, + replaceEmbeddableReduxInput, + replaceEmbeddableReduxOutput, }: { settings?: ReduxEmbeddableSyncSettings; store: EnhancedStore; + replaceEmbeddableReduxInput: (input: ReduxEmbeddableStateType['explicitInput']) => void; + replaceEmbeddableReduxOutput: (output: ReduxEmbeddableStateType['output']) => void; embeddable: IEmbeddable< ReduxEmbeddableStateType['explicitInput'], ReduxEmbeddableStateType['output'] >; - actions: ReduxEmbeddableContext['actions']; }) => { if (settings?.disableSync) { return; @@ -80,9 +83,7 @@ export const syncReduxEmbeddable = < >; if (!inputEqual(reduxExplicitInput, embeddableExplictInput)) { - store.dispatch( - actions.replaceEmbeddableReduxInput(cleanInputForRedux(embeddableExplictInput)) - ); + replaceEmbeddableReduxInput(cleanInputForRedux(embeddableExplictInput)); } embeddableToReduxInProgress = false; }); @@ -93,7 +94,7 @@ export const syncReduxEmbeddable = < embeddableToReduxInProgress = true; const reduxState = store.getState(); if (!outputEqual(reduxState.output, embeddableOutput)) { - store.dispatch(actions.replaceEmbeddableReduxOutput(embeddableOutput)); + replaceEmbeddableReduxOutput(embeddableOutput); } embeddableToReduxInProgress = false; }); diff --git a/src/plugins/presentation_util/public/redux_tools/redux_embeddables/types.ts b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/types.ts new file mode 100644 index 0000000000000..44d6d4933a7ae --- /dev/null +++ b/src/plugins/presentation_util/public/redux_tools/redux_embeddables/types.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 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 { EnhancedStore } from '@reduxjs/toolkit'; +import { EmbeddableInput, EmbeddableOutput } from '@kbn/embeddable-plugin/public'; +import { ReduxToolsReducers, ReduxToolsSelect, ReduxToolsSetters } from '../types'; + +export interface ReduxEmbeddableSyncSettings< + ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState +> { + disableSync: boolean; + isInputEqual?: ( + a: Partial, + b: Partial + ) => boolean; + isOutputEqual?: ( + a: Partial, + b: Partial + ) => boolean; +} + +/** + * The return type from createReduxEmbeddableTools. Contains tools to get state, select state for react components, + * set state, and react to state changes. + */ +export interface ReduxEmbeddableTools< + ReduxEmbeddableStateType extends ReduxEmbeddableState = ReduxEmbeddableState, + ReducerType extends ReduxToolsReducers = ReduxToolsReducers +> { + cleanup: () => void; + store: EnhancedStore; + select: ReduxToolsSelect; + getState: EnhancedStore['getState']; + dispatch: ReduxToolsSetters; + onStateChange: EnhancedStore['subscribe']; +} + +/** + * The Embeddable Redux store should contain Input, Output and State. Input is serialized and used to create the embeddable, + * Output is used as a communication layer for state that the Embeddable creates, and State is used to store ephemeral state which needs + * to be communicated between an embeddable and its inner React components. + */ +export interface ReduxEmbeddableState< + InputType extends EmbeddableInput = EmbeddableInput, + OutputType extends EmbeddableOutput = EmbeddableOutput, + StateType extends unknown = unknown +> { + explicitInput: InputType; + output: OutputType; + componentState: StateType; +} diff --git a/src/plugins/presentation_util/public/redux_tools/types.ts b/src/plugins/presentation_util/public/redux_tools/types.ts new file mode 100644 index 0000000000000..1cf621f535846 --- /dev/null +++ b/src/plugins/presentation_util/public/redux_tools/types.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 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 { CaseReducer, PayloadAction, EnhancedStore } from '@reduxjs/toolkit'; + +/** + * The Redux Tools Selector is a react redux selector function that can be used to grab values from the state, and to make a component + * re-render when those values change. + */ +export type ReduxToolsSelect = ( + selector: (state: ReduxStateType) => Selected, + equalityFn?: ((previous: Selected, next: Selected) => boolean) | undefined +) => Selected; + +/** + * The Redux Embeddable Setters are a collection of functions which dispatch actions to the correct store. + */ +export type ReduxToolsSetters< + ReduxStateType extends unknown, + ReducerType extends ReduxToolsReducers = ReduxToolsReducers +> = { + [ReducerKey in keyof ReducerType]: ( + payload: Parameters[1]['payload'] + ) => void; +}; + +/** + * The return type from createReduxTools. Contains tools to get state, select state for react components, + * set state, and react to state changes. + */ +export interface ReduxTools< + ReduxStateType extends unknown, + ReducerType extends ReduxToolsReducers = ReduxToolsReducers +> { + store: EnhancedStore; + select: ReduxToolsSelect; + getState: EnhancedStore['getState']; + onStateChange: EnhancedStore['subscribe']; + dispatch: ReduxToolsSetters; +} + +/** + * The Redux Tools Reducers are the shape of the Raw reducers which will be passed into createSlice. These will be used to populate the actions + * object which the tools will return. + */ +export interface ReduxToolsReducers { + /** + * PayloadAction of type any is strategic here because we want to allow payloads of any shape in generic reducers. + * This type will be overridden to remove any and be type safe when returned by setupReduxEmbeddable. + */ + [key: string]: CaseReducer>; +} + +/** + * The package type is lazily exported from presentation_util and should contain all methods needed to use the redux embeddable tools. + */ +export interface ReduxToolsPackage { + createReduxTools: typeof import('./create_redux_tools')['createReduxTools']; + createReduxEmbeddableTools: typeof import('./redux_embeddables/create_redux_embeddable_tools')['createReduxEmbeddableTools']; +} diff --git a/src/plugins/saved_search/server/saved_objects/search.ts b/src/plugins/saved_search/server/saved_objects/search.ts index 6995442737b36..b9d41b48a60ea 100644 --- a/src/plugins/saved_search/server/saved_objects/search.ts +++ b/src/plugins/saved_search/server/saved_objects/search.ts @@ -7,6 +7,7 @@ */ import { schema } from '@kbn/config-schema'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; import { VIEW_MODE } from '../../common'; @@ -17,6 +18,7 @@ export function getSavedSearchObjectType( ): SavedObjectsType { return { name: 'search', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/src/plugins/saved_search/tsconfig.json b/src/plugins/saved_search/tsconfig.json index cf6225bf1f223..7ecf44063dac7 100644 --- a/src/plugins/saved_search/tsconfig.json +++ b/src/plugins/saved_search/tsconfig.json @@ -17,6 +17,7 @@ "@kbn/saved-objects-tagging-oss-plugin", "@kbn/i18n", "@kbn/config-schema", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 08ee05196ff39..2d90314d1e464 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -31,11 +31,14 @@ export { visualizeGeoFieldTrigger, ROW_CLICK_TRIGGER, rowClickTrigger, + CATEGORIZE_FIELD_TRIGGER, + categorizeFieldTrigger, } from './triggers'; -export type { VisualizeFieldContext } from './types'; +export type { VisualizeFieldContext, CategorizeFieldContext } from './types'; export { ACTION_VISUALIZE_FIELD, ACTION_VISUALIZE_GEO_FIELD, ACTION_VISUALIZE_LENS_FIELD, + ACTION_CATEGORIZE_FIELD, } from './types'; export type { ActionExecutionContext, ActionExecutionMeta, ActionMenuItemProps } from './actions'; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 14c19f0b32184..7deb21e125113 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -9,7 +9,12 @@ import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/public'; import { PublicMethodsOf } from '@kbn/utility-types'; import { UiActionsService } from './service'; -import { rowClickTrigger, visualizeFieldTrigger, visualizeGeoFieldTrigger } from './triggers'; +import { + categorizeFieldTrigger, + rowClickTrigger, + visualizeFieldTrigger, + visualizeGeoFieldTrigger, +} from './triggers'; import { setTheme } from './services'; export type UiActionsSetup = Pick< @@ -34,6 +39,7 @@ export class UiActionsPlugin implements Plugin { this.service.registerTrigger(rowClickTrigger); this.service.registerTrigger(visualizeFieldTrigger); this.service.registerTrigger(visualizeGeoFieldTrigger); + this.service.registerTrigger(categorizeFieldTrigger); return this.service; } diff --git a/src/plugins/ui_actions/public/triggers/categorize_field_trigger.ts b/src/plugins/ui_actions/public/triggers/categorize_field_trigger.ts new file mode 100644 index 0000000000000..9171e463737ee --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/categorize_field_trigger.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 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 { Trigger } from '.'; + +export const CATEGORIZE_FIELD_TRIGGER = 'CATEGORIZE_FIELD_TRIGGER'; +export const categorizeFieldTrigger: Trigger = { + id: CATEGORIZE_FIELD_TRIGGER, + title: 'Run pattern analysis', + description: 'Triggered when user wants to run pattern analysis on a field.', +}; diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index 47b9d84284e91..f6851649f4736 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -13,3 +13,4 @@ export * from './row_click_trigger'; export * from './visualize_field_trigger'; export * from './visualize_geo_field_trigger'; export * from './default_trigger'; +export * from './categorize_field_trigger'; diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index fb2d9869e21c7..98ad307ff1f97 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -7,7 +7,7 @@ */ import type { AggregateQuery } from '@kbn/es-query'; -import type { DataViewSpec } from '@kbn/data-views-plugin/public'; +import type { DataViewField, DataViewSpec, DataView } from '@kbn/data-views-plugin/public'; import { ActionInternal } from './actions/action_internal'; import { TriggerInternal } from './triggers/trigger_internal'; @@ -23,6 +23,13 @@ export interface VisualizeFieldContext { query?: AggregateQuery; } +export interface CategorizeFieldContext { + field: DataViewField; + dataView: DataView; + originatingApp?: string; +} + export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; export const ACTION_VISUALIZE_LENS_FIELD = 'ACTION_VISUALIZE_LENS_FIELD'; +export const ACTION_CATEGORIZE_FIELD = 'ACTION_CATEGORIZE_FIELD'; diff --git a/src/plugins/unified_field_list/public/components/field_categorize_button/categorize_trigger_utils.test.ts b/src/plugins/unified_field_list/public/components/field_categorize_button/categorize_trigger_utils.test.ts new file mode 100644 index 0000000000000..79dacc2ed1fd0 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_categorize_button/categorize_trigger_utils.test.ts @@ -0,0 +1,74 @@ +/* + * 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 type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; +import type { Action, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { canCategorize } from './categorize_trigger_utils'; + +const textField = { + name: 'fieldName', + type: 'string', + esTypes: ['text'], + count: 1, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + visualizable: true, +} as DataViewField; + +const numberField = { + name: 'fieldName', + type: 'number', + esTypes: ['double'], + count: 1, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + visualizable: true, +} as DataViewField; + +const mockGetActions = jest.fn>>, [string, { fieldName: string }]>( + () => Promise.resolve([]) +); + +const uiActions = { + getTriggerCompatibleActions: mockGetActions, +} as unknown as UiActionsStart; + +const action: Action = { + id: 'action', + type: 'CATEGORIZE_FIELD', + getIconType: () => undefined, + getDisplayName: () => 'Action', + isCompatible: () => Promise.resolve(true), + execute: () => Promise.resolve(), +}; + +const dataViewMock = { id: '1', toSpec: () => ({}) } as DataView; + +describe('categorize_trigger_utils', () => { + afterEach(() => { + mockGetActions.mockReset(); + }); + + describe('getCategorizeInformation', () => { + it('should return true for a categorizable field with an action', async () => { + mockGetActions.mockResolvedValue([action]); + const resp = await canCategorize(uiActions, textField, dataViewMock); + expect(resp).toBe(true); + }); + + it('should return false for a non-categorizable field with an action', async () => { + mockGetActions.mockResolvedValue([action]); + const resp = await canCategorize(uiActions, numberField, dataViewMock); + expect(resp).toBe(false); + }); + }); +}); diff --git a/src/plugins/unified_field_list/public/components/field_categorize_button/categorize_trigger_utils.ts b/src/plugins/unified_field_list/public/components/field_categorize_button/categorize_trigger_utils.ts new file mode 100644 index 0000000000000..560671c10d1c1 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_categorize_button/categorize_trigger_utils.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 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 { + type UiActionsStart, + CATEGORIZE_FIELD_TRIGGER, + CategorizeFieldContext, +} from '@kbn/ui-actions-plugin/public'; +import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; + +async function getCompatibleActions( + uiActions: UiActionsStart, + field: DataViewField, + dataView: DataView, + trigger: typeof CATEGORIZE_FIELD_TRIGGER +) { + const compatibleActions = await uiActions.getTriggerCompatibleActions(trigger, { + dataView, + field, + }); + return compatibleActions; +} + +export function triggerCategorizeActions( + uiActions: UiActionsStart, + field: DataViewField, + originatingApp: string, + dataView?: DataView +) { + if (!dataView) return; + const triggerOptions: CategorizeFieldContext = { + dataView, + field, + originatingApp, + }; + uiActions.getTrigger(CATEGORIZE_FIELD_TRIGGER).exec(triggerOptions); +} + +export async function canCategorize( + uiActions: UiActionsStart, + field: DataViewField, + dataView: DataView | undefined +): Promise { + if (field.name === '_id' || !dataView?.id || !field.esTypes?.includes('text')) { + return false; + } + + const actions = await getCompatibleActions(uiActions, field, dataView, CATEGORIZE_FIELD_TRIGGER); + + return actions.length > 0; +} diff --git a/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button.test.tsx b/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button.test.tsx new file mode 100644 index 0000000000000..4c65bf511809f --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button.test.tsx @@ -0,0 +1,90 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { ReactWrapper } from 'enzyme'; +import { EuiButton } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { ActionInternal } from '@kbn/ui-actions-plugin/public'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { getFieldCategorizeButton } from './field_categorize_button'; +import { ACTION_CATEGORIZE_FIELD, CategorizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import { CATEGORIZE_FIELD_TRIGGER, TriggerContract } from '@kbn/ui-actions-plugin/public/triggers'; + +const ORIGINATING_APP = 'test'; +const mockExecuteAction = jest.fn(); +const uiActions = uiActionsPluginMock.createStartContract(); +const categorizeAction = new ActionInternal({ + type: ACTION_CATEGORIZE_FIELD, + id: ACTION_CATEGORIZE_FIELD, + getDisplayName: () => 'test', + isCompatible: async () => true, + execute: async (context: CategorizeFieldContext) => { + mockExecuteAction(context); + }, + getHref: async () => '/app/test', +}); + +jest + .spyOn(uiActions, 'getTriggerCompatibleActions') + .mockResolvedValue([categorizeAction as ActionInternal]); +jest.spyOn(uiActions, 'getTrigger').mockReturnValue({ + id: ACTION_CATEGORIZE_FIELD, + exec: mockExecuteAction, +} as unknown as TriggerContract); + +describe('UnifiedFieldList ', () => { + it('should render correctly', async () => { + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + let wrapper: ReactWrapper; + + const button = await getFieldCategorizeButton({ + field, + dataView, + originatingApp: ORIGINATING_APP, + uiActions, + }); + await act(async () => { + wrapper = await mountWithIntl(button!); + }); + + await wrapper!.update(); + + expect(uiActions.getTriggerCompatibleActions).toHaveBeenCalledWith(CATEGORIZE_FIELD_TRIGGER, { + dataView, + field, + }); + + expect(wrapper!.text()).toBe('Run pattern analysis'); + wrapper!.find(`button[data-test-subj="fieldCategorize-${fieldName}"]`).simulate('click'); + + expect(mockExecuteAction).toHaveBeenCalledWith({ + dataView, + field, + originatingApp: ORIGINATING_APP, + }); + + expect(wrapper!.find(EuiButton).exists()).toBeTruthy(); + }); + + it('should not render for non text field', async () => { + const fieldName = 'phpmemory'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + + const button = await getFieldCategorizeButton({ + field, + dataView, + originatingApp: ORIGINATING_APP, + uiActions, + }); + + expect(button).toBe(null); + }); +}); diff --git a/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button.tsx b/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button.tsx new file mode 100644 index 0000000000000..5914c330f6661 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button.tsx @@ -0,0 +1,58 @@ +/* + * 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 { EuiButtonProps } from '@elastic/eui'; +import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; +import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { FieldCategorizeButtonInner } from './field_categorize_button_inner'; +import { triggerCategorizeActions, canCategorize } from './categorize_trigger_utils'; + +export interface FieldCategorizeButtonProps { + field: DataViewField; + dataView: DataView; + originatingApp: string; // plugin id + uiActions: UiActionsStart; + contextualFields?: string[]; // names of fields which were also selected (like columns in Discover grid) + trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + buttonProps?: Partial; + closePopover?: () => void; +} + +export const FieldCategorizeButton: React.FC = React.memo( + ({ field, dataView, trackUiMetric, originatingApp, uiActions, buttonProps, closePopover }) => { + const handleVisualizeLinkClick = async ( + event: React.MouseEvent + ) => { + // regular link click. let the uiActions code handle the navigation and show popup if needed + event.preventDefault(); + const triggerVisualization = (updatedDataView: DataView) => { + trackUiMetric?.(METRIC_TYPE.CLICK, 'categorize_link_click'); + triggerCategorizeActions(uiActions, field, originatingApp, updatedDataView); + }; + triggerVisualization(dataView); + if (closePopover) { + closePopover(); + } + }; + + return ( + + ); + } +); + +export async function getFieldCategorizeButton(props: FieldCategorizeButtonProps) { + const showButton = await canCategorize(props.uiActions, props.field, props.dataView); + return showButton ? : null; +} diff --git a/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button_inner.tsx b/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button_inner.tsx new file mode 100644 index 0000000000000..8571822dcd8e9 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_categorize_button/field_categorize_button_inner.tsx @@ -0,0 +1,42 @@ +/* + * 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 { EuiButton, EuiButtonProps } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface FieldVisualizeButtonInnerProps { + fieldName: string; + handleVisualizeLinkClick: (event: React.MouseEvent) => void; + buttonProps?: Partial; +} + +export const FieldCategorizeButtonInner: React.FC = ({ + fieldName, + handleVisualizeLinkClick, + buttonProps, +}) => { + return ( + <> + + + + + ); +}; diff --git a/src/plugins/unified_field_list/public/components/field_categorize_button/index.tsx b/src/plugins/unified_field_list/public/components/field_categorize_button/index.tsx new file mode 100755 index 0000000000000..8a07a8290ca84 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_categorize_button/index.tsx @@ -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 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 FieldCategorizeButtonProps, + FieldCategorizeButton, + getFieldCategorizeButton, +} from './field_categorize_button'; + +export { triggerCategorizeActions, canCategorize } from './categorize_trigger_utils'; diff --git a/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap b/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap new file mode 100644 index 0000000000000..899d6a7579123 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_item_button/__snapshots__/field_item_button.test.tsx.snap @@ -0,0 +1,307 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UnifiedFieldList renders properly 1`] = ` + + } + fieldName={ + + bytes + + } + isActive={false} + key="field-item-button-bytes" + onClick={[MockFunction]} + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly for Records (Lens field) 1`] = ` + + } + fieldName={ + + Records + + } + isActive={true} + key="field-item-button-___records___" + onClick={[MockFunction]} + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly for text-based column field 1`] = ` + + } + fieldName={ + + agent + + } + isActive={false} + key="field-item-button-agent" + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly for wildcard search 1`] = ` + + } + fieldName={ + + script date + + } + isActive={false} + key="field-item-button-script date" + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly when a conflict field 1`] = ` + + } + fieldInfoIcon={} + fieldName={ + + custom_user_field + + } + isActive={true} + key="field-item-button-custom_user_field" + onClick={[MockFunction]} + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly when empty 1`] = ` + + } + fieldName={ + + script date + + } + isActive={false} + key="field-item-button-script date" + onClick={[MockFunction]} + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly with a drag handle 1`] = ` + + dragHandle + + } + fieldIcon={ + + } + fieldName={ + + bytes + + } + isActive={false} + key="field-item-button-bytes" + size="xs" +/> +`; + +exports[`UnifiedFieldList renders properly with an action when deselected 1`] = ` + + + + } + fieldIcon={ + + } + fieldName={ + + bytes + + } + isActive={false} + key="field-item-button-bytes" + size="s" +/> +`; + +exports[`UnifiedFieldList renders properly with an action when selected 1`] = ` + + + + } + fieldIcon={ + + } + fieldName={ + + bytes + + } + isActive={false} + key="field-item-button-bytes" + onClick={[MockFunction click]} + size="s" +/> +`; diff --git a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.scss b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.scss index 591ed14133907..70073c37ea31c 100644 --- a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.scss +++ b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.scss @@ -24,13 +24,25 @@ } } -.unifiedFieldItemButton { - width: 100%; +/** + * 1. Only visually hide the action, so that it's still accessible to screen readers. + * 2. When tabbed to, this element needs to be visible for keyboard accessibility. + */ +.unifiedFieldListItemButton__action { + opacity: 0; /* 1 */ - &:hover:not([class*='isActive']) { - cursor: grab; + &--always { + opacity: 1; } + &:focus { + opacity: 1; /* 2 */ + } +} + +.unifiedFieldListItemButton { + width: 100%; + &.kbnFieldButton { &:focus-within, &-isActive { @@ -39,15 +51,22 @@ } .kbnFieldButton__button:focus { - @include passDownFocusRing('.kbnFieldButton__name > span'); + @include passDownFocusRing('.kbnFieldButton__nameInner'); + } + + & button .kbnFieldButton__nameInner:hover { + text-decoration: underline; + } - .kbnFieldButton__name > span { - text-decoration: underline; + &:hover, + &[class*='-isActive'] { + .unifiedFieldListItemButton__action { + opacity: 1; } } } -.unifiedFieldItemButton--missing { +.unifiedFieldListItemButton--missing { background: lightOrDarkTheme(transparentize($euiColorMediumShade, .9), $euiColorEmptyShade); color: $euiColorDarkShade; } diff --git a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.test.tsx b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.test.tsx new file mode 100644 index 0000000000000..140c727439efa --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.test.tsx @@ -0,0 +1,164 @@ +/* + * 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 { shallow } from 'enzyme'; +import { DataViewField } from '@kbn/data-views-plugin/common'; +import { DatatableColumn } from '@kbn/expressions-plugin/common'; +import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { FieldItemButton } from './field_item_button'; + +const bytesField = dataView.getFieldByName('bytes')!; +const scriptedField = dataView.getFieldByName('script date')!; +const conflictField = dataView.getFieldByName('custom_user_field')!; + +describe('UnifiedFieldList ', () => { + test('renders properly', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly when empty', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly when a conflict field', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly for Records (Lens field)', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly with an action when selected', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly with an action when deselected', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly with a drag handle', () => { + const component = shallow( + dragHandle} + field={bytesField} + fieldSearchHighlight={undefined} + isEmpty={false} + isSelected={false} + isActive={false} + onClick={undefined} + /> + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly for text-based column field', () => { + const component = shallow( + + field={{ id: 'test', name: 'agent', meta: { type: 'string' } }} + fieldSearchHighlight="ag" + getCustomFieldType={(f) => f.meta.type} + isEmpty={false} + isSelected={false} + isActive={false} + onClick={undefined} + /> + ); + expect(component).toMatchSnapshot(); + }); + + test('renders properly for wildcard search', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx index 4e9019f5692cd..ec0078431a8bc 100644 --- a/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx +++ b/src/plugins/unified_field_list/public/components/field_item_button/field_item_button.tsx @@ -10,10 +10,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import classnames from 'classnames'; import { FieldButton, type FieldButtonProps } from '@kbn/react-field'; -import { EuiHighlight } from '@elastic/eui'; +import { EuiButtonIcon, EuiButtonIconProps, EuiHighlight, EuiIcon, EuiToolTip } from '@elastic/eui'; import type { DataViewField } from '@kbn/data-views-plugin/common'; import { type FieldListItem, type GetCustomFieldType } from '../../types'; import { FieldIcon, getFieldIconProps } from '../field_icon'; +import { fieldNameWildcardMatcher } from '../../utils/field_name_wildcard_matcher'; import './field_item_button.scss'; /** @@ -22,72 +23,223 @@ import './field_item_button.scss'; export interface FieldItemButtonProps { field: T; fieldSearchHighlight?: string; - isActive?: FieldButtonProps['isActive']; - isEmpty?: boolean; // whether the field has data or not + isSelected: boolean; // whether a field is under Selected section + isActive: FieldButtonProps['isActive']; // whether a popover is open + isEmpty: boolean; // whether the field has data or not infoIcon?: FieldButtonProps['fieldInfoIcon']; className?: FieldButtonProps['className']; + flush?: FieldButtonProps['flush']; + dragHandle?: FieldButtonProps['dragHandle']; getCustomFieldType?: GetCustomFieldType; + dataTestSubj?: string; + size?: FieldButtonProps['size']; onClick: FieldButtonProps['onClick']; + shouldAlwaysShowAction?: boolean; // should the field action be visible on hover or always + buttonAddFieldToWorkspaceProps?: Partial; + buttonRemoveFieldFromWorkspaceProps?: Partial; + onAddFieldToWorkspace?: (field: T) => unknown; + onRemoveFieldFromWorkspace?: (field: T) => unknown; } /** - * Inner part of field list item + * Field list item component * @param field * @param fieldSearchHighlight + * @param isSelected * @param isActive * @param isEmpty * @param infoIcon * @param className * @param getCustomFieldType + * @param dataTestSubj + * @param size * @param onClick + * @param shouldAlwaysShowAction + * @param buttonAddFieldToWorkspaceProps + * @param buttonRemoveFieldFromWorkspaceProps + * @param onAddFieldToWorkspace + * @param onRemoveFieldFromWorkspace * @param otherProps * @constructor */ export function FieldItemButton({ field, fieldSearchHighlight, + isSelected, isActive, isEmpty, infoIcon, className, getCustomFieldType, + dataTestSubj, + size, onClick, + shouldAlwaysShowAction, + buttonAddFieldToWorkspaceProps, + buttonRemoveFieldFromWorkspaceProps, + onAddFieldToWorkspace, + onRemoveFieldFromWorkspace, ...otherProps }: FieldItemButtonProps) { const displayName = field.displayName || field.name; + const title = + displayName !== field.name && field.name !== '___records___' + ? i18n.translate('unifiedFieldList.fieldItemButton.fieldTitle', { + defaultMessage: '{fieldDisplayName} ({fieldName})', + values: { + fieldName: field.name, + fieldDisplayName: displayName, + }, + }) + : displayName; + const iconProps = getCustomFieldType ? { type: getCustomFieldType(field) } : getFieldIconProps(field); const type = iconProps.type; const classes = classnames( - 'unifiedFieldItemButton', + 'unifiedFieldListItemButton', { - [`unifiedFieldItemButton--${type}`]: type, - [`unifiedFieldItemButton--exists`]: !isEmpty, - [`unifiedFieldItemButton--missing`]: isEmpty, + [`unifiedFieldListItemButton--${type}`]: type, + [`unifiedFieldListItemButton--exists`]: !isEmpty, + [`unifiedFieldListItemButton--missing`]: isEmpty, }, className ); + const addFieldToWorkspaceTooltip = + buttonAddFieldToWorkspaceProps?.['aria-label'] ?? + i18n.translate('unifiedFieldList.fieldItemButton.addFieldToWorkspaceLabel', { + defaultMessage: 'Add "{field}" field', + values: { + field: field.displayName, + }, + }); + + const removeFieldFromWorkspaceTooltip = + buttonRemoveFieldFromWorkspaceProps?.['aria-label'] ?? + i18n.translate('unifiedFieldList.fieldItemButton.removeFieldToWorkspaceLabel', { + defaultMessage: 'Remove "{field}" field', + values: { + field: field.displayName, + }, + }); + + const fieldActionClassName = classnames('unifiedFieldListItemButton__action', { + 'unifiedFieldListItemButton__action--always': shouldAlwaysShowAction, + }); + const fieldAction = isSelected + ? onRemoveFieldFromWorkspace && ( + + ) => { + event.preventDefault(); + event.stopPropagation(); + onRemoveFieldFromWorkspace(field); + }} + /> + + ) + : onAddFieldToWorkspace && ( + + ) => { + event.preventDefault(); + event.stopPropagation(); + onAddFieldToWorkspace(field); + }} + /> + + ); + + const conflictInfoIcon = field.type === 'conflict' ? : null; + return ( } - fieldName={{displayName}} - fieldInfoIcon={infoIcon} + fieldName={ + + {displayName} + + } + fieldAction={fieldAction} + fieldInfoIcon={conflictInfoIcon || infoIcon} onClick={onClick} {...otherProps} /> ); } + +function FieldConflictInfoIcon() { + return ( + + + + ); +} + +function getSearchHighlight(displayName: string, fieldSearchHighlight?: string): string { + const searchHighlight = fieldSearchHighlight || ''; + if ( + searchHighlight.includes('*') && + fieldNameWildcardMatcher({ name: displayName }, searchHighlight) + ) { + return displayName; + } + + return searchHighlight; +} diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover_footer.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover_footer.tsx new file mode 100644 index 0000000000000..fd639a6a529b4 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_popover/field_popover_footer.tsx @@ -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 React, { useEffect, useState } from 'react'; +import { EuiPopoverFooter, EuiSpacer } from '@elastic/eui'; +import { type FieldVisualizeButtonProps, getFieldVisualizeButton } from '../field_visualize_button'; +import { FieldCategorizeButtonProps, getFieldCategorizeButton } from '../field_categorize_button'; + +export type FieldPopoverFooterProps = FieldVisualizeButtonProps | FieldCategorizeButtonProps; + +export const FieldPopoverFooter: React.FC = (props) => { + const [visualizeButton, setVisualizeButton] = useState(null); + const [categorizeButton, setCategorizeButton] = useState(null); + + useEffect(() => { + getFieldVisualizeButton(props).then(setVisualizeButton); + getFieldCategorizeButton(props).then(setCategorizeButton); + }, [props]); + + return visualizeButton || categorizeButton ? ( + + {visualizeButton} + {visualizeButton && categorizeButton ? : null} + {categorizeButton} + + ) : null; +}; diff --git a/src/plugins/unified_field_list/public/components/field_popover/field_popover_visualize.tsx b/src/plugins/unified_field_list/public/components/field_popover/field_popover_visualize.tsx deleted file mode 100644 index 0f6b06c6239c7..0000000000000 --- a/src/plugins/unified_field_list/public/components/field_popover/field_popover_visualize.tsx +++ /dev/null @@ -1,21 +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 React from 'react'; -import { EuiPopoverFooter } from '@elastic/eui'; -import { FieldVisualizeButton, type FieldVisualizeButtonProps } from '../field_visualize_button'; - -export type FieldPopoverVisualizeProps = Omit; - -const wrapInContainer = (element: React.ReactElement): React.ReactElement => { - return {element}; -}; - -export const FieldPopoverVisualize: React.FC = (props) => { - return ; -}; diff --git a/src/plugins/unified_field_list/public/components/field_popover/index.tsx b/src/plugins/unified_field_list/public/components/field_popover/index.tsx index ecbcf36d83316..beadda61d2b8c 100755 --- a/src/plugins/unified_field_list/public/components/field_popover/index.tsx +++ b/src/plugins/unified_field_list/public/components/field_popover/index.tsx @@ -8,4 +8,4 @@ export { type FieldPopoverProps, FieldPopover } from './field_popover'; export { type FieldPopoverHeaderProps, FieldPopoverHeader } from './field_popover_header'; -export { type FieldPopoverVisualizeProps, FieldPopoverVisualize } from './field_popover_visualize'; +export { type FieldPopoverFooterProps, FieldPopoverFooter } from './field_popover_footer'; diff --git a/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx index c2e2a2fafde8b..3feccd5d22cc1 100644 --- a/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.test.tsx @@ -6,15 +6,14 @@ * Side Public License, v 1. */ -import React from 'react'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import { EuiButton, EuiPopoverFooter } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; import { ActionInternal } from '@kbn/ui-actions-plugin/public'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; -import { FieldVisualizeButton } from './field_visualize_button'; +import { getFieldVisualizeButton } from './field_visualize_button'; import { ACTION_VISUALIZE_LENS_FIELD, VISUALIZE_FIELD_TRIGGER, @@ -56,18 +55,16 @@ describe('UnifiedFieldList ', () => { jest.spyOn(fieldKeyword, 'visualizable', 'get').mockImplementationOnce(() => true); let wrapper: ReactWrapper; + const button = await getFieldVisualizeButton({ + field, + dataView, + multiFields: [fieldKeyword], + contextualFields, + originatingApp: ORIGINATING_APP, + uiActions, + }); await act(async () => { - wrapper = await mountWithIntl( - {element}} - /> - ); + wrapper = await mountWithIntl(button!); }); await wrapper!.update(); @@ -89,7 +86,6 @@ describe('UnifiedFieldList ', () => { }); expect(wrapper!.find(EuiButton).prop('href')).toBe('/app/test'); - expect(wrapper!.find(EuiPopoverFooter).find(EuiButton).exists()).toBeTruthy(); // wrapped in a container }); it('should render correctly for geo fields', async () => { @@ -98,15 +94,14 @@ describe('UnifiedFieldList ', () => { jest.spyOn(field, 'visualizable', 'get').mockImplementationOnce(() => true); let wrapper: ReactWrapper; + const button = await getFieldVisualizeButton({ + field, + dataView, + originatingApp: ORIGINATING_APP, + uiActions, + }); await act(async () => { - wrapper = await mountWithIntl( - - ); + wrapper = await mountWithIntl(button!); }); await wrapper!.update(); diff --git a/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx index aa9fe5266de09..7ce93d2f199dd 100644 --- a/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/field_visualize_button.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { EuiButtonProps } from '@elastic/eui'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; @@ -27,7 +27,7 @@ export interface FieldVisualizeButtonProps { contextualFields?: string[]; // names of fields which were also selected (like columns in Discover grid) trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; buttonProps?: Partial; - wrapInContainer?: (element: React.ReactElement) => React.ReactElement; + visualizeInfo?: VisualizeInformation; } export const FieldVisualizeButton: React.FC = React.memo( @@ -40,20 +40,11 @@ export const FieldVisualizeButton: React.FC = React.m originatingApp, uiActions, buttonProps, - wrapInContainer, + visualizeInfo, }) => { - const [visualizeInfo, setVisualizeInfo] = useState(); - - useEffect(() => { - getVisualizeInformation(uiActions, field, dataView, contextualFields, multiFields).then( - setVisualizeInfo - ); - }, [contextualFields, field, dataView, multiFields, uiActions]); - if (!visualizeInfo) { return null; } - const handleVisualizeLinkClick = async ( event: React.MouseEvent ) => { @@ -73,7 +64,7 @@ export const FieldVisualizeButton: React.FC = React.m triggerVisualization(dataView); }; - const element = ( + return ( = React.m buttonProps={buttonProps} /> ); - - return wrapInContainer?.(element) || element; } ); + +export async function getFieldVisualizeButton(props: FieldVisualizeButtonProps) { + const visualizeInfo = await getVisualizeInformation( + props.uiActions, + props.field, + props.dataView, + props.contextualFields, + props.multiFields + ); + return visualizeInfo ? : null; +} diff --git a/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx b/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx index 812ffbcb0d0e1..20fb43f461be9 100755 --- a/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx +++ b/src/plugins/unified_field_list/public/components/field_visualize_button/index.tsx @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -export { type FieldVisualizeButtonProps, FieldVisualizeButton } from './field_visualize_button'; +export { + type FieldVisualizeButtonProps, + FieldVisualizeButton, + getFieldVisualizeButton, +} from './field_visualize_button'; export { triggerVisualizeActions, diff --git a/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx b/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx index 77327fcbee402..1295d14f374b2 100644 --- a/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx +++ b/src/plugins/unified_field_list/public/hooks/use_field_filters.test.tsx @@ -68,6 +68,33 @@ describe('UnifiedFieldList useFieldFilters()', () => { expect(result.current.onFilterField!(dataView.getFieldByName('bytes')!)).toBe(false); }); + it('should update correctly on search by name which has a wildcard', async () => { + const props: FieldFiltersParams = { + allFields: dataView.fields, + services: mockedServices, + }; + const { result } = renderHook(useFieldFilters, { + initialProps: props, + }); + + expect(result.current.fieldSearchHighlight).toBe(''); + expect(result.current.onFilterField).toBeUndefined(); + + act(() => { + result.current.fieldListFiltersProps.onChangeNameFilter('message*me1'); + }); + + expect(result.current.fieldSearchHighlight).toBe('message*me1'); + expect(result.current.onFilterField).toBeDefined(); + expect(result.current.onFilterField!({ displayName: 'test' } as DataViewField)).toBe(false); + expect(result.current.onFilterField!({ displayName: 'message' } as DataViewField)).toBe(false); + expect(result.current.onFilterField!({ displayName: 'message.name1' } as DataViewField)).toBe( + true + ); + expect(result.current.onFilterField!({ name: 'messagename10' } as DataViewField)).toBe(false); + expect(result.current.onFilterField!({ name: 'message.test' } as DataViewField)).toBe(false); + }); + it('should update correctly on filter by type', async () => { const props: FieldFiltersParams = { allFields: dataView.fields, diff --git a/src/plugins/unified_field_list/public/hooks/use_field_filters.ts b/src/plugins/unified_field_list/public/hooks/use_field_filters.ts index 803739caba7c6..c3e08ff335602 100644 --- a/src/plugins/unified_field_list/public/hooks/use_field_filters.ts +++ b/src/plugins/unified_field_list/public/hooks/use_field_filters.ts @@ -13,6 +13,7 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser'; import { type FieldListFiltersProps } from '../components/field_list_filters'; import { type FieldListItem, type FieldTypeKnown, GetCustomFieldType } from '../types'; import { getFieldIconType } from '../utils/field_types'; +import { fieldNameWildcardMatcher } from '../utils/field_name_wildcard_matcher'; const htmlId = htmlIdGenerator('fieldList'); @@ -74,11 +75,7 @@ export function useFieldFilters({ onFilterField: fieldSearchHighlight?.length || selectedFieldTypes.length > 0 ? (field: T) => { - if ( - fieldSearchHighlight?.length && - !field.name?.toLowerCase().includes(fieldSearchHighlight) && - !field.displayName?.toLowerCase().includes(fieldSearchHighlight) - ) { + if (fieldSearchHighlight && !fieldNameWildcardMatcher(field, fieldSearchHighlight)) { return false; } if (selectedFieldTypes.length > 0) { diff --git a/src/plugins/unified_field_list/public/index.ts b/src/plugins/unified_field_list/public/index.ts index 5ea557dccca08..be3591a80cd15 100755 --- a/src/plugins/unified_field_list/public/index.ts +++ b/src/plugins/unified_field_list/public/index.ts @@ -34,8 +34,8 @@ export { type FieldPopoverProps, FieldPopoverHeader, type FieldPopoverHeaderProps, - FieldPopoverVisualize, - type FieldPopoverVisualizeProps, + FieldPopoverFooter, + type FieldPopoverFooterProps, } from './components/field_popover'; export { FieldVisualizeButton, @@ -60,6 +60,7 @@ export type { FieldListGroups, FieldsGroupDetails, FieldTypeKnown, + FieldListItem, GetCustomFieldType, } from './types'; export { ExistenceFetchStatus, FieldsGroupNames } from './types'; diff --git a/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.test.tsx b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.test.tsx new file mode 100644 index 0000000000000..2637ddf4046d2 --- /dev/null +++ b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.test.tsx @@ -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 { type DataViewField } from '@kbn/data-views-plugin/common'; +import { fieldNameWildcardMatcher } from './field_name_wildcard_matcher'; + +describe('UnifiedFieldList fieldNameWildcardMatcher()', () => { + it('should work correctly', async () => { + expect(fieldNameWildcardMatcher({ displayName: 'test' } as DataViewField, 'no')).toBe(false); + expect( + fieldNameWildcardMatcher({ displayName: 'test', name: 'yes' } as DataViewField, 'yes') + ).toBe(true); + + const search = 'test*ue'; + expect(fieldNameWildcardMatcher({ displayName: 'test' } as DataViewField, search)).toBe(false); + expect(fieldNameWildcardMatcher({ displayName: 'test.value' } as DataViewField, search)).toBe( + true + ); + expect(fieldNameWildcardMatcher({ name: 'test.this_value' } as DataViewField, search)).toBe( + true + ); + expect(fieldNameWildcardMatcher({ name: 'message.test' } as DataViewField, search)).toBe(false); + expect( + fieldNameWildcardMatcher({ name: 'test.this_value.maybe' } as DataViewField, search) + ).toBe(false); + expect( + fieldNameWildcardMatcher({ name: 'test.this_value.maybe' } as DataViewField, `${search}*`) + ).toBe(true); + expect( + fieldNameWildcardMatcher({ name: 'test.this_value.maybe' } as DataViewField, '*value*') + ).toBe(true); + }); +}); diff --git a/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.ts b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.ts new file mode 100644 index 0000000000000..98b0e64b7bc78 --- /dev/null +++ b/src/plugins/unified_field_list/public/utils/field_name_wildcard_matcher.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 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 { escapeRegExp, memoize } from 'lodash'; + +const makeRegEx = memoize(function makeRegEx(glob: string) { + const globRegex = glob.split('*').map(escapeRegExp).join('.*'); + return new RegExp(globRegex.includes('*') ? `^${globRegex}$` : globRegex, 'i'); +}); + +/** + * Checks if field displayName or name matches the provided search string. + * The search string can have wildcard. + * @param field + * @param fieldSearchHighlight + */ +export const fieldNameWildcardMatcher = ( + field: { name: string; displayName?: string }, + fieldSearchHighlight: string +): boolean => { + if (!fieldSearchHighlight) { + return false; + } + + return ( + (!!field.displayName && makeRegEx(fieldSearchHighlight).test(field.displayName)) || + makeRegEx(fieldSearchHighlight).test(field.name) + ); +}; diff --git a/src/plugins/unified_field_list/tsconfig.json b/src/plugins/unified_field_list/tsconfig.json index 23726803a1096..bf24189ddaeb7 100644 --- a/src/plugins/unified_field_list/tsconfig.json +++ b/src/plugins/unified_field_list/tsconfig.json @@ -28,6 +28,7 @@ "@kbn/core-lifecycle-browser", "@kbn/react-field", "@kbn/field-types", + "@kbn/expressions-plugin", ], "exclude": [ "target/**/*", diff --git a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts index b38b42cf2a249..5ec2d8a1fc638 100644 --- a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts +++ b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts @@ -31,5 +31,20 @@ describe('fieldSupportsBreakdown', () => { expect( fieldSupportsBreakdown({ aggregatable: true, scripted: false, type: 'string' } as any) ).toBe(true); + + expect( + fieldSupportsBreakdown({ aggregatable: true, scripted: false, type: 'number' } as any) + ).toBe(true); + }); + + it('should return false if field is aggregatable but it is a time series counter metric', () => { + expect( + fieldSupportsBreakdown({ + aggregatable: true, + scripted: false, + type: 'number', + timeSeriesMetric: 'counter', + } as any) + ).toBe(false); }); }); diff --git a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts index 302a5950fefcb..88ec604c1462e 100644 --- a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts +++ b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts @@ -11,4 +11,7 @@ import { DataViewField } from '@kbn/data-views-plugin/public'; const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); export const fieldSupportsBreakdown = (field: DataViewField) => - supportedTypes.has(field.type) && field.aggregatable && !field.scripted; + supportedTypes.has(field.type) && + field.aggregatable && + !field.scripted && + field.timeSeriesMetric !== 'counter'; diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx index 839983c9c6b32..d5c6377d20942 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu.test.tsx @@ -230,7 +230,7 @@ describe('Querybar Menu component', () => { expect(languageSwitcher.length).toBeTruthy(); }); - it('should render the save query quick buttons', async () => { + it('should render the save query quick button', async () => { const newProps = { ...props, openQueryBarMenu: true, @@ -254,10 +254,6 @@ describe('Querybar Menu component', () => { '[data-test-subj="saved-query-management-save-changes-button"]' ); expect(saveChangesButton.length).toBeTruthy(); - const saveChangesAsNewButton = component.find( - '[data-test-subj="saved-query-management-save-as-new-button"]' - ); - expect(saveChangesAsNewButton.length).toBeTruthy(); }); it('should render all filter panel options by default', async () => { diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx index a9342a65ea74f..9ba28c72c47e3 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu.tsx @@ -32,7 +32,11 @@ import { export const strings = { getFilterSetButtonLabel: () => i18n.translate('unifiedSearch.filter.options.filterSetButtonLabel', { - defaultMessage: 'Saved query menu', + defaultMessage: 'Query menu', + }), + getSavedQueryPopoverSaveChangesButtonText: () => + i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText', { + defaultMessage: 'Update query', }), }; @@ -171,7 +175,10 @@ function QueryBarMenuComponent({ ); case 'saveForm': return ( - {saveFormComponent}]} /> + {saveFormComponent}]} + /> ); case 'saveAsNewForm': return ( diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx index 20f155db53832..d7c8bd8b64a45 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx @@ -63,7 +63,7 @@ export const strings = { }), getLoadOtherFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.loadOtherFilterSetLabel', { - defaultMessage: 'Load a different query', + defaultMessage: 'Load query', }), getLoadCurrentFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.loadCurrentFilterSetLabel', { @@ -71,7 +71,7 @@ export const strings = { }), getSaveAsNewFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.saveAsNewFilterSetLabel', { - defaultMessage: 'Save as new', + defaultMessage: 'Save query', }), getSaveFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.saveFilterSetLabel', { @@ -88,7 +88,7 @@ export const strings = { }), getSavedQueryPopoverSaveChangesButtonText: () => i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText', { - defaultMessage: 'Save changes', + defaultMessage: 'Update query', }), getSavedQueryPopoverSaveAsNewButtonAriaLabel: () => i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel', { @@ -100,7 +100,7 @@ export const strings = { }), getSaveCurrentFilterSetLabel: () => i18n.translate('unifiedSearch.filter.options.saveCurrentFilterSetLabel', { - defaultMessage: 'Save current query', + defaultMessage: 'Save as new', }), getApplyAllFiltersButtonLabel: () => i18n.translate('unifiedSearch.filter.options.applyAllFiltersButtonLabel', { @@ -238,10 +238,6 @@ export function QueryBarMenuPanels({ }; }; - const handleSaveAsNew = useCallback(() => { - setRenderedComponent('saveAsNewForm'); - }, [setRenderedComponent]); - const handleSave = useCallback(() => { setRenderedComponent('saveForm'); }, [setRenderedComponent]); @@ -413,38 +409,17 @@ export function QueryBarMenuPanels({ {savedQuery && savedQueryHasChanged && Boolean(showSaveQuery) && hasFiltersOrQuery && ( - - - - {strings.getSavedQueryPopoverSaveChangesButtonText()} - - - - - {strings.getSavedQueryPopoverSaveAsNewButtonText()} - - - + {strings.getSavedQueryPopoverSaveChangesButtonText()} + )} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 8ca32108a2555..aecd588673e09 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -120,6 +120,7 @@ export interface QueryBarTopRowProps isScreenshotMode?: boolean; onTextLangQuerySubmit: (query?: Query | AggregateQuery) => void; onTextLangQueryChange: (query: AggregateQuery) => void; + submitOnBlur?: boolean; } export const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -556,6 +557,7 @@ export const QueryBarTopRow = React.memo( size={props.suggestionsSize} isDisabled={props.isDisabled} appName={appName} + submitOnBlur={props.submitOnBlur} deps={{ unifiedSearch, data, diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx index 01837e26bb892..c891174986510 100644 --- a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx +++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.test.tsx @@ -136,7 +136,7 @@ describe('Saved query management list component', () => { .find('[data-test-subj="saved-query-management-apply-changes-button"]') .first() .text() - ).toBe('Apply query'); + ).toBe('Load query'); const newProps = { ...props, diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx index daaf523abf68b..5ebb89bd88afc 100644 --- a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx +++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.tsx @@ -309,22 +309,7 @@ export function SavedQueryManagementList({ )} - - {canEditSavedObjects && ( - - - Manage - - - )} + - {hasFiltersOrQuery - ? i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel', - { - defaultMessage: 'Load query', - } - ) - : i18n.translate( - 'unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel', - { - defaultMessage: 'Apply query', - } - )} + {i18n.translate( + 'unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel', + { + defaultMessage: 'Load query', + } + )} + {canEditSavedObjects && ( + + + {i18n.translate('unifiedSearch.search.searchBar.savedQueryPopoverManageLabel', { + defaultMessage: 'Manage saved objects', + })} + + + )} {showDeletionConfirmationModal && toBeDeletedSavedQuery && ( diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index 0a83a45de1d03..3326d81e29109 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -105,6 +105,8 @@ export interface SearchBarOwnProps { * Disables all inputs and interactive elements, */ isDisabled?: boolean; + + submitOnBlur?: boolean; } export type SearchBarProps = SearchBarOwnProps & @@ -585,6 +587,7 @@ class SearchBarUI extends C isScreenshotMode={this.props.isScreenshotMode} onTextLangQuerySubmit={this.onTextLangQuerySubmit} onTextLangQueryChange={this.onTextLangQueryChange} + submitOnBlur={this.props.submitOnBlur} /> ); diff --git a/src/plugins/usage_collection/server/plugin.ts b/src/plugins/usage_collection/server/plugin.ts index c8c44679cfa23..7139de5091b50 100644 --- a/src/plugins/usage_collection/server/plugin.ts +++ b/src/plugins/usage_collection/server/plugin.ts @@ -98,7 +98,7 @@ export class UsageCollectionPlugin implements Plugin { public setup(core: CoreSetup): UsageCollectionSetup { const config = this.initializerContext.config.get(); - const kibanaIndex = core.savedObjects.getKibanaIndex(); + const kibanaIndex = core.savedObjects.getDefaultIndex(); const collectorSet = new CollectorSet({ logger: this.logger.get('usage-collection', 'collector-set'), diff --git a/src/plugins/vis_types/heatmap/public/vis_type/heatmap.tsx b/src/plugins/vis_types/heatmap/public/vis_type/heatmap.ts similarity index 94% rename from src/plugins/vis_types/heatmap/public/vis_type/heatmap.tsx rename to src/plugins/vis_types/heatmap/public/vis_type/heatmap.ts index 6c4b63c1095a7..97f6db9668a2a 100644 --- a/src/plugins/vis_types/heatmap/public/vis_type/heatmap.tsx +++ b/src/plugins/vis_types/heatmap/public/vis_type/heatmap.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React from 'react'; import { i18n } from '@kbn/i18n'; import { Position } from '@elastic/charts'; @@ -15,7 +14,6 @@ import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '@kbn/visualizations-plu import { HeatmapTypeProps, HeatmapVisParams, AxisType, ScaleType } from '../types'; import { toExpressionAst } from '../to_ast'; import { getHeatmapOptions } from '../editor/components'; -import { SplitTooltip } from './split_tooltip'; import { convertToLens } from '../convert_to_lens'; export const getHeatmapVisTypeDefinition = ({ @@ -129,11 +127,6 @@ export const getHeatmapVisTypeDefinition = ({ { group: AggGroupNames.Buckets, name: 'split', - // TODO: Remove when split chart aggs are supported - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), title: i18n.translate('visTypeHeatmap.heatmap.splitTitle', { defaultMessage: 'Split chart', }), diff --git a/src/plugins/vis_types/heatmap/public/vis_type/split_tooltip.tsx b/src/plugins/vis_types/heatmap/public/vis_type/split_tooltip.tsx deleted file mode 100644 index 571ec2f33bca1..0000000000000 --- a/src/plugins/vis_types/heatmap/public/vis_type/split_tooltip.tsx +++ /dev/null @@ -1,19 +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 React from 'react'; - -import { FormattedMessage } from '@kbn/i18n-react'; - -export function SplitTooltip() { - return ( - - ); -} diff --git a/src/plugins/vis_types/heatmap/server/plugin.ts b/src/plugins/vis_types/heatmap/server/plugin.ts index b14afa2ac67e8..70b9b9adad8d4 100644 --- a/src/plugins/vis_types/heatmap/server/plugin.ts +++ b/src/plugins/vis_types/heatmap/server/plugin.ts @@ -23,13 +23,23 @@ export const getUiSettingsConfig: () => Record } ), requiresPageReload: true, - value: true, + value: false, description: i18n.translate( 'visTypeHeatmap.advancedSettings.visualization.legacyHeatmapChartsLibrary.description', { defaultMessage: 'Enables legacy charts library for heatmap charts in visualize.', } ), + deprecation: { + message: i18n.translate( + 'visTypeHeatmap.advancedSettings.visualization.legacyHeatmapChartsLibrary.deprecation', + { + defaultMessage: + 'The legacy charts library for heatmap in visualize is deprecated and will not be supported in a future version.', + } + ), + docLinksKey: 'visualizationSettings', + }, category: ['visualization'], schema: schema.boolean(), }, diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index 179e0cbfa53fb..c90227521bc21 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -34,6 +34,7 @@ const createSetupContract = (): VisualizationsSetup => ({ registerAlias: jest.fn(), hideTypes: jest.fn(), visEditorsRegistry: { registerDefault: jest.fn(), register: jest.fn(), get: jest.fn() }, + listingViewRegistry: { add: jest.fn() }, }); const createStartContract = (): VisualizationsStart => ({ diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_editor_common.test.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_editor_common.test.tsx index b60a0ae931872..cb33226318742 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_editor_common.test.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_editor_common.test.tsx @@ -120,47 +120,6 @@ describe('VisualizeEditorCommon', () => { }); }); - it('should display a warning callout for new heatmap implementation with split aggs', async () => { - const wrapper = shallowWithIntl( - {}} - hasUnappliedChanges={false} - isEmbeddableRendered={false} - onAppLeave={() => {}} - visEditorRef={React.createRef()} - visInstance={ - { - savedVis: { - id: 'test', - sharingSavedObjectProps: { - outcome: 'conflict', - aliasTargetId: 'alias_id', - }, - }, - vis: { - type: { - title: 'Heatmap', - name: 'heatmap', - }, - data: { - aggs: { - aggs: [ - { - schema: 'split', - }, - ], - }, - }, - }, - } as unknown as VisualizeEditorVisInstance - } - /> - ); - expect(wrapper.find(VizChartWarning).length).toBe(1); - }); - it('should not display a warning callout for XY charts with split aggs', async () => { const wrapper = shallowWithIntl( = (props) => { ); }; -const HeatmapWarningFormatMessage: FC = (props) => { - return ( - - - - - ), - }} - /> - ); -}; - const PieWarningFormatMessage: FC = (props) => { return ( = (props) => { }; const warningMessages = { - [CHARTS_WITHOUT_SMALL_MULTIPLES.heatmap]: HeatmapWarningFormatMessage, [CHARTS_WITHOUT_SMALL_MULTIPLES.gauge]: GaugeWarningFormatMessage, [CHARTS_TO_BE_DEPRECATED.pie]: PieWarningFormatMessage, [CHARTS_TO_BE_DEPRECATED.controls]: ControlsWarningFormatMessage, diff --git a/src/plugins/visualizations/public/visualize_app/constants.ts b/src/plugins/visualizations/public/visualize_app/constants.ts index fd256cb5bbb86..4a489abec1f2e 100644 --- a/src/plugins/visualizations/public/visualize_app/constants.ts +++ b/src/plugins/visualizations/public/visualize_app/constants.ts @@ -6,5 +6,4 @@ * Side Public License, v 1. */ -export const NEW_HEATMAP_CHARTS_LIBRARY = 'visualization:visualize:legacyHeatmapChartsLibrary'; export const NEW_GAUGE_CHARTS_LIBRARY = 'visualization:visualize:legacyGaugeChartsLibrary'; diff --git a/src/plugins/visualizations/public/visualize_app/utils/split_chart_warning_helpers.ts b/src/plugins/visualizations/public/visualize_app/utils/split_chart_warning_helpers.ts index 5efbe7494ef59..09699d749158a 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/split_chart_warning_helpers.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/split_chart_warning_helpers.ts @@ -8,10 +8,9 @@ import { $Values } from '@kbn/utility-types'; import { AggConfigs } from '@kbn/data-plugin/common'; -import { NEW_HEATMAP_CHARTS_LIBRARY, NEW_GAUGE_CHARTS_LIBRARY } from '../constants'; +import { NEW_GAUGE_CHARTS_LIBRARY } from '../constants'; export const CHARTS_WITHOUT_SMALL_MULTIPLES = { - heatmap: 'heatmap', gauge: 'gauge', } as const; @@ -24,7 +23,6 @@ export type CHARTS_WITHOUT_SMALL_MULTIPLES = $Values; export const CHARTS_CONFIG_TOKENS = { - [CHARTS_WITHOUT_SMALL_MULTIPLES.heatmap]: NEW_HEATMAP_CHARTS_LIBRARY, [CHARTS_WITHOUT_SMALL_MULTIPLES.gauge]: NEW_GAUGE_CHARTS_LIBRARY, } as const; @@ -32,7 +30,6 @@ export const isSplitChart = (chartType: string | undefined, aggs?: AggConfigs) = const defaultIsSplitChart = () => aggs?.aggs.some((agg) => agg.schema === 'split'); const knownCheckers = { - [CHARTS_WITHOUT_SMALL_MULTIPLES.heatmap]: defaultIsSplitChart, [CHARTS_WITHOUT_SMALL_MULTIPLES.gauge]: () => aggs?.aggs.some((agg) => agg.schema === 'group'), }; diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts index 178eba2eacb3e..c7c3ae37ab0dd 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; import { getAllMigrations } from '../migrations/visualization_saved_object_migrations'; @@ -14,6 +15,7 @@ export const getVisualizationSavedObjectType = ( getSearchSourceMigrations: () => MigrateFunctionsObject ): SavedObjectsType => ({ name: 'visualization', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index a15ec048cb784..340631d4b0582 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -54,6 +54,7 @@ "@kbn/shared-ux-router", "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-finder-plugin", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/test/api_integration/apis/guided_onboarding/get_config.ts b/test/api_integration/apis/guided_onboarding/get_config.ts index 3341ab87d0552..58e596b72bb8e 100644 --- a/test/api_integration/apis/guided_onboarding/get_config.ts +++ b/test/api_integration/apis/guided_onboarding/get_config.ts @@ -7,13 +7,14 @@ */ import expect from '@kbn/expect'; +import { API_BASE_PATH } from '@kbn/guided-onboarding-plugin/common'; import type { FtrProviderContext } from '../../ftr_provider_context'; -const getConfigsPath = '/api/guided_onboarding/configs'; +const getConfigsPath = `${API_BASE_PATH}/configs`; export default function testGetGuideConfig({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('GET /api/guided_onboarding/configs', () => { + describe(`GET ${getConfigsPath}`, () => { // check that production guides are present ['siem', 'appSearch', 'websiteSearch', 'databaseSearch', 'kubernetes'].map((guideId) => { it(`returns config for ${guideId}`, async () => { diff --git a/test/api_integration/apis/guided_onboarding/get_guides.ts b/test/api_integration/apis/guided_onboarding/get_guides.ts index d9136c6602c09..34165b55f93f4 100644 --- a/test/api_integration/apis/guided_onboarding/get_guides.ts +++ b/test/api_integration/apis/guided_onboarding/get_guides.ts @@ -13,15 +13,16 @@ import { pluginStateSavedObjectsType, } from '@kbn/guided-onboarding-plugin/server/saved_objects/guided_setup'; import { appSearchGuideId } from '@kbn/enterprise-search-plugin/common/guided_onboarding/search_guide_config'; +import { API_BASE_PATH } from '@kbn/guided-onboarding-plugin/common'; import type { FtrProviderContext } from '../../ftr_provider_context'; import { createGuides } from './helpers'; -const getGuidesPath = '/api/guided_onboarding/guides'; +const getGuidesPath = `${API_BASE_PATH}/guides`; export default function testGetGuidesState({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); - describe('GET /api/guided_onboarding/guides', () => { + describe(`GET ${getGuidesPath}`, () => { afterEach(async () => { // Clean up saved objects await kibanaServer.savedObjects.clean({ diff --git a/test/api_integration/apis/guided_onboarding/get_state.ts b/test/api_integration/apis/guided_onboarding/get_state.ts index 489bf26830585..2334505318a55 100644 --- a/test/api_integration/apis/guided_onboarding/get_state.ts +++ b/test/api_integration/apis/guided_onboarding/get_state.ts @@ -11,11 +11,13 @@ import { testGuideStep1ActiveState, testGuideNotActiveState, mockPluginStateNotStarted, + testGuideParams, } from '@kbn/guided-onboarding-plugin/public/services/api.mocks'; import { guideStateSavedObjectsType, pluginStateSavedObjectsType, } from '@kbn/guided-onboarding-plugin/server/saved_objects/guided_setup'; +import { API_BASE_PATH } from '@kbn/guided-onboarding-plugin/common'; import type { FtrProviderContext } from '../../ftr_provider_context'; import { createPluginState, createGuides } from './helpers'; @@ -25,12 +27,12 @@ const getDateXDaysAgo = (daysAgo: number): string => { return date.toISOString(); }; -const getStatePath = '/api/guided_onboarding/state'; +const getStatePath = `${API_BASE_PATH}/state`; export default function testGetState({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); - describe('GET /api/guided_onboarding/state', () => { + describe(`GET ${getStatePath}`, () => { afterEach(async () => { // Clean up saved objects await kibanaServer.savedObjects.clean({ @@ -110,5 +112,19 @@ export default function testGetState({ getService }: FtrProviderContext) { expect(response.body.pluginState).not.to.be.empty(); expect(response.body.pluginState.isActivePeriod).to.eql(true); }); + + it('returns the dynamic params', async () => { + // Create an active guide + await createGuides(kibanaServer, [{ ...testGuideStep1ActiveState, params: testGuideParams }]); + + // Create a plugin state + await createPluginState(kibanaServer, { + status: 'in_progress', + creationDate: new Date().toISOString(), + }); + + const response = await supertest.get(getStatePath).expect(200); + expect(response.body.pluginState.activeGuide.params).to.eql(testGuideParams); + }); }); } diff --git a/test/api_integration/apis/guided_onboarding/put_state.ts b/test/api_integration/apis/guided_onboarding/put_state.ts index 7b287b56bb77c..d2ce7739b2098 100644 --- a/test/api_integration/apis/guided_onboarding/put_state.ts +++ b/test/api_integration/apis/guided_onboarding/put_state.ts @@ -10,6 +10,9 @@ import expect from '@kbn/expect'; import { testGuideStep1ActiveState, testGuideNotActiveState, + testGuideStep1InProgressState, + testGuideStep2ActiveState, + testGuideParams, } from '@kbn/guided-onboarding-plugin/public/services/api.mocks'; import { pluginStateSavedObjectsType, @@ -18,15 +21,16 @@ import { } from '@kbn/guided-onboarding-plugin/server/saved_objects/guided_setup'; import { testGuideId } from '@kbn/guided-onboarding'; import { appSearchGuideId } from '@kbn/enterprise-search-plugin/common/guided_onboarding/search_guide_config'; +import { API_BASE_PATH } from '@kbn/guided-onboarding-plugin/common'; import type { FtrProviderContext } from '../../ftr_provider_context'; import { createGuides, createPluginState } from './helpers'; -const putStatePath = `/api/guided_onboarding/state`; +const putStatePath = `${API_BASE_PATH}/state`; export default function testPutState({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); - describe('PUT /api/guided_onboarding/state', () => { + describe(`PUT ${putStatePath}`, () => { afterEach(async () => { // Clean up saved objects await kibanaServer.savedObjects.clean({ @@ -161,5 +165,29 @@ export default function testPutState({ getService }: FtrProviderContext) { }); expect(kubernetesGuide.attributes.isActive).to.eql(true); }); + + it('saves dynamic params if provided', async () => { + // create a guide + await createGuides(kibanaServer, [testGuideStep1InProgressState]); + + // complete step1 with dynamic params + await supertest + .put(putStatePath) + .set('kbn-xsrf', 'true') + .send({ + guide: { + ...testGuideStep2ActiveState, + params: testGuideParams, + }, + }) + .expect(200); + + // check that params object was saved + const testGuideSO = await kibanaServer.savedObjects.get({ + type: guideStateSavedObjectsType, + id: testGuideId, + }); + expect(testGuideSO.attributes.params).to.eql(testGuideParams); + }); }); } diff --git a/test/api_integration/apis/kql_telemetry/kql_telemetry.ts b/test/api_integration/apis/kql_telemetry/kql_telemetry.ts index 310b99a5fb781..151a417c8aa6e 100644 --- a/test/api_integration/apis/kql_telemetry/kql_telemetry.ts +++ b/test/api_integration/apis/kql_telemetry/kql_telemetry.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { get } from 'lodash'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -27,7 +28,7 @@ export default function ({ getService }: FtrProviderContext) { ); }); - it('should increment the opt *in* counter in the .kibana/kql-telemetry document', async () => { + it('should increment the opt *in* counter in the .kibana_analytics/kql-telemetry document', async () => { await supertest .post('/api/kibana/kql_opt_in_stats') .set('content-type', 'application/json') @@ -36,7 +37,7 @@ export default function ({ getService }: FtrProviderContext) { return es .search({ - index: '.kibana', + index: ANALYTICS_SAVED_OBJECT_INDEX, q: 'type:kql-telemetry', }) .then((response) => { @@ -45,7 +46,7 @@ export default function ({ getService }: FtrProviderContext) { }); }); - it('should increment the opt *out* counter in the .kibana/kql-telemetry document', async () => { + it('should increment the opt *out* counter in the .kibana_analytics/kql-telemetry document', async () => { await supertest .post('/api/kibana/kql_opt_in_stats') .set('content-type', 'application/json') @@ -54,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) { return es .search({ - index: '.kibana', + index: ANALYTICS_SAVED_OBJECT_INDEX, q: 'type:kql-telemetry', }) .then((response) => { diff --git a/test/api_integration/apis/saved_objects/bulk_create.ts b/test/api_integration/apis/saved_objects/bulk_create.ts index 975eb73a49532..b365df10f7d74 100644 --- a/test/api_integration/apis/saved_objects/bulk_create.ts +++ b/test/api_integration/apis/saved_objects/bulk_create.ts @@ -75,6 +75,7 @@ export default function ({ getService }: FtrProviderContext) { }, coreMigrationVersion: '8.8.0', typeMigrationVersion: resp.body.saved_objects[1].typeMigrationVersion, + managed: resp.body.saved_objects[1].managed, references: [], namespaces: [SPACE_ID], }, diff --git a/test/api_integration/apis/saved_objects/bulk_get.ts b/test/api_integration/apis/saved_objects/bulk_get.ts index d1873b22b0d62..6c97efd94d70a 100644 --- a/test/api_integration/apis/saved_objects/bulk_get.ts +++ b/test/api_integration/apis/saved_objects/bulk_get.ts @@ -74,6 +74,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: resp.body.saved_objects[0].migrationVersion, coreMigrationVersion: '8.8.0', typeMigrationVersion: resp.body.saved_objects[0].typeMigrationVersion, + managed: resp.body.saved_objects[0].managed, namespaces: ['default'], references: [ { @@ -106,6 +107,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: resp.body.saved_objects[2].migrationVersion, coreMigrationVersion: '8.8.0', typeMigrationVersion: resp.body.saved_objects[2].typeMigrationVersion, + managed: resp.body.saved_objects[2].managed, references: [], }, ], diff --git a/test/api_integration/apis/saved_objects/create.ts b/test/api_integration/apis/saved_objects/create.ts index c1977589255a7..865eb44596ec1 100644 --- a/test/api_integration/apis/saved_objects/create.ts +++ b/test/api_integration/apis/saved_objects/create.ts @@ -52,6 +52,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: resp.body.migrationVersion, coreMigrationVersion: '8.8.0', typeMigrationVersion: resp.body.typeMigrationVersion, + managed: resp.body.managed, updated_at: resp.body.updated_at, created_at: resp.body.created_at, version: resp.body.version, @@ -63,6 +64,7 @@ export default function ({ getService }: FtrProviderContext) { }); expect(resp.body.migrationVersion).to.be.ok(); expect(resp.body.typeMigrationVersion).to.be.ok(); + expect(resp.body.managed).to.not.be.ok(); }); }); diff --git a/test/api_integration/apis/saved_objects/delete_unknown_types.ts b/test/api_integration/apis/saved_objects/delete_unknown_types.ts index af9e413de0279..9a4b6fd517c5b 100644 --- a/test/api_integration/apis/saved_objects/delete_unknown_types.ts +++ b/test/api_integration/apis/saved_objects/delete_unknown_types.ts @@ -7,6 +7,10 @@ */ import expect from '@kbn/expect'; +import { + MAIN_SAVED_OBJECT_INDEX, + ANALYTICS_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../ftr_provider_context'; const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -32,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { const fetchIndexContent = async () => { const body = await es.search<{ type: string }>({ - index: '.kibana', + index: [MAIN_SAVED_OBJECT_INDEX, ANALYTICS_SAVED_OBJECT_INDEX], body: { size: 100, }, diff --git a/test/api_integration/apis/saved_objects/export.ts b/test/api_integration/apis/saved_objects/export.ts index bec4305ab7d4c..ec7b82453ffce 100644 --- a/test/api_integration/apis/saved_objects/export.ts +++ b/test/api_integration/apis/saved_objects/export.ts @@ -350,6 +350,7 @@ export default function ({ getService }: FtrProviderContext) { id: 'be3733a0-9efe-11e7-acb3-3dab96693fab', coreMigrationVersion: '8.8.0', typeMigrationVersion: objects[0].typeMigrationVersion, + managed: objects[0].managed, references: [ { id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', @@ -363,6 +364,7 @@ export default function ({ getService }: FtrProviderContext) { version: objects[0].version, }); expect(objects[0].typeMigrationVersion).to.be.ok(); + expect(objects[0].managed).to.not.be.ok(); expect(() => JSON.parse(objects[0].attributes.kibanaSavedObjectMeta.searchSourceJSON) ).not.to.throwError(); @@ -411,6 +413,7 @@ export default function ({ getService }: FtrProviderContext) { id: 'be3733a0-9efe-11e7-acb3-3dab96693fab', coreMigrationVersion: '8.8.0', typeMigrationVersion: objects[0].typeMigrationVersion, + managed: objects[0].managed, references: [ { id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', @@ -424,6 +427,7 @@ export default function ({ getService }: FtrProviderContext) { version: objects[0].version, }); expect(objects[0].typeMigrationVersion).to.be.ok(); + expect(objects[0].managed).to.not.be.ok(); expect(() => JSON.parse(objects[0].attributes.kibanaSavedObjectMeta.searchSourceJSON) ).not.to.throwError(); @@ -477,6 +481,7 @@ export default function ({ getService }: FtrProviderContext) { id: 'be3733a0-9efe-11e7-acb3-3dab96693fab', coreMigrationVersion: '8.8.0', typeMigrationVersion: objects[0].typeMigrationVersion, + managed: objects[0].managed, references: [ { id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', @@ -490,6 +495,7 @@ export default function ({ getService }: FtrProviderContext) { version: objects[0].version, }); expect(objects[0].typeMigrationVersion).to.be.ok(); + expect(objects[0].managed).to.not.be.ok(); expect(() => JSON.parse(objects[0].attributes.kibanaSavedObjectMeta.searchSourceJSON) ).not.to.throwError(); diff --git a/test/api_integration/apis/saved_objects/get.ts b/test/api_integration/apis/saved_objects/get.ts index db127924b5ec6..a7b1f58c531f4 100644 --- a/test/api_integration/apis/saved_objects/get.ts +++ b/test/api_integration/apis/saved_objects/get.ts @@ -39,6 +39,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: resp.body.migrationVersion, coreMigrationVersion: '8.8.0', typeMigrationVersion: resp.body.typeMigrationVersion, + managed: resp.body.managed, attributes: { title: 'Count of requests', description: '', @@ -59,6 +60,7 @@ export default function ({ getService }: FtrProviderContext) { }); expect(resp.body.migrationVersion).to.be.ok(); expect(resp.body.typeMigrationVersion).to.be.ok(); + expect(resp.body.managed).to.not.be.ok(); })); describe('doc does not exist', () => { diff --git a/test/api_integration/apis/saved_objects/resolve.ts b/test/api_integration/apis/saved_objects/resolve.ts index eaae0cb1be4c2..73f7ad03582cf 100644 --- a/test/api_integration/apis/saved_objects/resolve.ts +++ b/test/api_integration/apis/saved_objects/resolve.ts @@ -44,6 +44,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: resp.body.saved_object.migrationVersion, coreMigrationVersion: '8.8.0', typeMigrationVersion: resp.body.saved_object.typeMigrationVersion, + managed: resp.body.saved_object.managed, attributes: { title: 'Count of requests', description: '', @@ -66,6 +67,7 @@ export default function ({ getService }: FtrProviderContext) { }); expect(resp.body.saved_object.migrationVersion).to.be.ok(); expect(resp.body.saved_object.typeMigrationVersion).to.be.ok(); + expect(resp.body.saved_object.managed).to.not.be.ok(); })); describe('doc does not exist', () => { diff --git a/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/data.json b/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/data.json index 3d6ecd160db00..067be99543a95 100644 --- a/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/data.json +++ b/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/data.json @@ -2,7 +2,7 @@ "type": "doc", "value": { "id": "index-pattern:8963ca30-3224-11e8-a572-ffca06da1357", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "coreMigrationVersion": "7.14.0", "index-pattern": { @@ -25,7 +25,7 @@ "type": "doc", "value": { "id": "search:960372e0-3224-11e8-a572-ffca06da1357", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "coreMigrationVersion": "7.14.0", "migrationVersion": { @@ -67,7 +67,7 @@ "type": "doc", "value": { "id": "visualization:a42c0580-3224-11e8-a572-ffca06da1357", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "coreMigrationVersion": "7.14.0", "migrationVersion": { @@ -102,7 +102,7 @@ "type": "doc", "value": { "id": "dashboard:b70c7ae0-3224-11e8-a572-ffca06da1357", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "coreMigrationVersion": "7.14.0", "dashboard": { diff --git a/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/mappings.json b/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/mappings.json index fb2337c15216c..885a64189a059 100644 --- a/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/mappings.json +++ b/test/api_integration/fixtures/es_archiver/saved_objects/delete_unknown_types/mappings.json @@ -37,6 +37,764 @@ "url": "c7f66a0df8b1b52f17c28c4adb111105", "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", "visualization": "f819cf6636b75c9e76ba733a0c6ef355" + }, + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] + } + }, + "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" + } + } + }, + "unknown-type": { + "dynamic": "false", + "properties": { + "foo": { + "type": "keyword" + } + } + }, + "unknown-shareable-type": { + "dynamic": "false", + "properties": { + "foo": { + "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" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "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", + "mapping": { + "total_fields": { + "limit": 1500 + } + } + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana_analytics_$KIBANA_PACKAGE_VERSION": {}, + ".kibana_analytics": {} + }, + "index": ".kibana_analytics_$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", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", + "visualization": "f819cf6636b75c9e76ba733a0c6ef355" + }, + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] } }, "dynamic": "strict", @@ -525,7 +1283,9 @@ "refresh_interval": "1s", "routing_partition_size": "1", "mapping": { - "total_fields": { "limit": 1500 } + "total_fields": { + "limit": 1500 + } } } } diff --git a/test/examples/content_management/index.ts b/test/examples/content_management/index.ts index d5e3f14bfc0e6..c02b022781187 100644 --- a/test/examples/content_management/index.ts +++ b/test/examples/content_management/index.ts @@ -12,5 +12,6 @@ import { PluginFunctionalProviderContext } from '../../plugin_functional/service export default function ({ loadTestFile }: PluginFunctionalProviderContext) { describe('content management examples', function () { loadTestFile(require.resolve('./todo_app')); + loadTestFile(require.resolve('./msearch')); }); } diff --git a/test/examples/content_management/msearch.ts b/test/examples/content_management/msearch.ts new file mode 100644 index 0000000000000..0a583b455cd04 --- /dev/null +++ b/test/examples/content_management/msearch.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 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 { PluginFunctionalProviderContext } from '../../plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'home', 'header']); + const listingTable = getService('listingTable'); + + describe('MSearch demo', () => { + before(async () => { + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.addSampleDataSet('flights'); + }); + after(async () => { + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.removeSampleDataSet('flights'); + }); + + it('MSearch demo works', async () => { + const appId = 'contentManagementExamples'; + await PageObjects.common.navigateToApp(appId, { + path: 'msearch', + }); + + await listingTable.waitUntilTableIsLoaded(); + await listingTable.searchForItemWithName('Origin Time Delayed'); + + await testSubjects.existOrFail( + `cm-msearch-tableListingTitleLink-[Flights]-Origin-Time-Delayed` + ); + }); + }); +} diff --git a/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts b/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts index 7fbdc66313eba..51669b395b6f1 100644 --- a/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts +++ b/test/functional/apps/dashboard/group1/dashboard_unsaved_state.ts @@ -85,10 +85,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await validateQueryAndFilter(); }); - after(async () => { - // discard changes made in view mode - await PageObjects.dashboard.switchToEditMode(); - await PageObjects.dashboard.clickCancelOutOfEditMode(); + it('can discard changes', async () => { + await PageObjects.dashboard.clickDiscardChanges(); + await PageObjects.dashboard.waitForRenderComplete(); + + const query = await queryBar.getQueryString(); + expect(query).to.eql(''); + const filterCount = await filterBar.getFilterCount(); + expect(filterCount).to.eql(0); }); }); @@ -140,8 +144,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(currentPanelCount).to.eql(unsavedPanelCount); }); - it('resets to original panel count after discarding changes', async () => { + it('can discard changes', async () => { + unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + + await PageObjects.dashboard.clickDiscardChanges(); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(originalPanelCount); + }); + + it('resets to original panel count after switching to view mode and discarding changes', async () => { + await addPanels(); await PageObjects.header.waitUntilLoadingHasFinished(); + unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + await PageObjects.dashboard.clickCancelOutOfEditMode(); await PageObjects.header.waitUntilLoadingHasFinished(); const currentPanelCount = await PageObjects.dashboard.getPanelCount(); diff --git a/test/functional/apps/dashboard/group1/embeddable_rendering.ts b/test/functional/apps/dashboard/group1/embeddable_rendering.ts index d2da02ff1459d..f266b07a986f9 100644 --- a/test/functional/apps/dashboard/group1/embeddable_rendering.ts +++ b/test/functional/apps/dashboard/group1/embeddable_rendering.ts @@ -75,7 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const expectNoDataRenders = async () => { await pieChart.expectEmptyPieChart(); - await dashboardExpect.seriesElementCount(0); + await dashboardExpect.heatMapNoResults(); await dashboardExpect.dataTableNoResult(); await dashboardExpect.savedSearchNoResult(); await dashboardExpect.inputControlItemCount(5); diff --git a/test/functional/apps/dashboard/group2/dashboard_filtering.ts b/test/functional/apps/dashboard/group2/dashboard_filtering.ts index 68b80c0168e2d..1fb70ef508c2b 100644 --- a/test/functional/apps/dashboard/group2/dashboard_filtering.ts +++ b/test/functional/apps/dashboard/group2/dashboard_filtering.ts @@ -91,7 +91,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('area, bar and heatmap charts filtered', async () => { - await dashboardExpect.seriesElementCount(0); + await dashboardExpect.heatMapNoResults(); }); it('data tables are filtered', async () => { @@ -156,7 +156,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('area, bar and heatmap charts filtered', async () => { - await dashboardExpect.seriesElementCount(0); + await dashboardExpect.heatMapNoResults(); }); it('data tables are filtered', async () => { @@ -212,7 +212,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('area, bar and heatmap charts', async () => { - await dashboardExpect.seriesElementCount(2); + await dashboardExpect.heatmapXAxisBuckets(11); }); it('data tables', async () => { diff --git a/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts b/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts index 1397de3712bb8..bda198fc16e8b 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list/options_list_dashboard_interaction.ts @@ -257,21 +257,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.clearUnsavedChanges(); }); - it('changes to selections can be discarded', async () => { - await dashboardControls.optionsListOpenPopover(controlId); - await dashboardControls.optionsListPopoverSelectOption('bark'); - await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); - let selections = await dashboardControls.optionsListGetSelectionsString(controlId); - expect(selections).to.equal('hiss, grr, bark'); + describe('discarding changes', async () => { + describe('changes can be discarded', async () => { + let selections = ''; + + beforeEach(async () => { + await dashboardControls.optionsListOpenPopover(controlId); + await dashboardControls.optionsListPopoverSelectOption('bark'); + await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); + selections = await dashboardControls.optionsListGetSelectionsString(controlId); + expect(selections).to.equal('hiss, grr, bark'); + }); - await dashboard.clickCancelOutOfEditMode(); - selections = await dashboardControls.optionsListGetSelectionsString(controlId); - expect(selections).to.equal('hiss, grr'); - }); + afterEach(async () => { + selections = await dashboardControls.optionsListGetSelectionsString(controlId); + expect(selections).to.equal('hiss, grr'); + }); - it('dashboard does not load with unsaved changes when changes are discarded', async () => { - await dashboard.switchToEditMode(); - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + it('by clicking the discard changes button', async () => { + await dashboard.clickDiscardChanges(); + }); + + it('by switching to view mode', async () => { + await dashboard.clickCancelOutOfEditMode(); + }); + }); + + it('dashboard does not load with unsaved changes when changes are discarded', async () => { + await dashboard.switchToEditMode(); + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); }); }); diff --git a/test/functional/apps/discover/group2/_data_grid_pagination.ts b/test/functional/apps/discover/group2/_data_grid_pagination.ts index c79bf291aa2e4..7da02308c73be 100644 --- a/test/functional/apps/discover/group2/_data_grid_pagination.ts +++ b/test/functional/apps/discover/group2/_data_grid_pagination.ts @@ -64,7 +64,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // footer is shown now await retry.try(async function () { await testSubjects.existOrFail('discoverTableFooter'); - await testSubjects.existOrFail('discoverTableSampleSizeSettingsLink'); }); }); diff --git a/test/functional/apps/discover/group3/_drag_drop.ts b/test/functional/apps/discover/group3/_drag_drop.ts new file mode 100644 index 0000000000000..49fa1ea16f5eb --- /dev/null +++ b/test/functional/apps/discover/group3/_drag_drop.ts @@ -0,0 +1,71 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'header']); + + describe('discover drag and drop', function describeIndexTests() { + before(async function () { + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + beforeEach(async () => { + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + await kibanaServer.uiSettings.replace({ + defaultIndex: 'logstash-*', + }); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + }); + + afterEach(async () => { + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.replace({}); + await PageObjects.discover.cleanSidebarLocalStorage(); + }); + + describe('should add fields as columns via drag and drop', function () { + it('should support dragging and dropping a field onto the grid', async function () { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSidebarHasLoaded(); + + expect(await PageObjects.discover.getSidebarAriaDescription()).to.be( + '53 available fields. 0 empty fields. 3 meta fields.' + ); + expect((await PageObjects.discover.getColumnHeaders()).join(', ')).to.be( + '@timestamp, Document' + ); + + await PageObjects.discover.dragFieldToTable('extension'); + + expect((await PageObjects.discover.getColumnHeaders()).join(', ')).to.be( + '@timestamp, extension' + ); + + await PageObjects.discover.dragFieldWithKeyboardToTable('@message'); + + expect((await PageObjects.discover.getColumnHeaders()).join(', ')).to.be( + '@timestamp, extension, @message' + ); + + await PageObjects.discover.waitUntilSearchingHasFinished(); + + expect( + (await PageObjects.discover.getSidebarSectionFieldNames('selected')).join(', ') + ).to.be('extension, @message'); + }); + }); + }); +} diff --git a/test/functional/apps/discover/group3/index.ts b/test/functional/apps/discover/group3/index.ts index acaa8424c5284..a6481a8e7b82b 100644 --- a/test/functional/apps/discover/group3/index.ts +++ b/test/functional/apps/discover/group3/index.ts @@ -20,6 +20,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); }); + loadTestFile(require.resolve('./_drag_drop')); loadTestFile(require.resolve('./_sidebar')); }); } diff --git a/test/functional/apps/management/_handle_version_conflict.ts b/test/functional/apps/management/_handle_version_conflict.ts index 2f65f966c5596..c2f9f3f7e6393 100644 --- a/test/functional/apps/management/_handle_version_conflict.ts +++ b/test/functional/apps/management/_handle_version_conflict.ts @@ -16,6 +16,7 @@ */ import expect from '@kbn/expect'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { @@ -28,7 +29,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'header']); const log = getService('log'); - describe('index version conflict', function describeIndexTests() { + describe('FOO index version conflict', function describeIndexTests() { before(async function () { await browser.setWindowSize(1200, 800); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); @@ -48,7 +49,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.settings.setScriptedFieldScript(`doc['bytes'].value`); const response = await es.update( { - index: '.kibana', + index: ANALYTICS_SAVED_OBJECT_INDEX, id: 'index-pattern:logstash-*', body: { doc: { 'index-pattern': { fieldFormatMap: '{"geo.src":{"id":"number"}}' } }, @@ -82,7 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.settings.setFieldFormat('url'); const response = await es.update( { - index: '.kibana', + index: ANALYTICS_SAVED_OBJECT_INDEX, id: 'index-pattern:logstash-*', body: { doc: { 'index-pattern': { fieldFormatMap: '{"geo.dest":{"id":"number"}}' } }, diff --git a/test/functional/apps/visualize/group2/index.ts b/test/functional/apps/visualize/group2/index.ts index 5a83044deee17..7ece37d611ed2 100644 --- a/test/functional/apps/visualize/group2/index.ts +++ b/test/functional/apps/visualize/group2/index.ts @@ -21,6 +21,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.uiSettings.update({ 'histogram:maxBars': 100, + 'visualization:visualize:legacyHeatmapChartsLibrary': true, }); await browser.refresh(); @@ -31,6 +32,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { after(async () => { await kibanaServer.uiSettings.update({ 'histogram:maxBars': 1000, + 'visualization:visualize:legacyHeatmapChartsLibrary': false, }); await browser.refresh(); }); diff --git a/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts b/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts index d8e35e8f12bfc..a2118e55ecd52 100644 --- a/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts +++ b/test/functional/apps/visualize/replaced_vislib_chart_types/index.ts @@ -27,7 +27,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { before(async () => { await kibanaServer.uiSettings.update({ - 'visualization:visualize:legacyHeatmapChartsLibrary': false, 'histogram:maxBars': 100, }); await browser.refresh(); @@ -35,7 +34,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { after(async () => { await kibanaServer.uiSettings.update({ - 'visualization:visualize:legacyHeatmapChartsLibrary': true, 'histogram:maxBars': 1000, }); await browser.refresh(); diff --git a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json index 47bd39ebf8bcf..33968127fe42c 100644 --- a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json +++ b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json @@ -36,6 +36,117 @@ "url": "c7f66a0df8b1b52f17c28c4adb111105", "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", "visualization": "f819cf6636b75c9e76ba733a0c6ef355" + }, + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] } }, "dynamic": "strict", @@ -425,9 +536,11 @@ "refresh_interval": "1s", "routing_partition_size": "1", "mapping": { - "total_fields": { "limit": 1500 } + "total_fields": { + "limit": 1500 + } } } } } -} \ No newline at end of file +} diff --git a/test/functional/fixtures/es_archiver/huge_fields/data.json.gz b/test/functional/fixtures/es_archiver/huge_fields/data.json.gz index 1ce42c64c53a34db899fedd40bc012fa2e6769ba..794ee4f944c846e97d9514075a8876fb20148634 100644 GIT binary patch delta 1882 zcmZWoc~nzZ9&RMb02L_EVG%XpD1%~xE1AN8QmGvVFsFip6(o#0AP7W2NLb>lOF;>O z@*Fv*G>XLnWlIAINqi*HQjAC;MNv{>$i#wxA$dSHmV`{G2VzhEy1(-~_dDNqZ|@g2 zk}qs*F*IA-H@5pvF>MdFh2f#>uaA6KJkGN}SM0aL2XNZ$yOxKzeEq6ZlEUa08Ww&7 zrmtsYd~9?qKXruD1T{4YhL7u1I*%W>d{9*wlTQH4+XB|iKD8WJq|{Dh|Jx8e*Y*u4 zv}_!L@|VErHmoOFiW@;k%m-bG^u}G{B45oyO}6o_twj!%?H+c81dO1Tej>XVbmn-2 zHEj;4mS>x<`GTf43~1l(h*EwZap@Z91NnjbAtZVzi1zg8DNxaY0iD_%0@>w_dt&ED zAKh}qk;`=65Va*(nxV4Sr78$chHMH&l< zZq=dcu@Z167mG6eoLUkHW_4gu7|QSi2k;?pJL-k`ww}Cl8Q-ci)vM(7cu5|MA68%6 zzo95(A@i?Oi4@{N;WY!P5l;I9`OC=BKU*YXk(|`_w*jjTzYRFZ@!Q`Ayy|v#^7-|a zO>qMBtJT%=(8~XzL@9r-a^~bpl4$maRhPLQpf?-yTL)OauVU|N2a&3X(-qbZo|d}& zYqbLu`)1wcJuBtmvGi?<*ifjQK-h=3CwmA68oCBH{5NDf37%5t%MT0d3~&dD{5x|k zTm6rLOcoaH_LIgU6=brvSOP`aa>mci1ped6kcHUIYksP<_g0lZ(I(4t`Spb*tjWsw z1k;)1{Q(fwAtS<)uZ&I*)l{WB&{Kiix4yW<@)v@D%(`>^>hMdR8??79ZNdvTAf;x?ah! zYTP`Hyx?VZhX_GTSdr%m)6hFe*p)(BK?3Z1W~6EH)f>>wk9=Ewb)i2l zof?`cQ*G!b;>`;uYN~ChS&#b;2#)^m^%_#Z<3nc=yy(aL8i{XrVp5uGOu*k*J$Kz} zenIxVUzuJxirN$^>zxPFj@lD@O=9+U~xZi<4j)yG{H=+IF5|l&Bc{Aeje-I zGZcKn;Pua4vZH?V!_yot)>s;LHX7~r!pdWO4$R`+>A!-1F$xR*7P z=%G$0ib6AuHs;g(Km^V z)<=h{qeiQ*OFen`o$TE)q>ITZr$@rO`(r!p3h7Kvd;FnHd~Qq5P^Nv{_*q$OUH^aX zZw;Y8%ZZ5(;*8Z%&XtRzb0-o?pfP#j5?Kiu1hCs#7y&om1{Y*&RkELe`mmA zAb)Ode2l;l6c~TioCFGHo{wm0^&(=ZVp0eFDYU3$A4Bg%AjtXgx(X6TeV1dFCc^kf U0di?T$1s3M8BM46Jh6H2|GY=fw*UYD delta 1952 zcmZvbc~nzp8pZ_^77?@+iGYy+5~o0txCY0H6ZsmnBA|S3u0(>UK{7U+932)8|m~CShE|b zW$prI>Y>n(=u-}v0EU4uTw3oRIF)_N-AVlN_vW$g+QJA@<-*w}ilhLnlgE<%c82w?Ighk$6KkG_-9FT%~oqj|zJN ze>it-bIxa>X5U%1MlK(4jbGP38!;@w&Qhq9S;u|emFl~ilMp|Y1{|tKLpBv8UidHH z2FQ(4Yte^zdc{Q=)+qA{kO`HsT3w@PwGz#oj)O% z8xt%?8T0-DRY|r6Odf9oHb1sp{vfe}s1=IhwG_SuU;ZD8cOI`RoWH?Wk*!z2-V+L2 zWD9ZYo@>^u|8CIXP~-Zc^l)It;eB`iRPB7+wiXUbyok5-0Gyc!L$?+y^?ez9IM1l} zp&!qVcP|q4m*x~X-t^ljz3^iAO?O8jos^)Oc@kGf=<6(%w~1{6wZ6B55a{#<$jB99 z$KBPwY@z1!YihE9`>X&0vkW^&9DAg0Y^+1Drm=5&W&Km+=WjoZG#>7vHb#Qdy>cHN zdXE(8i9FxC7Rih_`0x&WpRq4!NJ*57m!W19l#R&-8)@9mGM4 zXGF8wTV;O_CN-+aoTv3|atcKchJ*J&xUtYN@X*?aW66g1KYMovoMSk^aGY|+wFx5( zQ_Z8QZL+q*jq{*xJ9i-lKMeYuZ+XjmHKjaKDlDn*|43-a z2*p&fETqwuMZeIB!YIF+j#c$>PD&TbVU><7zj&#p-(;%$bzwW{fI5|{U=pS}r2_x> z<@bkDjL0ASSnZnIdUns^rq1+>lW3n&v1;8&bv})LXL)?i~-JLK4so1gKZxgjQ zPKEJ9f$>D;#4BDqGpZ^~`Bz38I<3M!6o&8jEYFO%gzUp_@ydVWlAPCj^^{jvIh)o_ zkE#z%2xJ5$4onpDHfNP2h34Y>Jkc+HEwh2e@)OdDb8$IKUQH~aK94J0pmg=fsa)i% zL=(c^n5J{sp~#x1dcNvrcuIuMwfa~MB~5@9P}xLBqVy&61d}#7bbIJ%f_$I6lwJQ& zl^vRb*Liq%L_gpKq$fIvGIX@=2Hu^Cg6q^RHGb=LNEZ@v?pVA0o+v+e=%+I5A;#W) zasN0U?JmjSbiWk#qhPH3#sM}Fd$ZzdL#%u7#2)dFh23x2WvBw-mEZ%uZLdpw6RMsb zoKx&wN>PfVrD}_Yo>bDA9=v|XE!D+02W_W2ra!BD&~EI?k&h ze}a=j#r}{2)5eylVkgC?r^23{+51V!`X`i*i0 zU*|I*{Z`E=<^@$9h@(&=FUekb2pW_8inj{4qW={-Z7T85gtaHTHJNZ+|FDjM4^BfA z_EhuaETyhRcb~UN8b>gIWx0;hS^Rs>EAzx9{mK;`69n3N38EGl+@3*((mg*l5F zuxnEsFNwM!7Z47BI;@m?Jvn)CS|;^BNL^B9If0-?_WdTsu^x;+Pr+Iu>jYQ=@A}_B zJ=UMB*7kVl`YX2TrB?nNC5x3spw8;&L%_ux{P&w^;h<7g(dLiisY { + await this.expectDiscardChangesButtonEnabled(); + this.log.debug('clickDiscardChanges'); + await this.testSubjects.click('dashboardDiscardChangesMenuItem'); + }); + await this.common.expectConfirmModalOpenState(true); + if (accept) { + await this.common.clickConfirmOnModal(); + } + } + public async clickQuickSave() { await this.retry.try(async () => { await this.expectQuickSaveButtonEnabled(); @@ -734,6 +746,15 @@ export class DashboardPageObject extends FtrService { await this.testSubjects.existOrFail('dashboardQuickSaveMenuItem'); } + public async expectDiscardChangesButtonEnabled() { + this.log.debug('expectDiscardChangesButtonEnabled'); + const quickSaveButton = await this.testSubjects.find('dashboardDiscardChangesMenuItem'); + const isDisabled = await quickSaveButton.getAttribute('disabled'); + if (isDisabled) { + throw new Error('Discard changes button disabled'); + } + } + public async expectQuickSaveButtonEnabled() { this.log.debug('expectQuickSaveButtonEnabled'); const quickSaveButton = await this.testSubjects.find('dashboardQuickSaveMenuItem'); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 0187825a03130..7873e70494569 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -394,7 +394,7 @@ export class DiscoverPageObject extends FtrService { public async getAllFieldNames() { const sidebar = await this.testSubjects.find('discover-sidebar'); const $ = await sidebar.parseDomContent(); - return $('.dscSidebarField__name') + return $('.kbnFieldButton__name') .toArray() .map((field) => $(field).text()); } @@ -868,4 +868,51 @@ export class DiscoverPageObject extends FtrService { await this.fieldEditor.save(); await this.header.waitUntilLoadingHasFinished(); } + + private async waitForDropToFinish() { + await this.retry.try(async () => { + const exists = await this.find.existsByCssSelector('.domDragDrop-isActiveGroup'); + if (exists) { + throw new Error('UI still in drag/drop mode'); + } + }); + await this.header.waitUntilLoadingHasFinished(); + await this.waitUntilSearchingHasFinished(); + } + + /** + * Drags field to add as a column + * + * @param fieldName + * */ + public async dragFieldToTable(fieldName: string) { + await this.waitUntilSidebarHasLoaded(); + + const from = `dscFieldListPanelField-${fieldName}`; + await this.find.existsByCssSelector(from); + await this.browser.html5DragAndDrop( + this.testSubjects.getCssSelector(from), + this.testSubjects.getCssSelector('dscMainContent') + ); + await this.waitForDropToFinish(); + } + + /** + * Drags field with keyboard actions to add as a column + * + * @param fieldName + * */ + public async dragFieldWithKeyboardToTable(fieldName: string) { + const field = await this.find.byCssSelector( + `[data-test-subj="domDragDrop_draggable-${fieldName}"] [data-test-subj="domDragDrop-keyboardHandler"]` + ); + await field.focus(); + await this.retry.try(async () => { + await this.browser.pressKeys(this.browser.keys.ENTER); + await this.testSubjects.exists('.domDragDrop-isDropTarget'); // checks if we're in dnd mode and there's any drop target active + }); + await this.browser.pressKeys(this.browser.keys.RIGHT); + await this.browser.pressKeys(this.browser.keys.ENTER); + await this.waitForDropToFinish(); + } } diff --git a/test/functional/services/common/screenshots.ts b/test/functional/services/common/screenshots.ts index 5fca603407760..191fc5202c417 100644 --- a/test/functional/services/common/screenshots.ts +++ b/test/functional/services/common/screenshots.ts @@ -9,6 +9,7 @@ import { resolve, dirname } from 'path'; import { writeFile, readFileSync, mkdir } from 'fs'; import { promisify } from 'util'; +import { NoSuchSessionError } from 'selenium-webdriver/lib/error'; import del from 'del'; @@ -92,6 +93,12 @@ export class ScreenshotsService extends FtrService { } catch (err) { this.log.error('SCREENSHOT FAILED'); this.log.error(err); + if (err instanceof NoSuchSessionError) { + // https://developer.mozilla.org/en-US/docs/Web/WebDriver/Errors/InvalidSessionID + this.log.error( + `WebDriver session is no longer valid.\nProbably Chrome process crashed when it tried to use more memory than what was available.` + ); + } } } } diff --git a/test/functional/services/dashboard/expectations.ts b/test/functional/services/dashboard/expectations.ts index 266022d1a8157..677e16914f3de 100644 --- a/test/functional/services/dashboard/expectations.ts +++ b/test/functional/services/dashboard/expectations.ts @@ -16,6 +16,7 @@ export class DashboardExpectService extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly find = this.ctx.getService('find'); private readonly filterBar = this.ctx.getService('filterBar'); + private readonly elasticChart = this.ctx.getService('elasticChart'); private readonly dashboard = this.ctx.getPageObject('dashboard'); private readonly visChart = this.ctx.getPageObject('visChart'); @@ -304,12 +305,22 @@ export class DashboardExpectService extends FtrService { }); } + // heatmap data async seriesElementCount(expectedCount: number) { this.log.debug(`DashboardExpect.seriesElementCount(${expectedCount})`); - await this.retry.try(async () => { - const seriesElements = await this.find.allByCssSelector('.series', this.findTimeout); - expect(seriesElements.length).to.be(expectedCount); - }); + const heatmapData = await this.elasticChart.getChartDebugData('heatmapChart'); + this.log.debug(heatmapData.axes?.y[0]); + expect(heatmapData.axes?.y[0].labels.length).to.be(expectedCount); + } + + async heatmapXAxisBuckets(expectedCount: number) { + this.log.debug(`DashboardExpect.heatmapXAxisBuckets(${expectedCount})`); + const heatmapData = await this.elasticChart.getChartDebugData('heatmapChart'); + expect(heatmapData.axes?.x[0].labels.length).to.be(expectedCount); + } + + async heatMapNoResults() { + await this.testSubjects.find('heatmapChart>emptyPlaceholder'); } // legacy controls visualization diff --git a/test/functional/services/field_editor.ts b/test/functional/services/field_editor.ts index 68fc3707ca3a9..439eb289ec95f 100644 --- a/test/functional/services/field_editor.ts +++ b/test/functional/services/field_editor.ts @@ -12,6 +12,7 @@ export class FieldEditorService extends FtrService { private readonly browser = this.ctx.getService('browser'); private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly retry = this.ctx.getService('retry'); + private readonly find = this.ctx.getService('find'); public async setName(name: string, clearFirst = false, typeCharByChar = false) { await this.testSubjects.setValue('nameField > input', name, { @@ -50,6 +51,47 @@ export class FieldEditorService extends FtrService { await this.testSubjects.click('fieldSaveButton'); } + async setUrlFieldFormat(template: string) { + const urlTemplateField = await this.find.byCssSelector( + 'input[data-test-subj="urlEditorUrlTemplate"]' + ); + await urlTemplateField.type(template); + } + + public async setStaticLookupFormat(oldValue: string, newValue: string) { + await this.testSubjects.click('staticLookupEditorAddEntry'); + await this.testSubjects.setValue('~staticLookupEditorKey', oldValue); + await this.testSubjects.setValue('~staticLookupEditorValue', newValue); + } + + public async setColorFormat(value: string, color: string, backgroundColor?: string) { + await this.testSubjects.click('colorEditorAddColor'); + await this.testSubjects.setValue('~colorEditorKeyPattern', value); + await this.testSubjects.setValue('~colorEditorColorPicker', color); + if (backgroundColor) { + await this.testSubjects.setValue('~colorEditorBackgroundPicker', backgroundColor); + } + } + + public async setStringFormat(transform: string) { + await this.testSubjects.selectValue('stringEditorTransform', transform); + } + + public async setTruncateFormatLength(length: string) { + await this.testSubjects.setValue('truncateEditorLength', length); + } + + public async setFieldFormat(format: string) { + await this.find.clickByCssSelector( + 'select[data-test-subj="editorSelectedFormatId"] > option[value="' + format + '"]' + ); + } + + public async setFormat(format: string) { + await this.testSubjects.setEuiSwitch('formatRow > toggle', 'check'); + await this.setFieldFormat(format); + } + public async confirmSave() { await this.retry.try(async () => { await this.testSubjects.setValue('saveModalConfirmText', 'change'); diff --git a/test/functional/services/inspector.ts b/test/functional/services/inspector.ts index c3f357ea3875b..cee65bf1c4b52 100644 --- a/test/functional/services/inspector.ts +++ b/test/functional/services/inspector.ts @@ -238,6 +238,19 @@ export class InspectorService extends FtrService { }); } + public async getTableDataWithId(tableTestSubj: string): Promise { + const chooserDataTestId = 'inspectorTableChooser'; + if (!(await this.testSubjects.exists(chooserDataTestId))) { + return []; + } + + return await this.retry.try(async () => { + await this.testSubjects.click(chooserDataTestId); + await this.testSubjects.click(tableTestSubj); + return this.getTableData(); + }); + } + /** * Returns the selected option value from combobox */ diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 6106f430dbafd..7d624e7d1c94f 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -201,6 +201,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.graph.savePolicy (alternatives)', 'xpack.ilm.ui.enabled (boolean)', 'xpack.index_management.ui.enabled (boolean)', + 'xpack.infra.logs.app_target (alternatives)', 'xpack.infra.sources.default.fields.message (array)', 'xpack.license_management.ui.enabled (boolean)', 'xpack.maps.preserveDrawingBuffer (boolean)', @@ -235,6 +236,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.observability.unsafe.alertDetails.metrics.enabled (boolean)', 'xpack.observability.unsafe.alertDetails.logs.enabled (boolean)', 'xpack.observability.unsafe.alertDetails.uptime.enabled (boolean)', + 'xpack.observability_onboarding.ui.enabled (boolean)', ]; // We don't assert that actualExposedConfigKeys and expectedExposedConfigKeys are equal, because test failure messages with large // arrays are hard to grok. Instead, we take the difference between the two arrays and assert them separately, that way it's diff --git a/test/tsconfig.json b/test/tsconfig.json index cd738c09ff2ab..098788a8bc0e5 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -65,6 +65,7 @@ "@kbn/safer-lodash-set", "@kbn/utility-types", "@kbn/dev-proc-runner", - "@kbn/enterprise-search-plugin" + "@kbn/enterprise-search-plugin", + "@kbn/core-saved-objects-server" ] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 6bede7391b146..b6f66fe1f806e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -170,6 +170,8 @@ "@kbn/content-management-plugin/*": ["src/plugins/content_management/*"], "@kbn/content-management-table-list": ["packages/content-management/table_list"], "@kbn/content-management-table-list/*": ["packages/content-management/table_list/*"], + "@kbn/content-management-utils": ["packages/kbn-content-management-utils"], + "@kbn/content-management-utils/*": ["packages/kbn-content-management-utils/*"], "@kbn/controls-example-plugin": ["examples/controls_example"], "@kbn/controls-example-plugin/*": ["examples/controls_example/*"], "@kbn/controls-plugin": ["src/plugins/controls"], @@ -564,6 +566,12 @@ "@kbn/core-usage-data-server-internal/*": ["packages/core/usage-data/core-usage-data-server-internal/*"], "@kbn/core-usage-data-server-mocks": ["packages/core/usage-data/core-usage-data-server-mocks"], "@kbn/core-usage-data-server-mocks/*": ["packages/core/usage-data/core-usage-data-server-mocks/*"], + "@kbn/core-user-settings-server": ["packages/core/user-settings/core-user-settings-server"], + "@kbn/core-user-settings-server/*": ["packages/core/user-settings/core-user-settings-server/*"], + "@kbn/core-user-settings-server-internal": ["packages/core/user-settings/core-user-settings-server-internal"], + "@kbn/core-user-settings-server-internal/*": ["packages/core/user-settings/core-user-settings-server-internal/*"], + "@kbn/core-user-settings-server-mocks": ["packages/core/user-settings/core-user-settings-server-mocks"], + "@kbn/core-user-settings-server-mocks/*": ["packages/core/user-settings/core-user-settings-server-mocks/*"], "@kbn/cross-cluster-replication-plugin": ["x-pack/plugins/cross_cluster_replication"], "@kbn/cross-cluster-replication-plugin/*": ["x-pack/plugins/cross_cluster_replication/*"], "@kbn/crypto": ["packages/kbn-crypto"], @@ -904,6 +912,8 @@ "@kbn/ml-agg-utils/*": ["x-pack/packages/ml/agg_utils/*"], "@kbn/ml-date-picker": ["x-pack/packages/ml/date_picker"], "@kbn/ml-date-picker/*": ["x-pack/packages/ml/date_picker/*"], + "@kbn/ml-error-utils": ["x-pack/packages/ml/error_utils"], + "@kbn/ml-error-utils/*": ["x-pack/packages/ml/error_utils/*"], "@kbn/ml-is-defined": ["x-pack/packages/ml/is_defined"], "@kbn/ml-is-defined/*": ["x-pack/packages/ml/is_defined/*"], "@kbn/ml-is-populated-object": ["x-pack/packages/ml/is_populated_object"], @@ -948,6 +958,8 @@ "@kbn/observability-alert-details/*": ["x-pack/packages/observability/alert_details/*"], "@kbn/observability-fixtures-plugin": ["x-pack/test/cases_api_integration/common/plugins/observability"], "@kbn/observability-fixtures-plugin/*": ["x-pack/test/cases_api_integration/common/plugins/observability/*"], + "@kbn/observability-onboarding-plugin": ["x-pack/plugins/observability_onboarding"], + "@kbn/observability-onboarding-plugin/*": ["x-pack/plugins/observability_onboarding/*"], "@kbn/observability-plugin": ["x-pack/plugins/observability"], "@kbn/observability-plugin/*": ["x-pack/plugins/observability/*"], "@kbn/observability-shared-plugin": ["x-pack/plugins/observability_shared"], @@ -1360,6 +1372,8 @@ "@kbn/url-drilldown-plugin/*": ["x-pack/plugins/drilldowns/url_drilldown/*"], "@kbn/url-forwarding-plugin": ["src/plugins/url_forwarding"], "@kbn/url-forwarding-plugin/*": ["src/plugins/url_forwarding/*"], + "@kbn/url-state": ["packages/kbn-url-state"], + "@kbn/url-state/*": ["packages/kbn-url-state/*"], "@kbn/usage-collection-plugin": ["src/plugins/usage_collection"], "@kbn/usage-collection-plugin/*": ["src/plugins/usage_collection/*"], "@kbn/usage-collection-test-plugin": ["test/plugin_functional/plugins/usage_collection"], diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index ff4718c2c7f3a..8071085e9cc8b 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -50,6 +50,7 @@ "xpack.monitoring": ["plugins/monitoring"], "xpack.observability": "plugins/observability", "xpack.observabilityShared": "plugins/observability_shared", + "xpack.observability_onboarding": "plugins/observability_onboarding", "xpack.osquery": ["plugins/osquery"], "xpack.painlessLab": "plugins/painless_lab", "xpack.profiling": ["plugins/profiling"], diff --git a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts index 9b653fc08576e..3c048702e4723 100644 --- a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts +++ b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts @@ -8,6 +8,7 @@ import { v4 as uuidv4 } from 'uuid'; import { range } from 'lodash'; import { RuleType } from '@kbn/alerting-plugin/server'; +import { schema } from '@kbn/config-schema'; import { DEFAULT_INSTANCES_TO_GENERATE, ALERTING_EXAMPLE_APP_ID, @@ -78,4 +79,16 @@ export const alertType: RuleType< }; }, producer: ALERTING_EXAMPLE_APP_ID, + validate: { + params: schema.object({ + instances: schema.maybe(schema.number()), + thresholds: schema.maybe( + schema.object({ + small: schema.maybe(schema.number()), + medium: schema.maybe(schema.number()), + large: schema.maybe(schema.number()), + }) + ), + }), + }, }; diff --git a/x-pack/examples/alerting_example/server/alert_types/astros.ts b/x-pack/examples/alerting_example/server/alert_types/astros.ts index c73ec8ecbc8bf..39cf751702dd7 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -7,6 +7,7 @@ import axios from 'axios'; import { RuleType } from '@kbn/alerting-plugin/server'; +import { schema } from '@kbn/config-schema'; import { Operator, Craft, ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; interface PeopleInSpace { @@ -84,4 +85,11 @@ export const alertType: RuleType< getViewInAppRelativeUrl({ rule }) { return `/app/${ALERTING_EXAMPLE_APP_ID}/astros/${rule.id}`; }, + validate: { + params: schema.object({ + outerSpaceCapacity: schema.number(), + craft: schema.string(), + op: schema.string(), + }), + }, }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/ecs_allowed_values/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/ecs_allowed_values/index.test.tsx new file mode 100644 index 0000000000000..07af65877da01 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/ecs_allowed_values/index.test.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 { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { mockAllowedValues } from '../../mock/allowed_values/mock_allowed_values'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { EcsAllowedValues } from '.'; + +describe('EcsAllowedValues', () => { + describe('when `allowedValues` exists', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it renders the allowed values', () => { + expect(screen.getByTestId('ecsAllowedValues')).toHaveTextContent( + mockAllowedValues.map(({ name }) => `${name}`).join('') + ); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('ecsAllowedValuesEmpty')).not.toBeInTheDocument(); + }); + }); + + describe('when `allowedValues` is undefined', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it does NOT render the allowed values', () => { + expect(screen.queryByTestId('ecsAllowedValues')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('ecsAllowedValuesEmpty')).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_common_table_columns/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_common_table_columns/index.test.tsx similarity index 87% rename from x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_common_table_columns/helpers.test.tsx rename to x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_common_table_columns/index.test.tsx index 75a34735abff1..e3e68152f4c06 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_common_table_columns/helpers.test.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_common_table_columns/index.test.tsx @@ -10,57 +10,57 @@ import { omit } from 'lodash/fp'; import React from 'react'; import { SAME_FAMILY } from '../../data_quality_panel/same_family/translations'; -import { TestProviders } from '../../mock/test_providers'; import { eventCategory, eventCategoryWithUnallowedValues, -} from '../../mock/enriched_field_metadata'; +} from '../../mock/enriched_field_metadata/mock_enriched_field_metadata'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { + DOCUMENT_VALUES_ACTUAL, + ECS_DESCRIPTION, + ECS_MAPPING_TYPE_EXPECTED, + ECS_VALUES_EXPECTED, + FIELD, + INDEX_MAPPING_TYPE_ACTUAL, +} from '../translations'; import { EnrichedFieldMetadata } from '../../types'; import { EMPTY_PLACEHOLDER, getCommonTableColumns } from '.'; describe('getCommonTableColumns', () => { test('it returns the expected column configuration', () => { - const columns = getCommonTableColumns().map((x) => omit('render', x)); - - expect(columns).toEqual([ - { - field: 'indexFieldName', - name: 'Field', - sortable: true, - truncateText: false, - width: '20%', - }, + expect(getCommonTableColumns().map((x) => omit('render', x))).toEqual([ + { field: 'indexFieldName', name: FIELD, sortable: true, truncateText: false, width: '20%' }, { field: 'type', - name: 'ECS mapping type (expected)', + name: ECS_MAPPING_TYPE_EXPECTED, sortable: true, truncateText: false, width: '15%', }, { field: 'indexFieldType', - name: 'Index mapping type (actual)', + name: INDEX_MAPPING_TYPE_ACTUAL, sortable: true, truncateText: false, width: '15%', }, { field: 'allowed_values', - name: 'ECS values (expected)', + name: ECS_VALUES_EXPECTED, sortable: false, truncateText: false, width: '15%', }, { field: 'indexInvalidValues', - name: 'Document values (actual)', + name: DOCUMENT_VALUES_ACTUAL, sortable: false, truncateText: false, width: '15%', }, { field: 'description', - name: 'ECS description', + name: ECS_DESCRIPTION, sortable: false, truncateText: false, width: '20%', @@ -141,7 +141,7 @@ describe('getCommonTableColumns', () => { const withTypeMismatchDifferentFamily: EnrichedFieldMetadata = { ...eventCategory, // `event.category` is a `keyword` per the ECS spec indexFieldType, // this index has a mapping of `text` instead of `keyword` - isInSameFamily: false, // `text` and `keyword` are not in the same family + isInSameFamily: false, // `text` and `wildcard` are not in the same family }; render( @@ -159,29 +159,18 @@ describe('getCommonTableColumns', () => { }); describe('when the index field matches the ECS type', () => { - const indexFieldType = 'keyword'; - test('it renders the expected type with success styling', () => { const columns = getCommonTableColumns(); const indexFieldTypeColumnRender = columns[2].render; - const withTypeMismatchDifferentFamily: EnrichedFieldMetadata = { - ...eventCategory, // `event.category` is a `keyword` per the ECS spec - indexFieldType, // exactly matches the ECS spec - isInSameFamily: true, // `keyword` is a member of the `keyword` family - }; - render( {indexFieldTypeColumnRender != null && - indexFieldTypeColumnRender( - withTypeMismatchDifferentFamily.indexFieldType, - withTypeMismatchDifferentFamily - )} + indexFieldTypeColumnRender(eventCategory.indexFieldType, eventCategory)} ); - expect(screen.getByTestId('codeSuccess')).toHaveTextContent(indexFieldType); + expect(screen.getByTestId('codeSuccess')).toHaveTextContent(eventCategory.indexFieldType); }); }); }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_incompatible_mappings_table_columns/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_incompatible_mappings_table_columns/index.test.tsx similarity index 97% rename from x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_incompatible_mappings_table_columns/helpers.test.tsx rename to x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_incompatible_mappings_table_columns/index.test.tsx index 160b300e08934..132e8b2fc302b 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_incompatible_mappings_table_columns/helpers.test.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/get_incompatible_mappings_table_columns/index.test.tsx @@ -10,8 +10,8 @@ import { omit } from 'lodash/fp'; import React from 'react'; import { SAME_FAMILY } from '../../data_quality_panel/same_family/translations'; -import { TestProviders } from '../../mock/test_providers'; -import { eventCategory } from '../../mock/enriched_field_metadata'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { eventCategory } from '../../mock/enriched_field_metadata/mock_enriched_field_metadata'; import { EnrichedFieldMetadata } from '../../types'; import { EMPTY_PLACEHOLDER, getIncompatibleMappingsTableColumns } from '.'; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.test.tsx new file mode 100644 index 0000000000000..7c72289290942 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.test.tsx @@ -0,0 +1,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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import { omit } from 'lodash/fp'; +import React from 'react'; + +import { + EMPTY_PLACEHOLDER, + getCustomTableColumns, + getEcsCompliantTableColumns, + getIncompatibleValuesTableColumns, +} from './helpers'; +import { + eventCategory, + eventCategoryWithUnallowedValues, + someField, +} from '../mock/enriched_field_metadata/mock_enriched_field_metadata'; +import { TestProviders } from '../mock/test_providers/test_providers'; + +describe('helpers', () => { + describe('getCustomTableColumns', () => { + test('it returns the expected columns', () => { + expect(getCustomTableColumns().map((x) => omit('render', x))).toEqual([ + { + field: 'indexFieldName', + name: 'Field', + sortable: true, + truncateText: false, + width: '50%', + }, + { + field: 'indexFieldType', + name: 'Index mapping type', + sortable: true, + truncateText: false, + width: '50%', + }, + ]); + }); + + describe('indexFieldType render()', () => { + test('it renders the indexFieldType', () => { + const columns = getCustomTableColumns(); + const indexFieldTypeRender = columns[1].render; + + render( + + <> + {indexFieldTypeRender != null && + indexFieldTypeRender(someField.indexFieldType, someField)} + + + ); + + expect(screen.getByTestId('indexFieldType')).toHaveTextContent(someField.indexFieldType); + }); + }); + }); + + describe('getEcsCompliantTableColumns', () => { + test('it returns the expected columns', () => { + expect(getEcsCompliantTableColumns().map((x) => omit('render', x))).toEqual([ + { + field: 'indexFieldName', + name: 'Field', + sortable: true, + truncateText: false, + width: '25%', + }, + { + field: 'type', + name: 'ECS mapping type', + sortable: true, + truncateText: false, + width: '25%', + }, + { + field: 'allowed_values', + name: 'ECS values', + sortable: false, + truncateText: false, + width: '25%', + }, + { + field: 'description', + name: 'ECS description', + sortable: false, + truncateText: false, + width: '25%', + }, + ]); + }); + + describe('type render()', () => { + describe('when `type` exists', () => { + beforeEach(() => { + const columns = getEcsCompliantTableColumns(); + const typeRender = columns[1].render; + + render( + + <>{typeRender != null && typeRender(eventCategory.type, eventCategory)} + + ); + }); + + test('it renders the expected `type`', () => { + expect(screen.getByTestId('type')).toHaveTextContent('keyword'); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('typePlaceholder')).not.toBeInTheDocument(); + }); + }); + + describe('when `type` is undefined', () => { + beforeEach(() => { + const withUndefinedType = { + ...eventCategory, + type: undefined, // <-- + }; + const columns = getEcsCompliantTableColumns(); + const typeRender = columns[1].render; + + render( + + <>{typeRender != null && typeRender(withUndefinedType.type, withUndefinedType)} + + ); + }); + + test('it does NOT render the `type`', () => { + expect(screen.queryByTestId('type')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('typePlaceholder')).toHaveTextContent(EMPTY_PLACEHOLDER); + }); + }); + }); + + describe('allowed values render()', () => { + describe('when `allowedValues` exists', () => { + beforeEach(() => { + const columns = getEcsCompliantTableColumns(); + const allowedValuesRender = columns[2].render; + + render( + + <> + {allowedValuesRender != null && + allowedValuesRender(eventCategory.allowed_values, eventCategory)} + + + ); + }); + + test('it renders the expected `AllowedValue` names', () => { + expect(screen.getByTestId('ecsAllowedValues')).toHaveTextContent( + eventCategory.allowed_values?.map(({ name }) => name).join('') ?? '' + ); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('ecsAllowedValuesEmpty')).not.toBeInTheDocument(); + }); + }); + + describe('when `allowedValues` is undefined', () => { + const withUndefinedAllowedValues = { + ...eventCategory, + allowed_values: undefined, // <-- + }; + + beforeEach(() => { + const columns = getEcsCompliantTableColumns(); + const allowedValuesRender = columns[2].render; + + render( + + <> + {allowedValuesRender != null && + allowedValuesRender( + withUndefinedAllowedValues.allowed_values, + withUndefinedAllowedValues + )} + + + ); + }); + + test('it does NOT render the `AllowedValue` names', () => { + expect(screen.queryByTestId('ecsAllowedValues')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('ecsAllowedValuesEmpty')).toBeInTheDocument(); + }); + }); + }); + + describe('description render()', () => { + describe('when `description` exists', () => { + beforeEach(() => { + const columns = getEcsCompliantTableColumns(); + const descriptionRender = columns[3].render; + + render( + + <> + {descriptionRender != null && + descriptionRender(eventCategory.description, eventCategory)} + + + ); + }); + + test('it renders the expected `description`', () => { + expect(screen.getByTestId('description')).toHaveTextContent( + eventCategory.description?.replaceAll('\n', ' ') ?? '' + ); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('emptyPlaceholder')).not.toBeInTheDocument(); + }); + }); + + describe('when `description` is undefined', () => { + const withUndefinedDescription = { + ...eventCategory, + description: undefined, // <-- + }; + + beforeEach(() => { + const columns = getEcsCompliantTableColumns(); + const descriptionRender = columns[3].render; + + render( + + <> + {descriptionRender != null && + descriptionRender(withUndefinedDescription.description, withUndefinedDescription)} + + + ); + }); + + test('it does NOT render the `description`', () => { + expect(screen.queryByTestId('description')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('emptyPlaceholder')).toBeInTheDocument(); + }); + }); + }); + }); + + describe('getIncompatibleValuesTableColumns', () => { + test('it returns the expected columns', () => { + expect(getIncompatibleValuesTableColumns().map((x) => omit('render', x))).toEqual([ + { + field: 'indexFieldName', + name: 'Field', + sortable: true, + truncateText: false, + width: '25%', + }, + { + field: 'allowed_values', + name: 'ECS values (expected)', + sortable: false, + truncateText: false, + width: '25%', + }, + { + field: 'indexInvalidValues', + name: 'Document values (actual)', + sortable: false, + truncateText: false, + width: '25%', + }, + { + field: 'description', + name: 'ECS description', + sortable: false, + truncateText: false, + width: '25%', + }, + ]); + }); + + describe('allowed values render()', () => { + describe('when `allowedValues` exists', () => { + beforeEach(() => { + const columns = getIncompatibleValuesTableColumns(); + const allowedValuesRender = columns[1].render; + + render( + + <> + {allowedValuesRender != null && + allowedValuesRender(eventCategory.allowed_values, eventCategory)} + + + ); + }); + + test('it renders the expected `AllowedValue` names', () => { + expect(screen.getByTestId('ecsAllowedValues')).toHaveTextContent( + eventCategory.allowed_values?.map(({ name }) => name).join('') ?? '' + ); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('ecsAllowedValuesEmpty')).not.toBeInTheDocument(); + }); + }); + + describe('when `allowedValues` is undefined', () => { + const withUndefinedAllowedValues = { + ...eventCategory, + allowed_values: undefined, // <-- + }; + + beforeEach(() => { + const columns = getIncompatibleValuesTableColumns(); + const allowedValuesRender = columns[1].render; + + render( + + <> + {allowedValuesRender != null && + allowedValuesRender( + withUndefinedAllowedValues.allowed_values, + withUndefinedAllowedValues + )} + + + ); + }); + + test('it does NOT render the `AllowedValue` names', () => { + expect(screen.queryByTestId('ecsAllowedValues')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('ecsAllowedValuesEmpty')).toBeInTheDocument(); + }); + }); + }); + + describe('indexInvalidValues render()', () => { + describe('when `indexInvalidValues` is populated', () => { + beforeEach(() => { + const columns = getIncompatibleValuesTableColumns(); + const indexInvalidValuesRender = columns[2].render; + + render( + + <> + {indexInvalidValuesRender != null && + indexInvalidValuesRender( + eventCategoryWithUnallowedValues.indexInvalidValues, + eventCategoryWithUnallowedValues + )} + + + ); + }); + + test('it renders the expected `indexInvalidValues`', () => { + expect(screen.getByTestId('indexInvalidValues')).toHaveTextContent( + 'an_invalid_category (2)theory (1)' + ); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('emptyPlaceholder')).not.toBeInTheDocument(); + }); + }); + + describe('when `indexInvalidValues` is empty', () => { + beforeEach(() => { + const columns = getIncompatibleValuesTableColumns(); + const indexInvalidValuesRender = columns[2].render; + + render( + + <> + {indexInvalidValuesRender != null && + indexInvalidValuesRender(eventCategory.indexInvalidValues, eventCategory)} + + + ); + }); + + test('it does NOT render the index invalid values', () => { + expect(screen.queryByTestId('indexInvalidValues')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('emptyPlaceholder')).toBeInTheDocument(); + }); + }); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.tsx index e9a85c2908b89..8153380c140c3 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/helpers.tsx @@ -30,7 +30,9 @@ export const getCustomTableColumns = (): Array< { field: 'indexFieldType', name: i18n.INDEX_MAPPING_TYPE, - render: (indexFieldType: string) => {indexFieldType}, + render: (indexFieldType: string) => ( + {indexFieldType} + ), sortable: true, truncateText: false, width: '50%', @@ -50,8 +52,12 @@ export const getEcsCompliantTableColumns = (): Array< { field: 'type', name: i18n.ECS_MAPPING_TYPE, - render: (type: string) => - type != null ? {type} : {EMPTY_PLACEHOLDER}, + render: (type: string | undefined) => + type != null ? ( + {type} + ) : ( + {EMPTY_PLACEHOLDER} + ), sortable: true, truncateText: false, width: '25%', @@ -69,8 +75,12 @@ export const getEcsCompliantTableColumns = (): Array< { field: 'description', name: i18n.ECS_DESCRIPTION, - render: (description: string) => - description != null ? description : {EMPTY_PLACEHOLDER}, + render: (description: string | undefined) => + description != null ? ( + {description} + ) : ( + {EMPTY_PLACEHOLDER} + ), sortable: false, truncateText: false, width: '25%', diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.test.tsx new file mode 100644 index 0000000000000..8732f27702bc2 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.test.tsx @@ -0,0 +1,39 @@ +/* + * 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, screen } from '@testing-library/react'; +import React from 'react'; + +import { INCOMPATIBLE_FIELD_MAPPINGS_TABLE_TITLE } from '../data_quality_panel/tabs/incompatible_tab/translations'; +import { eventCategory } from '../mock/enriched_field_metadata/mock_enriched_field_metadata'; +import { TestProviders } from '../mock/test_providers/test_providers'; +import { CompareFieldsTable } from '.'; +import { getIncompatibleMappingsTableColumns } from './get_incompatible_mappings_table_columns'; + +describe('CompareFieldsTable', () => { + describe('rendering', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it renders the expected title', () => { + expect(screen.getByTestId('title')).toHaveTextContent('Incompatible field mappings - foo'); + }); + + test('it renders the table', () => { + expect(screen.getByTestId('table')).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.tsx index 2efc9355c710f..145686785cafa 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index.tsx @@ -36,11 +36,12 @@ const CompareFieldsTableComponent: React.FC = ({ return ( <> - <>{title} + {title} { + test('it renders a placeholder with the expected content when `indexInvalidValues` is empty', () => { + render( + + + + ); + + expect(screen.getByTestId('emptyPlaceholder')).toHaveTextContent(EMPTY_PLACEHOLDER); + }); + + test('it renders the expected field names and counts when the index has invalid values', () => { + const indexInvalidValues: UnallowedValueCount[] = [ + { + count: 2, + fieldName: 'an_invalid_category', + }, + { + count: 1, + fieldName: 'theory', + }, + ]; + + render( + + + + ); + + expect(screen.getByTestId('indexInvalidValues')).toHaveTextContent( + 'an_invalid_category (2)theory (1)' + ); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index_invalid_values/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index_invalid_values/index.tsx index d3df809215d08..2b58ea98b8b28 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index_invalid_values/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/compare_fields_table/index_invalid_values/index.tsx @@ -23,7 +23,7 @@ interface Props { const IndexInvalidValuesComponent: React.FC = ({ indexInvalidValues }) => indexInvalidValues.length === 0 ? ( - {EMPTY_PLACEHOLDER} + {EMPTY_PLACEHOLDER} ) : ( {indexInvalidValues.map(({ fieldName, count }, i) => ( diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/allowed_values/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/allowed_values/helpers.test.tsx new file mode 100644 index 0000000000000..7dde4254708b7 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/allowed_values/helpers.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EcsFlat } from '@kbn/ecs'; +import { omit } from 'lodash/fp'; + +import { getUnallowedValueRequestItems, getValidValues, hasAllowedValues } from './helpers'; +import { AllowedValue, EcsMetadata } from '../../types'; + +const ecsMetadata: Record = EcsFlat as unknown as Record; + +describe('helpers', () => { + describe('hasAllowedValues', () => { + test('it returns true for a field that has `allowed_values`', () => { + expect( + hasAllowedValues({ + ecsMetadata, + fieldName: 'event.category', + }) + ).toBe(true); + }); + + test('it returns false for a field that does NOT have `allowed_values`', () => { + expect( + hasAllowedValues({ + ecsMetadata, + fieldName: 'host.name', + }) + ).toBe(false); + }); + + test('it returns false for a field that does NOT exist in `ecsMetadata`', () => { + expect( + hasAllowedValues({ + ecsMetadata, + fieldName: 'does.NOT.exist', + }) + ).toBe(false); + }); + + test('it returns false when `ecsMetadata` is null', () => { + expect( + hasAllowedValues({ + ecsMetadata: null, // <-- + fieldName: 'event.category', + }) + ).toBe(false); + }); + }); + + describe('getValidValues', () => { + test('it returns the expected valid values', () => { + expect(getValidValues(ecsMetadata['event.category'])).toEqual([ + 'authentication', + 'configuration', + 'database', + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ]); + }); + + test('it returns an empty array when the `field` does NOT have `allowed_values`', () => { + expect(getValidValues(ecsMetadata['host.name'])).toEqual([]); + }); + + test('it returns an empty array when `field` is undefined', () => { + expect(getValidValues(undefined)).toEqual([]); + }); + + test('it skips `allowed_values` where `name` is undefined', () => { + // omit the `name` property from the `database` `AllowedValue`: + const missingDatabase = + ecsMetadata['event.category'].allowed_values?.map((x) => + x.name === 'database' ? omit('name', x) : x + ) ?? []; + + const field = { + ...ecsMetadata['event.category'], + allowed_values: missingDatabase, + }; + + expect(getValidValues(field)).toEqual([ + 'authentication', + 'configuration', + // no entry for 'database' + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ]); + }); + }); + + describe('getUnallowedValueRequestItems', () => { + test('it returns the expected request items', () => { + expect( + getUnallowedValueRequestItems({ + ecsMetadata, + indexName: 'auditbeat-*', + }) + ).toEqual([ + { + indexName: 'auditbeat-*', + indexFieldName: 'event.category', + allowedValues: [ + 'authentication', + 'configuration', + 'database', + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ], + }, + { + indexName: 'auditbeat-*', + indexFieldName: 'event.kind', + allowedValues: [ + 'alert', + 'enrichment', + 'event', + 'metric', + 'state', + 'pipeline_error', + 'signal', + ], + }, + { + indexName: 'auditbeat-*', + indexFieldName: 'event.outcome', + allowedValues: ['failure', 'success', 'unknown'], + }, + { + indexName: 'auditbeat-*', + indexFieldName: 'event.type', + allowedValues: [ + 'access', + 'admin', + 'allowed', + 'change', + 'connection', + 'creation', + 'deletion', + 'denied', + 'end', + 'error', + 'group', + 'indicator', + 'info', + 'installation', + 'protocol', + 'start', + 'user', + ], + }, + ]); + }); + + test('it returns an empty array when `ecsMetadata` is null', () => { + expect( + getUnallowedValueRequestItems({ + ecsMetadata: null, // <-- + indexName: 'auditbeat-*', + }) + ).toEqual([]); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/index.test.tsx new file mode 100644 index 0000000000000..8b7c9b01e3c5e --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/index.test.tsx @@ -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 { DARK_THEME } from '@elastic/charts'; +import numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EMPTY_STAT } from '../../../helpers'; +import { alertIndexWithAllResults } from '../../../mock/pattern_rollup/mock_alerts_pattern_rollup'; +import { auditbeatWithAllResults } from '../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { packetbeatNoResults } from '../../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { TestProviders } from '../../../mock/test_providers/test_providers'; +import { PatternRollup } from '../../../types'; +import { Props, DataQualityDetails } from '.'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +const patternIndexNames: Record = { + 'auditbeat-*': [ + '.ds-auditbeat-8.6.1-2023.02.07-000001', + 'auditbeat-custom-empty-index-1', + 'auditbeat-custom-index-1', + ], + '.alerts-security.alerts-default': ['.internal.alerts-security.alerts-default-000001'], + 'packetbeat-*': [ + '.ds-packetbeat-8.5.3-2023.02.04-000001', + '.ds-packetbeat-8.6.1-2023.02.04-000001', + ], +}; + +const defaultProps: Props = { + addSuccessToast: jest.fn(), + canUserCreateAndReadCases: jest.fn(), + formatBytes, + formatNumber, + getGroupByFieldsOnClick: jest.fn(), + ilmPhases, + openCreateCaseFlyout: jest.fn(), + patternIndexNames, + patternRollups, + patterns, + theme: DARK_THEME, + updatePatternIndexNames: jest.fn(), + updatePatternRollup: jest.fn(), +}; + +describe('DataQualityDetails', () => { + describe('when ILM phases are provided', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders the storage details', () => { + expect(screen.getByTestId('storageDetails')).toBeInTheDocument(); + }); + + test('it renders the indices details', () => { + expect(screen.getByTestId('indicesDetails')).toBeInTheDocument(); + }); + }); + + describe('when ILM phases are are empty', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders an empty prompt when ilmPhases is empty', () => { + expect(screen.getByTestId('ilmPhasesEmptyPrompt')).toBeInTheDocument(); + }); + + test('it does NOT render the storage details', () => { + expect(screen.queryByTestId('storageDetails')).not.toBeInTheDocument(); + }); + + test('it does NOT render the indices details', () => { + expect(screen.queryByTestId('indicesDetails')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/index.tsx new file mode 100644 index 0000000000000..3c996dd095dc8 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/index.tsx @@ -0,0 +1,123 @@ +/* + * 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 { + FlameElementEvent, + HeatmapElementEvent, + MetricElementEvent, + PartitionElementEvent, + Theme, + WordCloudElementEvent, + XYChartElementEvent, +} from '@elastic/charts'; + +import React, { useCallback, useState } from 'react'; + +import { IlmPhasesEmptyPrompt } from '../../../ilm_phases_empty_prompt'; +import { IndicesDetails } from './indices_details'; +import { StorageDetails } from './storage_details'; +import { PatternRollup, SelectedIndex } from '../../../types'; + +export interface Props { + addSuccessToast: (toast: { title: string }) => void; + canUserCreateAndReadCases: () => boolean; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; + getGroupByFieldsOnClick: ( + elements: Array< + | FlameElementEvent + | HeatmapElementEvent + | MetricElementEvent + | PartitionElementEvent + | WordCloudElementEvent + | XYChartElementEvent + > + ) => { + groupByField0: string; + groupByField1: string; + }; + ilmPhases: string[]; + openCreateCaseFlyout: ({ + comments, + headerContent, + }: { + comments: string[]; + headerContent?: React.ReactNode; + }) => void; + patternIndexNames: Record; + patternRollups: Record; + patterns: string[]; + theme: Theme; + updatePatternIndexNames: ({ + indexNames, + pattern, + }: { + indexNames: string[]; + pattern: string; + }) => void; + updatePatternRollup: (patternRollup: PatternRollup) => void; +} + +const DataQualityDetailsComponent: React.FC = ({ + addSuccessToast, + canUserCreateAndReadCases, + formatBytes, + formatNumber, + getGroupByFieldsOnClick, + ilmPhases, + openCreateCaseFlyout, + patternIndexNames, + patternRollups, + patterns, + theme, + updatePatternIndexNames, + updatePatternRollup, +}) => { + const [selectedIndex, setSelectedIndex] = useState(null); + + const onIndexSelected = useCallback(async ({ indexName, pattern }: SelectedIndex) => { + setSelectedIndex({ indexName, pattern }); + }, []); + + if (ilmPhases.length === 0) { + return ; + } + + return ( + <> + + + + + ); +}; + +DataQualityDetailsComponent.displayName = 'DataQualityDetailsComponent'; +export const DataQualityDetails = React.memo(DataQualityDetailsComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/indices_details/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/indices_details/index.test.tsx new file mode 100644 index 0000000000000..ee4977ebe7858 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/indices_details/index.test.tsx @@ -0,0 +1,93 @@ +/* + * 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 { DARK_THEME } from '@elastic/charts'; +import numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EMPTY_STAT } from '../../../../helpers'; +import { alertIndexWithAllResults } from '../../../../mock/pattern_rollup/mock_alerts_pattern_rollup'; +import { auditbeatWithAllResults } from '../../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { packetbeatNoResults } from '../../../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { TestProviders } from '../../../../mock/test_providers/test_providers'; +import { PatternRollup } from '../../../../types'; +import { Props, IndicesDetails } from '.'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +const patternIndexNames: Record = { + 'auditbeat-*': [ + '.ds-auditbeat-8.6.1-2023.02.07-000001', + 'auditbeat-custom-empty-index-1', + 'auditbeat-custom-index-1', + ], + '.alerts-security.alerts-default': ['.internal.alerts-security.alerts-default-000001'], + 'packetbeat-*': [ + '.ds-packetbeat-8.5.3-2023.02.04-000001', + '.ds-packetbeat-8.6.1-2023.02.04-000001', + ], +}; + +const defaultProps: Props = { + addSuccessToast: jest.fn(), + canUserCreateAndReadCases: jest.fn(), + formatBytes, + formatNumber, + getGroupByFieldsOnClick: jest.fn(), + ilmPhases, + openCreateCaseFlyout: jest.fn(), + patternIndexNames, + patternRollups, + patterns, + selectedIndex: null, + setSelectedIndex: jest.fn(), + theme: DARK_THEME, + updatePatternIndexNames: jest.fn(), + updatePatternRollup: jest.fn(), +}; + +describe('IndicesDetails', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + describe('rendering patterns', () => { + patterns.forEach((pattern) => { + test(`it renders the ${pattern} pattern`, () => { + expect(screen.getByTestId(`${pattern}PatternPanel`)).toBeInTheDocument(); + }); + }); + }); + + describe('rendering spacers', () => { + test('it renders the expected number of spacers', () => { + expect(screen.getAllByTestId('bodyPatternSpacer')).toHaveLength(patterns.length - 1); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/indices_details/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/indices_details/index.tsx new file mode 100644 index 0000000000000..9b59a78430e1c --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/indices_details/index.tsx @@ -0,0 +1,110 @@ +/* + * 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 { + FlameElementEvent, + HeatmapElementEvent, + MetricElementEvent, + PartitionElementEvent, + Theme, + WordCloudElementEvent, + XYChartElementEvent, +} from '@elastic/charts'; +import { EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import React from 'react'; + +import { Pattern } from '../../../pattern'; +import { PatternRollup, SelectedIndex } from '../../../../types'; + +export interface Props { + addSuccessToast: (toast: { title: string }) => void; + canUserCreateAndReadCases: () => boolean; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; + getGroupByFieldsOnClick: ( + elements: Array< + | FlameElementEvent + | HeatmapElementEvent + | MetricElementEvent + | PartitionElementEvent + | WordCloudElementEvent + | XYChartElementEvent + > + ) => { + groupByField0: string; + groupByField1: string; + }; + ilmPhases: string[]; + openCreateCaseFlyout: ({ + comments, + headerContent, + }: { + comments: string[]; + headerContent?: React.ReactNode; + }) => void; + patternIndexNames: Record; + patternRollups: Record; + patterns: string[]; + selectedIndex: SelectedIndex | null; + setSelectedIndex: (selectedIndex: SelectedIndex | null) => void; + theme: Theme; + updatePatternIndexNames: ({ + indexNames, + pattern, + }: { + indexNames: string[]; + pattern: string; + }) => void; + updatePatternRollup: (patternRollup: PatternRollup) => void; +} + +const IndicesDetailsComponent: React.FC = ({ + addSuccessToast, + canUserCreateAndReadCases, + formatBytes, + formatNumber, + getGroupByFieldsOnClick, + ilmPhases, + openCreateCaseFlyout, + patternIndexNames, + patternRollups, + patterns, + selectedIndex, + setSelectedIndex, + theme, + updatePatternIndexNames, + updatePatternRollup, +}) => ( +
    + {patterns.map((pattern, i) => ( + + + {patterns[i + 1] && } + + ))} +
    +); + +IndicesDetailsComponent.displayName = 'IndicesDetailsComponent'; + +export const IndicesDetails = React.memo(IndicesDetailsComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.test.ts new file mode 100644 index 0000000000000..45e8f1ba1b4ad --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.test.ts @@ -0,0 +1,382 @@ +/* + * 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 numeral from '@elastic/numeral'; +import { euiThemeVars } from '@kbn/ui-theme'; + +import { EMPTY_STAT } from '../../../../helpers'; +import { + DEFAULT_INDEX_COLOR, + getFillColor, + getFlattenedBuckets, + getGroupFromPath, + getLayersMultiDimensional, + getLegendItems, + getLegendItemsForPattern, + getPathToFlattenedBucketMap, + getPatternLegendItem, + getPatternSizeInBytes, +} from './helpers'; +import { alertIndexWithAllResults } from '../../../../mock/pattern_rollup/mock_alerts_pattern_rollup'; +import { auditbeatWithAllResults } from '../../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { packetbeatNoResults } from '../../../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { PatternRollup } from '../../../../types'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +/** a valid `PatternRollup` that has an undefined `sizeInBytes` */ +const noSizeInBytes: Record = { + 'valid-*': { + docsCount: 19127, + error: null, + ilmExplain: null, + ilmExplainPhaseCounts: { + hot: 1, + warm: 0, + cold: 0, + frozen: 0, + unmanaged: 2, + }, + indices: 3, + pattern: 'valid-*', + results: undefined, + sizeInBytes: undefined, // <-- + stats: null, + }, +}; + +describe('helpers', () => { + describe('getPatternSizeInBytes', () => { + test('it returns the expected size when the pattern exists in the rollup', () => { + const pattern = 'auditbeat-*'; + + expect(getPatternSizeInBytes({ pattern, patternRollups })).toEqual( + auditbeatWithAllResults.sizeInBytes + ); + }); + + test('it returns zero when the pattern exists in the rollup, but does not have a sizeInBytes', () => { + const pattern = 'valid-*'; + + expect(getPatternSizeInBytes({ pattern, patternRollups: noSizeInBytes })).toEqual(0); + }); + + test('it returns zero when the pattern does NOT exist in the rollup', () => { + const pattern = 'does-not-exist-*'; + + expect(getPatternSizeInBytes({ pattern, patternRollups })).toEqual(0); + }); + }); + + describe('getPatternLegendItem', () => { + test('it returns the expected legend item', () => { + const pattern = 'auditbeat-*'; + + expect(getPatternLegendItem({ pattern, patternRollups })).toEqual({ + color: null, + ilmPhase: null, + index: null, + pattern, + sizeInBytes: auditbeatWithAllResults.sizeInBytes, + }); + }); + }); + + describe('getLegendItemsForPattern', () => { + test('it returns the expected legend items', () => { + const pattern = 'auditbeat-*'; + const flattenedBuckets = getFlattenedBuckets({ + ilmPhases, + patternRollups, + }); + + expect(getLegendItemsForPattern({ pattern, flattenedBuckets })).toEqual([ + { + color: euiThemeVars.euiColorSuccess, + ilmPhase: 'hot', + index: '.ds-auditbeat-8.6.1-2023.02.07-000001', + pattern: 'auditbeat-*', + sizeInBytes: 18791790, + }, + { + color: euiThemeVars.euiColorDanger, + ilmPhase: 'unmanaged', + index: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + sizeInBytes: 28409, + }, + { + color: euiThemeVars.euiColorDanger, + ilmPhase: 'unmanaged', + index: 'auditbeat-custom-empty-index-1', + pattern: 'auditbeat-*', + sizeInBytes: 247, + }, + ]); + }); + }); + + describe('getLegendItems', () => { + test('it returns the expected legend items', () => { + const flattenedBuckets = getFlattenedBuckets({ + ilmPhases, + patternRollups, + }); + + expect(getLegendItems({ flattenedBuckets, patterns, patternRollups })).toEqual([ + { + color: null, + ilmPhase: null, + index: null, + pattern: '.alerts-security.alerts-default', + sizeInBytes: 29717961631, + }, + { + color: euiThemeVars.euiColorSuccess, + ilmPhase: 'hot', + index: '.internal.alerts-security.alerts-default-000001', + pattern: '.alerts-security.alerts-default', + sizeInBytes: 0, + }, + { color: null, ilmPhase: null, index: null, pattern: 'auditbeat-*', sizeInBytes: 18820446 }, + { + color: euiThemeVars.euiColorSuccess, + ilmPhase: 'hot', + index: '.ds-auditbeat-8.6.1-2023.02.07-000001', + pattern: 'auditbeat-*', + sizeInBytes: 18791790, + }, + { + color: euiThemeVars.euiColorDanger, + ilmPhase: 'unmanaged', + index: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + sizeInBytes: 28409, + }, + { + color: euiThemeVars.euiColorDanger, + ilmPhase: 'unmanaged', + index: 'auditbeat-custom-empty-index-1', + pattern: 'auditbeat-*', + sizeInBytes: 247, + }, + { + color: null, + ilmPhase: null, + index: null, + pattern: 'packetbeat-*', + sizeInBytes: 1096520898, + }, + { + color: euiThemeVars.euiColorPrimary, + ilmPhase: 'hot', + index: '.ds-packetbeat-8.5.3-2023.02.04-000001', + pattern: 'packetbeat-*', + sizeInBytes: 584326147, + }, + { + color: euiThemeVars.euiColorPrimary, + ilmPhase: 'hot', + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + pattern: 'packetbeat-*', + sizeInBytes: 512194751, + }, + ]); + }); + }); + + describe('getFlattenedBuckets', () => { + test('it returns the expected flattened buckets', () => { + expect( + getFlattenedBuckets({ + ilmPhases, + patternRollups, + }) + ).toEqual([ + { + ilmPhase: 'hot', + incompatible: 0, + indexName: '.internal.alerts-security.alerts-default-000001', + pattern: '.alerts-security.alerts-default', + sizeInBytes: 0, + }, + { + ilmPhase: 'hot', + incompatible: 0, + indexName: '.ds-auditbeat-8.6.1-2023.02.07-000001', + pattern: 'auditbeat-*', + sizeInBytes: 18791790, + }, + { + ilmPhase: 'unmanaged', + incompatible: 1, + indexName: 'auditbeat-custom-empty-index-1', + pattern: 'auditbeat-*', + sizeInBytes: 247, + }, + { + ilmPhase: 'unmanaged', + incompatible: 3, + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + sizeInBytes: 28409, + }, + { + ilmPhase: 'hot', + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + pattern: 'packetbeat-*', + sizeInBytes: 512194751, + }, + { + ilmPhase: 'hot', + indexName: '.ds-packetbeat-8.5.3-2023.02.04-000001', + pattern: 'packetbeat-*', + sizeInBytes: 584326147, + }, + ]); + }); + }); + + describe('getFillColor', () => { + test('it returns success when `incompatible` is zero', () => { + const incompatible = 0; + + expect(getFillColor(incompatible)).toEqual(euiThemeVars.euiColorSuccess); + }); + + test('it returns danger when `incompatible` is greater than 0', () => { + const incompatible = 1; + + expect(getFillColor(incompatible)).toEqual(euiThemeVars.euiColorDanger); + }); + + test('it returns the default color when `incompatible` is undefined', () => { + const incompatible = undefined; + + expect(getFillColor(incompatible)).toEqual(DEFAULT_INDEX_COLOR); + }); + }); + + describe('getPathToFlattenedBucketMap', () => { + test('it returns the expected map', () => { + const flattenedBuckets = getFlattenedBuckets({ + ilmPhases, + patternRollups, + }); + + expect(getPathToFlattenedBucketMap(flattenedBuckets)).toEqual({ + '.alerts-security.alerts-default.internal.alerts-security.alerts-default-000001': { + pattern: '.alerts-security.alerts-default', + indexName: '.internal.alerts-security.alerts-default-000001', + ilmPhase: 'hot', + incompatible: 0, + sizeInBytes: 0, + }, + 'auditbeat-*.ds-auditbeat-8.6.1-2023.02.07-000001': { + pattern: 'auditbeat-*', + indexName: '.ds-auditbeat-8.6.1-2023.02.07-000001', + ilmPhase: 'hot', + incompatible: 0, + sizeInBytes: 18791790, + }, + 'auditbeat-*auditbeat-custom-empty-index-1': { + pattern: 'auditbeat-*', + indexName: 'auditbeat-custom-empty-index-1', + ilmPhase: 'unmanaged', + incompatible: 1, + sizeInBytes: 247, + }, + 'auditbeat-*auditbeat-custom-index-1': { + pattern: 'auditbeat-*', + indexName: 'auditbeat-custom-index-1', + ilmPhase: 'unmanaged', + incompatible: 3, + sizeInBytes: 28409, + }, + 'packetbeat-*.ds-packetbeat-8.6.1-2023.02.04-000001': { + pattern: 'packetbeat-*', + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + ilmPhase: 'hot', + sizeInBytes: 512194751, + }, + 'packetbeat-*.ds-packetbeat-8.5.3-2023.02.04-000001': { + pattern: 'packetbeat-*', + indexName: '.ds-packetbeat-8.5.3-2023.02.04-000001', + ilmPhase: 'hot', + sizeInBytes: 584326147, + }, + }); + }); + }); + + describe('getGroupFromPath', () => { + it('returns the expected group from the path', () => { + expect( + getGroupFromPath([ + { + index: 0, + value: '__null_small_multiples_key__', + }, + { + index: 0, + value: '__root_key__', + }, + { + index: 0, + value: 'auditbeat-*', + }, + { + index: 1, + value: 'auditbeat-custom-empty-index-1', + }, + ]) + ).toEqual('auditbeat-*'); + }); + + it('returns undefined when path is an empty array', () => { + expect(getGroupFromPath([])).toBeUndefined(); + }); + + it('returns undefined when path is an array with only one value', () => { + expect( + getGroupFromPath([{ index: 0, value: '__null_small_multiples_key__' }]) + ).toBeUndefined(); + }); + }); + + describe('getLayersMultiDimensional', () => { + const layer0FillColor = 'transparent'; + const flattenedBuckets = getFlattenedBuckets({ + ilmPhases, + patternRollups, + }); + const pathToFlattenedBucketMap = getPathToFlattenedBucketMap(flattenedBuckets); + + it('returns the expected number of layers', () => { + expect( + getLayersMultiDimensional({ formatBytes, layer0FillColor, pathToFlattenedBucketMap }).length + ).toEqual(2); + }); + + it('returns the expected fillLabel valueFormatter function', () => { + getLayersMultiDimensional({ formatBytes, layer0FillColor, pathToFlattenedBucketMap }).forEach( + (x) => expect(x.fillLabel.valueFormatter(123)).toEqual('123B') + ); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts new file mode 100644 index 0000000000000..09ed53402a89f --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts @@ -0,0 +1,223 @@ +/* + * 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 { Datum, Key, ArrayNode } from '@elastic/charts'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { orderBy } from 'lodash/fp'; + +import { getSizeInBytes } from '../../../../helpers'; +import { getIlmPhase } from '../../../pattern/helpers'; +import { PatternRollup } from '../../../../types'; + +export interface LegendItem { + color: string | null; + ilmPhase: string | null; + index: string | null; + pattern: string; + sizeInBytes: number; +} + +export interface FlattenedBucket { + ilmPhase: string | undefined; + incompatible: number | undefined; + indexName: string | undefined; + pattern: string; + sizeInBytes: number; +} + +export const getPatternSizeInBytes = ({ + pattern, + patternRollups, +}: { + pattern: string; + patternRollups: Record; +}): number => { + if (patternRollups[pattern] != null) { + return patternRollups[pattern].sizeInBytes ?? 0; + } else { + return 0; + } +}; + +export const getPatternLegendItem = ({ + pattern, + patternRollups, +}: { + pattern: string; + patternRollups: Record; +}): LegendItem => ({ + color: null, + ilmPhase: null, + index: null, + pattern, + sizeInBytes: getPatternSizeInBytes({ pattern, patternRollups }), +}); + +export const getLegendItemsForPattern = ({ + pattern, + flattenedBuckets, +}: { + pattern: string; + flattenedBuckets: FlattenedBucket[]; +}): LegendItem[] => + orderBy( + ['sizeInBytes'], + ['desc'], + flattenedBuckets + .filter((x) => x.pattern === pattern) + .map((flattenedBucket) => ({ + color: getFillColor(flattenedBucket.incompatible), + ilmPhase: flattenedBucket.ilmPhase ?? null, + index: flattenedBucket.indexName ?? null, + pattern: flattenedBucket.pattern, + sizeInBytes: flattenedBucket.sizeInBytes ?? 0, + })) + ); + +export const getLegendItems = ({ + patterns, + flattenedBuckets, + patternRollups, +}: { + patterns: string[]; + flattenedBuckets: FlattenedBucket[]; + patternRollups: Record; +}): LegendItem[] => + patterns.reduce( + (acc, pattern) => [ + ...acc, + getPatternLegendItem({ pattern, patternRollups }), + ...getLegendItemsForPattern({ pattern, flattenedBuckets }), + ], + [] + ); + +export const getFlattenedBuckets = ({ + ilmPhases, + patternRollups, +}: { + ilmPhases: string[]; + patternRollups: Record; +}): FlattenedBucket[] => + Object.values(patternRollups).reduce((acc, patternRollup) => { + // enables fast lookup of valid phase names: + const ilmPhasesMap = ilmPhases.reduce>( + (phasesMap, phase) => ({ ...phasesMap, [phase]: 0 }), + {} + ); + const { ilmExplain, pattern, results, stats } = patternRollup; + + if (ilmExplain != null && stats != null) { + return [ + ...acc, + ...Object.entries(stats).reduce( + (validStats, [indexName, indexStats]) => { + const ilmPhase = getIlmPhase(ilmExplain[indexName]); + const isSelectedPhase = ilmPhase != null && ilmPhasesMap[ilmPhase] != null; + + if (isSelectedPhase) { + const incompatible = + results != null && results[indexName] != null + ? results[indexName].incompatible + : undefined; + const sizeInBytes = getSizeInBytes({ indexName, stats }); + + return [ + ...validStats, + { + ilmPhase, + incompatible, + indexName, + pattern, + sizeInBytes, + }, + ]; + } else { + return validStats; + } + }, + [] + ), + ]; + } + + return acc; + }, []); + +const groupByRollup = (d: Datum) => d.pattern; // the treemap is grouped by this field + +export const DEFAULT_INDEX_COLOR = euiThemeVars.euiColorPrimary; + +export const getFillColor = (incompatible: number | undefined): string => { + if (incompatible === 0) { + return euiThemeVars.euiColorSuccess; + } else if (incompatible != null && incompatible > 0) { + return euiThemeVars.euiColorDanger; + } else { + return DEFAULT_INDEX_COLOR; + } +}; + +export const getPathToFlattenedBucketMap = ( + flattenedBuckets: FlattenedBucket[] +): Record => + flattenedBuckets.reduce>( + (acc, { pattern, indexName, ...remaining }) => ({ + ...acc, + [`${pattern}${indexName}`]: { pattern, indexName, ...remaining }, + }), + {} + ); + +/** + * Extracts the first group name from the data representing the second group + */ +export const getGroupFromPath = (path: ArrayNode['path']): string | undefined => { + const OFFSET_FROM_END = 2; // The offset from the end of the path array containing the group + const groupIndex = path.length - OFFSET_FROM_END; + return groupIndex > 0 ? path[groupIndex].value : undefined; +}; + +export const getLayersMultiDimensional = ({ + formatBytes, + layer0FillColor, + pathToFlattenedBucketMap, +}: { + formatBytes: (value: number | undefined) => string; + layer0FillColor: string; + pathToFlattenedBucketMap: Record; +}) => { + const valueFormatter = (d: number) => formatBytes(d); + + return [ + { + fillLabel: { + valueFormatter, + }, + groupByRollup, + nodeLabel: (ilmPhase: Datum) => ilmPhase, + shape: { + fillColor: layer0FillColor, + }, + }, + { + fillLabel: { + valueFormatter, + }, + groupByRollup: (d: Datum) => d.indexName, + nodeLabel: (indexName: Datum) => indexName, + shape: { + fillColor: (indexName: Key, sortIndex: number, node: Pick) => { + const pattern = getGroupFromPath(node.path) ?? ''; + const flattenedBucket = pathToFlattenedBucketMap[`${pattern}${indexName}`]; + + return getFillColor(flattenedBucket?.incompatible); + }, + }, + }, + ]; +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/index.test.tsx new file mode 100644 index 0000000000000..366fb487309c3 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/index.test.tsx @@ -0,0 +1,59 @@ +/* + * 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 { DARK_THEME } from '@elastic/charts'; +import numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EMPTY_STAT } from '../../../../helpers'; +import { alertIndexWithAllResults } from '../../../../mock/pattern_rollup/mock_alerts_pattern_rollup'; +import { auditbeatWithAllResults } from '../../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { packetbeatNoResults } from '../../../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { TestProviders } from '../../../../mock/test_providers/test_providers'; +import { PatternRollup } from '../../../../types'; +import { Props, StorageDetails } from '.'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +const onIndexSelected = jest.fn(); + +const defaultProps: Props = { + formatBytes, + ilmPhases, + onIndexSelected, + patternRollups, + patterns, + theme: DARK_THEME, +}; + +describe('StorageDetails', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders the treemap', () => { + expect(screen.getByTestId('storageTreemap').querySelector('.echChart')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/index.tsx new file mode 100644 index 0000000000000..26340b31286fa --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/index.tsx @@ -0,0 +1,58 @@ +/* + * 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 { Theme } from '@elastic/charts'; +import React, { useMemo } from 'react'; + +import { getFlattenedBuckets } from './helpers'; +import { StorageTreemap } from '../../../storage_treemap'; +import { DEFAULT_MAX_CHART_HEIGHT, StorageTreemapContainer } from '../../../tabs/styles'; +import { PatternRollup, SelectedIndex } from '../../../../types'; + +export interface Props { + formatBytes: (value: number | undefined) => string; + ilmPhases: string[]; + onIndexSelected: ({ indexName, pattern }: SelectedIndex) => void; + patternRollups: Record; + patterns: string[]; + theme: Theme; +} + +const StorageDetailsComponent: React.FC = ({ + formatBytes, + ilmPhases, + onIndexSelected, + patternRollups, + patterns, + theme, +}) => { + const flattenedBuckets = useMemo( + () => + getFlattenedBuckets({ + ilmPhases, + patternRollups, + }), + [ilmPhases, patternRollups] + ); + + return ( + + + + ); +}; + +StorageDetailsComponent.displayName = 'StorageDetailsComponent'; +export const StorageDetails = React.memo(StorageDetailsComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/translations.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/translations.ts new file mode 100644 index 0000000000000..6b8ffed70f8c9 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/translations.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 { i18n } from '@kbn/i18n'; + +export const INDICES_TAB_TITLE = i18n.translate( + 'ecsDataQualityDashboard.body.tabs.indicesTabTitle', + { + defaultMessage: 'Indices', + } +); + +export const STORAGE_TAB_TITLE = i18n.translate( + 'ecsDataQualityDashboard.body.tabs.storageTabTitle', + { + defaultMessage: 'Storage', + } +); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.test.tsx new file mode 100644 index 0000000000000..0f27f307f7913 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.test.tsx @@ -0,0 +1,100 @@ +/* + * 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 { DARK_THEME } from '@elastic/charts'; +import numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EMPTY_STAT } from '../../helpers'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { Body } from '.'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const ilmPhases: string[] = ['hot', 'warm', 'unmanaged']; + +describe('IndexInvalidValues', () => { + test('it renders the data quality summary', () => { + render( + + + + ); + + expect(screen.getByTestId('dataQualitySummary')).toBeInTheDocument(); + }); + + describe('patterns', () => { + const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'logs-*', 'packetbeat-*']; + + patterns.forEach((pattern) => { + test(`it renders the '${pattern}' pattern`, () => { + render( + + + + ); + + expect(screen.getByTestId(`${pattern}PatternPanel`)).toBeInTheDocument(); + }); + }); + + test('it renders the expected number of spacers', async () => { + render( + + + + ); + + const items = await screen.findAllByTestId('bodyPatternSpacer'); + expect(items).toHaveLength(patterns.length - 1); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.tsx index 87aed178043cc..69de3b8c110e5 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/body/index.tsx @@ -17,14 +17,15 @@ import type { import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import React from 'react'; +import { DataQualityDetails } from './data_quality_details'; import { DataQualitySummary } from '../data_quality_summary'; -import { Pattern } from '../pattern'; import { useResultsRollup } from '../../use_results_rollup'; interface Props { addSuccessToast: (toast: { title: string }) => void; canUserCreateAndReadCases: () => boolean; - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; getGroupByFieldsOnClick: ( elements: Array< | FlameElementEvent @@ -55,7 +56,8 @@ interface Props { const BodyComponent: React.FC = ({ addSuccessToast, canUserCreateAndReadCases, - defaultNumberFormat, + formatBytes, + formatNumber, getGroupByFieldsOnClick, ilmPhases, lastChecked, @@ -72,6 +74,7 @@ const BodyComponent: React.FC = ({ totalIncompatible, totalIndices, totalIndicesChecked, + totalSizeInBytes, updatePatternIndexNames, updatePatternRollup, } = useResultsRollup({ ilmPhases, patterns }); @@ -82,7 +85,8 @@ const BodyComponent: React.FC = ({ = ({ totalIncompatible={totalIncompatible} totalIndices={totalIndices} totalIndicesChecked={totalIndicesChecked} + totalSizeInBytes={totalSizeInBytes} onCheckCompleted={onCheckCompleted} /> - {patterns.map((pattern, i) => ( - - - {i !== patterns.length - 1 ? : null} - - ))} + + +
    ); }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.test.tsx new file mode 100644 index 0000000000000..5085db2a93e51 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.test.tsx @@ -0,0 +1,212 @@ +/* + * 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 { act, render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../../mock/test_providers/test_providers'; +import { IndexToCheck } from '../../../types'; +import { CheckStatus, EMPTY_LAST_CHECKED_DATE } from '.'; + +const indexToCheck: IndexToCheck = { + pattern: 'auditbeat-*', + indexName: '.ds-auditbeat-8.6.1-2023.02.13-000001', +}; +const checkAllIndiciesChecked = 2; +const checkAllTotalIndiciesToCheck = 3; + +describe('CheckStatus', () => { + describe('when `indexToCheck` is not null', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it renders progress with the expected max value', () => { + expect(screen.getByTestId('progress')).toHaveAttribute( + 'max', + String(checkAllTotalIndiciesToCheck) + ); + }); + + test('it renders progress with the expected current value', () => { + expect(screen.getByTestId('progress')).toHaveAttribute( + 'value', + String(checkAllIndiciesChecked) + ); + }); + + test('it renders the expected "checking " message', () => { + expect(screen.getByTestId('checking')).toHaveTextContent( + `Checking ${indexToCheck.indexName}` + ); + }); + + test('it does NOT render the last checked message', () => { + expect(screen.queryByTestId('lastChecked')).not.toBeInTheDocument(); + }); + }); + + describe('when `indexToCheck` is null', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it does NOT render the progress bar', () => { + expect(screen.queryByTestId('progress')).not.toBeInTheDocument(); + }); + + test('it does NOT render the "checking " message', () => { + expect(screen.queryByTestId('checking')).not.toBeInTheDocument(); + }); + + test('it renders the expected last checked message', () => { + expect(screen.getByTestId('lastChecked')).toHaveTextContent(EMPTY_LAST_CHECKED_DATE); + }); + }); + + test('it renders the errors popover when errors have occurred', () => { + const errorSummary = [ + { + pattern: '.alerts-security.alerts-default', + indexName: null, + error: 'Error loading stats: Error: Forbidden', + }, + ]; + + render( + + + + ); + + expect(screen.getByTestId('errorsPopover')).toBeInTheDocument(); + }); + + test('it does NOT render the errors popover when errors have NOT occurred', () => { + render( + + + + ); + + expect(screen.queryByTestId('errorsPopover')).not.toBeInTheDocument(); + }); + + test('it invokes the `setLastChecked` callback when indexToCheck is not null', () => { + jest.useFakeTimers(); + const date = '2023-03-28T22:27:28.159Z'; + jest.setSystemTime(new Date(date)); + + const setLastChecked = jest.fn(); + + render( + + + + ); + + expect(setLastChecked).toBeCalledWith(date); + jest.useRealTimers(); + }); + + test('it updates the formatted date', async () => { + jest.useFakeTimers(); + const date = '2023-03-28T23:27:28.159Z'; + jest.setSystemTime(new Date(date)); + + const { rerender } = render( + + + + ); + + // re-render with an updated `lastChecked` + const lastChecked = '2023-03-28T22:27:28.159Z'; + + act(() => { + jest.advanceTimersByTime(1000 * 61); + }); + + rerender( + + + + ); + + act(() => { + // once again, advance time + jest.advanceTimersByTime(1000 * 61); + }); + + expect(await screen.getByTestId('lastChecked')).toHaveTextContent('Last checked: an hour ago'); + jest.useRealTimers(); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.tsx index 3ff69274ba06e..9245b0adee84c 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/check_status/index.tsx @@ -60,30 +60,31 @@ const CheckStatusComponent: React.FC = ({ }, [lastChecked]); return ( - + {indexToCheck != null && ( <> - + + {i18n.CHECKING(indexToCheck.indexName)} + - - {i18n.CHECKING(indexToCheck.indexName)} - + )} {indexToCheck == null && ( - + {i18n.LAST_CHECKED} {': '} {formattedDate} diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.test.tsx new file mode 100644 index 0000000000000..064ec92a1ca81 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.test.tsx @@ -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 userEvent from '@testing-library/user-event'; +import { act, render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../../mock/test_providers/test_providers'; +import { ErrorsPopover } from '.'; + +const mockCopyToClipboard = jest.fn((value) => true); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + copyToClipboard: (value: string) => mockCopyToClipboard(value), + }; +}); + +const errorSummary = [ + { + pattern: '.alerts-security.alerts-default', + indexName: null, + error: 'Error loading stats: Error: Forbidden', + }, +]; + +describe('ErrorsPopover', () => { + beforeEach(() => { + document.execCommand = jest.fn(); + }); + + test('it disables the view errors button when `errorSummary` is empty', () => { + render( + + + + ); + + expect(screen.getByTestId('viewErrors')).toBeDisabled(); + }); + + test('it enables the view errors button when `errorSummary` is NOT empty', () => { + render( + + + + ); + + expect(screen.getByTestId('viewErrors')).not.toBeDisabled(); + }); + + describe('popover content', () => { + const addSuccessToast = jest.fn(); + + beforeEach(() => { + jest.resetAllMocks(); + + render( + + + + ); + + const viewErrorsButton = screen.getByTestId('viewErrors'); + + act(() => { + userEvent.click(viewErrorsButton); + }); + }); + + test('it renders the expected callout content', () => { + expect(screen.getByTestId('callout')).toHaveTextContent( + "ErrorsSome indices were not checked for Data QualityErrors may occur when pattern or index metadata is temporarily unavailable, or because you don't have the privileges required for accessThe following privileges are required to check an index:monitor or manageview_index_metadatareadCopy to clipboard" + ); + }); + + test('it invokes `addSuccessToast` when the copy button is clicked', () => { + const copyToClipboardButton = screen.getByTestId('copyToClipboard'); + act(() => { + userEvent.click(copyToClipboardButton, undefined, { skipPointerEventsCheck: true }); + }); + + expect(addSuccessToast).toBeCalledWith({ title: 'Copied errors to the clipboard' }); + }); + + test('it renders the expected error summary text in the errors viewer', () => { + expect(screen.getByTestId('errorsViewer').textContent?.includes(errorSummary[0].error)).toBe( + true + ); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.tsx index b9e0fc61ec545..8f80e3fa3cab5 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_popover/index.tsx @@ -60,6 +60,7 @@ const ErrorsPopoverComponent: React.FC = ({ addSuccessToast, errorSummary () => ( = ({ addSuccessToast, errorSummary - +

    {i18n.ERRORS_CALLOUT_SUMMARY}

    {i18n.ERRORS_MAY_OCCUR}

    @@ -96,7 +98,13 @@ const ErrorsPopoverComponent: React.FC = ({ addSuccessToast, errorSummary - + {i18n.COPY_TO_CLIPBOARD}
    diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.test.tsx new file mode 100644 index 0000000000000..1954e92ae5fc7 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.test.tsx @@ -0,0 +1,122 @@ +/* + * 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, screen } from '@testing-library/react'; +import { omit } from 'lodash/fp'; +import React from 'react'; + +import { getErrorsViewerTableColumns } from './helpers'; +import { TestProviders } from '../../../mock/test_providers/test_providers'; +import { ErrorSummary } from '../../../types'; + +const errorSummary: ErrorSummary[] = [ + { + pattern: '.alerts-security.alerts-default', + indexName: null, + error: 'Error loading stats: Error: Forbidden', + }, + { + error: + 'Error: Error loading unallowed values for index auditbeat-7.2.1-2023.02.13-000001: Forbidden', + indexName: 'auditbeat-7.2.1-2023.02.13-000001', + pattern: 'auditbeat-*', + }, +]; + +const noIndexName: ErrorSummary = errorSummary[0]; // <-- indexName: null +const hasIndexName: ErrorSummary = errorSummary[1]; + +describe('helpers', () => { + describe('getCommonTableColumns', () => { + test('it returns the expected column configuration', () => { + const columns = getErrorsViewerTableColumns().map((x) => omit('render', x)); + + expect(columns).toEqual([ + { + field: 'pattern', + name: 'Pattern', + sortable: true, + truncateText: false, + width: '25%', + }, + { + field: 'indexName', + name: 'Index', + sortable: false, + truncateText: false, + width: '25%', + }, + { + field: 'error', + name: 'Error', + sortable: false, + truncateText: false, + width: '50%', + }, + ]); + }); + + describe('indexName column render()', () => { + describe('when the `ErrorSummary` has an `indexName`', () => { + beforeEach(() => { + const columns = getErrorsViewerTableColumns(); + const indexNameRender = columns[1].render; + + render( + + {indexNameRender != null && indexNameRender(hasIndexName.indexName, hasIndexName)} + + ); + }); + + test('it renders the expected `indexName`', () => { + expect(screen.getByTestId('indexName')).toHaveTextContent(String(hasIndexName.indexName)); + }); + + test('it does NOT render the placeholder', () => { + expect(screen.queryByTestId('emptyPlaceholder')).not.toBeInTheDocument(); + }); + }); + + describe('when the `ErrorSummary` does NOT have an `indexName`', () => { + beforeEach(() => { + const columns = getErrorsViewerTableColumns(); + const indexNameRender = columns[1].render; + + render( + + {indexNameRender != null && indexNameRender(noIndexName.indexName, noIndexName)} + + ); + }); + + test('it does NOT render `indexName`', () => { + expect(screen.queryByTestId('indexName')).not.toBeInTheDocument(); + }); + + test('it renders the placeholder', () => { + expect(screen.getByTestId('emptyPlaceholder')).toBeInTheDocument(); + }); + }); + }); + + describe('indexName error render()', () => { + test('it renders the expected `error`', () => { + const columns = getErrorsViewerTableColumns(); + const indexNameRender = columns[2].render; + + render( + + {indexNameRender != null && indexNameRender(hasIndexName.error, hasIndexName)} + + ); + + expect(screen.getByTestId('error')).toHaveTextContent(hasIndexName.error); + }); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.tsx index caac710cf8d13..35a4a74cca875 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/helpers.tsx @@ -28,7 +28,12 @@ export const getErrorsViewerTableColumns = (): Array (indexName != null && indexName !== '' ? indexName : EMPTY_PLACEHOLDER), + render: (indexName: string | null) => + indexName != null && indexName !== '' ? ( + {indexName} + ) : ( + {EMPTY_PLACEHOLDER} + ), sortable: false, truncateText: false, width: '25%', @@ -36,7 +41,7 @@ export const getErrorsViewerTableColumns = (): Array {errorText}, + render: (errorText) => {errorText}, sortable: false, truncateText: false, width: '50%', diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.test.tsx new file mode 100644 index 0000000000000..a1b6346eb2b8d --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../../mock/test_providers/test_providers'; +import { ERROR, INDEX, PATTERN } from './translations'; +import { ErrorSummary } from '../../../types'; +import { ErrorsViewer } from '.'; + +interface ExpectedColumns { + id: string; + expected: string; +} + +const errorSummary: ErrorSummary[] = [ + { + pattern: '.alerts-security.alerts-default', + indexName: null, + error: 'Error loading stats: Error: Forbidden', + }, + { + error: + 'Error: Error loading unallowed values for index auditbeat-7.2.1-2023.02.13-000001: Forbidden', + indexName: 'auditbeat-7.2.1-2023.02.13-000001', + pattern: 'auditbeat-*', + }, +]; + +describe('ErrorsViewer', () => { + const expectedColumns: ExpectedColumns[] = [ + { + id: 'pattern', + expected: PATTERN, + }, + { + id: 'indexName', + expected: INDEX, + }, + { + id: 'error', + expected: ERROR, + }, + ]; + + expectedColumns.forEach(({ id, expected }, i) => { + test(`it renders the expected '${id}' column header`, () => { + render( + + + + ); + + expect(screen.getByTestId(`tableHeaderCell_${id}_${i}`)).toHaveTextContent(expected); + }); + }); + + test(`it renders the expected the errors`, () => { + render( + + + + ); + + expect( + screen + .getAllByTestId('error') + .map((x) => x.textContent ?? '') + .reduce((acc, x) => acc.concat(x), '') + ).toEqual(`${errorSummary[0].error}${errorSummary[1].error}`); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.tsx index a40094c96b399..2336abe79c651 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/errors_viewer/index.tsx @@ -29,7 +29,7 @@ const ErrorsViewerComponent: React.FC = ({ errorSummary }) => { const columns = useMemo(() => getErrorsViewerTableColumns(), []); return ( - + + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +const patternIndexNames: Record = { + 'auditbeat-*': [ + '.ds-auditbeat-8.6.1-2023.02.07-000001', + 'auditbeat-custom-empty-index-1', + 'auditbeat-custom-index-1', + ], + '.alerts-security.alerts-default': ['.internal.alerts-security.alerts-default-000001'], + 'packetbeat-*': [ + '.ds-packetbeat-8.5.3-2023.02.04-000001', + '.ds-packetbeat-8.6.1-2023.02.04-000001', + ], +}; + +const lastChecked = '2023-03-28T23:27:28.159Z'; + +const totalDocsCount = getTotalDocsCount(patternRollups); +const totalIncompatible = getTotalIncompatible(patternRollups); +const totalIndices = getTotalIndices(patternRollups); +const totalIndicesChecked = getTotalIndicesChecked(patternRollups); +const totalSizeInBytes = getTotalSizeInBytes(patternRollups); + +const defaultProps: Props = { + addSuccessToast: jest.fn(), + canUserCreateAndReadCases: jest.fn(), + formatBytes, + formatNumber, + ilmPhases, + lastChecked, + openCreateCaseFlyout: jest.fn(), + patternIndexNames, + patternRollups, + patterns, + setLastChecked: jest.fn(), + totalDocsCount, + totalIncompatible, + totalIndices, + totalIndicesChecked, + totalSizeInBytes, + onCheckCompleted: jest.fn(), +}; + +describe('DataQualitySummary', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders the summary actions', () => { + expect(screen.getByTestId('summaryActions')).toBeInTheDocument(); + }); + + test('it renders the stats rollup', () => { + expect(screen.getByTestId('statsRollup')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/index.tsx index c6874d861ddb8..d3f9ad9d23303 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/index.tsx @@ -5,15 +5,14 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; -import React, { useCallback, useMemo, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; -import { CheckStatus } from './check_status'; +import { getErrorSummaries } from '../../helpers'; import { StatsRollup } from '../pattern/pattern_summary/stats_rollup'; import { SummaryActions } from './summary_actions'; -import type { IndexToCheck, OnCheckCompleted, PatternRollup } from '../../types'; -import { getErrorSummaries } from '../../helpers'; +import type { OnCheckCompleted, PatternRollup } from '../../types'; const MAX_SUMMARY_ACTIONS_CONTAINER_WIDTH = 400; const MIN_SUMMARY_ACTIONS_CONTAINER_WIDTH = 235; @@ -24,10 +23,11 @@ const SummaryActionsContainerFlexItem = styled(EuiFlexItem)` padding-right: ${({ theme }) => theme.eui.euiSizeXL}; `; -interface Props { +export interface Props { addSuccessToast: (toast: { title: string }) => void; canUserCreateAndReadCases: () => boolean; - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; ilmPhases: string[]; lastChecked: string; openCreateCaseFlyout: ({ @@ -45,13 +45,15 @@ interface Props { totalIncompatible: number | undefined; totalIndices: number | undefined; totalIndicesChecked: number | undefined; + totalSizeInBytes: number | undefined; onCheckCompleted: OnCheckCompleted; } const DataQualitySummaryComponent: React.FC = ({ addSuccessToast, canUserCreateAndReadCases, - defaultNumberFormat, + formatBytes, + formatNumber, ilmPhases, lastChecked, openCreateCaseFlyout, @@ -63,64 +65,46 @@ const DataQualitySummaryComponent: React.FC = ({ totalIncompatible, totalIndices, totalIndicesChecked, + totalSizeInBytes, onCheckCompleted, }) => { - const [indexToCheck, setIndexToCheck] = useState(null); - - const [checkAllIndiciesChecked, setCheckAllIndiciesChecked] = useState(0); - const [checkAllTotalIndiciesToCheck, setCheckAllTotalIndiciesToCheck] = useState(0); - - const incrementCheckAllIndiciesChecked = useCallback(() => { - setCheckAllIndiciesChecked((current) => current + 1); - }, []); - const errorSummary = useMemo(() => getErrorSummaries(patternRollups), [patternRollups]); return ( - + - - - - diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/actions/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/actions/index.test.tsx new file mode 100644 index 0000000000000..02b04225a544e --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/actions/index.test.tsx @@ -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 userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../../../mock/test_providers/test_providers'; +import { Props, Actions } from '.'; + +const mockCopyToClipboard = jest.fn((value) => true); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + copyToClipboard: (value: string) => mockCopyToClipboard(value), + }; +}); + +const ilmPhases = ['hot', 'warm', 'unmanaged']; + +const defaultProps: Props = { + addSuccessToast: jest.fn(), + canUserCreateAndReadCases: () => true, + getMarkdownComments: () => [], + ilmPhases, + openCreateCaseFlyout: jest.fn(), +}; + +describe('Actions', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('when the action buttons are clicked', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it invokes openCreateCaseFlyout when the add to new case button is clicked', () => { + const button = screen.getByTestId('addToNewCase'); + + userEvent.click(button); + + expect(defaultProps.openCreateCaseFlyout).toBeCalled(); + }); + + test('it invokes addSuccessToast when the copy to clipboard button is clicked', () => { + const button = screen.getByTestId('copyToClipboard'); + + userEvent.click(button); + + expect(defaultProps.addSuccessToast).toBeCalledWith({ + title: 'Copied results to the clipboard', + }); + }); + }); + + test('it disables the add to new case button when the user cannot create cases', () => { + const canUserCreateAndReadCases = () => false; + + render( + + + + ); + + const button = screen.getByTestId('addToNewCase'); + + expect(button).toBeDisabled(); + }); + + test('it disables the add to new case button when `ilmPhases` is empty', () => { + render( + + + + ); + + const button = screen.getByTestId('addToNewCase'); + + expect(button).toBeDisabled(); + }); + + test('it disables the copy to clipboard button when `ilmPhases` is empty', () => { + render( + + + + ); + + const button = screen.getByTestId('copyToClipboard'); + + expect(button).toBeDisabled(); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/actions/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/actions/index.tsx new file mode 100644 index 0000000000000..549f420c3fa9d --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/actions/index.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 { copyToClipboard, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback } from 'react'; + +import { + ADD_TO_NEW_CASE, + COPIED_RESULTS_TOAST_TITLE, + COPY_TO_CLIPBOARD, +} from '../../../../translations'; +import { useAddToNewCase } from '../../../../use_add_to_new_case'; + +export interface Props { + addSuccessToast: (toast: { title: string }) => void; + canUserCreateAndReadCases: () => boolean; + getMarkdownComments: () => string[]; + ilmPhases: string[]; + openCreateCaseFlyout: ({ + comments, + headerContent, + }: { + comments: string[]; + headerContent?: React.ReactNode; + }) => void; +} + +const ActionsComponent: React.FC = ({ + addSuccessToast, + canUserCreateAndReadCases, + getMarkdownComments, + ilmPhases, + openCreateCaseFlyout, +}) => { + const { disabled: addToNewCaseDisabled, onAddToNewCase } = useAddToNewCase({ + canUserCreateAndReadCases, + openCreateCaseFlyout, + }); + + const onClickAddToCase = useCallback( + () => onAddToNewCase([getMarkdownComments().join('\n')]), + [getMarkdownComments, onAddToNewCase] + ); + + const onCopy = useCallback(() => { + const markdown = getMarkdownComments().join('\n'); + copyToClipboard(markdown); + + addSuccessToast({ + title: COPIED_RESULTS_TOAST_TITLE, + }); + }, [addSuccessToast, getMarkdownComments]); + + const addToNewCaseContextMenuOnClick = useCallback(() => { + onClickAddToCase(); + }, [onClickAddToCase]); + + const disableAll = ilmPhases.length === 0; + + return ( + + + + {ADD_TO_NEW_CASE} + + + + + + {COPY_TO_CLIPBOARD} + + + + ); +}; + +ActionsComponent.displayName = 'ActionsComponent'; + +export const Actions = React.memo(ActionsComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.test.ts new file mode 100644 index 0000000000000..fd457193a9c6f --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.test.ts @@ -0,0 +1,338 @@ +/* + * 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 { EcsFlat, EcsVersion } from '@kbn/ecs'; + +import { checkIndex, EMPTY_PARTITIONED_FIELD_METADATA } from './check_index'; +import { EMPTY_STAT } from '../../../../helpers'; +import { mockMappingsResponse } from '../../../../mock/mappings_response/mock_mappings_response'; +import { mockUnallowedValuesResponse } from '../../../../mock/unallowed_values/mock_unallowed_values'; +import { EcsMetadata, UnallowedValueRequestItem } from '../../../../types'; + +const ecsMetadata = EcsFlat as unknown as Record; + +let mockFetchMappings = jest.fn( + ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => + new Promise((resolve) => { + resolve(mockMappingsResponse); // happy path + }) +); + +jest.mock('../../../../use_mappings/helpers', () => ({ + fetchMappings: ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => + mockFetchMappings({ + abortController, + patternOrIndexName, + }), +})); + +const mockFetchUnallowedValues = jest.fn( + ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => new Promise((resolve) => resolve(mockUnallowedValuesResponse)) +); + +jest.mock('../../../../use_unallowed_values/helpers', () => { + const original = jest.requireActual('../../../../use_unallowed_values/helpers'); + + return { + ...original, + fetchUnallowedValues: ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => + mockFetchUnallowedValues({ + abortController, + indexName, + requestItems, + }), + }; +}); + +describe('checkIndex', () => { + const defaultBytesFormat = '0,0.[0]b'; + const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + + const defaultNumberFormat = '0,0.[000]'; + const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + + const indexName = 'auditbeat-custom-index-1'; + const pattern = 'auditbeat-*'; + + describe('happy path', () => { + const onCheckCompleted = jest.fn(); + + beforeEach(async () => { + jest.clearAllMocks(); + + await checkIndex({ + abortController: new AbortController(), + ecsMetadata, + formatBytes, + formatNumber, + indexName, + onCheckCompleted, + pattern, + version: EcsVersion, + }); + }); + + test('it invokes onCheckCompleted with a null `error`', () => { + expect(onCheckCompleted.mock.calls[0][0].error).toBeNull(); + }); + + test('it invokes onCheckCompleted with the expected `indexName`', () => { + expect(onCheckCompleted.mock.calls[0][0].indexName).toEqual(indexName); + }); + + test('it invokes onCheckCompleted with the non-default `partitionedFieldMetadata`', () => { + expect(onCheckCompleted.mock.calls[0][0].partitionedFieldMetadata).not.toEqual( + EMPTY_PARTITIONED_FIELD_METADATA + ); + }); + + test('it invokes onCheckCompleted with the expected`pattern`', () => { + expect(onCheckCompleted.mock.calls[0][0].pattern).toEqual(pattern); + }); + + test('it invokes onCheckCompleted with the expected `version`', () => { + expect(onCheckCompleted.mock.calls[0][0].version).toEqual(EcsVersion); + }); + }); + + describe('happy path, when the signal is aborted', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('it does NOT invoke onCheckCompleted', async () => { + const onCheckCompleted = jest.fn(); + + const abortController = new AbortController(); + abortController.abort(); + + await checkIndex({ + abortController, + ecsMetadata, + formatBytes, + formatNumber, + indexName, + onCheckCompleted, + pattern, + version: EcsVersion, + }); + + expect(onCheckCompleted).not.toBeCalled(); + }); + }); + + describe('when `ecsMetadata` is null', () => { + const onCheckCompleted = jest.fn(); + + beforeEach(async () => { + jest.clearAllMocks(); + + await checkIndex({ + abortController: new AbortController(), + ecsMetadata: null, // <-- + formatBytes, + formatNumber, + indexName, + onCheckCompleted, + pattern, + version: EcsVersion, + }); + }); + + test('it invokes onCheckCompleted with a null `error`', () => { + expect(onCheckCompleted.mock.calls[0][0].error).toBeNull(); + }); + + test('it invokes onCheckCompleted with the expected `indexName`', () => { + expect(onCheckCompleted.mock.calls[0][0].indexName).toEqual(indexName); + }); + + test('it invokes onCheckCompleted with the default `partitionedFieldMetadata`', () => { + expect(onCheckCompleted.mock.calls[0][0].partitionedFieldMetadata).toEqual( + EMPTY_PARTITIONED_FIELD_METADATA + ); + }); + + test('it invokes onCheckCompleted with the expected `pattern`', () => { + expect(onCheckCompleted.mock.calls[0][0].pattern).toEqual(pattern); + }); + + test('it invokes onCheckCompleted with the expected `version`', () => { + expect(onCheckCompleted.mock.calls[0][0].version).toEqual(EcsVersion); + }); + }); + + describe('when an error occurs', () => { + const onCheckCompleted = jest.fn(); + const error = 'simulated fetch mappings error'; + + beforeEach(async () => { + jest.clearAllMocks(); + + mockFetchMappings = jest.fn( + ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => new Promise((_, reject) => reject(new Error(error))) + ); + + await checkIndex({ + abortController: new AbortController(), + ecsMetadata, + formatBytes, + formatNumber, + indexName, + onCheckCompleted, + pattern, + version: EcsVersion, + }); + }); + + test('it invokes onCheckCompleted with the expected `error`', () => { + expect(onCheckCompleted.mock.calls[0][0].error).toEqual(`Error: ${error}`); + }); + + test('it invokes onCheckCompleted with the expected `indexName`', () => { + expect(onCheckCompleted.mock.calls[0][0].indexName).toEqual(indexName); + }); + + test('it invokes onCheckCompleted with null `partitionedFieldMetadata`', () => { + expect(onCheckCompleted.mock.calls[0][0].partitionedFieldMetadata).toBeNull(); + }); + + test('it invokes onCheckCompleted with the expected `pattern`', () => { + expect(onCheckCompleted.mock.calls[0][0].pattern).toEqual(pattern); + }); + + test('it invokes onCheckCompleted with the expected `version`', () => { + expect(onCheckCompleted.mock.calls[0][0].version).toEqual(EcsVersion); + }); + }); + + describe('when an error occurs, but the error does not have a toString', () => { + const onCheckCompleted = jest.fn(); + + beforeEach(async () => { + jest.clearAllMocks(); + + mockFetchMappings = jest.fn( + ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + // eslint-disable-next-line prefer-promise-reject-errors + }) => new Promise((_, reject) => reject(undefined)) + ); + + await checkIndex({ + abortController: new AbortController(), + ecsMetadata, + formatBytes, + formatNumber, + indexName, + onCheckCompleted, + pattern, + version: EcsVersion, + }); + }); + + test('it invokes onCheckCompleted with the fallback `error`', () => { + expect(onCheckCompleted.mock.calls[0][0].error).toEqual( + `An error occurred checking index ${indexName}` + ); + }); + + test('it invokes onCheckCompleted with the expected `indexName`', () => { + expect(onCheckCompleted.mock.calls[0][0].indexName).toEqual(indexName); + }); + + test('it invokes onCheckCompleted with null `partitionedFieldMetadata`', () => { + expect(onCheckCompleted.mock.calls[0][0].partitionedFieldMetadata).toBeNull(); + }); + + test('it invokes onCheckCompleted with the expected `pattern`', () => { + expect(onCheckCompleted.mock.calls[0][0].pattern).toEqual(pattern); + }); + + test('it invokes onCheckCompleted with the expected `version`', () => { + expect(onCheckCompleted.mock.calls[0][0].version).toEqual(EcsVersion); + }); + }); + + describe('when an error occurs, and the signal is aborted', () => { + const onCheckCompleted = jest.fn(); + const abortController = new AbortController(); + abortController.abort(); + + const error = 'simulated fetch mappings error'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('it does NOT invoke onCheckCompleted', async () => { + mockFetchMappings = jest.fn( + ({ + // eslint-disable-next-line @typescript-eslint/no-shadow + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => new Promise((_, reject) => reject(new Error(error))) + ); + + await checkIndex({ + abortController, + ecsMetadata, + formatBytes, + formatNumber, + indexName, + onCheckCompleted, + pattern, + version: EcsVersion, + }); + + expect(onCheckCompleted).not.toBeCalled(); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.ts index cd1ce0940b391..c65b3f7559071 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/check_index.ts @@ -15,7 +15,7 @@ import type { EcsMetadata, OnCheckCompleted, PartitionedFieldMetadata } from '.. import { fetchMappings } from '../../../../use_mappings/helpers'; import { fetchUnallowedValues, getUnallowedValues } from '../../../../use_unallowed_values/helpers'; -const EMPTY_PARTITIONED_FIELD_METADATA: PartitionedFieldMetadata = { +export const EMPTY_PARTITIONED_FIELD_METADATA: PartitionedFieldMetadata = { all: [], custom: [], ecsCompliant: [], @@ -25,6 +25,7 @@ const EMPTY_PARTITIONED_FIELD_METADATA: PartitionedFieldMetadata = { export async function checkIndex({ abortController, ecsMetadata, + formatBytes, formatNumber, indexName, onCheckCompleted, @@ -33,6 +34,7 @@ export async function checkIndex({ }: { abortController: AbortController; ecsMetadata: Record | null; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; indexName: string; onCheckCompleted: OnCheckCompleted; @@ -74,6 +76,7 @@ export async function checkIndex({ if (!abortController.signal.aborted) { onCheckCompleted({ error: null, + formatBytes, formatNumber, indexName, partitionedFieldMetadata, @@ -84,10 +87,8 @@ export async function checkIndex({ } catch (error) { if (!abortController.signal.aborted) { onCheckCompleted({ - error: - error.toString() != null - ? error.toString() - : i18n.AN_ERROR_OCCURRED_CHECKING_INDEX(indexName), + error: error != null ? error.toString() : i18n.AN_ERROR_OCCURRED_CHECKING_INDEX(indexName), + formatBytes, formatNumber, indexName, partitionedFieldMetadata: null, diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.test.ts new file mode 100644 index 0000000000000..5f96cfa9953a6 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.test.ts @@ -0,0 +1,107 @@ +/* + * 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 { getAllIndicesToCheck, getIndexDocsCountFromRollup, getIndexToCheck } from './helpers'; +import { mockPacketbeatPatternRollup } from '../../../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; + +const patternIndexNames: Record = { + 'packetbeat-*': [ + '.ds-packetbeat-8.6.1-2023.02.04-000001', + '.ds-packetbeat-8.5.3-2023.02.04-000001', + ], + 'auditbeat-*': [ + 'auditbeat-7.17.9-2023.02.13-000001', + 'auditbeat-custom-index-1', + '.ds-auditbeat-8.6.1-2023.02.13-000001', + ], + 'logs-*': [ + '.ds-logs-endpoint.alerts-default-2023.02.24-000001', + '.ds-logs-endpoint.events.process-default-2023.02.24-000001', + ], + 'remote:*': [], + '.alerts-security.alerts-default': ['.internal.alerts-security.alerts-default-000001'], +}; + +describe('helpers', () => { + describe('getIndexToCheck', () => { + test('it returns the expected `IndexToCheck`', () => { + expect( + getIndexToCheck({ + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + }) + ).toEqual({ + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + }); + }); + }); + + describe('getAllIndicesToCheck', () => { + test('it returns the sorted collection of `IndexToCheck`', () => { + expect(getAllIndicesToCheck(patternIndexNames)).toEqual([ + { + indexName: '.internal.alerts-security.alerts-default-000001', + pattern: '.alerts-security.alerts-default', + }, + { + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + }, + { + indexName: 'auditbeat-7.17.9-2023.02.13-000001', + pattern: 'auditbeat-*', + }, + { + indexName: '.ds-auditbeat-8.6.1-2023.02.13-000001', + pattern: 'auditbeat-*', + }, + { + indexName: '.ds-logs-endpoint.events.process-default-2023.02.24-000001', + pattern: 'logs-*', + }, + { + indexName: '.ds-logs-endpoint.alerts-default-2023.02.24-000001', + pattern: 'logs-*', + }, + { + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + pattern: 'packetbeat-*', + }, + { + indexName: '.ds-packetbeat-8.5.3-2023.02.04-000001', + pattern: 'packetbeat-*', + }, + ]); + }); + }); + + describe('getIndexDocsCountFromRollup', () => { + test('it returns the expected count when the `patternRollup` has `stats`', () => { + expect( + getIndexDocsCountFromRollup({ + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + patternRollup: mockPacketbeatPatternRollup, + }) + ).toEqual(1628343); + }); + + test('it returns zero when the `patternRollup` `stats` is null', () => { + const patternRollup = { + ...mockPacketbeatPatternRollup, + stats: null, // <-- + }; + + expect( + getIndexDocsCountFromRollup({ + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + patternRollup, + }) + ).toEqual(0); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.ts index 4d07d4826f521..30f314c73ea3b 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/helpers.ts @@ -6,7 +6,7 @@ */ import type { IndicesStatsIndicesStats } from '@elastic/elasticsearch/lib/api/types'; -import { sortBy } from 'lodash/fp'; +import { orderBy } from 'lodash/fp'; import { getDocsCount } from '../../../../helpers'; import type { IndexToCheck, PatternRollup } from '../../../../types'; @@ -34,14 +34,14 @@ export const getAllIndicesToCheck = ( return a.localeCompare(b); }); - // return all `IndexToCheck` sorted first by pattern A-Z, and then by `docsCount` within the pattern + // return all `IndexToCheck` sorted first by pattern A-Z: return sortedPatterns.reduce((acc, pattern) => { - const indexNames = patternIndexNames[pattern] ?? []; + const indexNames = patternIndexNames[pattern]; const indicesToCheck = indexNames.map((indexName) => getIndexToCheck({ indexName, pattern }) ); - const sortedIndicesToCheck = sortBy('indexName', indicesToCheck).reverse(); + const sortedIndicesToCheck = orderBy(['indexName'], ['desc'], indicesToCheck); return [...acc, ...sortedIndicesToCheck]; }, []); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.test.tsx new file mode 100644 index 0000000000000..f2aa7a2666c33 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.test.tsx @@ -0,0 +1,377 @@ +/* + * 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 numeral from '@elastic/numeral'; +import userEvent from '@testing-library/user-event'; +import { act, render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { mockMappingsResponse } from '../../../../mock/mappings_response/mock_mappings_response'; +import { TestProviders } from '../../../../mock/test_providers/test_providers'; +import { mockUnallowedValuesResponse } from '../../../../mock/unallowed_values/mock_unallowed_values'; +import { CANCEL, CHECK_ALL } from '../../../../translations'; +import { + OnCheckCompleted, + PartitionedFieldMetadata, + UnallowedValueRequestItem, +} from '../../../../types'; +import { CheckAll } from '.'; +import { EMPTY_STAT } from '../../../../helpers'; + +const defaultBytesFormat = '0,0.[0]b'; +const mockFormatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const mockFormatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const mockFetchMappings = jest.fn( + ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => + new Promise((resolve) => { + resolve(mockMappingsResponse); // happy path + }) +); + +jest.mock('../../../../use_mappings/helpers', () => ({ + fetchMappings: ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => + mockFetchMappings({ + abortController, + patternOrIndexName, + }), +})); + +const mockFetchUnallowedValues = jest.fn( + ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => new Promise((resolve) => resolve(mockUnallowedValuesResponse)) +); + +jest.mock('../../../../use_unallowed_values/helpers', () => { + const original = jest.requireActual('../../../../use_unallowed_values/helpers'); + + return { + ...original, + fetchUnallowedValues: ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => + mockFetchUnallowedValues({ + abortController, + indexName, + requestItems, + }), + }; +}); + +const patternIndexNames = { + '.alerts-security.alerts-default': ['.internal.alerts-security.alerts-default-000001'], + 'auditbeat-*': [ + 'auditbeat-7.3.2-2023.03.27-000001', + '.ds-auditbeat-8.6.1-2023.03.29-000001', + 'auditbeat-custom-empty-index-1', + 'auditbeat-7.10.2-2023.03.27-000001', + 'auditbeat-7.2.1-2023.03.27-000001', + 'auditbeat-custom-index-1', + ], + 'logs-*': [ + '.ds-logs-endpoint.events.process-default-2023.03.27-000001', + '.ds-logs-endpoint.alerts-default-2023.03.27-000001', + ], + 'packetbeat-*': [ + '.ds-packetbeat-8.6.1-2023.03.27-000001', + '.ds-packetbeat-8.5.3-2023.03.27-000001', + ], +}; + +const ilmPhases: string[] = ['hot', 'warm', 'unmanaged']; + +describe('CheckAll', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('it renders the expected button text when a check is NOT running', () => { + render( + + + + ); + + expect(screen.getByTestId('checkAll')).toHaveTextContent(CHECK_ALL); + }); + + test('it renders the expected button text when a check is running', () => { + render( + + + + ); + + const button = screen.getByTestId('checkAll'); + + userEvent.click(button); // <-- START the check + + expect(screen.getByTestId('checkAll')).toHaveTextContent(CANCEL); + }); + + describe('formatNumber', () => { + test('it renders a comma-separated `value` via the `defaultNumberFormat`', async () => { + /** stores the result of invoking `CheckAll`'s `formatNumber` function */ + let formatNumberResult = ''; + + const onCheckCompleted: OnCheckCompleted = jest.fn( + ({ + formatBytes, + formatNumber, + }: { + error: string | null; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; + indexName: string; + partitionedFieldMetadata: PartitionedFieldMetadata | null; + pattern: string; + version: string; + }) => { + const value = 123456789; // numeric input to `CheckAll`'s `formatNumber` function + + formatNumberResult = formatNumber(value); + } + ); + + render( + + + + ); + + const button = screen.getByTestId('checkAll'); + + userEvent.click(button); // <-- START the check + + await waitFor(() => { + expect(formatNumberResult).toEqual('123,456,789'); // a comma-separated `value`, because it's numeric + }); + }); + + test('it renders an empty stat placeholder when `value` is undefined', async () => { + /** stores the result of invoking `CheckAll`'s `formatNumber` function */ + let formatNumberResult = ''; + + const onCheckCompleted: OnCheckCompleted = jest.fn( + ({ + formatBytes, + formatNumber, + }: { + error: string | null; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; + indexName: string; + partitionedFieldMetadata: PartitionedFieldMetadata | null; + pattern: string; + version: string; + }) => { + const value = undefined; // undefined input to `CheckAll`'s `formatNumber` function + + formatNumberResult = formatNumber(value); + } + ); + + render( + + + + ); + + const button = screen.getByTestId('checkAll'); + + userEvent.click(button); // <-- START the check + + await waitFor(() => { + expect(formatNumberResult).toEqual(EMPTY_STAT); // a placeholder, because `value` is undefined + }); + }); + }); + + describe('when a running check is cancelled', () => { + const setCheckAllIndiciesChecked = jest.fn(); + const setCheckAllTotalIndiciesToCheck = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + + const button = screen.getByTestId('checkAll'); + + userEvent.click(button); // <-- START the check + + userEvent.click(button); // <-- STOP the check + }); + + test('it invokes `setCheckAllIndiciesChecked` twice: when the check was started, and when it was cancelled', () => { + expect(setCheckAllIndiciesChecked).toHaveBeenCalledTimes(2); + }); + + test('it invokes `setCheckAllTotalIndiciesToCheck` with the expected index count when the check is STARTED', () => { + expect(setCheckAllTotalIndiciesToCheck.mock.calls[0][0]).toEqual(11); + }); + + test('it invokes `setCheckAllTotalIndiciesToCheck` with the expected index count when the check is STOPPED', () => { + expect(setCheckAllTotalIndiciesToCheck.mock.calls[1][0]).toEqual(0); + }); + }); + + describe('when all checks have completed', () => { + const setIndexToCheck = jest.fn(); + + beforeEach(async () => { + jest.clearAllMocks(); + jest.useFakeTimers(); + + render( + + + + ); + + const button = screen.getByTestId('checkAll'); + + userEvent.click(button); // <-- start the check + + const totalIndexNames = Object.values(patternIndexNames).reduce( + (total, indices) => total + indices.length, + 0 + ); + + // simulate the wall clock advancing + for (let i = 0; i < totalIndexNames + 1; i++) { + act(() => { + jest.advanceTimersByTime(1000 * 10); + }); + + await waitFor(() => {}); + } + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + test('it invokes setIndexToCheck with `null` after all the checks have completed', () => { + expect(setIndexToCheck).toBeCalledWith(null); + }); + + // test all the patterns + Object.entries(patternIndexNames).forEach((pattern) => { + const [patternName, indexNames] = pattern; + + // test each index in the pattern + indexNames.forEach((indexName) => { + test(`it invokes setIndexToCheck with the expected value for the '${patternName}' pattern's index, named '${indexName}'`, () => { + expect(setIndexToCheck).toBeCalledWith({ + indexName, + pattern: patternName, + }); + }); + }); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.tsx index ab5de8d7ccdcb..ef768249aa2a4 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/check_all/index.tsx @@ -8,15 +8,18 @@ import { EcsFlat, EcsVersion } from '@kbn/ecs'; import { EuiButton } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import React, { useCallback, useEffect, useRef, useState } from 'react'; +import styled from 'styled-components'; import { checkIndex } from './check_index'; -import { EMPTY_STAT } from '../../../../helpers'; import { getAllIndicesToCheck } from './helpers'; import * as i18n from '../../../../translations'; import type { EcsMetadata, IndexToCheck, OnCheckCompleted } from '../../../../types'; +const CheckAllButton = styled(EuiButton)` + width: 112px; +`; + async function wait(ms: number) { const delay = () => new Promise((resolve) => @@ -29,7 +32,8 @@ async function wait(ms: number) { } interface Props { - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; ilmPhases: string[]; incrementCheckAllIndiciesChecked: () => void; onCheckCompleted: OnCheckCompleted; @@ -43,7 +47,8 @@ interface Props { const DELAY_AFTER_EVERY_CHECK_COMPLETES = 3000; // ms const CheckAllComponent: React.FC = ({ - defaultNumberFormat, + formatBytes, + formatNumber, ilmPhases, incrementCheckAllIndiciesChecked, onCheckCompleted, @@ -55,11 +60,6 @@ const CheckAllComponent: React.FC = ({ }) => { const abortController = useRef(new AbortController()); const [isRunning, setIsRunning] = useState(false); - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); const cancelIfRunning = useCallback(() => { if (isRunning) { @@ -89,6 +89,7 @@ const CheckAllComponent: React.FC = ({ await checkIndex({ abortController: abortController.current, ecsMetadata: EcsFlat as unknown as Record, + formatBytes, formatNumber, indexName, onCheckCompleted, @@ -118,6 +119,7 @@ const CheckAllComponent: React.FC = ({ } }, [ cancelIfRunning, + formatBytes, formatNumber, incrementCheckAllIndiciesChecked, isRunning, @@ -141,14 +143,18 @@ const CheckAllComponent: React.FC = ({ }; }, [abortController]); + const disabled = ilmPhases.length === 0; + return ( - {isRunning ? i18n.CANCEL : i18n.CHECK_ALL} - + ); }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.test.tsx new file mode 100644 index 0000000000000..7d139121afbc4 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.test.tsx @@ -0,0 +1,128 @@ +/* + * 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 numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { EMPTY_STAT } from '../../../helpers'; +import { alertIndexWithAllResults } from '../../../mock/pattern_rollup/mock_alerts_pattern_rollup'; +import { auditbeatWithAllResults } from '../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { packetbeatNoResults } from '../../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { TestProviders } from '../../../mock/test_providers/test_providers'; +import { PatternRollup } from '../../../types'; +import { Props, SummaryActions } from '.'; +import { + getTotalDocsCount, + getTotalIncompatible, + getTotalIndices, + getTotalIndicesChecked, + getTotalSizeInBytes, +} from '../../../use_results_rollup/helpers'; + +const mockCopyToClipboard = jest.fn((value) => true); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + copyToClipboard: (value: string) => mockCopyToClipboard(value), + }; +}); + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +const patternIndexNames: Record = { + 'auditbeat-*': [ + '.ds-auditbeat-8.6.1-2023.02.07-000001', + 'auditbeat-custom-empty-index-1', + 'auditbeat-custom-index-1', + ], + '.alerts-security.alerts-default': ['.internal.alerts-security.alerts-default-000001'], + 'packetbeat-*': [ + '.ds-packetbeat-8.5.3-2023.02.04-000001', + '.ds-packetbeat-8.6.1-2023.02.04-000001', + ], +}; + +const lastChecked = '2023-03-28T23:27:28.159Z'; + +const totalDocsCount = getTotalDocsCount(patternRollups); +const totalIncompatible = getTotalIncompatible(patternRollups); +const totalIndices = getTotalIndices(patternRollups); +const totalIndicesChecked = getTotalIndicesChecked(patternRollups); +const totalSizeInBytes = getTotalSizeInBytes(patternRollups); + +const defaultProps: Props = { + addSuccessToast: jest.fn(), + canUserCreateAndReadCases: () => true, + errorSummary: [], + formatBytes, + formatNumber, + ilmPhases, + lastChecked, + openCreateCaseFlyout: jest.fn(), + onCheckCompleted: jest.fn(), + patternIndexNames, + patternRollups, + patterns, + setLastChecked: jest.fn(), + totalDocsCount, + totalIncompatible, + totalIndices, + totalIndicesChecked, + sizeInBytes: totalSizeInBytes, +}; + +describe('SummaryActions', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders the check all button', () => { + expect(screen.getByTestId('checkAll')).toBeInTheDocument(); + }); + + test('it renders the check status indicator', () => { + expect(screen.getByTestId('checkStatus')).toBeInTheDocument(); + }); + + test('it renders the actions', () => { + expect(screen.getByTestId('actions')).toBeInTheDocument(); + }); + + test('it invokes addSuccessToast when the copy to clipboard button is clicked', () => { + const button = screen.getByTestId('copyToClipboard'); + + userEvent.click(button); + + expect(defaultProps.addSuccessToast).toBeCalledWith({ + title: 'Copied results to the clipboard', + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.tsx index d659d81d93199..db53376746281 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/index.tsx @@ -6,15 +6,14 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import { sortBy } from 'lodash/fp'; -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import styled from 'styled-components'; import { CheckAll } from './check_all'; +import { CheckStatus } from '../check_status'; import { ERROR, INDEX, PATTERN } from '../errors_viewer/translations'; import { ERRORS } from '../errors_popover/translations'; -import { EMPTY_STAT } from '../../../helpers'; import { getDataQualitySummaryMarkdownComment, getErrorsMarkdownTable, @@ -23,8 +22,8 @@ import { getSummaryTableMarkdownHeader, getSummaryTableMarkdownRow, } from '../../index_properties/markdown/helpers'; -import { getSummaryTableItems } from '../../pattern/helpers'; -import { TakeActionMenu } from './take_action_menu'; +import { defaultSort, getSummaryTableItems } from '../../pattern/helpers'; +import { Actions } from './actions'; import type { DataQualityCheckResult, ErrorSummary, @@ -32,6 +31,7 @@ import type { OnCheckCompleted, PatternRollup, } from '../../../types'; +import { getSizeInBytes } from '../../../helpers'; const SummaryActionsFlexGroup = styled(EuiFlexGroup)` gap: ${({ theme }) => theme.eui.euiSizeS}; @@ -43,10 +43,12 @@ export const getResultsSortedByDocsCount = ( results != null ? sortBy('docsCount', Object.values(results)).reverse() : []; export const getAllMarkdownCommentsFromResults = ({ + formatBytes, formatNumber, patternIndexNames, patternRollup, }: { + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; patternIndexNames: Record; patternRollup: PatternRollup; @@ -60,6 +62,8 @@ export const getAllMarkdownCommentsFromResults = ({ pattern: patternRollup.pattern, patternDocsCount: patternRollup.docsCount ?? 0, results: patternRollup.results, + sortByColumn: defaultSort.sort.field, + sortByDirection: defaultSort.sort.direction, stats: patternRollup.stats, }); @@ -69,11 +73,13 @@ export const getAllMarkdownCommentsFromResults = ({ return getSummaryTableMarkdownRow({ docsCount: item.docsCount, + formatBytes, formatNumber, ilmPhase: item.ilmPhase, indexName: item.indexName, incompatible: result?.incompatible, patternDocsCount: patternRollup.docsCount ?? 0, + sizeInBytes: getSizeInBytes({ indexName: item.indexName, stats: patternRollup.stats }), }).trim(); }); @@ -89,10 +95,12 @@ export const getAllMarkdownCommentsFromResults = ({ }; export const getAllMarkdownComments = ({ + formatBytes, formatNumber, patternIndexNames, patternRollups, }: { + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; patternIndexNames: Record; patternRollups: Record; @@ -108,10 +116,12 @@ export const getAllMarkdownComments = ({ (acc, pattern) => [ ...acc, getPatternSummaryMarkdownComment({ + formatBytes, formatNumber, patternRollup: patternRollups[pattern], }), ...getAllMarkdownCommentsFromResults({ + formatBytes, formatNumber, patternRollup: patternRollups[pattern], patternIndexNames, @@ -121,13 +131,14 @@ export const getAllMarkdownComments = ({ ); }; -interface Props { +export interface Props { addSuccessToast: (toast: { title: string }) => void; canUserCreateAndReadCases: () => boolean; - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; errorSummary: ErrorSummary[]; ilmPhases: string[]; - incrementCheckAllIndiciesChecked: () => void; + lastChecked: string; onCheckCompleted: OnCheckCompleted; openCreateCaseFlyout: ({ comments, @@ -139,51 +150,54 @@ interface Props { patternIndexNames: Record; patternRollups: Record; patterns: string[]; - setCheckAllIndiciesChecked: (checkAllIndiciesChecked: number) => void; - setCheckAllTotalIndiciesToCheck: (checkAllTotalIndiciesToCheck: number) => void; - setIndexToCheck: (indexToCheck: IndexToCheck | null) => void; + setLastChecked: (lastChecked: string) => void; totalDocsCount: number | undefined; totalIncompatible: number | undefined; totalIndices: number | undefined; totalIndicesChecked: number | undefined; + sizeInBytes: number | undefined; } const SummaryActionsComponent: React.FC = ({ addSuccessToast, canUserCreateAndReadCases, - defaultNumberFormat, + formatBytes, + formatNumber, errorSummary, ilmPhases, - incrementCheckAllIndiciesChecked, + lastChecked, onCheckCompleted, openCreateCaseFlyout, patternIndexNames, patternRollups, patterns, + setLastChecked, totalDocsCount, - setCheckAllIndiciesChecked, - setCheckAllTotalIndiciesToCheck, - setIndexToCheck, totalIncompatible, totalIndices, totalIndicesChecked, + sizeInBytes, }) => { - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); + const [indexToCheck, setIndexToCheck] = useState(null); + const [checkAllIndiciesChecked, setCheckAllIndiciesChecked] = useState(0); + const [checkAllTotalIndiciesToCheck, setCheckAllTotalIndiciesToCheck] = useState(0); + const incrementCheckAllIndiciesChecked = useCallback(() => { + setCheckAllIndiciesChecked((current) => current + 1); + }, []); const getMarkdownComments = useCallback( (): string[] => [ getDataQualitySummaryMarkdownComment({ + formatBytes, formatNumber, totalDocsCount, totalIncompatible, totalIndices, totalIndicesChecked, + sizeInBytes, }), ...getAllMarkdownComments({ + formatBytes, formatNumber, patternIndexNames, patternRollups, @@ -197,9 +211,11 @@ const SummaryActionsComponent: React.FC = ({ ], [ errorSummary, + formatBytes, formatNumber, patternIndexNames, patternRollups, + sizeInBytes, totalDocsCount, totalIncompatible, totalIndices, @@ -208,30 +224,46 @@ const SummaryActionsComponent: React.FC = ({ ); return ( - - - - - - - - - + <> + + + + + + + + + + + + + + ); }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/take_action_menu/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/take_action_menu/index.tsx deleted file mode 100644 index 4683a96a76987..0000000000000 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/take_action_menu/index.tsx +++ /dev/null @@ -1,125 +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 { - copyToClipboard, - EuiButtonEmpty, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiPopover, -} from '@elastic/eui'; -import React, { useCallback, useMemo, useState } from 'react'; - -import { - ADD_TO_NEW_CASE, - COPIED_RESULTS_TOAST_TITLE, - COPY_TO_CLIPBOARD, -} from '../../../../translations'; -import * as i18n from './translations'; -import { useAddToNewCase } from '../../../../use_add_to_new_case'; - -interface Props { - addSuccessToast: (toast: { title: string }) => void; - canUserCreateAndReadCases: () => boolean; - getMarkdownComments: () => string[]; - openCreateCaseFlyout: ({ - comments, - headerContent, - }: { - comments: string[]; - headerContent?: React.ReactNode; - }) => void; -} - -const TakeActionMenuComponent: React.FC = ({ - addSuccessToast, - canUserCreateAndReadCases, - getMarkdownComments, - openCreateCaseFlyout, -}) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const closePopover = useCallback(() => { - setIsPopoverOpen(false); - }, []); - const onButtonClick = useCallback(() => { - setIsPopoverOpen((current) => !current); - }, []); - - const takeActionButton = useMemo( - () => ( - - {i18n.TAKE_ACTION} - - ), - [onButtonClick] - ); - - const { disabled: addToNewCaseDisabled, onAddToNewCase } = useAddToNewCase({ - canUserCreateAndReadCases, - openCreateCaseFlyout, - }); - - const onClickAddToCase = useCallback( - () => onAddToNewCase([getMarkdownComments().join('\n')]), - [getMarkdownComments, onAddToNewCase] - ); - - const onCopy = useCallback(() => { - const markdown = getMarkdownComments().join('\n'); - copyToClipboard(markdown); - - closePopover(); - - addSuccessToast({ - title: COPIED_RESULTS_TOAST_TITLE, - }); - }, [addSuccessToast, closePopover, getMarkdownComments]); - - const addToNewCaseContextMenuOnClick = useCallback(() => { - closePopover(); - onClickAddToCase(); - }, [closePopover, onClickAddToCase]); - - const items = useMemo( - () => [ - - {ADD_TO_NEW_CASE} - , - - - {COPY_TO_CLIPBOARD} - , - ], - [addToNewCaseContextMenuOnClick, addToNewCaseDisabled, onCopy] - ); - - return ( - - - - ); -}; - -TakeActionMenuComponent.displayName = 'TakeActionMenuComponent'; - -export const TakeActionMenu = React.memo(TakeActionMenuComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.test.tsx new file mode 100644 index 0000000000000..f57d9f52737d7 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.test.tsx @@ -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 { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { ErrorEmptyPrompt } from '.'; + +describe('ErrorEmptyPrompt', () => { + test('it renders the expected content', () => { + const title = 'This is the title of this work'; + + render( + + + + ); + + expect(screen.getByTestId('errorEmptyPrompt').textContent?.includes(title)).toBe(true); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.tsx index a0b70daee0e40..3214b704dc685 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/error_empty_prompt/index.tsx @@ -15,7 +15,7 @@ interface Props { } const ErrorEmptyPromptComponent: React.FC = ({ title }) => ( - +

    {i18n.ERRORS_MAY_OCCUR}

    {i18n.THE_FOLLOWING_PRIVILEGES_ARE_REQUIRED} diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.test.tsx new file mode 100644 index 0000000000000..6efe7579d7325 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.test.tsx @@ -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 { + IlmExplainLifecycleLifecycleExplain, + IlmExplainLifecycleLifecycleExplainManaged, + IlmExplainLifecycleLifecycleExplainUnmanaged, +} from '@elastic/elasticsearch/lib/api/types'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { IlmPhaseCounts } from '.'; +import { getIlmExplainPhaseCounts } from '../pattern/helpers'; + +const hot: IlmExplainLifecycleLifecycleExplainManaged = { + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536751379, + time_since_index_creation: '3.98d', + lifecycle_date_millis: 1675536751379, + age: '3.98d', + phase: 'hot', + phase_time_millis: 1675536751809, + action: 'rollover', + action_time_millis: 1675536751809, + step: 'check-rollover-ready', + step_time_millis: 1675536751809, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, +}; +const warm = { + ...hot, + phase: 'warm', +}; +const cold = { + ...hot, + phase: 'cold', +}; +const frozen = { + ...hot, + phase: 'frozen', +}; + +const managed: Record = { + hot, + warm, + cold, + frozen, +}; + +const unmanaged: IlmExplainLifecycleLifecycleExplainUnmanaged = { + index: 'foo', + managed: false, +}; + +const ilmExplain: Record = { + ...managed, + [unmanaged.index]: unmanaged, +}; + +const ilmExplainPhaseCounts = getIlmExplainPhaseCounts(ilmExplain); + +const pattern = 'packetbeat-*'; + +describe('IlmPhaseCounts', () => { + test('it renders the expected counts', () => { + render( + + + + ); + + expect(screen.getByTestId('ilmPhaseCounts')).toHaveTextContent( + 'hot (1)unmanaged (1)warm (1)cold (1)frozen (1)' + ); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.tsx index 3aa738d4cb788..82664778becb0 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/ilm_phase_counts/index.tsx @@ -25,7 +25,7 @@ interface Props { } const IlmPhaseCountsComponent: React.FC = ({ ilmExplainPhaseCounts, pattern }) => ( - + {phases.map((phase) => ilmExplainPhaseCounts[phase] != null && ilmExplainPhaseCounts[phase] > 0 ? ( diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.test.tsx new file mode 100644 index 0000000000000..1c37ec799c53c --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.test.tsx @@ -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 { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EmptyPromptBody } from './empty_prompt_body'; +import { TestProviders } from '../../mock/test_providers/test_providers'; + +describe('EmptyPromptBody', () => { + const content = 'foo bar baz @baz'; + + test('it renders the expected content', () => { + render( + + + + ); + + expect(screen.getByTestId('emptyPromptBody')).toHaveTextContent(content); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.tsx index 80c9151eaa050..33283d11649a8 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_body.tsx @@ -11,7 +11,9 @@ interface Props { body: string; } -const EmptyPromptBodyComponent: React.FC = ({ body }) =>

    {body}

    ; +const EmptyPromptBodyComponent: React.FC = ({ body }) => ( +

    {body}

    +); EmptyPromptBodyComponent.displayName = 'EmptyPromptBodyComponent'; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.test.tsx new file mode 100644 index 0000000000000..6bb3b72ed3ece --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.test.tsx @@ -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 { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EmptyPromptTitle } from './empty_prompt_title'; +import { TestProviders } from '../../mock/test_providers/test_providers'; + +describe('EmptyPromptTitle', () => { + const title = 'What is a great title?'; + + test('it renders the expected content', () => { + render( + + + + ); + + expect(screen.getByTestId('emptyPromptTitle')).toHaveTextContent(title); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.tsx index a9d2e1f6a74d9..ee06b2f446858 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/empty_prompt_title.tsx @@ -11,7 +11,9 @@ interface Props { title: string; } -const EmptyPromptTitleComponent: React.FC = ({ title }) =>

    {title}

    ; +const EmptyPromptTitleComponent: React.FC = ({ title }) => ( +

    {title}

    +); EmptyPromptTitleComponent.displayName = 'EmptyPromptTitleComponent'; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/helpers.test.ts new file mode 100644 index 0000000000000..e0644fdc4a5c0 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/helpers.test.ts @@ -0,0 +1,800 @@ +/* + * 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 { EcsFlat } from '@kbn/ecs'; + +import { + getMappingsProperties, + getSortedPartitionedFieldMetadata, + hasAllDataFetchingCompleted, +} from './helpers'; +import { mockIndicesGetMappingIndexMappingRecords } from '../../mock/indices_get_mapping_index_mapping_record/mock_indices_get_mapping_index_mapping_record'; +import { mockMappingsProperties } from '../../mock/mappings_properties/mock_mappings_properties'; +import { EcsMetadata } from '../../types'; + +const ecsMetadata: Record = EcsFlat as unknown as Record; + +describe('helpers', () => { + describe('getSortedPartitionedFieldMetadata', () => { + test('it returns null when mappings are loading', () => { + expect( + getSortedPartitionedFieldMetadata({ + ecsMetadata, + loadingMappings: true, // <-- + mappingsProperties: mockMappingsProperties, + unallowedValues: {}, + }) + ).toBeNull(); + }); + + test('it returns null when `ecsMetadata` is null', () => { + expect( + getSortedPartitionedFieldMetadata({ + ecsMetadata: null, // <-- + loadingMappings: false, + mappingsProperties: mockMappingsProperties, + unallowedValues: {}, + }) + ).toBeNull(); + }); + + test('it returns null when `unallowedValues` is null', () => { + expect( + getSortedPartitionedFieldMetadata({ + ecsMetadata, + loadingMappings: false, + mappingsProperties: mockMappingsProperties, + unallowedValues: null, // <-- + }) + ).toBeNull(); + }); + + describe('when `mappingsProperties` is unknown', () => { + const expected = { + all: [], + custom: [], + ecsCompliant: [], + incompatible: [ + { + description: + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', + hasEcsMetadata: true, + indexFieldName: '@timestamp', + indexFieldType: '-', + indexInvalidValues: [], + isEcsCompliant: false, + isInSameFamily: false, + type: 'date', + }, + ], + }; + + test('it returns a `PartitionedFieldMetadata` with an `incompatible` `@timestamp` when `mappingsProperties` is undefined', () => { + expect( + getSortedPartitionedFieldMetadata({ + ecsMetadata, + loadingMappings: false, + mappingsProperties: undefined, // <-- + unallowedValues: {}, + }) + ).toEqual(expected); + }); + + test('it returns a `PartitionedFieldMetadata` with an `incompatible` `@timestamp` when `mappingsProperties` is null', () => { + expect( + getSortedPartitionedFieldMetadata({ + ecsMetadata, + loadingMappings: false, + mappingsProperties: null, // <-- + unallowedValues: {}, + }) + ).toEqual(expected); + }); + }); + + test('it returns the expected sorted field metadata', () => { + const unallowedValues = { + 'event.category': [ + { + count: 2, + fieldName: 'an_invalid_category', + }, + { + count: 1, + fieldName: 'theory', + }, + ], + 'event.kind': [], + 'event.outcome': [], + 'event.type': [], + }; + + expect( + getSortedPartitionedFieldMetadata({ + ecsMetadata, + loadingMappings: false, + mappingsProperties: mockMappingsProperties, + unallowedValues, + }) + ).toEqual({ + all: [ + { + dashed_name: 'timestamp', + description: + 'Date/time when the event originated.\nThis is the date/time extracted from the event, typically representing when the event was generated by the source.\nIf the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline.\nRequired field for all events.', + example: '2016-05-23T08:05:34.853Z', + flat_name: '@timestamp', + level: 'core', + name: '@timestamp', + normalize: [], + required: true, + short: 'Date/time when the event originated.', + type: 'date', + indexFieldName: '@timestamp', + indexFieldType: 'date', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: true, + isInSameFamily: false, + }, + { + allowed_values: [ + { + description: + 'Events in this category are related to the challenge and response process in which credentials are supplied and verified to allow the creation of a session. Common sources for these logs are Windows event logs and ssh logs. Visualize and analyze events in this category to look for failed logins, and other authentication-related activity.', + expected_event_types: ['start', 'end', 'info'], + name: 'authentication', + }, + { + description: + 'Events in the configuration category have to deal with creating, modifying, or deleting the settings or parameters of an application, process, or system.\nExample sources include security policy change logs, configuration auditing logging, and system integrity monitoring.', + expected_event_types: ['access', 'change', 'creation', 'deletion', 'info'], + name: 'configuration', + }, + { + description: + 'The database category denotes events and metrics relating to a data storage and retrieval system. Note that use of this category is not limited to relational database systems. Examples include event logs from MS SQL, MySQL, Elasticsearch, MongoDB, etc. Use this category to visualize and analyze database activity such as accesses and changes.', + expected_event_types: ['access', 'change', 'info', 'error'], + name: 'database', + }, + { + description: + 'Events in the driver category have to do with operating system device drivers and similar software entities such as Windows drivers, kernel extensions, kernel modules, etc.\nUse events and metrics in this category to visualize and analyze driver-related activity and status on hosts.', + expected_event_types: ['change', 'end', 'info', 'start'], + name: 'driver', + }, + { + description: + 'This category is used for events relating to email messages, email attachments, and email network or protocol activity.\nEmails events can be produced by email security gateways, mail transfer agents, email cloud service providers, or mail server monitoring applications.', + expected_event_types: ['info'], + name: 'email', + }, + { + description: + 'Relating to a set of information that has been created on, or has existed on a filesystem. Use this category of events to visualize and analyze the creation, access, and deletions of files. Events in this category can come from both host-based and network-based sources. An example source of a network-based detection of a file transfer would be the Zeek file.log.', + expected_event_types: ['change', 'creation', 'deletion', 'info'], + name: 'file', + }, + { + description: + 'Use this category to visualize and analyze information such as host inventory or host lifecycle events.\nMost of the events in this category can usually be observed from the outside, such as from a hypervisor or a control plane\'s point of view. Some can also be seen from within, such as "start" or "end".\nNote that this category is for information about hosts themselves; it is not meant to capture activity "happening on a host".', + expected_event_types: ['access', 'change', 'end', 'info', 'start'], + name: 'host', + }, + { + description: + 'Identity and access management (IAM) events relating to users, groups, and administration. Use this category to visualize and analyze IAM-related logs and data from active directory, LDAP, Okta, Duo, and other IAM systems.', + expected_event_types: [ + 'admin', + 'change', + 'creation', + 'deletion', + 'group', + 'info', + 'user', + ], + name: 'iam', + }, + { + description: + 'Relating to intrusion detections from IDS/IPS systems and functions, both network and host-based. Use this category to visualize and analyze intrusion detection alerts from systems such as Snort, Suricata, and Palo Alto threat detections.', + expected_event_types: ['allowed', 'denied', 'info'], + name: 'intrusion_detection', + }, + { + description: + 'Malware detection events and alerts. Use this category to visualize and analyze malware detections from EDR/EPP systems such as Elastic Endpoint Security, Symantec Endpoint Protection, Crowdstrike, and network IDS/IPS systems such as Suricata, or other sources of malware-related events such as Palo Alto Networks threat logs and Wildfire logs.', + expected_event_types: ['info'], + name: 'malware', + }, + { + description: + 'Relating to all network activity, including network connection lifecycle, network traffic, and essentially any event that includes an IP address. Many events containing decoded network protocol transactions fit into this category. Use events in this category to visualize or analyze counts of network ports, protocols, addresses, geolocation information, etc.', + expected_event_types: [ + 'access', + 'allowed', + 'connection', + 'denied', + 'end', + 'info', + 'protocol', + 'start', + ], + name: 'network', + }, + { + description: + 'Relating to software packages installed on hosts. Use this category to visualize and analyze inventory of software installed on various hosts, or to determine host vulnerability in the absence of vulnerability scan data.', + expected_event_types: [ + 'access', + 'change', + 'deletion', + 'info', + 'installation', + 'start', + ], + name: 'package', + }, + { + description: + 'Use this category of events to visualize and analyze process-specific information such as lifecycle events or process ancestry.', + expected_event_types: ['access', 'change', 'end', 'info', 'start'], + name: 'process', + }, + { + description: + 'Having to do with settings and assets stored in the Windows registry. Use this category to visualize and analyze activity such as registry access and modifications.', + expected_event_types: ['access', 'change', 'creation', 'deletion'], + name: 'registry', + }, + { + description: + 'The session category is applied to events and metrics regarding logical persistent connections to hosts and services. Use this category to visualize and analyze interactive or automated persistent connections between assets. Data for this category may come from Windows Event logs, SSH logs, or stateless sessions such as HTTP cookie-based sessions, etc.', + expected_event_types: ['start', 'end', 'info'], + name: 'session', + }, + { + description: + "Use this category to visualize and analyze events describing threat actors' targets, motives, or behaviors.", + expected_event_types: ['indicator'], + name: 'threat', + }, + { + description: + 'Relating to vulnerability scan results. Use this category to analyze vulnerabilities detected by Tenable, Qualys, internal scanners, and other vulnerability management sources.', + expected_event_types: ['info'], + name: 'vulnerability', + }, + { + description: + 'Relating to web server access. Use this category to create a dashboard of web server/proxy activity from apache, IIS, nginx web servers, etc. Note: events from network observers such as Zeek http log may also be included in this category.', + expected_event_types: ['access', 'error', 'info'], + name: 'web', + }, + ], + dashed_name: 'event-category', + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy.\n`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory.\nThis field is an array. This will allow proper categorization of some events that fall in multiple categories.', + example: 'authentication', + flat_name: 'event.category', + ignore_above: 1024, + level: 'core', + name: 'category', + normalize: ['array'], + short: 'Event category. The second categorization field in the hierarchy.', + type: 'keyword', + indexFieldName: 'event.category', + indexFieldType: 'keyword', + indexInvalidValues: [ + { + count: 2, + fieldName: 'an_invalid_category', + }, + { + count: 1, + fieldName: 'theory', + }, + ], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: true, + }, + { + dashed_name: 'host-name', + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + flat_name: 'host.name', + ignore_above: 1024, + level: 'core', + name: 'name', + normalize: [], + short: 'Name of the host.', + type: 'keyword', + indexFieldName: 'host.name', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'host.name.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'some.field', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'some.field.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + dashed_name: 'source-ip', + description: 'IP address of the source (IPv4 or IPv6).', + flat_name: 'source.ip', + level: 'core', + name: 'ip', + normalize: [], + short: 'IP address of the source.', + type: 'ip', + indexFieldName: 'source.ip', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'source.ip.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + dashed_name: 'source-port', + description: 'Port of the source.', + flat_name: 'source.port', + format: 'string', + level: 'core', + name: 'port', + normalize: [], + short: 'Port of the source.', + type: 'long', + indexFieldName: 'source.port', + indexFieldType: 'long', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: true, + isInSameFamily: false, + }, + ], + ecsCompliant: [ + { + dashed_name: 'timestamp', + description: + 'Date/time when the event originated.\nThis is the date/time extracted from the event, typically representing when the event was generated by the source.\nIf the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline.\nRequired field for all events.', + example: '2016-05-23T08:05:34.853Z', + flat_name: '@timestamp', + level: 'core', + name: '@timestamp', + normalize: [], + required: true, + short: 'Date/time when the event originated.', + type: 'date', + indexFieldName: '@timestamp', + indexFieldType: 'date', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: true, + isInSameFamily: false, + }, + { + dashed_name: 'source-port', + description: 'Port of the source.', + flat_name: 'source.port', + format: 'string', + level: 'core', + name: 'port', + normalize: [], + short: 'Port of the source.', + type: 'long', + indexFieldName: 'source.port', + indexFieldType: 'long', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: true, + isInSameFamily: false, + }, + ], + custom: [ + { + indexFieldName: 'host.name.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'some.field', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'some.field.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'source.ip.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + ], + incompatible: [ + { + allowed_values: [ + { + description: + 'Events in this category are related to the challenge and response process in which credentials are supplied and verified to allow the creation of a session. Common sources for these logs are Windows event logs and ssh logs. Visualize and analyze events in this category to look for failed logins, and other authentication-related activity.', + expected_event_types: ['start', 'end', 'info'], + name: 'authentication', + }, + { + description: + 'Events in the configuration category have to deal with creating, modifying, or deleting the settings or parameters of an application, process, or system.\nExample sources include security policy change logs, configuration auditing logging, and system integrity monitoring.', + expected_event_types: ['access', 'change', 'creation', 'deletion', 'info'], + name: 'configuration', + }, + { + description: + 'The database category denotes events and metrics relating to a data storage and retrieval system. Note that use of this category is not limited to relational database systems. Examples include event logs from MS SQL, MySQL, Elasticsearch, MongoDB, etc. Use this category to visualize and analyze database activity such as accesses and changes.', + expected_event_types: ['access', 'change', 'info', 'error'], + name: 'database', + }, + { + description: + 'Events in the driver category have to do with operating system device drivers and similar software entities such as Windows drivers, kernel extensions, kernel modules, etc.\nUse events and metrics in this category to visualize and analyze driver-related activity and status on hosts.', + expected_event_types: ['change', 'end', 'info', 'start'], + name: 'driver', + }, + { + description: + 'This category is used for events relating to email messages, email attachments, and email network or protocol activity.\nEmails events can be produced by email security gateways, mail transfer agents, email cloud service providers, or mail server monitoring applications.', + expected_event_types: ['info'], + name: 'email', + }, + { + description: + 'Relating to a set of information that has been created on, or has existed on a filesystem. Use this category of events to visualize and analyze the creation, access, and deletions of files. Events in this category can come from both host-based and network-based sources. An example source of a network-based detection of a file transfer would be the Zeek file.log.', + expected_event_types: ['change', 'creation', 'deletion', 'info'], + name: 'file', + }, + { + description: + 'Use this category to visualize and analyze information such as host inventory or host lifecycle events.\nMost of the events in this category can usually be observed from the outside, such as from a hypervisor or a control plane\'s point of view. Some can also be seen from within, such as "start" or "end".\nNote that this category is for information about hosts themselves; it is not meant to capture activity "happening on a host".', + expected_event_types: ['access', 'change', 'end', 'info', 'start'], + name: 'host', + }, + { + description: + 'Identity and access management (IAM) events relating to users, groups, and administration. Use this category to visualize and analyze IAM-related logs and data from active directory, LDAP, Okta, Duo, and other IAM systems.', + expected_event_types: [ + 'admin', + 'change', + 'creation', + 'deletion', + 'group', + 'info', + 'user', + ], + name: 'iam', + }, + { + description: + 'Relating to intrusion detections from IDS/IPS systems and functions, both network and host-based. Use this category to visualize and analyze intrusion detection alerts from systems such as Snort, Suricata, and Palo Alto threat detections.', + expected_event_types: ['allowed', 'denied', 'info'], + name: 'intrusion_detection', + }, + { + description: + 'Malware detection events and alerts. Use this category to visualize and analyze malware detections from EDR/EPP systems such as Elastic Endpoint Security, Symantec Endpoint Protection, Crowdstrike, and network IDS/IPS systems such as Suricata, or other sources of malware-related events such as Palo Alto Networks threat logs and Wildfire logs.', + expected_event_types: ['info'], + name: 'malware', + }, + { + description: + 'Relating to all network activity, including network connection lifecycle, network traffic, and essentially any event that includes an IP address. Many events containing decoded network protocol transactions fit into this category. Use events in this category to visualize or analyze counts of network ports, protocols, addresses, geolocation information, etc.', + expected_event_types: [ + 'access', + 'allowed', + 'connection', + 'denied', + 'end', + 'info', + 'protocol', + 'start', + ], + name: 'network', + }, + { + description: + 'Relating to software packages installed on hosts. Use this category to visualize and analyze inventory of software installed on various hosts, or to determine host vulnerability in the absence of vulnerability scan data.', + expected_event_types: [ + 'access', + 'change', + 'deletion', + 'info', + 'installation', + 'start', + ], + name: 'package', + }, + { + description: + 'Use this category of events to visualize and analyze process-specific information such as lifecycle events or process ancestry.', + expected_event_types: ['access', 'change', 'end', 'info', 'start'], + name: 'process', + }, + { + description: + 'Having to do with settings and assets stored in the Windows registry. Use this category to visualize and analyze activity such as registry access and modifications.', + expected_event_types: ['access', 'change', 'creation', 'deletion'], + name: 'registry', + }, + { + description: + 'The session category is applied to events and metrics regarding logical persistent connections to hosts and services. Use this category to visualize and analyze interactive or automated persistent connections between assets. Data for this category may come from Windows Event logs, SSH logs, or stateless sessions such as HTTP cookie-based sessions, etc.', + expected_event_types: ['start', 'end', 'info'], + name: 'session', + }, + { + description: + "Use this category to visualize and analyze events describing threat actors' targets, motives, or behaviors.", + expected_event_types: ['indicator'], + name: 'threat', + }, + { + description: + 'Relating to vulnerability scan results. Use this category to analyze vulnerabilities detected by Tenable, Qualys, internal scanners, and other vulnerability management sources.', + expected_event_types: ['info'], + name: 'vulnerability', + }, + { + description: + 'Relating to web server access. Use this category to create a dashboard of web server/proxy activity from apache, IIS, nginx web servers, etc. Note: events from network observers such as Zeek http log may also be included in this category.', + expected_event_types: ['access', 'error', 'info'], + name: 'web', + }, + ], + dashed_name: 'event-category', + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy.\n`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory.\nThis field is an array. This will allow proper categorization of some events that fall in multiple categories.', + example: 'authentication', + flat_name: 'event.category', + ignore_above: 1024, + level: 'core', + name: 'category', + normalize: ['array'], + short: 'Event category. The second categorization field in the hierarchy.', + type: 'keyword', + indexFieldName: 'event.category', + indexFieldType: 'keyword', + indexInvalidValues: [ + { + count: 2, + fieldName: 'an_invalid_category', + }, + { + count: 1, + fieldName: 'theory', + }, + ], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: true, + }, + { + dashed_name: 'host-name', + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + flat_name: 'host.name', + ignore_above: 1024, + level: 'core', + name: 'name', + normalize: [], + short: 'Name of the host.', + type: 'keyword', + indexFieldName: 'host.name', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + dashed_name: 'source-ip', + description: 'IP address of the source (IPv4 or IPv6).', + flat_name: 'source.ip', + level: 'core', + name: 'ip', + normalize: [], + short: 'IP address of the source.', + type: 'ip', + indexFieldName: 'source.ip', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: false, + }, + ], + }); + }); + }); + + describe('getMappingsProperties', () => { + test('it returns the expected mapping properties', () => { + expect( + getMappingsProperties({ + indexes: mockIndicesGetMappingIndexMappingRecords, + indexName: 'auditbeat-custom-index-1', + }) + ).toEqual({ + '@timestamp': { + type: 'date', + }, + event: { + properties: { + category: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + host: { + properties: { + name: { + fields: { + keyword: { + ignore_above: 256, + type: 'keyword', + }, + }, + type: 'text', + }, + }, + }, + some: { + properties: { + field: { + fields: { + keyword: { + ignore_above: 256, + type: 'keyword', + }, + }, + type: 'text', + }, + }, + }, + source: { + properties: { + ip: { + fields: { + keyword: { + ignore_above: 256, + type: 'keyword', + }, + }, + type: 'text', + }, + port: { + type: 'long', + }, + }, + }, + }); + }); + + test('it returns null when `indexes` is null', () => { + expect( + getMappingsProperties({ + indexes: null, // <-- + indexName: 'auditbeat-custom-index-1', + }) + ).toBeNull(); + }); + + test('it returns null when `indexName` does not exist in `indexes`', () => { + expect( + getMappingsProperties({ + indexes: mockIndicesGetMappingIndexMappingRecords, + indexName: 'does-not-exist', // <-- + }) + ).toBeNull(); + }); + + test('it returns null when `properties` does not exist in the mappings', () => { + const missingProperties = { + ...mockIndicesGetMappingIndexMappingRecords, + foozle: { + mappings: {}, // <-- does not have a `properties` + }, + }; + + expect( + getMappingsProperties({ + indexes: missingProperties, + indexName: 'foozle', + }) + ).toBeNull(); + }); + }); + + describe('hasAllDataFetchingCompleted', () => { + test('it returns false when both the mappings and unallowed values are loading', () => { + expect( + hasAllDataFetchingCompleted({ + loadingMappings: true, + loadingUnallowedValues: true, + }) + ).toBe(false); + }); + + test('it returns false when mappings are loading, and unallowed values are NOT loading', () => { + expect( + hasAllDataFetchingCompleted({ + loadingMappings: true, + loadingUnallowedValues: false, + }) + ).toBe(false); + }); + + test('it returns false when mappings are NOT loading, and unallowed values are loading', () => { + expect( + hasAllDataFetchingCompleted({ + loadingMappings: false, + loadingUnallowedValues: true, + }) + ).toBe(false); + }); + + test('it returns true when both the mappings and unallowed values have finished loading', () => { + expect( + hasAllDataFetchingCompleted({ + loadingMappings: false, + loadingUnallowedValues: false, + }) + ).toBe(true); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.test.tsx new file mode 100644 index 0000000000000..b6914daef0e7d --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.test.tsx @@ -0,0 +1,262 @@ +/* + * 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 { DARK_THEME } from '@elastic/charts'; +import numeral from '@elastic/numeral'; +import { render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { EMPTY_STAT } from '../../helpers'; +import { mockMappingsResponse } from '../../mock/mappings_response/mock_mappings_response'; +import { auditbeatWithAllResults } from '../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { mockUnallowedValuesResponse } from '../../mock/unallowed_values/mock_unallowed_values'; +import { LOADING_MAPPINGS, LOADING_UNALLOWED_VALUES } from './translations'; +import { UnallowedValueRequestItem } from '../../types'; +import { IndexProperties, Props } from '.'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const pattern = 'auditbeat-*'; +const patternRollup = auditbeatWithAllResults; + +let mockFetchMappings = jest.fn( + ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => + new Promise((resolve) => { + resolve(mockMappingsResponse); // happy path + }) +); + +jest.mock('../../use_mappings/helpers', () => ({ + fetchMappings: ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => + mockFetchMappings({ + abortController, + patternOrIndexName, + }), +})); + +let mockFetchUnallowedValues = jest.fn( + ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => new Promise((resolve) => resolve(mockUnallowedValuesResponse)) +); + +jest.mock('../../use_unallowed_values/helpers', () => { + const original = jest.requireActual('../../use_unallowed_values/helpers'); + + return { + ...original, + fetchUnallowedValues: ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => + mockFetchUnallowedValues({ + abortController, + indexName, + requestItems, + }), + }; +}); + +const defaultProps: Props = { + addSuccessToast: jest.fn(), + canUserCreateAndReadCases: jest.fn(), + docsCount: auditbeatWithAllResults.docsCount ?? 0, + formatBytes, + formatNumber, + getGroupByFieldsOnClick: jest.fn(), + ilmPhase: 'hot', + indexName: 'auditbeat-custom-index-1', + openCreateCaseFlyout: jest.fn(), + pattern, + patternRollup, + theme: DARK_THEME, + updatePatternRollup: jest.fn(), +}; + +describe('IndexProperties', () => { + test('it renders the tab content', async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('incompatibleTab')).toBeInTheDocument(); + }); + }); + + describe('when an error occurs loading mappings', () => { + const abortController = new AbortController(); + abortController.abort(); + + const error = 'simulated fetch mappings error'; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('it displays the expected empty prompt content', async () => { + mockFetchMappings = jest.fn( + ({ + // eslint-disable-next-line @typescript-eslint/no-shadow + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => new Promise((_, reject) => reject(new Error(error))) + ); + + render( + + + + ); + + await waitFor(() => { + expect( + screen + .getByTestId('errorEmptyPrompt') + .textContent?.includes('Unable to load index mappings') + ).toBe(true); + }); + }); + }); + + describe('when an error occurs loading unallowed values', () => { + const abortController = new AbortController(); + abortController.abort(); + + const error = 'simulated fetch unallowed values error'; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('it displays the expected empty prompt content', async () => { + mockFetchUnallowedValues = jest.fn( + ({ + // eslint-disable-next-line @typescript-eslint/no-shadow + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => new Promise((_, reject) => reject(new Error(error))) + ); + + render( + + + + ); + + await waitFor(() => { + expect( + screen + .getByTestId('errorEmptyPrompt') + .textContent?.includes('Unable to load unallowed values') + ).toBe(true); + }); + }); + }); + + describe('when mappings are loading', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('it displays the expected loading prompt content', async () => { + mockFetchMappings = jest.fn( + ({ + abortController, + patternOrIndexName, + }: { + abortController: AbortController; + patternOrIndexName: string; + }) => new Promise(() => {}) // <-- will never resolve or reject + ); + + render( + + + + ); + + await waitFor(() => { + expect( + screen.getByTestId('loadingEmptyPrompt').textContent?.includes(LOADING_MAPPINGS) + ).toBe(true); + }); + }); + }); + + describe('when unallowed values are loading', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('it displays the expected loading prompt content', async () => { + mockFetchUnallowedValues = jest.fn( + ({ + abortController, + indexName, + requestItems, + }: { + abortController: AbortController; + indexName: string; + requestItems: UnallowedValueRequestItem[]; + }) => new Promise(() => {}) // <-- will never resolve or reject + ); + + render( + + + + ); + + await waitFor(() => { + expect( + screen.getByTestId('loadingEmptyPrompt').textContent?.includes(LOADING_UNALLOWED_VALUES) + ).toBe(true); + }); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx index 34fac241a2d82..ca9eda507f8dd 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/index.tsx @@ -16,7 +16,6 @@ import type { XYChartElementEvent, } from '@elastic/charts'; import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { getUnallowedValueRequestItems } from '../allowed_values/helpers'; @@ -28,8 +27,8 @@ import { hasAllDataFetchingCompleted, INCOMPATIBLE_TAB_ID, } from './helpers'; -import { EMPTY_STAT } from '../../helpers'; import { LoadingEmptyPrompt } from '../loading_empty_prompt'; +import { getIndexPropertiesContainerId } from '../pattern/helpers'; import { getTabs } from '../tabs/helpers'; import { getAllIncompatibleMarkdownComments } from '../tabs/incompatible_tab/helpers'; import * as i18n from './translations'; @@ -40,10 +39,11 @@ import { useUnallowedValues } from '../../use_unallowed_values'; const EMPTY_MARKDOWN_COMMENTS: string[] = []; -interface Props { +export interface Props { addSuccessToast: (toast: { title: string }) => void; canUserCreateAndReadCases: () => boolean; - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; docsCount: number; getGroupByFieldsOnClick: ( elements: Array< @@ -76,7 +76,8 @@ interface Props { const IndexPropertiesComponent: React.FC = ({ addSuccessToast, canUserCreateAndReadCases, - defaultNumberFormat, + formatBytes, + formatNumber, docsCount, getGroupByFieldsOnClick, ilmPhase, @@ -87,11 +88,6 @@ const IndexPropertiesComponent: React.FC = ({ theme, updatePatternRollup, }) => { - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); const { error: mappingsError, indexes, loading: loadingMappings } = useMappings(indexName); const requestItems = useMemo( @@ -142,7 +138,8 @@ const IndexPropertiesComponent: React.FC = ({ getTabs({ addSuccessToast, addToNewCaseDisabled, - defaultNumberFormat, + formatBytes, + formatNumber, docsCount, getGroupByFieldsOnClick, ilmPhase, @@ -152,13 +149,15 @@ const IndexPropertiesComponent: React.FC = ({ pattern, patternDocsCount: patternRollup?.docsCount ?? 0, setSelectedTabId, + stats: patternRollup?.stats ?? null, theme, }), [ addSuccessToast, addToNewCaseDisabled, - defaultNumberFormat, docsCount, + formatBytes, + formatNumber, getGroupByFieldsOnClick, ilmPhase, indexName, @@ -166,6 +165,7 @@ const IndexPropertiesComponent: React.FC = ({ partitionedFieldMetadata, pattern, patternRollup?.docsCount, + patternRollup?.stats, theme, ] ); @@ -212,11 +212,13 @@ const IndexPropertiesComponent: React.FC = ({ partitionedFieldMetadata != null ? getAllIncompatibleMarkdownComments({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount: patternRollup.docsCount ?? 0, + sizeInBytes: patternRollup.sizeInBytes, }) : EMPTY_MARKDOWN_COMMENTS; @@ -239,6 +241,7 @@ const IndexPropertiesComponent: React.FC = ({ } }, [ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, @@ -265,10 +268,10 @@ const IndexPropertiesComponent: React.FC = ({ } return indexes != null ? ( - <> +
    {renderTabs()} {selectedTabContent} - +
    ) : null; }; IndexPropertiesComponent.displayName = 'IndexPropertiesComponent'; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.test.ts index 0ea7a13d1c710..b51a49ecc89df 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.test.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.test.ts @@ -5,11 +5,244 @@ * 2.0. */ -import { eventCategory, sourceIpWithTextMapping } from '../../../mock/enriched_field_metadata'; +import numeral from '@elastic/numeral'; + +import { + ECS_MAPPING_TYPE_EXPECTED, + FIELD, + INDEX_MAPPING_TYPE_ACTUAL, +} from '../../../compare_fields_table/translations'; +import { ERRORS } from '../../data_quality_summary/errors_popover/translations'; +import { ERROR, INDEX, PATTERN } from '../../data_quality_summary/errors_viewer/translations'; +import { + escape, + escapePreserveNewlines, + getAllowedValues, + getCodeFormattedValue, + getCustomMarkdownTableRows, + getDataQualitySummaryMarkdownComment, + getErrorsMarkdownTable, + getErrorsMarkdownTableRows, + getHeaderSeparator, + getIlmExplainPhaseCountsMarkdownComment, + getIncompatibleMappingsMarkdownTableRows, + getIncompatibleValuesMarkdownTableRows, + getIndexInvalidValues, + getMarkdownComment, + getMarkdownTable, + getMarkdownTableHeader, + getPatternSummaryMarkdownComment, + getResultEmoji, + getSameFamilyBadge, + getStatsRollupMarkdownComment, + getSummaryMarkdownComment, + getSummaryTableMarkdownComment, + getSummaryTableMarkdownHeader, + getSummaryTableMarkdownRow, + getTabCountsMarkdownComment, +} from './helpers'; +import { EMPTY_STAT } from '../../../helpers'; +import { mockAllowedValues } from '../../../mock/allowed_values/mock_allowed_values'; +import { + eventCategory, + mockCustomFields, + mockIncompatibleMappings, + sourceIpWithTextMapping, +} from '../../../mock/enriched_field_metadata/mock_enriched_field_metadata'; +import { mockPartitionedFieldMetadata } from '../../../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; +import { + auditbeatNoResults, + auditbeatWithAllResults, +} from '../../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; import { SAME_FAMILY } from '../../same_family/translations'; -import { getIncompatibleMappingsMarkdownTableRows, getSameFamilyBadge } from './helpers'; +import { INCOMPATIBLE_FIELD_MAPPINGS_TABLE_TITLE } from '../../tabs/incompatible_tab/translations'; +import { + EnrichedFieldMetadata, + ErrorSummary, + PatternRollup, + UnallowedValueCount, +} from '../../../types'; + +const errorSummary: ErrorSummary[] = [ + { + pattern: '.alerts-security.alerts-default', + indexName: null, + error: 'Error loading stats: Error: Forbidden', + }, + { + error: + 'Error: Error loading unallowed values for index auditbeat-7.2.1-2023.02.13-000001: Forbidden', + indexName: 'auditbeat-7.2.1-2023.02.13-000001', + pattern: 'auditbeat-*', + }, +]; + +const indexName = 'auditbeat-custom-index-1'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; describe('helpers', () => { + describe('escape', () => { + test('it returns undefined when `content` is undefined', () => { + expect(escape(undefined)).toBeUndefined(); + }); + + test("it returns the content unmodified when there's nothing to escape", () => { + const content = "there's nothing to escape in this content"; + expect(escape(content)).toEqual(content); + }); + + test('it replaces all newlines in the content with spaces', () => { + const content = '\nthere were newlines in the beginning, middle,\nand end\n'; + expect(escape(content)).toEqual(' there were newlines in the beginning, middle, and end '); + }); + + test('it escapes all column separators in the content with spaces', () => { + const content = '|there were column separators in the beginning, middle,|and end|'; + expect(escape(content)).toEqual( + '\\|there were column separators in the beginning, middle,\\|and end\\|' + ); + }); + + test('it escapes content containing BOTH newlines and column separators', () => { + const content = + '|\nthere were newlines and column separators in the beginning, middle,\n|and end|\n'; + expect(escape(content)).toEqual( + '\\| there were newlines and column separators in the beginning, middle, \\|and end\\| ' + ); + }); + }); + + describe('escapePreserveNewlines', () => { + test('it returns undefined when `content` is undefined', () => { + expect(escapePreserveNewlines(undefined)).toBeUndefined(); + }); + + test("it returns the content unmodified when there's nothing to escape", () => { + const content = "there's (also) nothing to escape in this content"; + expect(escapePreserveNewlines(content)).toEqual(content); + }); + + test('it escapes all column separators in the content with spaces', () => { + const content = '|there were column separators in the beginning, middle,|and end|'; + expect(escapePreserveNewlines(content)).toEqual( + '\\|there were column separators in the beginning, middle,\\|and end\\|' + ); + }); + + test('it does NOT escape newlines in the content', () => { + const content = + '|\nthere were newlines and column separators in the beginning, middle,\n|and end|\n'; + expect(escapePreserveNewlines(content)).toEqual( + '\\|\nthere were newlines and column separators in the beginning, middle,\n\\|and end\\|\n' + ); + }); + }); + + describe('getHeaderSeparator', () => { + test('it returns a sequence of dashes equal to the length of the header, plus two additional dashes to pad each end of the cntent', () => { + const content = '0123456789'; // content.length === 10 + const expected = '------------'; // expected.length === 12 + + expect(getHeaderSeparator(content)).toEqual(expected); + }); + }); + + describe('getMarkdownTableHeader', () => { + const headerNames = [ + '|\nthere were newlines and column separators in the beginning, middle,\n|and end|\n', + 'A second column', + 'A third column', + ]; + + test('it returns the expected table header', () => { + expect(getMarkdownTableHeader(headerNames)).toEqual( + '\n| \\| there were newlines and column separators in the beginning, middle, \\|and end\\| | A second column | A third column | \n|----------------------------------------------------------------------------------|-----------------|----------------|' + ); + }); + }); + + describe('getCodeFormattedValue', () => { + test('it returns the expected placeholder when `value` is undefined', () => { + expect(getCodeFormattedValue(undefined)).toEqual('`--`'); + }); + + test('it returns the content formatted as markdown code', () => { + const value = 'foozle'; + + expect(getCodeFormattedValue(value)).toEqual('`foozle`'); + }); + + test('it escapes content such that `value` may be included in a markdown table cell', () => { + const value = + '|\nthere were newlines and column separators in the beginning, middle,\n|and end|\n'; + + expect(getCodeFormattedValue(value)).toEqual( + '`\\| there were newlines and column separators in the beginning, middle, \\|and end\\| `' + ); + }); + }); + + describe('getAllowedValues', () => { + test('it returns the expected placeholder when `allowedValues` is undefined', () => { + expect(getAllowedValues(undefined)).toEqual('`--`'); + }); + + test('it joins the `allowedValues` `name`s as a markdown-code-formatted, comma separated, string', () => { + expect(getAllowedValues(mockAllowedValues)).toEqual( + '`authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web`' + ); + }); + }); + + describe('getIndexInvalidValues', () => { + test('it returns the expected placeholder when `indexInvalidValues` is empty', () => { + expect(getIndexInvalidValues([])).toEqual('`--`'); + }); + + test('it returns markdown-code-formatted `fieldName`s, and their associated `count`s', () => { + const indexInvalidValues: UnallowedValueCount[] = [ + { + count: 2, + fieldName: 'an_invalid_category', + }, + { + count: 1, + fieldName: 'theory', + }, + ]; + + expect(getIndexInvalidValues(indexInvalidValues)).toEqual( + `\`an_invalid_category\` (2), \`theory\` (1)` + ); + }); + }); + + describe('getCustomMarkdownTableRows', () => { + test('it returns the expected table rows', () => { + expect(getCustomMarkdownTableRows(mockCustomFields)).toEqual( + '| host.name.keyword | `keyword` | `--` |\n| some.field | `text` | `--` |\n| some.field.keyword | `keyword` | `--` |\n| source.ip.keyword | `keyword` | `--` |' + ); + }); + + test('it returns the expected table rows when some have allowed values', () => { + const withAllowedValues = [ + ...mockCustomFields, + eventCategory, // note: this is not a real-world use case, because custom fields don't have allowed values + ]; + + expect(getCustomMarkdownTableRows(withAllowedValues)).toEqual( + '| host.name.keyword | `keyword` | `--` |\n| some.field | `text` | `--` |\n| some.field.keyword | `keyword` | `--` |\n| source.ip.keyword | `keyword` | `--` |\n| event.category | `keyword` | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` |' + ); + }); + }); + describe('getSameFamilyBadge', () => { test('it returns the expected badge text when the field is in the same family', () => { const inSameFamily = { @@ -32,7 +265,7 @@ describe('helpers', () => { describe('getIncompatibleMappingsMarkdownTableRows', () => { test('it returns the expected table rows when the field is in the same family', () => { - const eventCategoryWithWildcard = { + const eventCategoryWithWildcard: EnrichedFieldMetadata = { ...eventCategory, // `event.category` is a `keyword` per the ECS spec indexFieldType: 'wildcard', // this index has a mapping of `wildcard` instead of `keyword` isInSameFamily: true, // `wildcard` and `keyword` are in the same family @@ -49,18 +282,367 @@ describe('helpers', () => { }); test('it returns the expected table rows when the field is NOT in the same family', () => { - const eventCategoryWithWildcard = { + const eventCategoryWithText: EnrichedFieldMetadata = { ...eventCategory, // `event.category` is a `keyword` per the ECS spec indexFieldType: 'text', // this index has a mapping of `text` instead of `keyword` isInSameFamily: false, // `text` and `keyword` are NOT in the same family }; expect( - getIncompatibleMappingsMarkdownTableRows([ - eventCategoryWithWildcard, - sourceIpWithTextMapping, - ]) + getIncompatibleMappingsMarkdownTableRows([eventCategoryWithText, sourceIpWithTextMapping]) ).toEqual('| event.category | `keyword` | `text` |\n| source.ip | `ip` | `text` |'); }); }); + + describe('getIncompatibleValuesMarkdownTableRows', () => { + test('it returns the expected table rows', () => { + expect( + getIncompatibleValuesMarkdownTableRows([ + { + ...eventCategory, + hasEcsMetadata: true, + indexInvalidValues: [ + { + count: 2, + fieldName: 'an_invalid_category', + }, + { + count: 1, + fieldName: 'theory', + }, + ], + isEcsCompliant: false, + }, + ]) + ).toEqual( + '| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |' + ); + }); + }); + + describe('getMarkdownComment', () => { + test('it returns the expected markdown comment', () => { + const suggestedAction = + '|\nthere were newlines and column separators in this suggestedAction beginning, middle,\n|and end|\n'; + const title = + '|\nthere were newlines and column separators in this title beginning, middle,\n|and end|\n'; + + expect(getMarkdownComment({ suggestedAction, title })).toEqual( + '#### \\| there were newlines and column separators in this title beginning, middle, \\|and end\\| \n\n\\|\nthere were newlines and column separators in this suggestedAction beginning, middle,\n\\|and end\\|\n' + ); + }); + }); + + describe('getErrorsMarkdownTableRows', () => { + test('it returns the expected markdown table rows', () => { + expect(getErrorsMarkdownTableRows(errorSummary)).toEqual( + '| .alerts-security.alerts-default | -- | `Error loading stats: Error: Forbidden` |\n| auditbeat-* | auditbeat-7.2.1-2023.02.13-000001 | `Error: Error loading unallowed values for index auditbeat-7.2.1-2023.02.13-000001: Forbidden` |' + ); + }); + }); + + describe('getErrorsMarkdownTable', () => { + test('it returns the expected table contents', () => { + expect( + getErrorsMarkdownTable({ + errorSummary, + getMarkdownTableRows: getErrorsMarkdownTableRows, + headerNames: [PATTERN, INDEX, ERROR], + title: ERRORS, + }) + ).toEqual( + `## Errors\n\nSome indices were not checked for Data Quality\n\nErrors may occur when pattern or index metadata is temporarily unavailable, or because you don't have the privileges required for access\n\nThe following privileges are required to check an index:\n- \`monitor\` or \`manage\`\n- \`view_index_metadata\`\n- \`read\`\n\n\n| Pattern | Index | Error | \n|---------|-------|-------|\n| .alerts-security.alerts-default | -- | \`Error loading stats: Error: Forbidden\` |\n| auditbeat-* | auditbeat-7.2.1-2023.02.13-000001 | \`Error: Error loading unallowed values for index auditbeat-7.2.1-2023.02.13-000001: Forbidden\` |\n` + ); + }); + + test('it returns an empty string when the error summary is empty', () => { + expect( + getErrorsMarkdownTable({ + errorSummary: [], // <-- empty + getMarkdownTableRows: getErrorsMarkdownTableRows, + headerNames: [PATTERN, INDEX, ERROR], + title: ERRORS, + }) + ).toEqual(''); + }); + }); + + describe('getMarkdownTable', () => { + test('it returns the expected table contents', () => { + expect( + getMarkdownTable({ + enrichedFieldMetadata: mockIncompatibleMappings, + getMarkdownTableRows: getIncompatibleMappingsMarkdownTableRows, + headerNames: [FIELD, ECS_MAPPING_TYPE_EXPECTED, INDEX_MAPPING_TYPE_ACTUAL], + title: INCOMPATIBLE_FIELD_MAPPINGS_TABLE_TITLE(indexName), + }) + ).toEqual( + '#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n' + ); + }); + + test('it returns an empty string when `enrichedFieldMetadata` is empty', () => { + expect( + getMarkdownTable({ + enrichedFieldMetadata: [], // <-- empty + getMarkdownTableRows: getIncompatibleMappingsMarkdownTableRows, + headerNames: [FIELD, ECS_MAPPING_TYPE_EXPECTED, INDEX_MAPPING_TYPE_ACTUAL], + title: INCOMPATIBLE_FIELD_MAPPINGS_TABLE_TITLE(indexName), + }) + ).toEqual(''); + }); + }); + + describe('getSummaryMarkdownComment', () => { + test('it returns the expected markdown comment', () => { + expect(getSummaryMarkdownComment(indexName)).toEqual('### auditbeat-custom-index-1\n'); + }); + }); + + describe('getTabCountsMarkdownComment', () => { + test('it returns a comment with the expected counts', () => { + expect(getTabCountsMarkdownComment(mockPartitionedFieldMetadata)).toBe( + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n' + ); + }); + }); + + describe('getResultEmoji', () => { + test('it returns the expected placeholder when `incompatible` is undefined', () => { + expect(getResultEmoji(undefined)).toEqual('--'); + }); + + test('it returns a ✅ when the incompatible count is zero', () => { + expect(getResultEmoji(0)).toEqual('✅'); + }); + + test('it returns a ❌ when the incompatible count is NOT zero', () => { + expect(getResultEmoji(1)).toEqual('❌'); + }); + }); + + describe('getSummaryTableMarkdownHeader', () => { + test('it returns the expected header', () => { + expect(getSummaryTableMarkdownHeader()).toEqual( + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|' + ); + }); + }); + + describe('getSummaryTableMarkdownRow', () => { + test('it returns the expected row when all values are provided', () => { + expect( + getSummaryTableMarkdownRow({ + docsCount: 4, + formatBytes, + formatNumber, + incompatible: 3, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual('| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` | 27.7KB |\n'); + }); + + test('it returns the expected row when optional values are NOT provided', () => { + expect( + getSummaryTableMarkdownRow({ + docsCount: 4, + formatBytes, + formatNumber, + incompatible: undefined, // <-- + ilmPhase: undefined, // <-- + indexName: 'auditbeat-custom-index-1', + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual('| -- | auditbeat-custom-index-1 | 4 (0.0%) | -- | -- | 27.7KB |\n'); + }); + }); + + describe('getSummaryTableMarkdownComment', () => { + test('it returns the expected comment', () => { + expect( + getSummaryTableMarkdownComment({ + docsCount: 4, + formatBytes, + formatNumber, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual( + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` | 27.7KB |\n\n' + ); + }); + }); + + describe('getStatsRollupMarkdownComment', () => { + test('it returns the expected comment', () => { + expect( + getStatsRollupMarkdownComment({ + docsCount: 57410, + formatBytes, + formatNumber, + incompatible: 3, + indices: 25, + indicesChecked: 1, + sizeInBytes: 28413, + }) + ).toEqual( + '| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| 3 | 1 | 25 | 27.7KB | 57,410 |\n' + ); + }); + + test('it returns the expected comment when optional values are undefined', () => { + expect( + getStatsRollupMarkdownComment({ + docsCount: 0, + formatBytes, + formatNumber, + incompatible: undefined, + indices: undefined, + indicesChecked: undefined, + sizeInBytes: undefined, + }) + ).toEqual( + '| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| -- | -- | -- | -- | 0 |\n' + ); + }); + }); + + describe('getDataQualitySummaryMarkdownComment', () => { + test('it returns the expected comment', () => { + expect( + getDataQualitySummaryMarkdownComment({ + formatBytes, + formatNumber, + totalDocsCount: 3343719, + totalIncompatible: 4, + totalIndices: 30, + totalIndicesChecked: 2, + sizeInBytes: 4294967296, + }) + ).toEqual( + '# Data quality\n\n| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| 4 | 2 | 30 | 4GB | 3,343,719 |\n\n' + ); + }); + + test('it returns the expected comment when optional values are undefined', () => { + expect( + getDataQualitySummaryMarkdownComment({ + formatBytes, + formatNumber, + totalDocsCount: undefined, + totalIncompatible: undefined, + totalIndices: undefined, + totalIndicesChecked: undefined, + sizeInBytes: undefined, + }) + ).toEqual( + '# Data quality\n\n| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| -- | -- | -- | -- | 0 |\n\n' + ); + }); + }); + + describe('getIlmExplainPhaseCountsMarkdownComment', () => { + test('it returns the expected comment when _all_ of the counts are greater than zero', () => { + expect( + getIlmExplainPhaseCountsMarkdownComment({ + hot: 99, + warm: 8, + unmanaged: 77, + cold: 6, + frozen: 55, + }) + ).toEqual('`hot(99)` `warm(8)` `unmanaged(77)` `cold(6)` `frozen(55)`'); + }); + + test('it returns the expected comment when _some_ of the counts are greater than zero', () => { + expect( + getIlmExplainPhaseCountsMarkdownComment({ + hot: 9, + warm: 0, + unmanaged: 2, + cold: 1, + frozen: 0, + }) + ).toEqual('`hot(9)` `unmanaged(2)` `cold(1)`'); + }); + + test('it returns the expected comment when _none_ of the counts are greater than zero', () => { + expect( + getIlmExplainPhaseCountsMarkdownComment({ + hot: 0, + warm: 0, + unmanaged: 0, + cold: 0, + frozen: 0, + }) + ).toEqual(''); + }); + }); + + describe('getPatternSummaryMarkdownComment', () => { + test('it returns the expected comment when the rollup contains results for all of the indices in the pattern', () => { + expect( + getPatternSummaryMarkdownComment({ + formatBytes, + formatNumber, + patternRollup: auditbeatWithAllResults, + }) + ).toEqual( + '## auditbeat-*\n`hot(1)` `unmanaged(2)`\n\n| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| 4 | 3 | 3 | 17.9MB | 19,127 |\n\n' + ); + }); + + test('it returns the expected comment when the rollup contains no results', () => { + expect( + getPatternSummaryMarkdownComment({ + formatBytes, + formatNumber, + patternRollup: auditbeatNoResults, + }) + ).toEqual( + '## auditbeat-*\n`hot(1)` `unmanaged(2)`\n\n| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| -- | 0 | 3 | 17.9MB | 19,127 |\n\n' + ); + }); + + test('it returns the expected comment when the rollup does NOT have `ilmExplainPhaseCounts`', () => { + const noIlmExplainPhaseCounts: PatternRollup = { + ...auditbeatWithAllResults, + ilmExplainPhaseCounts: undefined, // <-- + }; + + expect( + getPatternSummaryMarkdownComment({ + formatBytes, + formatNumber, + patternRollup: noIlmExplainPhaseCounts, + }) + ).toEqual( + '## auditbeat-*\n\n\n| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| 4 | 3 | 3 | 17.9MB | 19,127 |\n\n' + ); + }); + + test('it returns the expected comment when `docsCount` is undefined', () => { + const noDocsCount: PatternRollup = { + ...auditbeatWithAllResults, + docsCount: undefined, // <-- + }; + + expect( + getPatternSummaryMarkdownComment({ + formatBytes, + formatNumber, + patternRollup: noDocsCount, + }) + ).toEqual( + '## auditbeat-*\n`hot(1)` `unmanaged(2)`\n\n| Incompatible fields | Indices checked | Indices | Size | Docs |\n|---------------------|-----------------|---------|------|------|\n| 4 | 3 | 3 | 17.9MB | 0 |\n\n' + ); + }); + }); }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.ts index 9137bb346d660..32424dff66883 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/index_properties/markdown/helpers.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { repeat } from 'lodash/fp'; - import { ERRORS_MAY_OCCUR, ERRORS_CALLOUT_SUMMARY, @@ -44,6 +42,7 @@ import { INDICES, INDICES_CHECKED, RESULT, + SIZE, } from '../../summary_table/translations'; import { DATA_QUALITY_TITLE } from '../../../translations'; @@ -65,11 +64,11 @@ export const escape = (content: string | undefined): string | undefined => export const escapePreserveNewlines = (content: string | undefined): string | undefined => content != null ? content.replaceAll('|', '\\|') : content; -export const getHeaderSeparator = (headerLength: number): string => repeat(headerLength + 2, '-'); +export const getHeaderSeparator = (headerText: string): string => '-'.repeat(headerText.length + 2); // 2 extra, for the spaces on both sides of the column name export const getMarkdownTableHeader = (headerNames: string[]) => ` | ${headerNames.map((name) => `${escape(name)} | `).join('')} -|${headerNames.map((name) => `${getHeaderSeparator(name.length)}|`).join('')}`; +|${headerNames.map((name) => `${getHeaderSeparator(name)}|`).join('')}`; export const getCodeFormattedValue = (value: string | undefined) => `\`${escape(value ?? EMPTY_PLACEHOLDER)}\``; @@ -84,21 +83,7 @@ export const getIndexInvalidValues = (indexInvalidValues: UnallowedValueCount[]) ? getCodeFormattedValue(undefined) : indexInvalidValues .map(({ fieldName, count }) => `${getCodeFormattedValue(escape(fieldName))} (${count})`) - .join(',\n'); - -export const getCommonMarkdownTableRows = ( - enrichedFieldMetadata: EnrichedFieldMetadata[] -): string => - enrichedFieldMetadata - .map( - (x) => - `| ${escape(x.indexFieldName)} | ${getCodeFormattedValue(x.type)} | ${getCodeFormattedValue( - x.indexFieldType - )} | ${getAllowedValues(x.allowed_values)} | ${getIndexInvalidValues( - x.indexInvalidValues - )} | ${escape(x.description ?? EMPTY_PLACEHOLDER)} |` - ) - .join('\n'); + .join(', '); // newlines are instead joined with spaces export const getCustomMarkdownTableRows = ( enrichedFieldMetadata: EnrichedFieldMetadata[] @@ -207,19 +192,7 @@ ${getMarkdownTableRows(enrichedFieldMetadata)} ` : ''; -export const getSummaryMarkdownComment = ({ - ecsFieldReferenceUrl, - ecsReferenceUrl, - incompatible, - indexName, - mappingUrl, -}: { - ecsFieldReferenceUrl: string; - ecsReferenceUrl: string; - incompatible: number | undefined; - indexName: string; - mappingUrl: string; -}): string => +export const getSummaryMarkdownComment = (indexName: string) => `### ${escape(indexName)} `; @@ -244,23 +217,31 @@ export const getResultEmoji = (incompatible: number | undefined): string => { }; export const getSummaryTableMarkdownHeader = (): string => - `| ${RESULT} | ${INDEX} | ${DOCS} | ${INCOMPATIBLE_FIELDS} | ${ILM_PHASE} | -|--------|-------|------|---------------------|-----------|`; + `| ${RESULT} | ${INDEX} | ${DOCS} | ${INCOMPATIBLE_FIELDS} | ${ILM_PHASE} | ${SIZE} | +|${getHeaderSeparator(RESULT)}|${getHeaderSeparator(INDEX)}|${getHeaderSeparator( + DOCS + )}|${getHeaderSeparator(INCOMPATIBLE_FIELDS)}|${getHeaderSeparator( + ILM_PHASE + )}|${getHeaderSeparator(SIZE)}|`; export const getSummaryTableMarkdownRow = ({ docsCount, + formatBytes, formatNumber, ilmPhase, incompatible, indexName, patternDocsCount, + sizeInBytes, }: { docsCount: number; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; incompatible: number | undefined; indexName: string; patternDocsCount: number; + sizeInBytes: number | undefined; }): string => `| ${getResultEmoji(incompatible)} | ${escape(indexName)} | ${formatNumber( docsCount @@ -268,77 +249,95 @@ export const getSummaryTableMarkdownRow = ({ docsCount, patternDocsCount, })}) | ${incompatible ?? EMPTY_PLACEHOLDER} | ${ - ilmPhase != null ? getCodeFormattedValue(ilmPhase) : '' - } | + ilmPhase != null ? getCodeFormattedValue(ilmPhase) : EMPTY_PLACEHOLDER + } | ${formatBytes(sizeInBytes)} | `; export const getSummaryTableMarkdownComment = ({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }: { docsCount: number; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata; patternDocsCount: number; + sizeInBytes: number | undefined; }): string => `${getSummaryTableMarkdownHeader()} ${getSummaryTableMarkdownRow({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, incompatible: partitionedFieldMetadata.incompatible.length, patternDocsCount, + sizeInBytes, })} `; export const getStatsRollupMarkdownComment = ({ docsCount, + formatBytes, formatNumber, incompatible, indices, indicesChecked, + sizeInBytes, }: { docsCount: number; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; incompatible: number | undefined; indices: number | undefined; indicesChecked: number | undefined; + sizeInBytes: number | undefined; }): string => - `| ${INCOMPATIBLE_FIELDS} | ${INDICES_CHECKED} | ${INDICES} | ${DOCS} | -|---------------------|-----------------|---------|------| + `| ${INCOMPATIBLE_FIELDS} | ${INDICES_CHECKED} | ${INDICES} | ${SIZE} | ${DOCS} | +|${getHeaderSeparator(INCOMPATIBLE_FIELDS)}|${getHeaderSeparator( + INDICES_CHECKED + )}|${getHeaderSeparator(INDICES)}|${getHeaderSeparator(SIZE)}|${getHeaderSeparator(DOCS)}| | ${incompatible ?? EMPTY_STAT} | ${indicesChecked ?? EMPTY_STAT} | ${ indices ?? EMPTY_STAT - } | ${formatNumber(docsCount)} | + } | ${formatBytes(sizeInBytes)} | ${formatNumber(docsCount)} | `; export const getDataQualitySummaryMarkdownComment = ({ + formatBytes, formatNumber, totalDocsCount, totalIncompatible, totalIndices, totalIndicesChecked, + sizeInBytes, }: { + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; totalDocsCount: number | undefined; totalIncompatible: number | undefined; totalIndices: number | undefined; totalIndicesChecked: number | undefined; + sizeInBytes: number | undefined; }): string => `# ${DATA_QUALITY_TITLE} ${getStatsRollupMarkdownComment({ docsCount: totalDocsCount ?? 0, + formatBytes, formatNumber, incompatible: totalIncompatible, indices: totalIndices, indicesChecked: totalIndicesChecked, + sizeInBytes, })} `; @@ -355,13 +354,17 @@ export const getIlmExplainPhaseCountsMarkdownComment = ({ unmanaged > 0 ? getCodeFormattedValue(`${UNMANAGED}(${unmanaged})`) : '', cold > 0 ? getCodeFormattedValue(`${COLD}(${cold})`) : '', frozen > 0 ? getCodeFormattedValue(`${FROZEN}(${frozen})`) : '', - ].join(' '); + ] + .filter((x) => x !== '') // prevents extra whitespace + .join(' '); export const getPatternSummaryMarkdownComment = ({ + formatBytes, formatNumber, patternRollup, patternRollup: { docsCount, indices, ilmExplainPhaseCounts, pattern, results }, }: { + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; patternRollup: PatternRollup; }): string => @@ -374,9 +377,11 @@ ${ ${getStatsRollupMarkdownComment({ docsCount: docsCount ?? 0, + formatBytes, formatNumber, incompatible: getTotalPatternIncompatible(results), indices, indicesChecked: getTotalPatternIndicesChecked(patternRollup), + sizeInBytes: patternRollup.sizeInBytes, })} `; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/loading_empty_prompt/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/loading_empty_prompt/index.tsx index 84e701c0aa244..7eda6d039ffe0 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/loading_empty_prompt/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/loading_empty_prompt/index.tsx @@ -15,7 +15,9 @@ interface Props { const LoadingEmptyPromptComponent: React.FC = ({ loading }) => { const icon = useMemo(() => , []); - return {loading}} />; + return ( + {loading}} /> + ); }; LoadingEmptyPromptComponent.displayName = 'LoadingEmptyPromptComponent'; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/panel_subtitle/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/panel_subtitle/index.tsx deleted file mode 100644 index 10e460d9c24f5..0000000000000 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/panel_subtitle/index.tsx +++ /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 { EuiCode, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import React from 'react'; - -import * as i18n from '../../translations'; - -interface Props { - error: string | null; - loading: boolean; - version: string | null; - versionLoading: boolean; -} - -const PanelSubtitleComponent: React.FC = ({ error, loading, version, versionLoading }) => { - const allDataLoaded = !loading && !versionLoading && error == null && version != null; - - return allDataLoaded ? ( - - - - {i18n.SELECT_AN_INDEX} {version} - - - - ) : null; -}; - -export const PanelSubtitle = React.memo(PanelSubtitleComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts new file mode 100644 index 0000000000000..c40f6a73ffa03 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.test.ts @@ -0,0 +1,865 @@ +/* + * 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 { + IlmExplainLifecycleLifecycleExplain, + IlmExplainLifecycleLifecycleExplainManaged, + IlmExplainLifecycleLifecycleExplainUnmanaged, +} from '@elastic/elasticsearch/lib/api/types'; + +import { + defaultSort, + getIlmPhase, + getIndexPropertiesContainerId, + getIlmExplainPhaseCounts, + getIndexIncompatible, + getPageIndex, + getPhaseCount, + getSummaryTableItems, + isManaged, + shouldCreateIndexNames, + shouldCreatePatternRollup, +} from './helpers'; +import { mockIlmExplain } from '../../mock/ilm_explain/mock_ilm_explain'; +import { mockDataQualityCheckResult } from '../../mock/data_quality_check_result/mock_index'; +import { auditbeatWithAllResults } from '../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { mockStats } from '../../mock/stats/mock_stats'; +import { IndexSummaryTableItem } from '../summary_table/helpers'; +import { DataQualityCheckResult } from '../../types'; + +const hot: IlmExplainLifecycleLifecycleExplainManaged = { + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536751379, + time_since_index_creation: '3.98d', + lifecycle_date_millis: 1675536751379, + age: '3.98d', + phase: 'hot', + phase_time_millis: 1675536751809, + action: 'rollover', + action_time_millis: 1675536751809, + step: 'check-rollover-ready', + step_time_millis: 1675536751809, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, +}; +const warm = { + ...hot, + phase: 'warm', +}; +const cold = { + ...hot, + phase: 'cold', +}; +const frozen = { + ...hot, + phase: 'frozen', +}; +const other = { + ...hot, + phase: 'other', // not a valid phase +}; + +const managed: Record = { + hot, + warm, + cold, + frozen, +}; + +const unmanaged: IlmExplainLifecycleLifecycleExplainUnmanaged = { + index: 'michael', + managed: false, +}; + +describe('helpers', () => { + const indexName = '.ds-packetbeat-8.6.1-2023.02.04-000001'; + + describe('isManaged', () => { + test('it returns true when the `ilmExplainRecord` `managed` property is true', () => { + const ilmExplain = mockIlmExplain[indexName]; + + expect(isManaged(ilmExplain)).toBe(true); + }); + + test('it returns false when the `ilmExplainRecord` is undefined', () => { + expect(isManaged(undefined)).toBe(false); + }); + }); + + describe('getPhaseCount', () => { + test('it returns the expected count when an index with the specified `ilmPhase` exists in the `IlmExplainLifecycleLifecycleExplain` record', () => { + expect( + getPhaseCount({ + ilmExplain: mockIlmExplain, + ilmPhase: 'hot', // this phase is in the record + indexName, // valid index name + }) + ).toEqual(1); + }); + + test('it returns zero when `ilmPhase` is null', () => { + expect( + getPhaseCount({ + ilmExplain: null, + ilmPhase: 'hot', + indexName, + }) + ).toEqual(0); + }); + + test('it returns zero when the `indexName` does NOT exist in the `IlmExplainLifecycleLifecycleExplain` record', () => { + expect( + getPhaseCount({ + ilmExplain: mockIlmExplain, + ilmPhase: 'hot', + indexName: 'invalid', // this index does NOT exist + }) + ).toEqual(0); + }); + + test('it returns zero when the specified `ilmPhase` does NOT exist in the `IlmExplainLifecycleLifecycleExplain` record', () => { + expect( + getPhaseCount({ + ilmExplain: mockIlmExplain, + ilmPhase: 'warm', // this phase is NOT in the record + indexName, // valid index name + }) + ).toEqual(0); + }); + + describe('when `ilmPhase` is `unmanaged`', () => { + test('it returns the expected count for an `unmanaged` index', () => { + const index = 'auditbeat-custom-index-1'; + const ilmExplainRecord: IlmExplainLifecycleLifecycleExplain = { + index, + managed: false, + }; + const ilmExplain = { + [index]: ilmExplainRecord, + }; + + expect( + getPhaseCount({ + ilmExplain, + ilmPhase: 'unmanaged', // ilmPhase is unmanaged + indexName: index, // an unmanaged index + }) + ).toEqual(1); + }); + + test('it returns zero for a managed index', () => { + expect( + getPhaseCount({ + ilmExplain: mockIlmExplain, + ilmPhase: 'unmanaged', // ilmPhase is unmanaged + indexName, // a managed (`hot`) index + }) + ).toEqual(0); + }); + }); + }); + + describe('getIlmPhase', () => { + test('it returns undefined when the `ilmExplainRecord` is undefined', () => { + expect(getIlmPhase(undefined)).toBeUndefined(); + }); + + describe('when the `ilmExplainRecord` is a `IlmExplainLifecycleLifecycleExplainManaged` record', () => { + Object.keys(managed).forEach((phase) => + test(`it returns the expected phase when 'phase' is '${phase}'`, () => { + expect(getIlmPhase(managed[phase])).toEqual(phase); + }) + ); + + test(`it returns undefined when the 'phase' is unknown`, () => { + expect(getIlmPhase(other)).toBeUndefined(); + }); + }); + + describe('when the `ilmExplainRecord` is a `IlmExplainLifecycleLifecycleExplainUnmanaged` record', () => { + test('it returns `unmanaged`', () => { + expect(getIlmPhase(unmanaged)).toEqual('unmanaged'); + }); + }); + }); + + describe('getIlmExplainPhaseCounts', () => { + test('it returns the expected counts (all zeros) when `ilmExplain` is null', () => { + expect(getIlmExplainPhaseCounts(null)).toEqual({ + cold: 0, + frozen: 0, + hot: 0, + unmanaged: 0, + warm: 0, + }); + }); + + test('it returns the expected counts', () => { + const ilmExplain: Record = { + ...managed, + [unmanaged.index]: unmanaged, + }; + + expect(getIlmExplainPhaseCounts(ilmExplain)).toEqual({ + cold: 1, + frozen: 1, + hot: 1, + unmanaged: 1, + warm: 1, + }); + }); + }); + + describe('getIndexIncompatible', () => { + test('it returns undefined when `results` is undefined', () => { + expect( + getIndexIncompatible({ + indexName, + results: undefined, // <-- + }) + ).toBeUndefined(); + }); + + test('it returns undefined when `indexName` is not in the `results`', () => { + expect( + getIndexIncompatible({ + indexName: 'not_in_the_results', // <-- + results: mockDataQualityCheckResult, + }) + ).toBeUndefined(); + }); + + test('it returns the expected count', () => { + expect( + getIndexIncompatible({ + indexName: 'auditbeat-custom-index-1', + results: mockDataQualityCheckResult, + }) + ).toEqual(3); + }); + }); + + describe('getSummaryTableItems', () => { + const indexNames = [ + '.ds-packetbeat-8.6.1-2023.02.04-000001', + '.ds-packetbeat-8.5.3-2023.02.04-000001', + 'auditbeat-custom-index-1', + ]; + const pattern = 'auditbeat-*'; + const patternDocsCount = 4; + const results: Record = { + 'auditbeat-custom-index-1': { + docsCount: 4, + error: null, + ilmPhase: 'unmanaged', + incompatible: 3, + indexName: 'auditbeat-custom-index-1', + markdownComments: [ + '### auditbeat-custom-index-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase |\n|--------|-------|------|---------------------|-----------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.6.1.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - auditbeat-custom-index-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2),\n`theory` (1) |\n\n', + ], + pattern: 'auditbeat-*', + }, + }; + + test('it returns the expected summary table items', () => { + expect( + getSummaryTableItems({ + ilmExplain: mockIlmExplain, + indexNames, + pattern, + patternDocsCount, + results, + sortByColumn: defaultSort.sort.field, + sortByDirection: defaultSort.sort.direction, + stats: mockStats, + }) + ).toEqual([ + { + docsCount: 1630289, + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-8.5.3-2023.02.04-000001', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 733175040, + }, + { + docsCount: 1628343, + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 731583142, + }, + { + docsCount: 4, + ilmPhase: 'unmanaged', + incompatible: 3, + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 28413, + }, + ]); + }); + + test('it returns the expected summary table items when `sortByDirection` is ascending', () => { + expect( + getSummaryTableItems({ + ilmExplain: mockIlmExplain, + indexNames, + pattern, + patternDocsCount, + results, + sortByColumn: defaultSort.sort.field, + sortByDirection: 'asc', // <-- ascending + stats: mockStats, + }) + ).toEqual([ + { + docsCount: 4, + ilmPhase: 'unmanaged', + incompatible: 3, + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 28413, + }, + { + docsCount: 1628343, + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 731583142, + }, + { + docsCount: 1630289, + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-8.5.3-2023.02.04-000001', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 733175040, + }, + ]); + }); + + test('it returns the expected summary table items when data is unavailable', () => { + expect( + getSummaryTableItems({ + ilmExplain: null, // <-- no data + indexNames, + pattern, + patternDocsCount, + results: undefined, // <-- no data + sortByColumn: defaultSort.sort.field, + sortByDirection: defaultSort.sort.direction, + stats: null, // <-- no data + }) + ).toEqual([ + { + docsCount: 0, + ilmPhase: undefined, + incompatible: undefined, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 0, + }, + { + docsCount: 0, + ilmPhase: undefined, + incompatible: undefined, + indexName: '.ds-packetbeat-8.5.3-2023.02.04-000001', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 0, + }, + { + docsCount: 0, + ilmPhase: undefined, + incompatible: undefined, + indexName: 'auditbeat-custom-index-1', + pattern: 'auditbeat-*', + patternDocsCount: 4, + sizeInBytes: 0, + }, + ]); + }); + }); + + describe('shouldCreateIndexNames', () => { + const indexNames = [ + '.ds-packetbeat-8.6.1-2023.02.04-000001', + '.ds-packetbeat-8.5.3-2023.02.04-000001', + 'auditbeat-custom-index-1', + ]; + + test('returns true when `indexNames` does NOT exist, and the required `stats` and `ilmExplain` are available', () => { + expect( + shouldCreateIndexNames({ + ilmExplain: mockIlmExplain, + indexNames: undefined, + stats: mockStats, + }) + ).toBe(true); + }); + + test('returns false when `indexNames` exists, and the required `stats` and `ilmExplain` are available', () => { + expect( + shouldCreateIndexNames({ + ilmExplain: mockIlmExplain, + indexNames, + stats: mockStats, + }) + ).toBe(false); + }); + + test('returns false when `indexNames` does NOT exist, `stats` is NOT available, and `ilmExplain` is available', () => { + expect( + shouldCreateIndexNames({ + ilmExplain: mockIlmExplain, + indexNames: undefined, + stats: null, + }) + ).toBe(false); + }); + + test('returns false when `indexNames` does NOT exist, `stats` is available, and `ilmExplain` is NOT available', () => { + expect( + shouldCreateIndexNames({ + ilmExplain: null, + indexNames: undefined, + stats: mockStats, + }) + ).toBe(false); + }); + + test('returns false when `indexNames` does NOT exist, `stats` is NOT available, and `ilmExplain` is NOT available', () => { + expect( + shouldCreateIndexNames({ + ilmExplain: null, + indexNames: undefined, + stats: null, + }) + ).toBe(false); + }); + + test('returns false when `indexNames` exists, `stats` is NOT available, and `ilmExplain` is NOT available', () => { + expect( + shouldCreateIndexNames({ + ilmExplain: null, + indexNames, + stats: null, + }) + ).toBe(false); + }); + }); + + describe('shouldCreatePatternRollup', () => { + test('it returns false when the `patternRollup` already exists', () => { + expect( + shouldCreatePatternRollup({ + error: null, + ilmExplain: mockIlmExplain, + patternRollup: auditbeatWithAllResults, + stats: mockStats, + }) + ).toBe(false); + }); + + test('it returns true when all data was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: null, + ilmExplain: mockIlmExplain, + patternRollup: undefined, + stats: mockStats, + }) + ).toBe(true); + }); + + test('it returns false when `stats`, but NOT `ilmExplain` was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: null, + ilmExplain: null, + patternRollup: undefined, + stats: mockStats, + }) + ).toBe(false); + }); + + test('it returns false when `stats` was NOT loaded, and `ilmExplain` was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: null, + ilmExplain: mockIlmExplain, + patternRollup: undefined, + stats: null, + }) + ).toBe(false); + }); + + test('it returns true if an error occurred, and NO data was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: 'whoops', + ilmExplain: null, + patternRollup: undefined, + stats: null, + }) + ).toBe(true); + }); + + test('it returns true if an error occurred, and just `stats` was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: 'something went', + ilmExplain: null, + patternRollup: undefined, + stats: mockStats, + }) + ).toBe(true); + }); + + test('it returns true if an error occurred, and just `ilmExplain` was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: 'horribly wrong', + ilmExplain: mockIlmExplain, + patternRollup: undefined, + stats: null, + }) + ).toBe(true); + }); + + test('it returns true if an error occurred, and all data was loaded', () => { + expect( + shouldCreatePatternRollup({ + error: 'over here', + ilmExplain: mockIlmExplain, + patternRollup: undefined, + stats: mockStats, + }) + ).toBe(true); + }); + }); + + describe('getIndexPropertiesContainerId', () => { + const pattern = 'auditbeat-*'; + + test('it returns the expected id', () => { + expect(getIndexPropertiesContainerId({ indexName, pattern })).toEqual( + 'index-properties-container-auditbeat-*.ds-packetbeat-8.6.1-2023.02.04-000001' + ); + }); + }); + + describe('getPageIndex', () => { + const getPageIndexArgs: { + indexName: string; + items: IndexSummaryTableItem[]; + pageSize: number; + } = { + indexName: 'auditbeat-7.17.9-2023.04.09-000001', // <-- on page 2 of 3 (page index 1) + items: [ + { + docsCount: 48077, + incompatible: undefined, + indexName: 'auditbeat-7.14.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 43357342, + }, + { + docsCount: 48068, + incompatible: undefined, + indexName: 'auditbeat-7.3.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 32460397, + }, + { + docsCount: 48064, + incompatible: undefined, + indexName: 'auditbeat-7.11.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 42782794, + }, + { + docsCount: 47868, + incompatible: undefined, + indexName: 'auditbeat-7.6.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 31575964, + }, + { + docsCount: 47827, + incompatible: 20, + indexName: 'auditbeat-7.15.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 44130657, + }, + { + docsCount: 47642, + incompatible: undefined, + indexName: '.ds-auditbeat-8.4.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 42412521, + }, + { + docsCount: 47545, + incompatible: undefined, + indexName: 'auditbeat-7.16.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41423244, + }, + { + docsCount: 47531, + incompatible: undefined, + indexName: 'auditbeat-7.5.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 32394133, + }, + { + docsCount: 47530, + incompatible: undefined, + indexName: 'auditbeat-7.12.1-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 43015519, + }, + { + docsCount: 47520, + incompatible: undefined, + indexName: '.ds-auditbeat-8.0.1-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 42230604, + }, + { + docsCount: 47496, + incompatible: undefined, + indexName: '.ds-auditbeat-8.2.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41710968, + }, + { + docsCount: 47486, + incompatible: undefined, + indexName: '.ds-auditbeat-8.5.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 42295944, + }, + { + docsCount: 47486, + incompatible: undefined, + indexName: '.ds-auditbeat-8.3.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41761321, + }, + { + docsCount: 47460, + incompatible: undefined, + indexName: 'auditbeat-7.2.1-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 30481198, + }, + { + docsCount: 47439, + incompatible: undefined, + indexName: 'auditbeat-7.17.9-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41554041, + }, + { + docsCount: 47395, + incompatible: undefined, + indexName: 'auditbeat-7.9.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 42815907, + }, + { + docsCount: 47394, + incompatible: undefined, + indexName: '.ds-auditbeat-8.7.0-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41157112, + }, + { + docsCount: 47372, + incompatible: undefined, + indexName: 'auditbeat-7.4.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 31626792, + }, + { + docsCount: 47369, + incompatible: undefined, + indexName: 'auditbeat-7.13.4-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41828969, + }, + { + docsCount: 47348, + incompatible: undefined, + indexName: 'auditbeat-7.7.1-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 40010773, + }, + { + docsCount: 47339, + incompatible: undefined, + indexName: 'auditbeat-7.10.2-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 43480570, + }, + { + docsCount: 47325, + incompatible: undefined, + indexName: '.ds-auditbeat-8.1.3-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 41822475, + }, + { + docsCount: 47294, + incompatible: undefined, + indexName: 'auditbeat-7.8.0-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 43018490, + }, + { + docsCount: 24276, + incompatible: undefined, + indexName: '.ds-auditbeat-8.6.1-2023.04.09-000001', + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 23579440, + }, + { + docsCount: 4, + incompatible: undefined, + indexName: 'auditbeat-custom-index-1', + ilmPhase: 'unmanaged', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 28409, + }, + { + docsCount: 0, + incompatible: undefined, + indexName: 'auditbeat-custom-empty-index-1', + ilmPhase: 'unmanaged', + pattern: 'auditbeat-*', + patternDocsCount: 1118155, + sizeInBytes: 247, + }, + ], + pageSize: 10, + }; + + test('it returns the expected page index', () => { + expect(getPageIndex(getPageIndexArgs)).toEqual(1); + }); + + test('it returns the expected page index for the first item', () => { + const firstItemIndexName = 'auditbeat-7.14.2-2023.04.09-000001'; + + expect( + getPageIndex({ + ...getPageIndexArgs, + indexName: firstItemIndexName, + }) + ).toEqual(0); + }); + + test('it returns the expected page index for the last item', () => { + const lastItemIndexName = 'auditbeat-custom-empty-index-1'; + + expect( + getPageIndex({ + ...getPageIndexArgs, + indexName: lastItemIndexName, + }) + ).toEqual(2); + }); + + test('it returns null when the index cannot be found', () => { + expect( + getPageIndex({ + ...getPageIndexArgs, + indexName: 'does_not_exist', // <-- this index is not in the items + }) + ).toBeNull(); + }); + + test('it returns null when `pageSize` is zero', () => { + expect( + getPageIndex({ + ...getPageIndexArgs, + pageSize: 0, // <-- invalid + }) + ).toBeNull(); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts index efa9dbfa69d5e..40d0bbfb26293 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/helpers.ts @@ -9,7 +9,7 @@ import type { IlmExplainLifecycleLifecycleExplain, IndicesStatsIndicesStats, } from '@elastic/elasticsearch/lib/api/types'; -import { sortBy } from 'lodash/fp'; +import { orderBy } from 'lodash/fp'; import type { IndexSummaryTableItem } from '../summary_table/helpers'; import type { @@ -17,8 +17,9 @@ import type { IlmExplainPhaseCounts, DataQualityCheckResult, PatternRollup, + SortConfig, } from '../../types'; -import { getDocsCount } from '../../helpers'; +import { getDocsCount, getSizeInBytes } from '../../helpers'; export const isManaged = ( ilmExplainRecord: IlmExplainLifecycleLifecycleExplain | undefined @@ -144,6 +145,8 @@ export const getSummaryTableItems = ({ pattern, patternDocsCount, results, + sortByColumn, + sortByDirection, stats, }: { ilmExplain: Record | null; @@ -151,6 +154,8 @@ export const getSummaryTableItems = ({ pattern: string; patternDocsCount: number; results: Record | undefined; + sortByColumn: string; + sortByDirection: 'desc' | 'asc'; stats: Record | null; }): IndexSummaryTableItem[] => { const summaryTableItems = indexNames.map((indexName) => ({ @@ -160,47 +165,10 @@ export const getSummaryTableItems = ({ ilmPhase: ilmExplain != null ? getIlmPhase(ilmExplain[indexName]) : undefined, pattern, patternDocsCount, + sizeInBytes: getSizeInBytes({ stats, indexName }), })); - return sortBy('docsCount', summaryTableItems).reverse(); -}; - -export const getDefaultIndexIncompatibleCounts = ( - indexNames: string[] -): Record => - indexNames.reduce>( - (acc, indexName) => ({ - ...acc, - [indexName]: undefined, - }), - {} - ); - -export const createPatternIncompatibleEntries = ({ - indexNames, - patternIncompatible, -}: { - indexNames: string[]; - patternIncompatible: Record; -}): Record => - indexNames.reduce>( - (acc, indexName) => - indexName in patternIncompatible - ? { ...acc, [indexName]: patternIncompatible[indexName] } - : { ...acc, [indexName]: undefined }, - {} - ); - -export const getIncompatible = ( - patternIncompatible: Record -): number | undefined => { - const allIndexes = Object.values(patternIncompatible); - const allIndexesHaveValues = allIndexes.every((incompatible) => Number.isInteger(incompatible)); - - // only return a number when all indexes have an `incompatible` count: - return allIndexesHaveValues - ? allIndexes.reduce((acc, incompatible) => acc + Number(incompatible), 0) - : undefined; + return orderBy([sortByColumn], [sortByDirection], summaryTableItems); }; export const shouldCreateIndexNames = ({ @@ -233,3 +201,38 @@ export const shouldCreatePatternRollup = ({ return allDataLoaded || errorOccurred; }; + +export const getIndexPropertiesContainerId = ({ + indexName, + pattern, +}: { + indexName: string; + pattern: string; +}): string => `index-properties-container-${pattern}${indexName}`; + +export const defaultSort: SortConfig = { + sort: { + direction: 'desc', + field: 'docsCount', + }, +}; + +export const MIN_PAGE_SIZE = 10; + +export const getPageIndex = ({ + indexName, + items, + pageSize, +}: { + indexName: string; + items: IndexSummaryTableItem[]; + pageSize: number; +}): number | null => { + const index = items.findIndex((x) => x.indexName === indexName); + + if (index !== -1 && pageSize !== 0) { + return Math.floor(index / pageSize); + } else { + return null; + } +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.test.tsx index 4fa3b34884929..d9b002a63dc68 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.test.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.test.tsx @@ -6,12 +6,22 @@ */ import { DARK_THEME } from '@elastic/charts'; +import numeral from '@elastic/numeral'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { TestProviders } from '../../mock/test_providers'; +import { EMPTY_STAT } from '../../helpers'; +import { TestProviders } from '../../mock/test_providers/test_providers'; import { Pattern } from '.'; +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + jest.mock('../../use_stats', () => ({ useStats: jest.fn(() => ({ stats: {}, @@ -31,12 +41,15 @@ jest.mock('../../use_ilm_explain', () => ({ const defaultProps = { addSuccessToast: jest.fn(), canUserCreateAndReadCases: jest.fn(), - defaultNumberFormat: '0,0.[000]', + formatBytes, + formatNumber, getGroupByFieldsOnClick: jest.fn(), ilmPhases: ['hot', 'warm', 'unmanaged'], indexNames: undefined, openCreateCaseFlyout: jest.fn(), patternRollup: undefined, + selectedIndex: null, + setSelectedIndex: jest.fn(), theme: DARK_THEME, updatePatternIndexNames: jest.fn(), updatePatternRollup: jest.fn(), diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.tsx index bd6479490dab8..cb430d75ef12e 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/index.tsx @@ -16,14 +16,17 @@ import type { } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { ErrorEmptyPrompt } from '../error_empty_prompt'; import { + defaultSort, getIlmExplainPhaseCounts, getIlmPhase, + getPageIndex, getSummaryTableItems, + MIN_PAGE_SIZE, shouldCreateIndexNames, shouldCreatePatternRollup, } from './helpers'; @@ -33,6 +36,7 @@ import { getTotalDocsCount, getTotalPatternIncompatible, getTotalPatternIndicesChecked, + getTotalSizeInBytes, } from '../../helpers'; import { IndexProperties } from '../index_properties'; import { LoadingEmptyPrompt } from '../loading_empty_prompt'; @@ -41,7 +45,7 @@ import { RemoteClustersCallout } from '../remote_clusters_callout'; import { SummaryTable } from '../summary_table'; import { getSummaryTableColumns } from '../summary_table/helpers'; import * as i18n from './translations'; -import type { PatternRollup } from '../../types'; +import type { PatternRollup, SelectedIndex, SortConfig } from '../../types'; import { useIlmExplain } from '../../use_ilm_explain'; import { useStats } from '../../use_stats'; @@ -55,7 +59,8 @@ const EMPTY_INDEX_NAMES: string[] = []; interface Props { addSuccessToast: (toast: { title: string }) => void; canUserCreateAndReadCases: () => boolean; - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; getGroupByFieldsOnClick: ( elements: Array< | FlameElementEvent @@ -80,6 +85,8 @@ interface Props { }) => void; pattern: string; patternRollup: PatternRollup | undefined; + selectedIndex: SelectedIndex | null; + setSelectedIndex: (selectedIndex: SelectedIndex | null) => void; theme: Theme; updatePatternIndexNames: ({ indexNames, @@ -94,17 +101,25 @@ interface Props { const PatternComponent: React.FC = ({ addSuccessToast, canUserCreateAndReadCases, - defaultNumberFormat, + formatBytes, + formatNumber, getGroupByFieldsOnClick, indexNames, ilmPhases, openCreateCaseFlyout, pattern, patternRollup, + selectedIndex, + setSelectedIndex, theme, updatePatternIndexNames, updatePatternRollup, }) => { + const containerRef = useRef(null); + const [sorting, setSorting] = useState(defaultSort); + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(MIN_PAGE_SIZE); + const { error: statsError, loading: loadingStats, stats } = useStats(pattern); const { error: ilmExplainError, loading: loadingIlmExplain, ilmExplain } = useIlmExplain(pattern); @@ -129,7 +144,8 @@ const PatternComponent: React.FC = ({ = ({ [ addSuccessToast, canUserCreateAndReadCases, - defaultNumberFormat, + formatBytes, + formatNumber, getGroupByFieldsOnClick, ilmExplain, itemIdToExpandedRowMap, @@ -171,9 +188,20 @@ const PatternComponent: React.FC = ({ pattern, patternDocsCount: patternRollup?.docsCount ?? 0, results: patternRollup?.results, + sortByColumn: sorting.sort.field, + sortByDirection: sorting.sort.direction, stats, }), - [ilmExplain, indexNames, pattern, patternRollup, stats] + [ + ilmExplain, + indexNames, + pattern, + patternRollup?.docsCount, + patternRollup?.results, + sorting.sort.direction, + sorting.sort.field, + stats, + ] ); useEffect(() => { @@ -196,6 +224,10 @@ const PatternComponent: React.FC = ({ indices: getIndexNames({ stats, ilmExplain, ilmPhases }).length, pattern, results: undefined, + sizeInBytes: getTotalSizeInBytes({ + indexNames: getIndexNames({ stats, ilmExplain, ilmPhases }), + stats, + }), stats, }); } @@ -212,18 +244,49 @@ const PatternComponent: React.FC = ({ updatePatternRollup, ]); + useEffect(() => { + if (selectedIndex?.pattern === pattern) { + const selectedPageIndex = getPageIndex({ + indexName: selectedIndex.indexName, + items, + pageSize, + }); + + if (selectedPageIndex != null) { + setPageIndex(selectedPageIndex); + } + + if (itemIdToExpandedRowMap[selectedIndex.indexName] == null) { + toggleExpanded(selectedIndex.indexName); // expand the selected index + } + + containerRef.current?.scrollIntoView(); + setSelectedIndex(null); + } + }, [ + itemIdToExpandedRowMap, + items, + pageSize, + pattern, + selectedIndex, + setSelectedIndex, + toggleExpanded, + ]); + return ( - + @@ -242,19 +305,27 @@ const PatternComponent: React.FC = ({ {loading && } {!loading && error == null && ( - +
    + +
    )}
    ); }; -PatternComponent.displayName = 'PatternComponent'; - export const Pattern = React.memo(PatternComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/index.tsx index a8ae7a673b92e..233de9fc93a1f 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/index.tsx @@ -13,23 +13,27 @@ import { PatternLabel } from './pattern_label'; import { StatsRollup } from './stats_rollup'; interface Props { - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; ilmExplainPhaseCounts: IlmExplainPhaseCounts; incompatible: number | undefined; indices: number | undefined; indicesChecked: number | undefined; pattern: string; patternDocsCount: number; + patternSizeInBytes: number; } const PatternSummaryComponent: React.FC = ({ - defaultNumberFormat, + formatBytes, + formatNumber, ilmExplainPhaseCounts, incompatible, indices, indicesChecked, pattern, patternDocsCount, + patternSizeInBytes, }) => ( @@ -44,12 +48,14 @@ const PatternSummaryComponent: React.FC = ({ diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/pattern_label/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/pattern_label/helpers.test.ts new file mode 100644 index 0000000000000..dfa285d60b40a --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/pattern_label/helpers.test.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 { getResultToolTip, showResult } from './helpers'; +import { ALL_PASSED, SOME_FAILED, SOME_UNCHECKED } from './translations'; + +describe('helpers', () => { + describe('getResultToolTip', () => { + test('it returns the expected tool tip when `incompatible` is undefined', () => { + expect(getResultToolTip(undefined)).toEqual(SOME_UNCHECKED); + }); + + test('it returns the expected tool tip when `incompatible` is zero', () => { + expect(getResultToolTip(0)).toEqual(ALL_PASSED); + }); + + test('it returns the expected tool tip when `incompatible` is non-zero', () => { + expect(getResultToolTip(1)).toEqual(SOME_FAILED); + }); + }); + + describe('showResult', () => { + test('it returns true when `incompatible` is defined, and `indicesChecked` equals `indices`', () => { + const incompatible = 0; // none of the indices checked had incompatible fields + const indicesChecked = 2; // all indices were checked + const indices = 2; // total indices + + expect( + showResult({ + incompatible, + indices, + indicesChecked, + }) + ).toBe(true); + }); + + test('it returns false when `incompatible` is defined, and `indices` does NOT equal `indicesChecked`', () => { + const incompatible = 0; // the one index checked (so far) didn't have any incompatible fields + const indicesChecked = 1; // only one index has been checked so far + const indices = 2; // total indices + + expect( + showResult({ + incompatible, + indices, + indicesChecked, + }) + ).toBe(false); + }); + + test('it returns false when `incompatible` is undefined', () => { + const incompatible = undefined; // a state of undefined indicates there are no results + const indicesChecked = 1; // all indices were checked + const indices = 1; // total indices + + expect( + showResult({ + incompatible, + indices, + indicesChecked, + }) + ).toBe(false); + }); + + test('it returns false when `indices` is undefined', () => { + const incompatible = 0; // none of the indices checked had incompatible fields + const indicesChecked = 2; // all indices were checked + const indices = undefined; // the total number of indices is unknown + + expect( + showResult({ + incompatible, + indices, + indicesChecked, + }) + ).toBe(false); + }); + + test('it returns false when `indicesChecked` is undefined', () => { + const incompatible = 0; // none of the indices checked had incompatible fields + const indicesChecked = undefined; // no indices were checked + const indices = 2; // total indices + + expect( + showResult({ + incompatible, + indices, + indicesChecked, + }) + ).toBe(false); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/stats_rollup/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/stats_rollup/index.tsx index dcf28487a091c..4a51880404542 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/stats_rollup/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/pattern_summary/stats_rollup/index.tsx @@ -6,7 +6,6 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiToolTip } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import React, { useMemo } from 'react'; import styled from 'styled-components'; @@ -29,21 +28,25 @@ const DocsContainer = styled.div` const STAT_TITLE_SIZE = 's'; interface Props { - defaultNumberFormat: string; docsCount: number | undefined; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; incompatible: number | undefined; indices: number | undefined; indicesChecked: number | undefined; pattern?: string; + sizeInBytes: number | undefined; } const StatsRollupComponent: React.FC = ({ - defaultNumberFormat, docsCount, + formatBytes, + formatNumber, incompatible, indices, indicesChecked, pattern, + sizeInBytes, }) => { const incompatibleDescription = useMemo( () => , @@ -53,11 +56,17 @@ const StatsRollupComponent: React.FC = ({ () => , [] ); + const sizeDescription = useMemo(() => , []); const docsDescription = useMemo(() => , []); const indicesDescription = useMemo(() => , []); return ( - + = ({ > @@ -88,11 +95,7 @@ const StatsRollupComponent: React.FC = ({ > @@ -110,7 +113,25 @@ const StatsRollupComponent: React.FC = ({ > + + + + + + + + @@ -126,9 +147,7 @@ const StatsRollupComponent: React.FC = ({ > diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/translations.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/translations.ts index 776ceef776172..79375b1956169 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/translations.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/pattern/translations.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; export const ERROR_LOADING_METADATA_TITLE = (pattern: string) => i18n.translate('ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataTitle', { values: { pattern }, - defaultMessage: "Indices matching the {pattern} pattern won't checked", + defaultMessage: "Indices matching the {pattern} pattern won't be checked", }); export const ERROR_LOADING_METADATA_BODY = ({ diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/same_family/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/same_family/index.test.tsx index 94983d262404b..0c4a439f999bc 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/same_family/index.test.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/same_family/index.test.tsx @@ -9,7 +9,7 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { SAME_FAMILY } from './translations'; -import { TestProviders } from '../../mock/test_providers'; +import { TestProviders } from '../../mock/test_providers/test_providers'; import { SameFamily } from '.'; describe('SameFamily', () => { diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/stat_label/translations.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/stat_label/translations.ts index afaad5d08ed6e..1c52f29858ac4 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/stat_label/translations.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/stat_label/translations.ts @@ -69,6 +69,17 @@ export const INDICES = i18n.translate('ecsDataQualityDashboard.statLabels.indice defaultMessage: 'Indices', }); +export const SIZE = i18n.translate('ecsDataQualityDashboard.statLabels.sizeLabel', { + defaultMessage: 'Size', +}); + +export const INDICES_SIZE_PATTERN_TOOL_TIP = (pattern: string) => + i18n.translate('ecsDataQualityDashboard.statLabels.indicesSizePatternToolTip', { + values: { pattern }, + defaultMessage: + 'The total size of the primary indices matching the {pattern} pattern (does not include replicas)', + }); + export const TOTAL_COUNT_OF_INDICES_CHECKED_MATCHING_PATTERN_TOOL_TIP = (pattern: string) => i18n.translate( 'ecsDataQualityDashboard.statLabels.totalCountOfIndicesCheckedMatchingPatternToolTip', @@ -112,3 +123,10 @@ export const TOTAL_INDICES_TOOL_TIP = i18n.translate( defaultMessage: 'The total count of all indices', } ); + +export const TOTAL_SIZE_TOOL_TIP = i18n.translate( + 'ecsDataQualityDashboard.statLabels.totalSizeToolTip', + { + defaultMessage: 'The total size of all primary indices (does not include replicas)', + } +); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/index.test.tsx new file mode 100644 index 0000000000000..f523c96cc0801 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/index.test.tsx @@ -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 { DARK_THEME, Settings } from '@elastic/charts'; +import numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { + FlattenedBucket, + getFlattenedBuckets, + getLegendItems, +} from '../body/data_quality_details/storage_details/helpers'; +import { EMPTY_STAT } from '../../helpers'; +import { alertIndexWithAllResults } from '../../mock/pattern_rollup/mock_alerts_pattern_rollup'; +import { auditbeatWithAllResults } from '../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { packetbeatNoResults } from '../../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import type { Props } from '.'; +import { StorageTreemap } from '.'; +import { DEFAULT_MAX_CHART_HEIGHT } from '../tabs/styles'; +import { NO_DATA_LABEL } from './translations'; +import { PatternRollup } from '../../types'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const ilmPhases = ['hot', 'warm', 'unmanaged']; +const patterns = ['.alerts-security.alerts-default', 'auditbeat-*', 'packetbeat-*']; + +const patternRollups: Record = { + '.alerts-security.alerts-default': alertIndexWithAllResults, + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, +}; + +const flattenedBuckets = getFlattenedBuckets({ + ilmPhases, + patternRollups, +}); + +const onIndexSelected = jest.fn(); + +const defaultProps: Props = { + flattenedBuckets, + formatBytes, + maxChartHeight: DEFAULT_MAX_CHART_HEIGHT, + onIndexSelected, + patternRollups, + patterns, + theme: DARK_THEME, +}; + +jest.mock('@elastic/charts', () => { + const actual = jest.requireActual('@elastic/charts'); + return { + ...actual, + Settings: jest.fn().mockReturnValue(null), + }; +}); + +describe('StorageTreemap', () => { + describe('when data is provided', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders the treemap', () => { + expect(screen.getByTestId('storageTreemap').querySelector('.echChart')).toBeInTheDocument(); + }); + + test('it renders the legend with the expected overflow-y style', () => { + expect(screen.getByTestId('legend')).toHaveClass('eui-yScroll'); + }); + + test('it uses a theme with the expected `minFontSize` to show more labels at various screen resolutions', () => { + expect((Settings as jest.Mock).mock.calls[0][0].theme[0].partition.minFontSize).toEqual(4); + }); + + describe('legend items', () => { + const allLegendItems = getLegendItems({ patterns, flattenedBuckets, patternRollups }); + + describe('pattern legend items', () => { + const justPatterns = allLegendItems.filter((x) => x.ilmPhase == null); + + justPatterns.forEach(({ ilmPhase, index, pattern, sizeInBytes }) => { + test(`it renders the expend legend item for pattern: ilmPhase ${ilmPhase} pattern ${pattern} index ${index}`, () => { + expect( + screen.getByTestId(`chart-legend-item-${ilmPhase}${pattern}${index}`) + ).toHaveTextContent(`${pattern}${formatBytes(sizeInBytes)}`); + }); + }); + }); + + describe('index legend items', () => { + const justIndices = allLegendItems.filter((x) => x.ilmPhase != null); + + justIndices.forEach(({ ilmPhase, index, pattern, sizeInBytes }) => { + test(`it renders the expend legend item for index: ilmPhase ${ilmPhase} pattern ${pattern} index ${index}`, () => { + expect( + screen.getByTestId(`chart-legend-item-${ilmPhase}${pattern}${index}`) + ).toHaveTextContent(`${index}${formatBytes(sizeInBytes)}`); + }); + + test(`it invokes onIndexSelected() with the expected values for ilmPhase ${ilmPhase} pattern ${pattern} index ${index}`, () => { + const legendItem = screen.getByTestId( + `chart-legend-item-${ilmPhase}${pattern}${index}` + ); + + userEvent.click(legendItem); + + expect(onIndexSelected).toBeCalledWith({ indexName: index, pattern }); + }); + }); + }); + }); + }); + + describe('when the response does NOT have data', () => { + const emptyFlattenedBuckets: FlattenedBucket[] = []; + + beforeEach(() => { + render( + + + + ); + }); + + test('it does NOT render the treemap', () => { + expect(screen.queryByTestId('storageTreemap')).not.toBeInTheDocument(); + }); + + test('it does NOT render the legend', () => { + expect(screen.queryByTestId('legend')).not.toBeInTheDocument(); + }); + + test('it renders the "no data" message', () => { + expect(screen.getByText(NO_DATA_LABEL)).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/index.tsx new file mode 100644 index 0000000000000..b42cd6072ad82 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/index.tsx @@ -0,0 +1,201 @@ +/* + * 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 { isEmpty } from 'lodash/fp'; +import type { + Datum, + ElementClickListener, + FlameElementEvent, + HeatmapElementEvent, + MetricElementEvent, + PartialTheme, + PartitionElementEvent, + Theme, + WordCloudElementEvent, + XYChartElementEvent, +} from '@elastic/charts'; +import { Chart, Partition, PartitionLayout, Settings } from '@elastic/charts'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; + +import { + FlattenedBucket, + getLayersMultiDimensional, + getLegendItems, + getPathToFlattenedBucketMap, +} from '../body/data_quality_details/storage_details/helpers'; +import { ChartLegendItem } from '../../ecs_summary_donut_chart/chart_legend/chart_legend_item'; +import { NoData } from './no_data'; +import { ChartFlexItem, LegendContainer } from '../tabs/styles'; +import { PatternRollup, SelectedIndex } from '../../types'; + +export const DEFAULT_MIN_CHART_HEIGHT = 240; // px +export const LEGEND_WIDTH = 220; // px +export const LEGEND_TEXT_WITH = 120; // px + +export interface Props { + flattenedBuckets: FlattenedBucket[]; + formatBytes: (value: number | undefined) => string; + maxChartHeight?: number; + minChartHeight?: number; + onIndexSelected: ({ indexName, pattern }: SelectedIndex) => void; + patternRollups: Record; + patterns: string[]; + theme: Theme; +} + +interface GetGroupByFieldsResult { + pattern: string; + indexName: string; +} + +export const getGroupByFieldsOnClick = ( + elements: Array< + | FlameElementEvent + | HeatmapElementEvent + | MetricElementEvent + | PartitionElementEvent + | WordCloudElementEvent + | XYChartElementEvent + > +): GetGroupByFieldsResult => { + const flattened = elements.flat(2); + + const pattern = + flattened.length > 0 && 'groupByRollup' in flattened[0] && flattened[0].groupByRollup != null + ? `${flattened[0].groupByRollup}` + : ''; + + const indexName = + flattened.length > 1 && 'groupByRollup' in flattened[1] && flattened[1].groupByRollup != null + ? `${flattened[1].groupByRollup}` + : ''; + + return { + pattern, + indexName, + }; +}; + +const StorageTreemapComponent: React.FC = ({ + flattenedBuckets, + formatBytes, + maxChartHeight, + minChartHeight = DEFAULT_MIN_CHART_HEIGHT, + onIndexSelected, + patternRollups, + patterns, + theme, +}: Props) => { + const fillColor = useMemo(() => theme.background.color, [theme.background.color]); + + const treemapTheme: PartialTheme[] = useMemo( + () => [ + { + partition: { + fillLabel: { valueFont: { fontWeight: 700 } }, + idealFontSizeJump: 1.15, + maxFontSize: 16, + minFontSize: 4, + sectorLineStroke: fillColor, // draws the light or dark "lines" between partitions + sectorLineWidth: 1.5, + }, + }, + ], + [fillColor] + ); + + const onElementClick: ElementClickListener = useCallback( + (event) => { + const { indexName, pattern } = getGroupByFieldsOnClick(event); + + if (!isEmpty(indexName) && !isEmpty(pattern)) { + onIndexSelected({ indexName, pattern }); + } + }, + [onIndexSelected] + ); + + const pathToFlattenedBucketMap = getPathToFlattenedBucketMap(flattenedBuckets); + + const layers = useMemo( + () => + getLayersMultiDimensional({ + formatBytes, + layer0FillColor: fillColor, + pathToFlattenedBucketMap, + }), + [fillColor, formatBytes, pathToFlattenedBucketMap] + ); + + const valueAccessor = useCallback(({ sizeInBytes }: Datum) => sizeInBytes, []); + + const legendItems = useMemo( + () => getLegendItems({ patterns, flattenedBuckets, patternRollups }), + [flattenedBuckets, patternRollups, patterns] + ); + + if (flattenedBuckets.length === 0) { + return ; + } + + return ( + + + {flattenedBuckets.length === 0 ? ( + + ) : ( + + + formatBytes(d)} + /> + + )} + + + + + {legendItems.map(({ color, ilmPhase, index, pattern, sizeInBytes }) => ( + { + onIndexSelected({ indexName: index, pattern }); + } + : undefined + } + text={index ?? pattern} + textWidth={LEGEND_TEXT_WITH} + /> + ))} + + + + ); +}; + +export const StorageTreemap = React.memo(StorageTreemapComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/no_data/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/no_data/index.test.tsx new file mode 100644 index 0000000000000..0cf39beae7b2d --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/no_data/index.test.tsx @@ -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 { render, screen } from '@testing-library/react'; +import React from 'react'; + +import * as i18n from '../translations'; + +import { NoData } from '.'; + +describe('NoData', () => { + test('renders the expected "no data" message', () => { + render(); + + expect(screen.getByText(i18n.NO_DATA_LABEL)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/no_data/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/no_data/index.tsx new file mode 100644 index 0000000000000..a5edca17291d2 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/no_data/index.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +import * as i18n from '../translations'; + +const NoDataLabel = styled(EuiText)` + text-align: center; +`; + +interface Props { + reason?: string; +} + +const NoDataComponent: React.FC = ({ reason }) => ( + + + + {i18n.NO_DATA_LABEL} + + + {reason != null && ( + <> + + + {reason} + + + )} + + +); + +NoDataComponent.displayName = 'NoDataComponent'; + +export const NoData = React.memo(NoDataComponent); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/translations.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/translations.ts new file mode 100644 index 0000000000000..f60cb2366cf36 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/storage_treemap/translations.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 { i18n } from '@kbn/i18n'; + +export const NO_DATA_LABEL = i18n.translate('ecsDataQualityDashboard.storageTreemap.noDataLabel', { + defaultMessage: 'No data to display', +}); + +export const NO_DATA_REASON_LABEL = (stackByField1: string) => + i18n.translate('ecsDataQualityDashboard.storageTreemap.noDataReasonLabel', { + values: { + stackByField1, + }, + defaultMessage: 'The {stackByField1} field was not present in any groups', + }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx new file mode 100644 index 0000000000000..e913d38dbb07c --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.test.tsx @@ -0,0 +1,543 @@ +/* + * 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 { EuiScreenReaderOnly, EuiTableFieldDataColumnType } from '@elastic/eui'; +import numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { omit } from 'lodash/fp'; +import React from 'react'; + +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { EMPTY_STAT } from '../../helpers'; +import { + getDocsCountPercent, + getResultIcon, + getResultIconColor, + getResultToolTip, + getShowPagination, + getSummaryTableColumns, + getToggleButtonId, + IndexSummaryTableItem, +} from './helpers'; +import { COLLAPSE, EXPAND, FAILED, PASSED, THIS_INDEX_HAS_NOT_BEEN_CHECKED } from './translations'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +describe('helpers', () => { + describe('getResultToolTip', () => { + test('it shows a "this index has not been checked" tool tip when `incompatible` is undefined', () => { + expect(getResultToolTip(undefined)).toEqual(THIS_INDEX_HAS_NOT_BEEN_CHECKED); + }); + + test('it returns Passed when `incompatible` is zero', () => { + expect(getResultToolTip(0)).toEqual(PASSED); + }); + + test('it returns Failed when `incompatible` is NOT zero', () => { + expect(getResultToolTip(1)).toEqual(FAILED); + }); + }); + + describe('getResultIconColor', () => { + test('it returns `ghost` when `incompatible` is undefined', () => { + expect(getResultIconColor(undefined)).toEqual('ghost'); + }); + + test('it returns `success` when `incompatible` is zero', () => { + expect(getResultIconColor(0)).toEqual('success'); + }); + + test('it returns `danger` when `incompatible` is NOT zero', () => { + expect(getResultIconColor(1)).toEqual('danger'); + }); + }); + + describe('getResultIcon', () => { + test('it returns `cross` when `incompatible` is undefined', () => { + expect(getResultIcon(undefined)).toEqual('cross'); + }); + + test('it returns `check` when `incompatible` is zero', () => { + expect(getResultIcon(0)).toEqual('check'); + }); + + test('it returns `cross` when `incompatible` is NOT zero', () => { + expect(getResultIcon(1)).toEqual('cross'); + }); + }); + + describe('getDocsCountPercent', () => { + test('it returns an empty string when `patternDocsCount` is zero', () => { + expect( + getDocsCountPercent({ + docsCount: 0, + patternDocsCount: 0, + }) + ).toEqual(''); + }); + + test('it returns the expected format when when `patternDocsCount` is non-zero, and `locales` is undefined', () => { + expect( + getDocsCountPercent({ + docsCount: 2904, + locales: undefined, + patternDocsCount: 57410, + }) + ).toEqual('5.1%'); + }); + + test('it returns the expected format when when `patternDocsCount` is non-zero, and `locales` is provided', () => { + expect( + getDocsCountPercent({ + docsCount: 2904, + locales: 'en-US', + patternDocsCount: 57410, + }) + ).toEqual('5.1%'); + }); + }); + + describe('getToggleButtonId', () => { + test('it returns the expected id when the button is expanded', () => { + expect( + getToggleButtonId({ + indexName: 'auditbeat-custom-index-1', + isExpanded: true, + pattern: 'auditbeat-*', + }) + ).toEqual('collapseauditbeat-custom-index-1auditbeat-*'); + }); + + test('it returns the expected id when the button is collapsed', () => { + expect( + getToggleButtonId({ + indexName: 'auditbeat-custom-index-1', + isExpanded: false, + pattern: 'auditbeat-*', + }) + ).toEqual('expandauditbeat-custom-index-1auditbeat-*'); + }); + }); + + describe('getSummaryTableColumns', () => { + const indexName = '.ds-auditbeat-8.6.1-2023.02.07-000001'; + + const indexSummaryTableItem: IndexSummaryTableItem = { + indexName, + docsCount: 2796, + incompatible: undefined, + ilmPhase: 'hot', + pattern: 'auditbeat-*', + patternDocsCount: 57410, + sizeInBytes: 103344068, + }; + + const hasIncompatible: IndexSummaryTableItem = { + ...indexSummaryTableItem, + incompatible: 1, // <-- one incompatible field + }; + + test('it returns the expected column configuration', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }).map((x) => omit('render', x)); + + expect(columns).toEqual([ + { + align: 'right', + isExpander: true, + name: ( + + {'Expand rows'} + + ), + width: '40px', + }, + { + field: 'incompatible', + name: 'Result', + sortable: true, + truncateText: false, + width: '50px', + }, + { field: 'indexName', name: 'Index', sortable: true, truncateText: false, width: '300px' }, + { field: 'docsCount', name: 'Docs', sortable: true, truncateText: false }, + { + field: 'incompatible', + name: 'Incompatible fields', + sortable: true, + truncateText: false, + }, + { field: 'ilmPhase', name: 'ILM Phase', sortable: true, truncateText: false }, + { field: 'sizeInBytes', name: 'Size', sortable: true, truncateText: false }, + ]); + }); + + describe('expand rows render()', () => { + test('it renders an Expand button when the row is NOT expanded', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const expandRowsRender = (columns[0] as EuiTableFieldDataColumnType) + .render; + + render( + + {expandRowsRender != null && + expandRowsRender(indexSummaryTableItem, indexSummaryTableItem)} + + ); + + expect(screen.getByLabelText(EXPAND)).toBeInTheDocument(); + }); + + test('it renders a Collapse button when the row is expanded', () => { + const itemIdToExpandedRowMap: Record = { + [indexName]: () => null, + }; + + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const expandRowsRender = (columns[0] as EuiTableFieldDataColumnType) + .render; + + render( + + {expandRowsRender != null && + expandRowsRender(indexSummaryTableItem, indexSummaryTableItem)} + + ); + + expect(screen.getByLabelText(COLLAPSE)).toBeInTheDocument(); + }); + + test('it invokes the `toggleExpanded` with the index name when the button is clicked', () => { + const toggleExpanded = jest.fn(); + + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded, + }); + const expandRowsRender = (columns[0] as EuiTableFieldDataColumnType) + .render; + + render( + + {expandRowsRender != null && + expandRowsRender(indexSummaryTableItem, indexSummaryTableItem)} + + ); + + const button = screen.getByLabelText(EXPAND); + userEvent.click(button); + + expect(toggleExpanded).toBeCalledWith(indexName); + }); + }); + + describe('incompatible render()', () => { + test('it renders a placeholder when incompatible is undefined', () => { + const incompatibleIsUndefined: IndexSummaryTableItem = { + ...indexSummaryTableItem, + incompatible: undefined, // <-- + }; + + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const incompatibleRender = ( + columns[1] as EuiTableFieldDataColumnType + ).render; + + render( + + {incompatibleRender != null && + incompatibleRender(incompatibleIsUndefined, incompatibleIsUndefined)} + + ); + + expect(screen.getByTestId('incompatiblePlaceholder')).toHaveTextContent(EMPTY_STAT); + }); + + test('it renders the expected icon when there are incompatible fields', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const incompatibleRender = ( + columns[1] as EuiTableFieldDataColumnType + ).render; + + render( + + {incompatibleRender != null && incompatibleRender(hasIncompatible, hasIncompatible)} + + ); + + expect(screen.getByTestId('resultIcon')).toHaveAttribute('data-euiicon-type', 'cross'); + }); + + test('it renders the expected icon when there are zero fields', () => { + const zeroIncompatible: IndexSummaryTableItem = { + ...indexSummaryTableItem, + incompatible: 0, // <-- one incompatible field + }; + + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const incompatibleRender = ( + columns[1] as EuiTableFieldDataColumnType + ).render; + + render( + + {incompatibleRender != null && incompatibleRender(zeroIncompatible, zeroIncompatible)} + + ); + + expect(screen.getByTestId('resultIcon')).toHaveAttribute('data-euiicon-type', 'check'); + }); + }); + + describe('indexName render()', () => { + test('it renders the index name', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const indexNameRender = (columns[2] as EuiTableFieldDataColumnType) + .render; + + render( + + {indexNameRender != null && + indexNameRender(indexSummaryTableItem, indexSummaryTableItem)} + + ); + + expect(screen.getByTestId('indexName')).toHaveTextContent(indexName); + }); + }); + + describe('docsCount render()', () => { + beforeEach(() => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const docsCountRender = (columns[3] as EuiTableFieldDataColumnType) + .render; + + render( + + {docsCountRender != null && docsCountRender(hasIncompatible, hasIncompatible)} + + ); + }); + + test('it renders the expected value', () => { + expect(screen.getByTestId('docsCount')).toHaveAttribute( + 'value', + String(hasIncompatible.docsCount) + ); + }); + + test('it renders the expected max (progress)', () => { + expect(screen.getByTestId('docsCount')).toHaveAttribute( + 'max', + String(hasIncompatible.patternDocsCount) + ); + }); + }); + + describe('incompatible column render()', () => { + test('it renders the expected value', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const incompatibleRender = ( + columns[4] as EuiTableFieldDataColumnType + ).render; + + render( + + {incompatibleRender != null && incompatibleRender(hasIncompatible, hasIncompatible)} + + ); + + expect(screen.getByTestId('incompatibleStat')).toHaveTextContent('1'); + }); + + test('it renders the expected placeholder when incompatible is undefined', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const incompatibleRender = ( + columns[4] as EuiTableFieldDataColumnType + ).render; + + render( + + {incompatibleRender != null && + incompatibleRender(indexSummaryTableItem, indexSummaryTableItem)} + + ); + + expect(screen.getByTestId('incompatibleStat')).toHaveTextContent('-- --'); // the euiScreenReaderOnly content renders an additional set of -- + }); + }); + + describe('ilmPhase column render()', () => { + test('it renders the expected ilmPhase badge content', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const ilmPhaseRender = (columns[5] as EuiTableFieldDataColumnType) + .render; + + render( + + {ilmPhaseRender != null && ilmPhaseRender(hasIncompatible, hasIncompatible)} + + ); + + expect(screen.getByTestId('ilmPhase')).toHaveTextContent('hot'); + }); + + test('it does NOT render the ilmPhase badge when `ilmPhase` is undefined', () => { + const ilmPhaseIsUndefined: IndexSummaryTableItem = { + ...indexSummaryTableItem, + ilmPhase: undefined, // <-- + }; + + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + const ilmPhaseRender = (columns[5] as EuiTableFieldDataColumnType) + .render; + + render( + + {ilmPhaseRender != null && ilmPhaseRender(ilmPhaseIsUndefined, ilmPhaseIsUndefined)} + + ); + + expect(screen.queryByTestId('ilmPhase')).not.toBeInTheDocument(); + }); + }); + + describe('sizeInBytes render()', () => { + test('it renders the expected formatted bytes', () => { + const columns = getSummaryTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap: {}, + pattern: 'auditbeat-*', + toggleExpanded: jest.fn(), + }); + + const sizeInBytesRender = (columns[6] as EuiTableFieldDataColumnType) + .render; + + render( + + {sizeInBytesRender != null && + sizeInBytesRender(indexSummaryTableItem, indexSummaryTableItem)} + + ); + + expect(screen.getByTestId('sizeInBytes')).toHaveTextContent('98.6MB'); + }); + }); + }); + + describe('getShowPagination', () => { + test('it returns true when `totalItemCount` is greater than `minPageSize`', () => { + expect( + getShowPagination({ + minPageSize: 10, + totalItemCount: 11, + }) + ).toBe(true); + }); + + test('it returns false when `totalItemCount` equals `minPageSize`', () => { + expect( + getShowPagination({ + minPageSize: 10, + totalItemCount: 10, + }) + ).toBe(false); + }); + + test('it returns false when `totalItemCount` is less than `minPageSize`', () => { + expect( + getShowPagination({ + minPageSize: 10, + totalItemCount: 9, + }) + ).toBe(false); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx index e3789fce0bb5b..89e0d78fddb6b 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/helpers.tsx @@ -21,6 +21,7 @@ import styled from 'styled-components'; import { EMPTY_STAT, getIlmPhaseDescription, getIncompatibleStatColor } from '../../helpers'; import { INCOMPATIBLE_INDEX_TOOL_TIP } from '../stat_label/translations'; +import { INDEX_SIZE_TOOLTIP } from '../../translations'; import * as i18n from './translations'; import type { IlmPhase } from '../../types'; @@ -39,6 +40,7 @@ export interface IndexSummaryTableItem { ilmPhase: IlmPhase | undefined; pattern: string; patternDocsCount: number; + sizeInBytes: number; } export const getResultToolTip = (incompatible: number | undefined): string => { @@ -83,13 +85,27 @@ export const getDocsCountPercent = ({ }) : ''; +export const getToggleButtonId = ({ + indexName, + isExpanded, + pattern, +}: { + indexName: string; + isExpanded: boolean; + pattern: string; +}): string => (isExpanded ? `collapse${indexName}${pattern}` : `expand${indexName}${pattern}`); + export const getSummaryTableColumns = ({ + formatBytes, formatNumber, itemIdToExpandedRowMap, + pattern, toggleExpanded, }: { + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; itemIdToExpandedRowMap: Record; + pattern: string; toggleExpanded: (indexName: string) => void; }): Array> => [ { @@ -103,6 +119,11 @@ export const getSummaryTableColumns = ({ render: ({ indexName }: IndexSummaryTableItem) => ( toggleExpanded(indexName)} iconType={itemIdToExpandedRowMap[indexName] ? 'arrowDown' : 'arrowRight'} /> @@ -115,11 +136,15 @@ export const getSummaryTableColumns = ({ render: (_, { incompatible }) => incompatible != null ? ( - + ) : ( - {EMPTY_STAT} + {EMPTY_STAT} ), sortable: true, @@ -129,9 +154,11 @@ export const getSummaryTableColumns = ({ { field: 'indexName', name: i18n.INDEX, - render: (_, { indexName, pattern }) => ( + render: (_, { indexName }) => ( - {indexName} + + {indexName} + ), sortable: true, @@ -144,6 +171,7 @@ export const getSummaryTableColumns = ({ render: (_, { docsCount, patternDocsCount }) => ( ( ), - sortable: false, + sortable: true, truncateText: false, }, { @@ -178,10 +207,23 @@ export const getSummaryTableColumns = ({ render: (_, { ilmPhase }) => ilmPhase != null ? ( - {ilmPhase} + + {ilmPhase} + ) : null, - sortable: false, + sortable: true, + truncateText: false, + }, + { + field: 'sizeInBytes', + name: i18n.SIZE, + render: (_, { sizeInBytes }) => ( + + {formatBytes(sizeInBytes)} + + ), + sortable: true, truncateText: false, }, ]; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.test.tsx new file mode 100644 index 0000000000000..235ec61a204af --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.test.tsx @@ -0,0 +1,89 @@ +/* + * 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 numeral from '@elastic/numeral'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { EMPTY_STAT } from '../../helpers'; +import { getSummaryTableColumns } from './helpers'; +import { mockIlmExplain } from '../../mock/ilm_explain/mock_ilm_explain'; +import { auditbeatWithAllResults } from '../../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { mockStats } from '../../mock/stats/mock_stats'; +import { TestProviders } from '../../mock/test_providers/test_providers'; +import { getSummaryTableItems } from '../pattern/helpers'; +import { SortConfig } from '../../types'; +import { Props, SummaryTable } from '.'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const indexNames = [ + '.ds-auditbeat-8.6.1-2023.02.07-000001', + 'auditbeat-custom-empty-index-1', + 'auditbeat-custom-index-1', + '.internal.alerts-security.alerts-default-000001', + '.ds-packetbeat-8.5.3-2023.02.04-000001', + '.ds-packetbeat-8.6.1-2023.02.04-000001', +]; + +export const defaultSort: SortConfig = { + sort: { + direction: 'desc', + field: 'docsCount', + }, +}; + +const pattern = 'auditbeat-*'; + +const items = getSummaryTableItems({ + ilmExplain: mockIlmExplain, + indexNames: indexNames ?? [], + pattern, + patternDocsCount: auditbeatWithAllResults?.docsCount ?? 0, + results: auditbeatWithAllResults?.results, + sortByColumn: defaultSort.sort.field, + sortByDirection: defaultSort.sort.direction, + stats: mockStats, +}); + +const defaultProps: Props = { + formatBytes, + formatNumber, + getTableColumns: getSummaryTableColumns, + itemIdToExpandedRowMap: {}, + items, + pageIndex: 0, + pageSize: 10, + pattern, + setPageIndex: jest.fn(), + setPageSize: jest.fn(), + setSorting: jest.fn(), + sorting: defaultSort, + toggleExpanded: jest.fn(), +}; + +describe('SummaryTable', () => { + beforeEach(() => { + jest.clearAllMocks(); + + render( + + + + ); + }); + + test('it renders the summary table', () => { + expect(screen.getByTestId('summaryTable')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.tsx index 7b02add151803..2dd2c4e214dc0 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/summary_table/index.tsx @@ -5,79 +5,79 @@ * 2.0. */ -import type { - CriteriaWithPagination, - Direction, - EuiBasicTableColumn, - Pagination, -} from '@elastic/eui'; +import type { CriteriaWithPagination, EuiBasicTableColumn, Pagination } from '@elastic/eui'; import { EuiInMemoryTable } from '@elastic/eui'; -import numeral from '@elastic/numeral'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; -import { EMPTY_STAT } from '../../helpers'; import type { IndexSummaryTableItem } from './helpers'; import { getShowPagination } from './helpers'; +import { defaultSort, MIN_PAGE_SIZE } from '../pattern/helpers'; +import { SortConfig } from '../../types'; -const MIN_PAGE_SIZE = 10; - -interface SortConfig { - sort: { - direction: Direction; - field: string; - }; -} - -const defaultSort: SortConfig = { - sort: { - direction: 'desc', - field: 'docsCount', - }, -}; - -interface Props { - defaultNumberFormat: string; +export interface Props { + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; getTableColumns: ({ + formatBytes, formatNumber, itemIdToExpandedRowMap, + pattern, toggleExpanded, }: { + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; itemIdToExpandedRowMap: Record; + pattern: string; toggleExpanded: (indexName: string) => void; }) => Array>; itemIdToExpandedRowMap: Record; items: IndexSummaryTableItem[]; + pageIndex: number; + pageSize: number; + pattern: string; + setPageIndex: (pageIndex: number) => void; + setPageSize: (pageSize: number) => void; + setSorting: (sortConfig: SortConfig) => void; + sorting: SortConfig; toggleExpanded: (indexName: string) => void; } const SummaryTableComponent: React.FC = ({ - defaultNumberFormat, + formatBytes, + formatNumber, getTableColumns, itemIdToExpandedRowMap, items, + pageIndex, + pageSize, + pattern, + setPageIndex, + setPageSize, + setSorting, + sorting, toggleExpanded, }) => { - const [sorting, setSorting] = useState(defaultSort); - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); - const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(MIN_PAGE_SIZE); const columns = useMemo( - () => getTableColumns({ formatNumber, itemIdToExpandedRowMap, toggleExpanded }), - [formatNumber, getTableColumns, itemIdToExpandedRowMap, toggleExpanded] + () => + getTableColumns({ + formatBytes, + formatNumber, + itemIdToExpandedRowMap, + pattern, + toggleExpanded, + }), + [formatBytes, formatNumber, getTableColumns, itemIdToExpandedRowMap, pattern, toggleExpanded] ); const getItemId = useCallback((item: IndexSummaryTableItem) => item.indexName, []); - const onChange = useCallback(({ page, sort }: CriteriaWithPagination) => { - setSorting({ sort: sort ?? defaultSort.sort }); - - setPageIndex(page.index); - setPageSize(page.size); - }, []); + const onChange = useCallback( + ({ page, sort }: CriteriaWithPagination) => { + setSorting({ sort: sort ?? defaultSort.sort }); + setPageIndex(page.index); + setPageSize(page.size); + }, + [setPageIndex, setPageSize, setSorting] + ); const pagination: Pagination = useMemo( () => ({ @@ -91,9 +91,10 @@ const SummaryTableComponent: React.FC = ({ return ( + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; describe('helpers', () => { describe('getCustomMarkdownComment', () => { @@ -23,4 +44,52 @@ ${ECS_IS_A_PERMISSIVE_SCHEMA} `); }); }); + + describe('showCustomCallout', () => { + test('it returns false when `enrichedFieldMetadata` is empty', () => { + expect(showCustomCallout([])).toBe(false); + }); + + test('it returns true when `enrichedFieldMetadata` is NOT empty', () => { + expect(showCustomCallout([someField])).toBe(true); + }); + }); + + describe('getCustomColor', () => { + test('it returns the expected color when there are custom fields', () => { + expect(getCustomColor(mockPartitionedFieldMetadata)).toEqual(euiThemeVars.euiColorLightShade); + }); + + test('it returns the expected color when custom fields is empty', () => { + const noCustomFields: PartitionedFieldMetadata = { + ...mockPartitionedFieldMetadata, + custom: [], // <-- empty + }; + + expect(getCustomColor(noCustomFields)).toEqual(euiThemeVars.euiTextColor); + }); + }); + + describe('getAllCustomMarkdownComments', () => { + test('it returns the expected comment', () => { + expect( + getAllCustomMarkdownComments({ + docsCount: 4, + formatBytes, + formatNumber, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual([ + '### auditbeat-custom-index-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` | 27.7KB |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + `#### 4 Custom field mappings\n\nThese fields are not defined by the Elastic Common Schema (ECS), version ${EcsVersion}.\n\nECS is a permissive schema. If your events have additional data that cannot be mapped to ECS, you can simply add them to your events, using custom field names.\n`, + '#### Custom fields - auditbeat-custom-index-1\n\n\n| Field | Index mapping type | \n|-------|--------------------|\n| host.name.keyword | `keyword` | `--` |\n| some.field | `text` | `--` |\n| some.field.keyword | `keyword` | `--` |\n| source.ip.keyword | `keyword` | `--` |\n', + ]); + }); + }); }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/helpers.ts index 1d8de8ac57593..7701db46d6c98 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/helpers.ts @@ -10,14 +10,11 @@ import { euiThemeVars } from '@kbn/ui-theme'; import { FIELD, INDEX_MAPPING_TYPE } from '../../../compare_fields_table/translations'; import { - ECS_FIELD_REFERENCE_URL, - ECS_REFERENCE_URL, getSummaryMarkdownComment, getCustomMarkdownTableRows, getMarkdownComment, getMarkdownTable, getTabCountsMarkdownComment, - MAPPING_URL, getSummaryTableMarkdownComment, } from '../../index_properties/markdown/helpers'; import * as i18n from '../../index_properties/translations'; @@ -50,33 +47,33 @@ export const getCustomColor = (partitionedFieldMetadata: PartitionedFieldMetadat export const getAllCustomMarkdownComments = ({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }: { docsCount: number; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata; patternDocsCount: number; + sizeInBytes: number | undefined; }): string[] => [ - getSummaryMarkdownComment({ - ecsFieldReferenceUrl: ECS_FIELD_REFERENCE_URL, - ecsReferenceUrl: ECS_REFERENCE_URL, - incompatible: partitionedFieldMetadata.incompatible.length, - indexName, - mappingUrl: MAPPING_URL, - }), + getSummaryMarkdownComment(indexName), getSummaryTableMarkdownComment({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }), getTabCountsMarkdownComment(partitionedFieldMetadata), getCustomMarkdownComment({ diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/index.tsx index bfffbd1393ef6..09f7136d7108a 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/custom_tab/index.tsx @@ -13,13 +13,11 @@ import { EuiEmptyPrompt, EuiSpacer, } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import React, { useCallback, useMemo } from 'react'; import { CustomCallout } from '../callouts/custom_callout'; import { CompareFieldsTable } from '../../../compare_fields_table'; import { getCustomTableColumns } from '../../../compare_fields_table/helpers'; -import { EMPTY_STAT } from '../../../helpers'; import { EmptyPromptBody } from '../../index_properties/empty_prompt_body'; import { EmptyPromptTitle } from '../../index_properties/empty_prompt_title'; import { getAllCustomMarkdownComments, showCustomCallout } from './helpers'; @@ -29,39 +27,49 @@ import type { IlmPhase, PartitionedFieldMetadata } from '../../../types'; interface Props { addSuccessToast: (toast: { title: string }) => void; - defaultNumberFormat: string; docsCount: number; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata; patternDocsCount: number; + sizeInBytes: number | undefined; } const CustomTabComponent: React.FC = ({ addSuccessToast, - defaultNumberFormat, docsCount, + formatBytes, + formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }) => { - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); const markdownComments: string[] = useMemo( () => getAllCustomMarkdownComments({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }), - [docsCount, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount] + [ + docsCount, + formatBytes, + formatNumber, + ilmPhase, + indexName, + partitionedFieldMetadata, + patternDocsCount, + sizeInBytes, + ] ); const body = useMemo(() => , []); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.test.tsx new file mode 100644 index 0000000000000..c0dc6a8aaafe2 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.test.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 { DARK_THEME } from '@elastic/charts'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { omit } from 'lodash/fp'; + +import { + eventCategory, + someField, + timestamp, +} from '../../mock/enriched_field_metadata/mock_enriched_field_metadata'; +import { mockPartitionedFieldMetadata } from '../../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; +import { mockStatsGreenIndex } from '../../mock/stats/mock_stats_green_index'; +import { + getEcsCompliantColor, + getMissingTimestampComment, + getTabs, + showMissingTimestampCallout, +} from './helpers'; + +describe('helpers', () => { + describe('getMissingTimestampComment', () => { + test('it returns the expected comment', () => { + expect(getMissingTimestampComment()).toEqual( + '#### Missing an @timestamp (date) field mapping for this index\n\nConsider adding an @timestamp (date) field mapping to this index, as required by the Elastic Common Schema (ECS), because:\n\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n' + ); + }); + }); + + describe('showMissingTimestampCallout', () => { + test('it returns true when `enrichedFieldMetadata` is empty', () => { + expect(showMissingTimestampCallout([])).toBe(true); + }); + + test('it returns false when `enrichedFieldMetadata` contains an @timestamp field', () => { + expect(showMissingTimestampCallout([timestamp, eventCategory, someField])).toBe(false); + }); + + test('it returns true when `enrichedFieldMetadata` does NOT contain an @timestamp field', () => { + expect(showMissingTimestampCallout([eventCategory, someField])).toBe(true); + }); + }); + + describe('getEcsCompliantColor', () => { + test('it returns the expected color for the ECS compliant data when the data includes an @timestamp', () => { + expect(getEcsCompliantColor(mockPartitionedFieldMetadata)).toEqual( + euiThemeVars.euiColorSuccess + ); + }); + + test('it returns the expected color for the ECS compliant data does NOT includes an @timestamp', () => { + const noTimestamp = { + ...mockPartitionedFieldMetadata, + ecsCompliant: mockPartitionedFieldMetadata.ecsCompliant.filter( + ({ name }) => name !== '@timestamp' + ), + }; + + expect(getEcsCompliantColor(noTimestamp)).toEqual(euiThemeVars.euiColorDanger); + }); + }); + + describe('getTabs', () => { + test('it returns the expected tabs', () => { + expect( + getTabs({ + addSuccessToast: jest.fn(), + addToNewCaseDisabled: false, + docsCount: 4, + formatBytes: jest.fn(), + formatNumber: jest.fn(), + getGroupByFieldsOnClick: jest.fn(), + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + onAddToNewCase: jest.fn(), + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'auditbeat-*', + patternDocsCount: 57410, + setSelectedTabId: jest.fn(), + stats: mockStatsGreenIndex, + theme: DARK_THEME, + }).map((x) => omit(['append', 'content'], x)) + ).toEqual([ + { + id: 'summaryTab', + name: 'Summary', + }, + { + id: 'incompatibleTab', + name: 'Incompatible fields', + }, + { + id: 'customTab', + name: 'Custom fields', + }, + { + id: 'ecsCompliantTab', + name: 'ECS compliant fields', + }, + { + id: 'allTab', + name: 'All fields', + }, + ]); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx index a98ad92ba6cd9..c0cbebd45cb8d 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx @@ -14,6 +14,7 @@ import type { WordCloudElementEvent, XYChartElementEvent, } from '@elastic/charts'; +import type { IndicesStatsIndicesStats } from '@elastic/elasticsearch/lib/api/types'; import { EuiBadge } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import React from 'react'; @@ -35,6 +36,7 @@ import { getFillColor } from './summary_tab/helpers'; import * as i18n from '../index_properties/translations'; import { SummaryTab } from './summary_tab'; import type { EnrichedFieldMetadata, IlmPhase, PartitionedFieldMetadata } from '../../types'; +import { getSizeInBytes } from '../../helpers'; export const getMissingTimestampComment = (): string => getMarkdownComment({ @@ -48,7 +50,7 @@ ${i18n.PAGES_MAY_NOT_DISPLAY_EVENTS} export const showMissingTimestampCallout = ( enrichedFieldMetadata: EnrichedFieldMetadata[] -): boolean => enrichedFieldMetadata.length === 0; +): boolean => !enrichedFieldMetadata.some((x) => x.name === '@timestamp'); export const getEcsCompliantColor = (partitionedFieldMetadata: PartitionedFieldMetadata): string => showMissingTimestampCallout(partitionedFieldMetadata.ecsCompliant) @@ -58,8 +60,9 @@ export const getEcsCompliantColor = (partitionedFieldMetadata: PartitionedFieldM export const getTabs = ({ addSuccessToast, addToNewCaseDisabled, - defaultNumberFormat, docsCount, + formatBytes, + formatNumber, getGroupByFieldsOnClick, ilmPhase, indexName, @@ -68,11 +71,13 @@ export const getTabs = ({ pattern, patternDocsCount, setSelectedTabId, + stats, theme, }: { addSuccessToast: (toast: { title: string }) => void; addToNewCaseDisabled: boolean; - defaultNumberFormat: string; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; docsCount: number; getGroupByFieldsOnClick: ( elements: Array< @@ -94,6 +99,7 @@ export const getTabs = ({ pattern: string; patternDocsCount: number; setSelectedTabId: (tabId: string) => void; + stats: Record | null; theme: Theme; }) => [ { @@ -101,7 +107,8 @@ export const getTabs = ({ ), @@ -127,13 +135,15 @@ export const getTabs = ({ ), id: INCOMPATIBLE_TAB_ID, @@ -148,12 +158,14 @@ export const getTabs = ({ content: ( ), id: 'customTab', diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.test.ts index e82bfd4d2efdc..54babce560f25 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.test.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.test.ts @@ -5,11 +5,20 @@ * 2.0. */ -import { EcsVersion } from '@kbn/ecs'; import numeral from '@elastic/numeral'; +import { EcsVersion } from '@kbn/ecs'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { + getAllIncompatibleMarkdownComments, + getIncompatibleColor, + getIncompatibleFieldsMarkdownComment, + getIncompatibleFieldsMarkdownTablesComment, + getIncompatibleMappings, + getIncompatibleValues, + showInvalidCallout, +} from './helpers'; import { EMPTY_STAT } from '../../../helpers'; -import { mockPartitionedFieldMetadata } from '../../../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; import { DETECTION_ENGINE_RULES_MAY_NOT_MATCH, INCOMPATIBLE_FIELDS_WITH, @@ -17,10 +26,8 @@ import { PAGES_MAY_NOT_DISPLAY_EVENTS, WHEN_AN_INCOMPATIBLE_FIELD, } from '../../index_properties/translations'; -import { - getIncompatibleFieldsMarkdownComment, - getAllIncompatibleMarkdownComments, -} from './helpers'; +import { mockPartitionedFieldMetadata } from '../../../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; +import { PartitionedFieldMetadata } from '../../../types'; describe('helpers', () => { describe('getIncompatibleFieldsMarkdownComment', () => { @@ -44,27 +51,313 @@ ${MAPPINGS_THAT_CONFLICT_WITH_ECS} }); }); + describe('showInvalidCallout', () => { + test('it returns false when the `enrichedFieldMetadata` is empty', () => { + expect(showInvalidCallout([])).toBe(false); + }); + + test('it returns true when the `enrichedFieldMetadata` is NOT empty', () => { + expect(showInvalidCallout(mockPartitionedFieldMetadata.incompatible)).toBe(true); + }); + }); + + describe('getIncompatibleColor', () => { + test('it returns the expected color', () => { + expect(getIncompatibleColor()).toEqual(euiThemeVars.euiColorDanger); + }); + }); + + describe('getIncompatibleMappings', () => { + test('it (only) returns the mappings where type !== indexFieldType', () => { + expect(getIncompatibleMappings(mockPartitionedFieldMetadata.incompatible)).toEqual([ + { + dashed_name: 'host-name', + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + flat_name: 'host.name', + hasEcsMetadata: true, + ignore_above: 1024, + indexFieldName: 'host.name', + indexFieldType: 'text', + indexInvalidValues: [], + isEcsCompliant: false, + isInSameFamily: false, + level: 'core', + name: 'name', + normalize: [], + short: 'Name of the host.', + type: 'keyword', + }, + { + dashed_name: 'source-ip', + description: 'IP address of the source (IPv4 or IPv6).', + flat_name: 'source.ip', + hasEcsMetadata: true, + indexFieldName: 'source.ip', + indexFieldType: 'text', + indexInvalidValues: [], + isEcsCompliant: false, + isInSameFamily: false, + level: 'core', + name: 'ip', + normalize: [], + short: 'IP address of the source.', + type: 'ip', + }, + ]); + }); + + test('it filters-out ECS complaint fields', () => { + expect(getIncompatibleMappings(mockPartitionedFieldMetadata.ecsCompliant)).toEqual([]); + }); + }); + + describe('getIncompatibleValues', () => { + test('it (only) returns the mappings with indexInvalidValues', () => { + expect(getIncompatibleValues(mockPartitionedFieldMetadata.incompatible)).toEqual([ + { + allowed_values: [ + { + description: + 'Events in this category are related to the challenge and response process in which credentials are supplied and verified to allow the creation of a session. Common sources for these logs are Windows event logs and ssh logs. Visualize and analyze events in this category to look for failed logins, and other authentication-related activity.', + expected_event_types: ['start', 'end', 'info'], + name: 'authentication', + }, + { + description: + 'Events in the configuration category have to deal with creating, modifying, or deleting the settings or parameters of an application, process, or system.\nExample sources include security policy change logs, configuration auditing logging, and system integrity monitoring.', + expected_event_types: ['access', 'change', 'creation', 'deletion', 'info'], + name: 'configuration', + }, + { + description: + 'The database category denotes events and metrics relating to a data storage and retrieval system. Note that use of this category is not limited to relational database systems. Examples include event logs from MS SQL, MySQL, Elasticsearch, MongoDB, etc. Use this category to visualize and analyze database activity such as accesses and changes.', + expected_event_types: ['access', 'change', 'info', 'error'], + name: 'database', + }, + { + description: + 'Events in the driver category have to do with operating system device drivers and similar software entities such as Windows drivers, kernel extensions, kernel modules, etc.\nUse events and metrics in this category to visualize and analyze driver-related activity and status on hosts.', + expected_event_types: ['change', 'end', 'info', 'start'], + name: 'driver', + }, + { + description: + 'This category is used for events relating to email messages, email attachments, and email network or protocol activity.\nEmails events can be produced by email security gateways, mail transfer agents, email cloud service providers, or mail server monitoring applications.', + expected_event_types: ['info'], + name: 'email', + }, + { + description: + 'Relating to a set of information that has been created on, or has existed on a filesystem. Use this category of events to visualize and analyze the creation, access, and deletions of files. Events in this category can come from both host-based and network-based sources. An example source of a network-based detection of a file transfer would be the Zeek file.log.', + expected_event_types: ['change', 'creation', 'deletion', 'info'], + name: 'file', + }, + { + description: + 'Use this category to visualize and analyze information such as host inventory or host lifecycle events.\nMost of the events in this category can usually be observed from the outside, such as from a hypervisor or a control plane\'s point of view. Some can also be seen from within, such as "start" or "end".\nNote that this category is for information about hosts themselves; it is not meant to capture activity "happening on a host".', + expected_event_types: ['access', 'change', 'end', 'info', 'start'], + name: 'host', + }, + { + description: + 'Identity and access management (IAM) events relating to users, groups, and administration. Use this category to visualize and analyze IAM-related logs and data from active directory, LDAP, Okta, Duo, and other IAM systems.', + expected_event_types: [ + 'admin', + 'change', + 'creation', + 'deletion', + 'group', + 'info', + 'user', + ], + name: 'iam', + }, + { + description: + 'Relating to intrusion detections from IDS/IPS systems and functions, both network and host-based. Use this category to visualize and analyze intrusion detection alerts from systems such as Snort, Suricata, and Palo Alto threat detections.', + expected_event_types: ['allowed', 'denied', 'info'], + name: 'intrusion_detection', + }, + { + description: + 'Malware detection events and alerts. Use this category to visualize and analyze malware detections from EDR/EPP systems such as Elastic Endpoint Security, Symantec Endpoint Protection, Crowdstrike, and network IDS/IPS systems such as Suricata, or other sources of malware-related events such as Palo Alto Networks threat logs and Wildfire logs.', + expected_event_types: ['info'], + name: 'malware', + }, + { + description: + 'Relating to all network activity, including network connection lifecycle, network traffic, and essentially any event that includes an IP address. Many events containing decoded network protocol transactions fit into this category. Use events in this category to visualize or analyze counts of network ports, protocols, addresses, geolocation information, etc.', + expected_event_types: [ + 'access', + 'allowed', + 'connection', + 'denied', + 'end', + 'info', + 'protocol', + 'start', + ], + name: 'network', + }, + { + description: + 'Relating to software packages installed on hosts. Use this category to visualize and analyze inventory of software installed on various hosts, or to determine host vulnerability in the absence of vulnerability scan data.', + expected_event_types: [ + 'access', + 'change', + 'deletion', + 'info', + 'installation', + 'start', + ], + name: 'package', + }, + { + description: + 'Use this category of events to visualize and analyze process-specific information such as lifecycle events or process ancestry.', + expected_event_types: ['access', 'change', 'end', 'info', 'start'], + name: 'process', + }, + { + description: + 'Having to do with settings and assets stored in the Windows registry. Use this category to visualize and analyze activity such as registry access and modifications.', + expected_event_types: ['access', 'change', 'creation', 'deletion'], + name: 'registry', + }, + { + description: + 'The session category is applied to events and metrics regarding logical persistent connections to hosts and services. Use this category to visualize and analyze interactive or automated persistent connections between assets. Data for this category may come from Windows Event logs, SSH logs, or stateless sessions such as HTTP cookie-based sessions, etc.', + expected_event_types: ['start', 'end', 'info'], + name: 'session', + }, + { + description: + "Use this category to visualize and analyze events describing threat actors' targets, motives, or behaviors.", + expected_event_types: ['indicator'], + name: 'threat', + }, + { + description: + 'Relating to vulnerability scan results. Use this category to analyze vulnerabilities detected by Tenable, Qualys, internal scanners, and other vulnerability management sources.', + expected_event_types: ['info'], + name: 'vulnerability', + }, + { + description: + 'Relating to web server access. Use this category to create a dashboard of web server/proxy activity from apache, IIS, nginx web servers, etc. Note: events from network observers such as Zeek http log may also be included in this category.', + expected_event_types: ['access', 'error', 'info'], + name: 'web', + }, + ], + dashed_name: 'event-category', + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy.\n`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory.\nThis field is an array. This will allow proper categorization of some events that fall in multiple categories.', + example: 'authentication', + flat_name: 'event.category', + ignore_above: 1024, + level: 'core', + name: 'category', + normalize: ['array'], + short: 'Event category. The second categorization field in the hierarchy.', + type: 'keyword', + indexFieldName: 'event.category', + indexFieldType: 'keyword', + indexInvalidValues: [ + { count: 2, fieldName: 'an_invalid_category' }, + { count: 1, fieldName: 'theory' }, + ], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: true, + }, + ]); + }); + + test('it filters-out ECS complaint fields', () => { + expect(getIncompatibleValues(mockPartitionedFieldMetadata.ecsCompliant)).toEqual([]); + }); + }); + + describe('getIncompatibleFieldsMarkdownTablesComment', () => { + test('it returns the expected comment when the index has `incompatibleMappings` and `incompatibleValues`', () => { + expect( + getIncompatibleFieldsMarkdownTablesComment({ + incompatibleMappings: [ + mockPartitionedFieldMetadata.incompatible[1], + mockPartitionedFieldMetadata.incompatible[2], + ], + incompatibleValues: [mockPartitionedFieldMetadata.incompatible[0]], + indexName: 'auditbeat-custom-index-1', + }) + ).toEqual( + '\n#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - auditbeat-custom-index-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n' + ); + }); + + test('it returns the expected comment when the index does NOT have `incompatibleMappings` and `incompatibleValues`', () => { + expect( + getIncompatibleFieldsMarkdownTablesComment({ + incompatibleMappings: [], // <-- no `incompatibleMappings` + incompatibleValues: [], // <-- no `incompatibleValues` + indexName: 'auditbeat-custom-index-1', + }) + ).toEqual('\n\n\n'); + }); + }); + describe('getAllIncompatibleMarkdownComments', () => { - test('it returns the expected collection of comments', () => { - const defaultNumberFormat = '0,0.[000]'; - const formatNumber = (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + const defaultBytesFormat = '0,0.[0]b'; + const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + + const defaultNumberFormat = '0,0.[000]'; + const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + test('it returns the expected collection of comments', () => { expect( getAllIncompatibleMarkdownComments({ docsCount: 4, + formatBytes, formatNumber, ilmPhase: 'unmanaged', indexName: 'auditbeat-custom-index-1', partitionedFieldMetadata: mockPartitionedFieldMetadata, patternDocsCount: 57410, + sizeInBytes: 28413, }) ).toEqual([ '### auditbeat-custom-index-1\n', - '| Result | Index | Docs | Incompatible fields | ILM Phase |\n|--------|-------|------|---------------------|-----------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` |\n\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` | 27.7KB |\n\n', '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', `#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version ${EcsVersion}.\n\n${INCOMPATIBLE_FIELDS_WITH}\n\n${WHEN_AN_INCOMPATIBLE_FIELD}\n${DETECTION_ENGINE_RULES_MAY_NOT_MATCH}\n${PAGES_MAY_NOT_DISPLAY_EVENTS}\n${MAPPINGS_THAT_CONFLICT_WITH_ECS}\n`, - '\n#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - auditbeat-custom-index-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2),\n`theory` (1) |\n\n', + '\n#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - auditbeat-custom-index-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ]); + }); + + test('it returns the expected comment when `incompatible` is empty', () => { + const emptyIncompatible: PartitionedFieldMetadata = { + ...mockPartitionedFieldMetadata, + incompatible: [], // <-- empty + }; + + expect( + getAllIncompatibleMarkdownComments({ + docsCount: 4, + formatBytes, + formatNumber, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + partitionedFieldMetadata: emptyIncompatible, + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual([ + '### auditbeat-custom-index-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ✅ | auditbeat-custom-index-1 | 4 (0.0%) | 0 | `unmanaged` | 27.7KB |\n\n', + '### **Incompatible fields** `0` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + '\n\n\n', ]); }); }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.ts index 2e44ec53142b5..1f4e0b62b1c58 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/helpers.ts @@ -9,8 +9,6 @@ import { EcsVersion } from '@kbn/ecs'; import { getIncompatiableFieldsInSameFamilyCount } from '../callouts/incompatible_callout/helpers'; import { - ECS_FIELD_REFERENCE_URL, - ECS_REFERENCE_URL, getSummaryMarkdownComment, getIncompatibleMappingsMarkdownTableRows, getIncompatibleValuesMarkdownTableRows, @@ -18,7 +16,6 @@ import { getMarkdownTable, getSummaryTableMarkdownComment, getTabCountsMarkdownComment, - MAPPING_URL, } from '../../index_properties/markdown/helpers'; import { getFillColor } from '../summary_tab/helpers'; import * as i18n from '../../index_properties/translations'; @@ -106,18 +103,22 @@ ${ export const getAllIncompatibleMarkdownComments = ({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }: { docsCount: number; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata; patternDocsCount: number; + sizeInBytes: number | undefined; }): string[] => { const incompatibleMappings = getIncompatibleMappings(partitionedFieldMetadata.incompatible); const incompatibleValues = getIncompatibleValues(partitionedFieldMetadata.incompatible); @@ -134,20 +135,16 @@ export const getAllIncompatibleMarkdownComments = ({ : ''; return [ - getSummaryMarkdownComment({ - ecsFieldReferenceUrl: ECS_FIELD_REFERENCE_URL, - ecsReferenceUrl: ECS_REFERENCE_URL, - incompatible: partitionedFieldMetadata.incompatible.length, - indexName, - mappingUrl: MAPPING_URL, - }), + getSummaryMarkdownComment(indexName), getSummaryTableMarkdownComment({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }), getTabCountsMarkdownComment(partitionedFieldMetadata), incompatibleFieldsMarkdownComment, diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx index f4def1393c07c..2fa4fcdb4f8ed 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx @@ -13,14 +13,12 @@ import { EuiEmptyPrompt, EuiSpacer, } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import React, { useCallback, useMemo } from 'react'; import { IncompatibleCallout } from '../callouts/incompatible_callout'; import { CompareFieldsTable } from '../../../compare_fields_table'; import { getIncompatibleMappingsTableColumns } from '../../../compare_fields_table/get_incompatible_mappings_table_columns'; import { getIncompatibleValuesTableColumns } from '../../../compare_fields_table/helpers'; -import { EMPTY_STAT } from '../../../helpers'; import { EmptyPromptBody } from '../../index_properties/empty_prompt_body'; import { EmptyPromptTitle } from '../../index_properties/empty_prompt_title'; import { @@ -41,31 +39,30 @@ import type { IlmPhase, PartitionedFieldMetadata } from '../../../types'; interface Props { addSuccessToast: (toast: { title: string }) => void; addToNewCaseDisabled: boolean; - defaultNumberFormat: string; docsCount: number; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; onAddToNewCase: (markdownComments: string[]) => void; partitionedFieldMetadata: PartitionedFieldMetadata; patternDocsCount: number; + sizeInBytes: number | undefined; } const IncompatibleTabComponent: React.FC = ({ addSuccessToast, addToNewCaseDisabled, - defaultNumberFormat, docsCount, + formatBytes, + formatNumber, ilmPhase, indexName, onAddToNewCase, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }) => { - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); const body = useMemo(() => , []); const title = useMemo(() => , []); const incompatibleMappings = useMemo( @@ -80,13 +77,24 @@ const IncompatibleTabComponent: React.FC = ({ () => getAllIncompatibleMarkdownComments({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }), - [docsCount, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount] + [ + docsCount, + formatBytes, + formatNumber, + ilmPhase, + indexName, + partitionedFieldMetadata, + patternDocsCount, + sizeInBytes, + ] ); const onClickAddToCase = useCallback( () => onAddToNewCase([markdownComments.join('\n')]), @@ -101,7 +109,7 @@ const IncompatibleTabComponent: React.FC = ({ }, [addSuccessToast, markdownComments]); return ( - <> +
    {showInvalidCallout(partitionedFieldMetadata.incompatible) ? ( <> @@ -161,7 +169,7 @@ const IncompatibleTabComponent: React.FC = ({ titleSize="s" /> )} - +
    ); }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/styles.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/styles.tsx index cf83cf96d2812..2714d1002c40c 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/styles.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/styles.tsx @@ -5,13 +5,43 @@ * 2.0. */ -import { EuiButtonEmpty } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexItem, EuiLink } from '@elastic/eui'; import styled from 'styled-components'; +export const DEFAULT_LEGEND_HEIGHT = 300; // px +export const DEFAULT_MAX_CHART_HEIGHT = 300; // px + export const CalloutItem = styled.div` margin-left: ${({ theme }) => theme.eui.euiSizeS}; `; +export const ChartFlexItem = styled(EuiFlexItem)<{ + $maxChartHeight: number | undefined; + $minChartHeight: number; +}>` + ${({ $maxChartHeight }) => ($maxChartHeight != null ? `max-height: ${$maxChartHeight}px;` : '')} + min-height: ${({ $minChartHeight }) => `${$minChartHeight}px`}; +`; + export const CopyToClipboardButton = styled(EuiButtonEmpty)` margin-left: ${({ theme }) => theme.eui.euiSizeXS}; `; + +export const LegendContainer = styled.div<{ + $height?: number; + $width?: number; +}>` + margin-left: ${({ theme }) => theme.eui.euiSizeM}; + margin-top: ${({ theme }) => theme.eui.euiSizeM}; + ${({ $height }) => ($height != null ? `height: ${$height}px;` : '')} + scrollbar-width: thin; + ${({ $width }) => ($width != null ? `width: ${$width}px;` : '')} +`; + +export const StorageTreemapContainer = styled.div` + padding: ${({ theme }) => theme.eui.euiSizeM}; +`; + +export const ChartLegendLink = styled(EuiLink)` + width: 100%; +`; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx index 054ac5e003326..6d36fdd50370a 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx @@ -6,14 +6,12 @@ */ import { copyToClipboard, EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import React, { useCallback, useMemo } from 'react'; import { MissingTimestampCallout } from '../../callouts/missing_timestamp_callout'; import { IncompatibleCallout } from '../../callouts/incompatible_callout'; import { showMissingTimestampCallout } from '../../helpers'; import { getMarkdownComments } from '../helpers'; -import { EMPTY_STAT } from '../../../../helpers'; import { showInvalidCallout } from '../../incompatible_tab/helpers'; import { CopyToClipboardButton } from '../../styles'; import * as i18n from '../../../index_properties/translations'; @@ -23,52 +21,55 @@ import type { IlmPhase, PartitionedFieldMetadata } from '../../../../types'; interface Props { addSuccessToast: (toast: { title: string }) => void; addToNewCaseDisabled: boolean; - defaultNumberFormat: string; docsCount: number; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; onAddToNewCase: (markdownComment: string[]) => void; partitionedFieldMetadata: PartitionedFieldMetadata; pattern: string; patternDocsCount: number; + sizeInBytes: number | undefined; } const CalloutSummaryComponent: React.FC = ({ addSuccessToast, addToNewCaseDisabled, - defaultNumberFormat, docsCount, + formatBytes, + formatNumber, ilmPhase, indexName, onAddToNewCase, partitionedFieldMetadata, pattern, patternDocsCount, + sizeInBytes, }) => { - const formatNumber = useCallback( - (value: number | undefined): string => - value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, - [defaultNumberFormat] - ); const markdownComments: string[] = useMemo( () => getMarkdownComments({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, pattern, patternDocsCount, + sizeInBytes, }), [ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, pattern, patternDocsCount, + sizeInBytes, ] ); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.test.ts new file mode 100644 index 0000000000000..64e78a4a88cfb --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.test.ts @@ -0,0 +1,235 @@ +/* + * 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 numeral from '@elastic/numeral'; +import { EcsVersion } from '@kbn/ecs'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { EMPTY_STAT } from '../../../helpers'; + +import { mockPartitionedFieldMetadata } from '../../../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; +import { PartitionedFieldMetadata } from '../../../types'; +import { + ALL_TAB_ID, + CUSTOM_TAB_ID, + ECS_COMPLIANT_TAB_ID, + INCOMPATIBLE_TAB_ID, +} from '../../index_properties/helpers'; +import { + CUSTOM_FIELDS, + ECS_COMPLIANT_FIELDS, + INCOMPATIBLE_FIELDS, + UNKNOWN, +} from '../../index_properties/translations'; +import { + CategoryId, + getFillColor, + getMarkdownComments, + getNodeLabel, + getSummaryData, + getTabId, +} from './helpers'; + +describe('helpers', () => { + describe('getSummaryData', () => { + test('it returns the expected `SummaryData`', () => { + expect(getSummaryData(mockPartitionedFieldMetadata)).toEqual([ + { categoryId: 'incompatible', mappings: 3 }, + { categoryId: 'custom', mappings: 4 }, + { categoryId: 'ecs-compliant', mappings: 2 }, + ]); + }); + }); + + describe('getFillColor', () => { + const invalid: CategoryId = 'invalid-category-id' as CategoryId; + + const categories: Array<{ + categoryId: CategoryId; + expectedColor: string; + }> = [ + { + categoryId: 'incompatible', + expectedColor: euiThemeVars.euiColorDanger, + }, + { + categoryId: 'custom', + expectedColor: euiThemeVars.euiColorLightShade, + }, + { + categoryId: 'ecs-compliant', + expectedColor: euiThemeVars.euiColorSuccess, + }, + { + categoryId: invalid, + expectedColor: euiThemeVars.euiColorGhost, + }, + ]; + + categories.forEach(({ categoryId, expectedColor }) => { + test(`it returns the expected color for category '${categoryId}'`, () => { + expect(getFillColor(categoryId)).toEqual(expectedColor); + }); + }); + }); + + describe('getNodeLabel', () => { + const invalid: CategoryId = 'invalid-category-id' as CategoryId; + + const categories: Array<{ + categoryId: CategoryId; + expectedLabel: string; + }> = [ + { + categoryId: 'incompatible', + expectedLabel: INCOMPATIBLE_FIELDS, + }, + { + categoryId: 'custom', + expectedLabel: CUSTOM_FIELDS, + }, + { + categoryId: 'ecs-compliant', + expectedLabel: ECS_COMPLIANT_FIELDS, + }, + { + categoryId: invalid, + expectedLabel: UNKNOWN, + }, + ]; + + categories.forEach(({ categoryId, expectedLabel }) => { + test(`it returns the expected label for category '${categoryId}'`, () => { + expect(getNodeLabel(categoryId)).toEqual(expectedLabel); + }); + }); + }); + + describe('getTabId', () => { + const groupByFields: Array<{ + groupByField: string; + expectedTabId: string; + }> = [ + { + groupByField: 'incompatible', + expectedTabId: INCOMPATIBLE_TAB_ID, + }, + { + groupByField: 'custom', + expectedTabId: CUSTOM_TAB_ID, + }, + { + groupByField: 'ecs-compliant', + expectedTabId: ECS_COMPLIANT_TAB_ID, + }, + { + groupByField: 'some-other-group', + expectedTabId: ALL_TAB_ID, + }, + ]; + + groupByFields.forEach(({ groupByField, expectedTabId }) => { + test(`it returns the expected tab ID for groupByField '${groupByField}'`, () => { + expect(getTabId(groupByField)).toEqual(expectedTabId); + }); + }); + }); + + describe('getMarkdownComments', () => { + const defaultBytesFormat = '0,0.[0]b'; + const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + + const defaultNumberFormat = '0,0.[000]'; + const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + + test('it returns the expected comment when the index has incompatible fields ', () => { + expect( + getMarkdownComments({ + docsCount: 4, + formatBytes, + formatNumber, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'auditbeat-*', + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual([ + '### auditbeat-custom-index-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` | 27.7KB |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + `#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version ${EcsVersion}.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n`, + '\n#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - auditbeat-custom-index-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ]); + }); + + test('it returns an empty array when the index does NOT have incompatible fields ', () => { + const noIncompatible: PartitionedFieldMetadata = { + ...mockPartitionedFieldMetadata, + incompatible: [], // <-- no incompatible fields + }; + + expect( + getMarkdownComments({ + docsCount: 4, + formatBytes, + formatNumber, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-index-1', + partitionedFieldMetadata: noIncompatible, + pattern: 'auditbeat-*', + patternDocsCount: 57410, + sizeInBytes: 28413, + }) + ).toEqual([]); + }); + + test('it returns a missing timestamp comment for an empty index', () => { + const emptyIndex: PartitionedFieldMetadata = { + all: [], + ecsCompliant: [], + custom: [], + incompatible: [ + { + description: + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', + hasEcsMetadata: true, + indexFieldName: '@timestamp', + indexFieldType: '-', + indexInvalidValues: [], + isEcsCompliant: false, + isInSameFamily: false, + type: 'date', + }, + ], + }; + + expect( + getMarkdownComments({ + docsCount: 0, + formatBytes, + formatNumber, + ilmPhase: 'unmanaged', + indexName: 'auditbeat-custom-empty-index-1', + partitionedFieldMetadata: emptyIndex, + pattern: 'auditbeat-*', + patternDocsCount: 57410, + sizeInBytes: 247, + }) + ).toEqual([ + '### auditbeat-custom-empty-index-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | auditbeat-custom-empty-index-1 | 0 (0.0%) | 1 | `unmanaged` | 247B |\n\n', + '### **Incompatible fields** `1` **Custom fields** `0` **ECS compliant fields** `0` **All fields** `0`\n', + `#### 1 incompatible field, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version ${EcsVersion}.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n`, + '\n#### Incompatible field mappings - auditbeat-custom-empty-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| @timestamp | `date` | `-` |\n\n\n', + '#### Missing an @timestamp (date) field mapping for this index\n\nConsider adding an @timestamp (date) field mapping to this index, as required by the Elastic Common Schema (ECS), because:\n\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n', + ]); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.ts index 4ab85f87e01d0..1f728e3b60c86 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/helpers.ts @@ -79,28 +79,34 @@ const isString = (x: string | null): x is string => typeof x === 'string'; export const getMarkdownComments = ({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }: { docsCount: number; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; ilmPhase: IlmPhase | undefined; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata; pattern: string; patternDocsCount: number; + sizeInBytes: number | undefined; }): string[] => { const invalidMarkdownComments = showInvalidCallout(partitionedFieldMetadata.incompatible) ? getAllIncompatibleMarkdownComments({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }) : []; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/index.tsx index 087470b7e86dc..c830ac2f6c7be 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/index.tsx @@ -24,8 +24,9 @@ import type { IlmPhase, PartitionedFieldMetadata } from '../../../types'; interface Props { addSuccessToast: (toast: { title: string }) => void; addToNewCaseDisabled: boolean; - defaultNumberFormat: string; docsCount: number; + formatBytes: (value: number | undefined) => string; + formatNumber: (value: number | undefined) => string; getGroupByFieldsOnClick: ( elements: Array< | FlameElementEvent @@ -46,13 +47,15 @@ interface Props { pattern: string; patternDocsCount: number; setSelectedTabId: (tabId: string) => void; + sizeInBytes: number | undefined; theme: Theme; } const SummaryTabComponent: React.FC = ({ addSuccessToast, addToNewCaseDisabled, - defaultNumberFormat, + formatBytes, + formatNumber, docsCount, getGroupByFieldsOnClick, ilmPhase, @@ -62,13 +65,15 @@ const SummaryTabComponent: React.FC = ({ pattern, patternDocsCount, setSelectedTabId, + sizeInBytes, theme, }) => ( <> = ({ partitionedFieldMetadata={partitionedFieldMetadata} pattern={pattern} patternDocsCount={patternDocsCount} + sizeInBytes={sizeInBytes} /> void; + color: string | null; + count: number | string; + dataTestSubj?: string; + onClick: (() => void) | undefined; text: string; + textWidth?: number; } -const ChartLegendItemComponent: React.FC = ({ color, count, onClick, text }) => { - return ( +const ChartLegendItemComponent: React.FC = ({ + color, + count, + dataTestSubj = DEFAULT_DATA_TEST_SUBJ, + onClick, + text, + textWidth, +}) => ( + - - - {text} - - + + {color != null ? ( + + + {text} + + + ) : ( + + {text} + + )} + + - -
    {count}
    -
    + {count}
    - ); -}; +
    +); ChartLegendItemComponent.displayName = 'ChartLegendItemComponent'; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/chart_legend/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/chart_legend/index.tsx index 21dd3e0450f40..b7c05e75300d5 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/chart_legend/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/chart_legend/index.tsx @@ -5,9 +5,7 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useCallback } from 'react'; -import styled from 'styled-components'; import { ChartLegendItem } from './chart_legend_item'; import { getEcsCompliantColor } from '../../data_quality_panel/tabs/helpers'; @@ -20,10 +18,9 @@ import { getCustomColor } from '../../data_quality_panel/tabs/custom_tab/helpers import { getIncompatibleColor } from '../../data_quality_panel/tabs/incompatible_tab/helpers'; import type { PartitionedFieldMetadata } from '../../types'; import * as i18n from '../../data_quality_panel/index_properties/translations'; +import { LegendContainer } from '../../data_quality_panel/tabs/styles'; -const ChartLegendFlexGroup = styled(EuiFlexGroup)` - width: 210px; -`; +const LEGEND_WIDTH = 200; // px interface Props { partitionedFieldMetadata: PartitionedFieldMetadata; @@ -44,40 +41,34 @@ const ChartLegendComponent: React.FC = ({ partitionedFieldMetadata, setSe ); return ( - + {partitionedFieldMetadata.incompatible.length > 0 && ( - - - + )} {partitionedFieldMetadata.custom.length > 0 && ( - - - + )} {partitionedFieldMetadata.ecsCompliant.length > 0 && ( - - - + )} - + ); }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/helpers.test.ts new file mode 100644 index 0000000000000..30a5e9d13e4fc --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ecs_summary_donut_chart/helpers.test.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 { allMetadataIsEmpty } from './helpers'; +import { mockPartitionedFieldMetadata } from '../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; +import { PartitionedFieldMetadata } from '../types'; + +describe('helpers', () => { + describe('allMetadataIsEmpty', () => { + test('it returns false when `all` is NOT is empty', () => { + expect(allMetadataIsEmpty(mockPartitionedFieldMetadata)).toBe(false); + }); + + test('it returns true when `all` is is empty', () => { + const allIsEmpty: PartitionedFieldMetadata = { + all: [], // <-- empty + custom: [], + ecsCompliant: [], + incompatible: [], + }; + + expect(allMetadataIsEmpty(allIsEmpty)).toBe(true); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.test.ts index 14448d1a3baac..81e968bfe73a4 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.test.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.test.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { - IlmExplainLifecycleLifecycleExplain, - MappingProperty, -} from '@elastic/elasticsearch/lib/api/types'; +import { IlmExplainLifecycleLifecycleExplain } from '@elastic/elasticsearch/lib/api/types'; import { EcsFlat } from '@kbn/ecs'; import { omit } from 'lodash/fp'; @@ -28,9 +25,11 @@ import { getMissingTimestampFieldMetadata, getPartitionedFieldMetadata, getPartitionedFieldMetadataStats, + getSizeInBytes, getTotalDocsCount, getTotalPatternIncompatible, getTotalPatternIndicesChecked, + getTotalSizeInBytes, hasValidTimestampMapping, isMappingCompatible, } from './helpers'; @@ -44,8 +43,9 @@ import { sourcePort, timestamp, eventCategoryWithUnallowedValues, -} from './mock/enriched_field_metadata'; -import { mockIlmExplain } from './mock/ilm_explain'; +} from './mock/enriched_field_metadata/mock_enriched_field_metadata'; +import { mockIlmExplain } from './mock/ilm_explain/mock_ilm_explain'; +import { mockMappingsProperties } from './mock/mappings_properties/mock_mappings_properties'; import { alertIndexNoResults } from './mock/pattern_rollup/mock_alerts_pattern_rollup'; import { packetbeatNoResults, @@ -79,7 +79,7 @@ const ecsMetadata: Record = EcsFlat as unknown as Record { describe('getIndexNames', () => { - const ilmPhases: string[] = ['hot', 'warm', 'unmanaged']; + const ilmPhases = ['hot', 'warm', 'unmanaged']; test('returns the expected index names when they have an ILM phase included in the ilmPhases list', () => { expect( @@ -91,17 +91,18 @@ describe('helpers', () => { ).toEqual([ '.ds-packetbeat-8.6.1-2023.02.04-000001', '.ds-packetbeat-8.5.3-2023.02.04-000001', + 'auditbeat-custom-index-1', ]); }); test('returns the expected filtered index names when they do NOT have an ILM phase included in the ilmPhases list', () => { expect( getIndexNames({ - ilmExplain: mockIlmExplain, // <-- the mock indexes have 'hot' ILM phases... + ilmExplain: mockIlmExplain, // <-- the mock indexes have 'hot' and 'unmanaged' ILM phases... ilmPhases: ['warm', 'unmanaged'], // <-- ...but we don't ask for 'hot' stats: mockStats, }) - ).toEqual([]); + ).toEqual(['auditbeat-custom-index-1']); // <-- the 'unmanaged' index }); test('returns the expected index names when the `ilmExplain` is missing a record for an index', () => { @@ -117,7 +118,7 @@ describe('helpers', () => { ilmPhases: ['hot', 'warm', 'unmanaged'], stats: mockStats, }) - ).toEqual(['.ds-packetbeat-8.5.3-2023.02.04-000001']); // <-- only includes one of the two indexes, because the other one is missing an ILM explain record + ).toEqual(['.ds-packetbeat-8.5.3-2023.02.04-000001', 'auditbeat-custom-index-1']); // <-- only includes two of the three indices, because the other one is missing an ILM explain record }); test('returns empty index names when `ilmPhases` is empty', () => { @@ -162,105 +163,6 @@ describe('helpers', () => { }); describe('getFieldTypes', () => { - /** - * These `mappingsProperties` represent mappings that were generated by - * Elasticsearch automatically, for an index named `auditbeat-custom-index-1`: - * - * ``` - * DELETE auditbeat-custom-index-1 - * - * PUT auditbeat-custom-index-1 - * - * PUT auditbeat-custom-index-1/_mapping - * { - * "properties": { - * "@timestamp": { - * "type": "date" - * }, - * "event.category": { - * "type": "keyword", - * "ignore_above": 1024 - * } - * } - * } - * ``` - * - * when the following document was inserted: - * - * ``` - * POST auditbeat-custom-index-1/_doc - * { - * "@timestamp": "2023-02-06T09:41:49.668Z", - * "host": { - * "name": "foo" - * }, - * "event": { - * "category": "an_invalid_category" - * }, - * "some.field": "this", - * "source": { - * "port": 90210, - * "ip": "10.1.2.3" - * } - * } - * ``` - */ - const mappingsProperties: Record = { - '@timestamp': { - type: 'date', - }, - event: { - properties: { - category: { - type: 'keyword', - ignore_above: 1024, - }, - }, - }, - host: { - properties: { - name: { - type: 'text', - fields: { - keyword: { - type: 'keyword', - ignore_above: 256, - }, - }, - }, - }, - }, - some: { - properties: { - field: { - type: 'text', - fields: { - keyword: { - type: 'keyword', - ignore_above: 256, - }, - }, - }, - }, - }, - source: { - properties: { - ip: { - type: 'text', - fields: { - keyword: { - type: 'keyword', - ignore_above: 256, - }, - }, - }, - port: { - type: 'long', - }, - }, - }, - }; - const expected = [ { field: '@timestamp', @@ -301,7 +203,7 @@ describe('helpers', () => { ]; test('it flattens the field names and types in the mapping properties', () => { - expect(getFieldTypes(mappingsProperties)).toEqual(expected); + expect(getFieldTypes(mockMappingsProperties)).toEqual(expected); }); test('it throws a type error when mappingsProperties is not flatten-able', () => { @@ -876,6 +778,53 @@ describe('helpers', () => { }); }); + describe('getSizeInBytes', () => { + test('it returns the expected size when `stats` contains the `indexName`', () => { + const indexName = '.ds-packetbeat-8.6.1-2023.02.04-000001'; + const expectedCount = mockStatsYellowIndex[indexName].primaries?.store?.size_in_bytes; + + expect( + getSizeInBytes({ + indexName, + stats: mockStatsYellowIndex, + }) + ).toEqual(expectedCount); + }); + + test('it returns zero when `stats` does NOT contain the `indexName`', () => { + const indexName = 'not-gonna-find-it'; + + expect( + getSizeInBytes({ + indexName, + stats: mockStatsYellowIndex, + }) + ).toEqual(0); + }); + + test('it returns zero when `stats` is null', () => { + const indexName = '.ds-packetbeat-8.6.1-2023.02.04-000001'; + + expect( + getSizeInBytes({ + indexName, + stats: null, + }) + ).toEqual(0); + }); + + test('it returns the expected size for a green index, where `primaries.store.size_in_bytes` and `total.store.size_in_bytes` have different values', () => { + const indexName = 'auditbeat-custom-index-1'; + + expect( + getSizeInBytes({ + indexName, + stats: mockStatsGreenIndex, + }) + ).toEqual(mockStatsGreenIndex[indexName].primaries?.store?.size_in_bytes); + }); + }); + describe('getTotalDocsCount', () => { test('it returns the expected total given a subset of index names in the stats', () => { const indexName = '.ds-packetbeat-8.5.3-2023.02.04-000001'; @@ -925,6 +874,55 @@ describe('helpers', () => { }); }); + describe('getTotalSizeInBytes', () => { + test('it returns the expected total given a subset of index names in the stats', () => { + const indexName = '.ds-packetbeat-8.5.3-2023.02.04-000001'; + const expectedCount = mockStatsYellowIndex[indexName].primaries?.store?.size_in_bytes; + + expect( + getTotalSizeInBytes({ + indexNames: [indexName], + stats: mockStatsYellowIndex, + }) + ).toEqual(expectedCount); + }); + + test('it returns the expected total given all index names in the stats', () => { + const allIndexNamesInStats = [ + '.ds-packetbeat-8.6.1-2023.02.04-000001', + '.ds-packetbeat-8.5.3-2023.02.04-000001', + ]; + + expect( + getTotalSizeInBytes({ + indexNames: allIndexNamesInStats, + stats: mockStatsYellowIndex, + }) + ).toEqual(1464758182); + }); + + test('it returns zero given an empty collection of index names', () => { + expect( + getTotalSizeInBytes({ + indexNames: [], // <-- empty + stats: mockStatsYellowIndex, + }) + ).toEqual(0); + }); + + test('it returns the expected total for a green index', () => { + const indexName = 'auditbeat-custom-index-1'; + const expectedCount = mockStatsGreenIndex[indexName].primaries?.store?.size_in_bytes; + + expect( + getTotalSizeInBytes({ + indexNames: [indexName], + stats: mockStatsGreenIndex, + }) + ).toEqual(expectedCount); + }); + }); + describe('getIlmPhaseDescription', () => { const phases: Array<{ phase: string; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.ts index ea8a50a41580f..7cb638ad11550 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/helpers.ts @@ -255,6 +255,14 @@ export const getDocsCount = ({ stats: Record | null; }): number => (stats && stats[indexName]?.primaries?.docs?.count) ?? 0; +export const getSizeInBytes = ({ + indexName, + stats, +}: { + indexName: string; + stats: Record | null; +}): number => (stats && stats[indexName]?.primaries?.store?.size_in_bytes) ?? 0; + export const getTotalDocsCount = ({ indexNames, stats, @@ -267,6 +275,18 @@ export const getTotalDocsCount = ({ 0 ); +export const getTotalSizeInBytes = ({ + indexNames, + stats, +}: { + indexNames: string[]; + stats: Record | null; +}): number => + indexNames.reduce( + (acc: number, indexName: string) => acc + getSizeInBytes({ stats, indexName }), + 0 + ); + export const EMPTY_STAT = '--'; /** diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ilm_phases_empty_prompt/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ilm_phases_empty_prompt/index.test.tsx new file mode 100644 index 0000000000000..417f1419a7ca5 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/ilm_phases_empty_prompt/index.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../mock/test_providers/test_providers'; +import { IlmPhasesEmptyPrompt } from '.'; + +describe('IlmPhasesEmptyPrompt', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it renders the expected content', () => { + expect(screen.getByTestId('ilmPhasesEmptyPrompt')).toHaveTextContent( + "ILM phases that can be checked for data qualityhot: The index is actively being updated and queriedwarm: The index is no longer being updated but is still being queriedunmanaged: The index isn't managed by Index Lifecycle Management (ILM)ILM phases that cannot be checkedThe following ILM phases cannot be checked for data quality because they are slower to accesscold: The index is no longer being updated and is queried infrequently. The information still needs to be searchable, but it’s okay if those queries are slower.frozen: The index is no longer being updated and is queried rarely. The information still needs to be searchable, but it's okay if those queries are extremely slow." + ); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.test.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.test.tsx index 72affc8aec491..5f6711814a904 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.test.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.test.tsx @@ -9,12 +9,12 @@ import { DARK_THEME } from '@elastic/charts'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { TestProviders } from './mock/test_providers'; +import { TestProviders } from './mock/test_providers/test_providers'; import { DataQualityPanel } from '.'; describe('DataQualityPanel', () => { - describe('when no ILM phases are provided', () => { - const ilmPhases: string[] = []; + describe('when ILM phases are provided', () => { + const ilmPhases: string[] = ['hot', 'warm', 'unmanaged']; beforeEach(() => { render( @@ -22,6 +22,7 @@ describe('DataQualityPanel', () => { { ); }); - test('it renders the ILM phases empty prompt', () => { - expect(screen.getByTestId('ilmPhasesEmptyPrompt')).toBeInTheDocument(); + test('it does NOT render the ILM phases empty prompt', () => { + expect(screen.queryByTestId('ilmPhasesEmptyPrompt')).not.toBeInTheDocument(); }); - test('it does NOT render the body', () => { - expect(screen.queryByTestId('body')).not.toBeInTheDocument(); + test('it renders the body', () => { + expect(screen.getByTestId('body')).toBeInTheDocument(); }); }); - describe('when ILM phases are provided', () => { - const ilmPhases: string[] = ['hot', 'warm', 'unmanaged']; + describe('when ILM phases are NOT provided', () => { + test('it renders the ILM phases empty prompt', () => { + const ilmPhases: string[] = []; - beforeEach(() => { render( { /> ); - }); - - test('it does NOT render the ILM phases empty prompt', () => { - expect(screen.queryByTestId('ilmPhasesEmptyPrompt')).not.toBeInTheDocument(); - }); - test('it renders the body', () => { - expect(screen.getByTestId('body')).toBeInTheDocument(); + expect(screen.getByTestId('ilmPhasesEmptyPrompt')).toBeInTheDocument(); }); }); }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.tsx index 05ccaf9c4fc5e..dde54d8e97f64 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/index.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import numeral from '@elastic/numeral'; import type { FlameElementEvent, HeatmapElementEvent, @@ -14,15 +15,16 @@ import type { WordCloudElementEvent, XYChartElementEvent, } from '@elastic/charts'; -import React from 'react'; +import React, { useCallback } from 'react'; import { Body } from './data_quality_panel/body'; -import { IlmPhasesEmptyPrompt } from './ilm_phases_empty_prompt'; +import { EMPTY_STAT } from './helpers'; interface Props { addSuccessToast: (toast: { title: string }) => void; canUserCreateAndReadCases: () => boolean; defaultNumberFormat: string; + defaultBytesFormat: string; getGroupByFieldsOnClick: ( elements: Array< | FlameElementEvent @@ -54,6 +56,7 @@ interface Props { const DataQualityPanelComponent: React.FC = ({ addSuccessToast, canUserCreateAndReadCases, + defaultBytesFormat, defaultNumberFormat, getGroupByFieldsOnClick, ilmPhases, @@ -63,15 +66,24 @@ const DataQualityPanelComponent: React.FC = ({ setLastChecked, theme, }) => { - if (ilmPhases.length === 0) { - return ; - } + const formatBytes = useCallback( + (value: number | undefined): string => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT, + [defaultBytesFormat] + ); + + const formatNumber = useCallback( + (value: number | undefined): string => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT, + [defaultNumberFormat] + ); return ( = { + 'auditbeat-custom-index-1': { + docsCount: 4, + error: null, + ilmPhase: 'unmanaged', + incompatible: 3, + indexName: 'auditbeat-custom-index-1', + markdownComments: [ + '### auditbeat-custom-index-1\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase |\n|--------|-------|------|---------------------|-----------|\n| ❌ | auditbeat-custom-index-1 | 4 (0.0%) | 3 | `unmanaged` |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.6.1.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - auditbeat-custom-index-1\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - auditbeat-custom-index-1\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2),\n`theory` (1) |\n\n', + ], + pattern: 'auditbeat-*', + }, + 'auditbeat-7.9.3-2023.02.13-000001': { + docsCount: 2438, + error: null, + ilmPhase: 'hot', + incompatible: 12, + indexName: 'auditbeat-7.9.3-2023.02.13-000001', + markdownComments: [ + '### auditbeat-7.9.3-2023.02.13-000001\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase |\n|--------|-------|------|---------------------|-----------|\n| ❌ | auditbeat-7.9.3-2023.02.13-000001 | 2,438 (4.2%) | 12 | `hot` |\n\n', + '### **Incompatible fields** `12` **Custom fields** `439` **ECS compliant fields** `506` **All fields** `957`\n', + "#### 12 incompatible fields, 11 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.6.1.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - auditbeat-7.9.3-2023.02.13-000001\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| error.message | `match_only_text` | `text` `same family` |\n| error.stack_trace | `wildcard` | `keyword` `same family` |\n| http.request.body.content | `wildcard` | `keyword` `same family` |\n| http.response.body.content | `wildcard` | `keyword` `same family` |\n| message | `match_only_text` | `text` `same family` |\n| process.command_line | `wildcard` | `keyword` `same family` |\n| process.parent.command_line | `wildcard` | `keyword` `same family` |\n| registry.data.strings | `wildcard` | `keyword` `same family` |\n| url.full | `wildcard` | `keyword` `same family` |\n| url.original | `wildcard` | `keyword` `same family` |\n| url.path | `wildcard` | `keyword` `same family` |\n\n#### Incompatible field values - auditbeat-7.9.3-2023.02.13-000001\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.kind | `alert`, `enrichment`, `event`, `metric`, `state`, `pipeline_error`, `signal` | `error` (7) |\n\n', + ], + pattern: 'auditbeat-*', + }, +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/enriched_field_metadata/index.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/enriched_field_metadata/mock_enriched_field_metadata.ts similarity index 87% rename from x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/enriched_field_metadata/index.ts rename to x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/enriched_field_metadata/mock_enriched_field_metadata.ts index 654d608d7d351..be445b6ee6b51 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/enriched_field_metadata/index.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/enriched_field_metadata/mock_enriched_field_metadata.ts @@ -270,3 +270,75 @@ export const sourcePort: EnrichedFieldMetadata = { isEcsCompliant: true, isInSameFamily: false, // `long` is not a member of any families }; + +export const mockCustomFields: EnrichedFieldMetadata[] = [ + { + indexFieldName: 'host.name.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'some.field', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'some.field.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + indexFieldName: 'source.ip.keyword', + indexFieldType: 'keyword', + indexInvalidValues: [], + hasEcsMetadata: false, + isEcsCompliant: false, + isInSameFamily: false, + }, +]; + +export const mockIncompatibleMappings: EnrichedFieldMetadata[] = [ + { + dashed_name: 'host-name', + description: + 'Name of the host.\nIt can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + flat_name: 'host.name', + ignore_above: 1024, + level: 'core', + name: 'name', + normalize: [], + short: 'Name of the host.', + type: 'keyword', + indexFieldName: 'host.name', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: false, + }, + { + dashed_name: 'source-ip', + description: 'IP address of the source (IPv4 or IPv6).', + flat_name: 'source.ip', + level: 'core', + name: 'ip', + normalize: [], + short: 'IP address of the source.', + type: 'ip', + indexFieldName: 'source.ip', + indexFieldType: 'text', + indexInvalidValues: [], + hasEcsMetadata: true, + isEcsCompliant: false, + isInSameFamily: false, + }, +]; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/ilm_explain/index.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/ilm_explain/mock_ilm_explain.ts similarity index 94% rename from x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/ilm_explain/index.ts rename to x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/ilm_explain/mock_ilm_explain.ts index bf7bc667c9aaf..b5b35064a7320 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/ilm_explain/index.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/ilm_explain/mock_ilm_explain.ts @@ -48,4 +48,8 @@ export const mockIlmExplain: Record modified_date_in_millis: 1675536751205, }, }, + 'auditbeat-custom-index-1': { + index: 'auditbeat-custom-index-1', + managed: false, + }, }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/indices_get_mapping_index_mapping_record/mock_indices_get_mapping_index_mapping_record.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/indices_get_mapping_index_mapping_record/mock_indices_get_mapping_index_mapping_record.ts new file mode 100644 index 0000000000000..e63bae0530961 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/indices_get_mapping_index_mapping_record/mock_indices_get_mapping_index_mapping_record.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 { IndicesGetMappingIndexMappingRecord } from '@elastic/elasticsearch/lib/api/types'; + +export const mockIndicesGetMappingIndexMappingRecords: Record< + string, + IndicesGetMappingIndexMappingRecord +> = { + 'auditbeat-custom-index-1': { + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + event: { + properties: { + category: { + type: 'keyword', + ignore_above: 1024, + }, + }, + }, + host: { + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + some: { + properties: { + field: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + source: { + properties: { + ip: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + port: { + type: 'long', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/mappings_properties/mock_mappings_properties.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/mappings_properties/mock_mappings_properties.ts new file mode 100644 index 0000000000000..42b22d9c99aaa --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/mappings_properties/mock_mappings_properties.ts @@ -0,0 +1,107 @@ +/* + * 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 { MappingProperty } from '@elastic/elasticsearch/lib/api/types'; + +/** + * These `mappingsProperties` represent mappings that were generated by + * Elasticsearch automatically, for an index named `auditbeat-custom-index-1`: + * + * ``` + * DELETE auditbeat-custom-index-1 + * + * PUT auditbeat-custom-index-1 + * + * PUT auditbeat-custom-index-1/_mapping + * { + * "properties": { + * "@timestamp": { + * "type": "date" + * }, + * "event.category": { + * "type": "keyword", + * "ignore_above": 1024 + * } + * } + * } + * ``` + * + * when the following document was inserted: + * + * ``` + * POST auditbeat-custom-index-1/_doc + * { + * "@timestamp": "2023-02-06T09:41:49.668Z", + * "host": { + * "name": "foo" + * }, + * "event": { + * "category": "an_invalid_category" + * }, + * "some.field": "this", + * "source": { + * "port": 90210, + * "ip": "10.1.2.3" + * } + * } + * ``` + */ +export const mockMappingsProperties: Record = { + '@timestamp': { + type: 'date', + }, + event: { + properties: { + category: { + type: 'keyword', + ignore_above: 1024, + }, + }, + }, + host: { + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + some: { + properties: { + field: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + source: { + properties: { + ip: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + port: { + type: 'long', + }, + }, + }, +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/mappings_response/mock_mappings_response.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/mappings_response/mock_mappings_response.ts new file mode 100644 index 0000000000000..5e15bb4e2efdd --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/mappings_response/mock_mappings_response.ts @@ -0,0 +1,68 @@ +/* + * 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 mockMappingsResponse = { + 'auditbeat-custom-index-1': { + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + event: { + properties: { + category: { + type: 'keyword', + ignore_above: 1024, + }, + }, + }, + host: { + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + some: { + properties: { + field: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + }, + }, + source: { + properties: { + ip: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + ignore_above: 256, + }, + }, + }, + port: { + type: 'long', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts index cbca7ab9e1965..39c25cbc77c10 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_alerts_pattern_rollup.ts @@ -33,6 +33,7 @@ export const alertIndexNoResults: PatternRollup = { indices: 1, pattern: '.alerts-security.alerts-default', results: undefined, // <-- no results + sizeInBytes: 6423408623, stats: { '.internal.alerts-security.alerts-default-000001': { health: 'green', @@ -83,6 +84,7 @@ export const alertIndexWithAllResults: PatternRollup = { pattern: '.alerts-security.alerts-default', }, }, + sizeInBytes: 29717961631, stats: { '.internal.alerts-security.alerts-default-000001': { health: 'green', diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts index 776f02f9f4e74..3ece4fb4c248f 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_auditbeat_pattern_rollup.ts @@ -45,10 +45,17 @@ export const auditbeatNoResults: PatternRollup = { indices: 3, pattern: 'auditbeat-*', results: undefined, // <-- no results + sizeInBytes: 18820446, stats: { '.ds-auditbeat-8.6.1-2023.02.07-000001': { uuid: 'YpxavlUVTw2x_E_QtADrpg', health: 'yellow', + primaries: { + store: { + size_in_bytes: 18791790, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -60,6 +67,12 @@ export const auditbeatNoResults: PatternRollup = { 'auditbeat-custom-empty-index-1': { uuid: 'Iz5FJjsLQla34mD6kBAQBw', health: 'yellow', + primaries: { + store: { + size_in_bytes: 247, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -71,6 +84,12 @@ export const auditbeatNoResults: PatternRollup = { 'auditbeat-custom-index-1': { uuid: 'xJvgb2QCQPSjlr7UnW8tFA', health: 'yellow', + primaries: { + store: { + size_in_bytes: 28409, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -152,10 +171,17 @@ export const auditbeatWithAllResults: PatternRollup = { pattern: 'auditbeat-*', }, }, + sizeInBytes: 18820446, stats: { '.ds-auditbeat-8.6.1-2023.02.07-000001': { uuid: 'YpxavlUVTw2x_E_QtADrpg', health: 'yellow', + primaries: { + store: { + size_in_bytes: 18791790, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -167,6 +193,12 @@ export const auditbeatWithAllResults: PatternRollup = { 'auditbeat-custom-empty-index-1': { uuid: 'Iz5FJjsLQla34mD6kBAQBw', health: 'yellow', + primaries: { + store: { + size_in_bytes: 247, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -178,6 +210,12 @@ export const auditbeatWithAllResults: PatternRollup = { 'auditbeat-custom-index-1': { uuid: 'xJvgb2QCQPSjlr7UnW8tFA', health: 'yellow', + primaries: { + store: { + size_in_bytes: 28409, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts index 2b39901b9c954..f26ef180a3641 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/pattern_rollup/mock_packetbeat_pattern_rollup.ts @@ -43,10 +43,17 @@ export const packetbeatNoResults: PatternRollup = { indices: 2, pattern: 'packetbeat-*', results: undefined, + sizeInBytes: 1096520898, stats: { '.ds-packetbeat-8.6.1-2023.02.04-000001': { uuid: 'x5Uuw4j4QM2YidHLNixCwg', health: 'yellow', + primaries: { + store: { + size_in_bytes: 512194751, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -58,6 +65,12 @@ export const packetbeatNoResults: PatternRollup = { '.ds-packetbeat-8.5.3-2023.02.04-000001': { uuid: 'we0vNWm2Q6iz6uHubyHS6Q', health: 'yellow', + primaries: { + store: { + size_in_bytes: 584326147, + reserved_in_bytes: 0, + }, + }, status: 'open', total: { docs: { @@ -127,27 +140,650 @@ export const packetbeatWithSomeErrors: PatternRollup = { pattern: 'packetbeat-*', }, }, + sizeInBytes: 1096520898, + stats: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + uuid: 'x5Uuw4j4QM2YidHLNixCwg', + health: 'yellow', + primaries: { + store: { + size_in_bytes: 512194751, + reserved_in_bytes: 0, + }, + }, + status: 'open', + total: { + docs: { + count: 1628343, + deleted: 0, + }, + }, + }, + '.ds-packetbeat-8.5.3-2023.02.04-000001': { + uuid: 'we0vNWm2Q6iz6uHubyHS6Q', + health: 'yellow', + primaries: { + store: { + size_in_bytes: 584326147, + reserved_in_bytes: 0, + }, + }, + status: 'open', + total: { + docs: { + count: 1630289, + deleted: 0, + }, + }, + }, + }, +}; + +export const mockPacketbeatPatternRollup: PatternRollup = { + docsCount: 3258632, + error: null, + ilmExplain: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536751379, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536751379, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536751809, + action: 'rollover', + action_time_millis: 1675536751809, + step: 'check-rollover-ready', + step_time_millis: 1675536751809, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + '.ds-packetbeat-8.5.3-2023.02.04-000001': { + index: '.ds-packetbeat-8.5.3-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536774084, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536774084, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536774416, + action: 'rollover', + action_time_millis: 1675536774416, + step: 'check-rollover-ready', + step_time_millis: 1675536774416, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + }, + ilmExplainPhaseCounts: { + hot: 2, + warm: 0, + cold: 0, + frozen: 0, + unmanaged: 0, + }, + indices: 2, + pattern: 'packetbeat-*', + results: undefined, + sizeInBytes: 1464758182, stats: { '.ds-packetbeat-8.6.1-2023.02.04-000001': { uuid: 'x5Uuw4j4QM2YidHLNixCwg', health: 'yellow', status: 'open', + primaries: { + docs: { + count: 1628343, + deleted: 0, + }, + shard_stats: { + total_count: 1, + }, + store: { + size_in_bytes: 731583142, + total_data_set_size_in_bytes: 731583142, + reserved_in_bytes: 0, + }, + indexing: { + index_total: 0, + index_time_in_millis: 0, + index_current: 0, + index_failed: 0, + delete_total: 0, + delete_time_in_millis: 0, + delete_current: 0, + noop_update_total: 0, + is_throttled: false, + throttle_time_in_millis: 0, + }, + get: { + total: 0, + time_in_millis: 0, + exists_total: 0, + exists_time_in_millis: 0, + missing_total: 0, + missing_time_in_millis: 0, + current: 0, + }, + search: { + open_contexts: 0, + query_total: 120726, + query_time_in_millis: 234865, + query_current: 0, + fetch_total: 109324, + fetch_time_in_millis: 500584, + fetch_current: 0, + scroll_total: 10432, + scroll_time_in_millis: 3874632, + scroll_current: 0, + suggest_total: 0, + suggest_time_in_millis: 0, + suggest_current: 0, + }, + merges: { + current: 0, + current_docs: 0, + current_size_in_bytes: 0, + total: 0, + total_time_in_millis: 0, + total_docs: 0, + total_size_in_bytes: 0, + total_stopped_time_in_millis: 0, + total_throttled_time_in_millis: 0, + total_auto_throttle_in_bytes: 20971520, + }, + refresh: { + total: 2, + total_time_in_millis: 0, + external_total: 2, + external_total_time_in_millis: 1, + listeners: 0, + }, + flush: { + total: 1, + periodic: 1, + total_time_in_millis: 0, + }, + warmer: { + current: 0, + total: 1, + total_time_in_millis: 1, + }, + query_cache: { + memory_size_in_bytes: 8316098, + total_count: 34248343, + hit_count: 3138879, + miss_count: 31109464, + cache_size: 4585, + cache_count: 4585, + evictions: 0, + }, + fielddata: { + memory_size_in_bytes: 12424, + evictions: 0, + }, + completion: { + size_in_bytes: 0, + }, + segments: { + count: 19, + memory_in_bytes: 0, + terms_memory_in_bytes: 0, + stored_fields_memory_in_bytes: 0, + term_vectors_memory_in_bytes: 0, + norms_memory_in_bytes: 0, + points_memory_in_bytes: 0, + doc_values_memory_in_bytes: 0, + index_writer_memory_in_bytes: 0, + version_map_memory_in_bytes: 0, + fixed_bit_set_memory_in_bytes: 304, + max_unsafe_auto_id_timestamp: -1, + file_sizes: {}, + }, + translog: { + operations: 0, + size_in_bytes: 55, + uncommitted_operations: 0, + uncommitted_size_in_bytes: 55, + earliest_last_modified_age: 606298841, + }, + request_cache: { + memory_size_in_bytes: 89216, + evictions: 0, + hit_count: 704, + miss_count: 38, + }, + recovery: { + current_as_source: 0, + current_as_target: 0, + throttle_time_in_millis: 0, + }, + bulk: { + total_operations: 0, + total_time_in_millis: 0, + total_size_in_bytes: 0, + avg_time_in_millis: 0, + avg_size_in_bytes: 0, + }, + }, total: { docs: { count: 1628343, deleted: 0, }, + shard_stats: { + total_count: 1, + }, + store: { + size_in_bytes: 731583142, + total_data_set_size_in_bytes: 731583142, + reserved_in_bytes: 0, + }, + indexing: { + index_total: 0, + index_time_in_millis: 0, + index_current: 0, + index_failed: 0, + delete_total: 0, + delete_time_in_millis: 0, + delete_current: 0, + noop_update_total: 0, + is_throttled: false, + throttle_time_in_millis: 0, + }, + get: { + total: 0, + time_in_millis: 0, + exists_total: 0, + exists_time_in_millis: 0, + missing_total: 0, + missing_time_in_millis: 0, + current: 0, + }, + search: { + open_contexts: 0, + query_total: 120726, + query_time_in_millis: 234865, + query_current: 0, + fetch_total: 109324, + fetch_time_in_millis: 500584, + fetch_current: 0, + scroll_total: 10432, + scroll_time_in_millis: 3874632, + scroll_current: 0, + suggest_total: 0, + suggest_time_in_millis: 0, + suggest_current: 0, + }, + merges: { + current: 0, + current_docs: 0, + current_size_in_bytes: 0, + total: 0, + total_time_in_millis: 0, + total_docs: 0, + total_size_in_bytes: 0, + total_stopped_time_in_millis: 0, + total_throttled_time_in_millis: 0, + total_auto_throttle_in_bytes: 20971520, + }, + refresh: { + total: 2, + total_time_in_millis: 0, + external_total: 2, + external_total_time_in_millis: 1, + listeners: 0, + }, + flush: { + total: 1, + periodic: 1, + total_time_in_millis: 0, + }, + warmer: { + current: 0, + total: 1, + total_time_in_millis: 1, + }, + query_cache: { + memory_size_in_bytes: 8316098, + total_count: 34248343, + hit_count: 3138879, + miss_count: 31109464, + cache_size: 4585, + cache_count: 4585, + evictions: 0, + }, + fielddata: { + memory_size_in_bytes: 12424, + evictions: 0, + }, + completion: { + size_in_bytes: 0, + }, + segments: { + count: 19, + memory_in_bytes: 0, + terms_memory_in_bytes: 0, + stored_fields_memory_in_bytes: 0, + term_vectors_memory_in_bytes: 0, + norms_memory_in_bytes: 0, + points_memory_in_bytes: 0, + doc_values_memory_in_bytes: 0, + index_writer_memory_in_bytes: 0, + version_map_memory_in_bytes: 0, + fixed_bit_set_memory_in_bytes: 304, + max_unsafe_auto_id_timestamp: -1, + file_sizes: {}, + }, + translog: { + operations: 0, + size_in_bytes: 55, + uncommitted_operations: 0, + uncommitted_size_in_bytes: 55, + earliest_last_modified_age: 606298841, + }, + request_cache: { + memory_size_in_bytes: 89216, + evictions: 0, + hit_count: 704, + miss_count: 38, + }, + recovery: { + current_as_source: 0, + current_as_target: 0, + throttle_time_in_millis: 0, + }, + bulk: { + total_operations: 0, + total_time_in_millis: 0, + total_size_in_bytes: 0, + avg_time_in_millis: 0, + avg_size_in_bytes: 0, + }, }, }, '.ds-packetbeat-8.5.3-2023.02.04-000001': { uuid: 'we0vNWm2Q6iz6uHubyHS6Q', health: 'yellow', status: 'open', + primaries: { + docs: { + count: 1630289, + deleted: 0, + }, + shard_stats: { + total_count: 1, + }, + store: { + size_in_bytes: 733175040, + total_data_set_size_in_bytes: 733175040, + reserved_in_bytes: 0, + }, + indexing: { + index_total: 0, + index_time_in_millis: 0, + index_current: 0, + index_failed: 0, + delete_total: 0, + delete_time_in_millis: 0, + delete_current: 0, + noop_update_total: 0, + is_throttled: false, + throttle_time_in_millis: 0, + }, + get: { + total: 0, + time_in_millis: 0, + exists_total: 0, + exists_time_in_millis: 0, + missing_total: 0, + missing_time_in_millis: 0, + current: 0, + }, + search: { + open_contexts: 0, + query_total: 120726, + query_time_in_millis: 248138, + query_current: 0, + fetch_total: 109484, + fetch_time_in_millis: 500514, + fetch_current: 0, + scroll_total: 10432, + scroll_time_in_millis: 3871379, + scroll_current: 0, + suggest_total: 0, + suggest_time_in_millis: 0, + suggest_current: 0, + }, + merges: { + current: 0, + current_docs: 0, + current_size_in_bytes: 0, + total: 0, + total_time_in_millis: 0, + total_docs: 0, + total_size_in_bytes: 0, + total_stopped_time_in_millis: 0, + total_throttled_time_in_millis: 0, + total_auto_throttle_in_bytes: 20971520, + }, + refresh: { + total: 2, + total_time_in_millis: 0, + external_total: 2, + external_total_time_in_millis: 2, + listeners: 0, + }, + flush: { + total: 1, + periodic: 1, + total_time_in_millis: 0, + }, + warmer: { + current: 0, + total: 1, + total_time_in_millis: 1, + }, + query_cache: { + memory_size_in_bytes: 5387543, + total_count: 24212135, + hit_count: 2223357, + miss_count: 21988778, + cache_size: 3275, + cache_count: 3275, + evictions: 0, + }, + fielddata: { + memory_size_in_bytes: 12336, + evictions: 0, + }, + completion: { + size_in_bytes: 0, + }, + segments: { + count: 20, + memory_in_bytes: 0, + terms_memory_in_bytes: 0, + stored_fields_memory_in_bytes: 0, + term_vectors_memory_in_bytes: 0, + norms_memory_in_bytes: 0, + points_memory_in_bytes: 0, + doc_values_memory_in_bytes: 0, + index_writer_memory_in_bytes: 0, + version_map_memory_in_bytes: 0, + fixed_bit_set_memory_in_bytes: 320, + max_unsafe_auto_id_timestamp: -1, + file_sizes: {}, + }, + translog: { + operations: 0, + size_in_bytes: 55, + uncommitted_operations: 0, + uncommitted_size_in_bytes: 55, + earliest_last_modified_age: 606298805, + }, + request_cache: { + memory_size_in_bytes: 89320, + evictions: 0, + hit_count: 704, + miss_count: 38, + }, + recovery: { + current_as_source: 0, + current_as_target: 0, + throttle_time_in_millis: 0, + }, + bulk: { + total_operations: 0, + total_time_in_millis: 0, + total_size_in_bytes: 0, + avg_time_in_millis: 0, + avg_size_in_bytes: 0, + }, + }, total: { docs: { count: 1630289, deleted: 0, }, + shard_stats: { + total_count: 1, + }, + store: { + size_in_bytes: 733175040, + total_data_set_size_in_bytes: 733175040, + reserved_in_bytes: 0, + }, + indexing: { + index_total: 0, + index_time_in_millis: 0, + index_current: 0, + index_failed: 0, + delete_total: 0, + delete_time_in_millis: 0, + delete_current: 0, + noop_update_total: 0, + is_throttled: false, + throttle_time_in_millis: 0, + }, + get: { + total: 0, + time_in_millis: 0, + exists_total: 0, + exists_time_in_millis: 0, + missing_total: 0, + missing_time_in_millis: 0, + current: 0, + }, + search: { + open_contexts: 0, + query_total: 120726, + query_time_in_millis: 248138, + query_current: 0, + fetch_total: 109484, + fetch_time_in_millis: 500514, + fetch_current: 0, + scroll_total: 10432, + scroll_time_in_millis: 3871379, + scroll_current: 0, + suggest_total: 0, + suggest_time_in_millis: 0, + suggest_current: 0, + }, + merges: { + current: 0, + current_docs: 0, + current_size_in_bytes: 0, + total: 0, + total_time_in_millis: 0, + total_docs: 0, + total_size_in_bytes: 0, + total_stopped_time_in_millis: 0, + total_throttled_time_in_millis: 0, + total_auto_throttle_in_bytes: 20971520, + }, + refresh: { + total: 2, + total_time_in_millis: 0, + external_total: 2, + external_total_time_in_millis: 2, + listeners: 0, + }, + flush: { + total: 1, + periodic: 1, + total_time_in_millis: 0, + }, + warmer: { + current: 0, + total: 1, + total_time_in_millis: 1, + }, + query_cache: { + memory_size_in_bytes: 5387543, + total_count: 24212135, + hit_count: 2223357, + miss_count: 21988778, + cache_size: 3275, + cache_count: 3275, + evictions: 0, + }, + fielddata: { + memory_size_in_bytes: 12336, + evictions: 0, + }, + completion: { + size_in_bytes: 0, + }, + segments: { + count: 20, + memory_in_bytes: 0, + terms_memory_in_bytes: 0, + stored_fields_memory_in_bytes: 0, + term_vectors_memory_in_bytes: 0, + norms_memory_in_bytes: 0, + points_memory_in_bytes: 0, + doc_values_memory_in_bytes: 0, + index_writer_memory_in_bytes: 0, + version_map_memory_in_bytes: 0, + fixed_bit_set_memory_in_bytes: 320, + max_unsafe_auto_id_timestamp: -1, + file_sizes: {}, + }, + translog: { + operations: 0, + size_in_bytes: 55, + uncommitted_operations: 0, + uncommitted_size_in_bytes: 55, + earliest_last_modified_age: 606298805, + }, + request_cache: { + memory_size_in_bytes: 89320, + evictions: 0, + hit_count: 704, + miss_count: 38, + }, + recovery: { + current_as_source: 0, + current_as_target: 0, + throttle_time_in_millis: 0, + }, + bulk: { + total_operations: 0, + total_time_in_millis: 0, + total_size_in_bytes: 0, + avg_time_in_millis: 0, + avg_size_in_bytes: 0, + }, }, }, }, diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/stats/mock_stats.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/stats/mock_stats.tsx index 0362dcd70a53f..14465e815ad47 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/stats/mock_stats.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/stats/mock_stats.tsx @@ -558,4 +558,279 @@ export const mockStats: Record = { }, }, }, + 'auditbeat-custom-index-1': { + uuid: 'uyJDDqGrRQqdBTN0mCF-iw', + health: 'yellow', + status: 'open', + primaries: { + docs: { + count: 4, + deleted: 0, + }, + shard_stats: { + total_count: 1, + }, + store: { + size_in_bytes: 28413, + total_data_set_size_in_bytes: 28413, + reserved_in_bytes: 0, + }, + indexing: { + index_total: 0, + index_time_in_millis: 0, + index_current: 0, + index_failed: 0, + delete_total: 0, + delete_time_in_millis: 0, + delete_current: 0, + noop_update_total: 0, + is_throttled: false, + throttle_time_in_millis: 0, + }, + get: { + total: 0, + time_in_millis: 0, + exists_total: 0, + exists_time_in_millis: 0, + missing_total: 0, + missing_time_in_millis: 0, + current: 0, + }, + search: { + open_contexts: 0, + query_total: 24, + query_time_in_millis: 5, + query_current: 0, + fetch_total: 24, + fetch_time_in_millis: 0, + fetch_current: 0, + scroll_total: 0, + scroll_time_in_millis: 0, + scroll_current: 0, + suggest_total: 0, + suggest_time_in_millis: 0, + suggest_current: 0, + }, + merges: { + current: 0, + current_docs: 0, + current_size_in_bytes: 0, + total: 0, + total_time_in_millis: 0, + total_docs: 0, + total_size_in_bytes: 0, + total_stopped_time_in_millis: 0, + total_throttled_time_in_millis: 0, + total_auto_throttle_in_bytes: 20971520, + }, + refresh: { + total: 2, + total_time_in_millis: 0, + external_total: 2, + external_total_time_in_millis: 0, + listeners: 0, + }, + flush: { + total: 1, + periodic: 1, + total_time_in_millis: 0, + }, + warmer: { + current: 0, + total: 1, + total_time_in_millis: 0, + }, + query_cache: { + memory_size_in_bytes: 58, + total_count: 0, + hit_count: 0, + miss_count: 0, + cache_size: 0, + cache_count: 0, + evictions: 0, + }, + fielddata: { + memory_size_in_bytes: 608, + evictions: 0, + }, + completion: { + size_in_bytes: 0, + }, + segments: { + count: 4, + memory_in_bytes: 0, + terms_memory_in_bytes: 0, + stored_fields_memory_in_bytes: 0, + term_vectors_memory_in_bytes: 0, + norms_memory_in_bytes: 0, + points_memory_in_bytes: 0, + doc_values_memory_in_bytes: 0, + index_writer_memory_in_bytes: 0, + version_map_memory_in_bytes: 0, + fixed_bit_set_memory_in_bytes: 0, + max_unsafe_auto_id_timestamp: -1, + file_sizes: {}, + }, + translog: { + operations: 0, + size_in_bytes: 55, + uncommitted_operations: 0, + uncommitted_size_in_bytes: 55, + earliest_last_modified_age: 79289897, + }, + request_cache: { + memory_size_in_bytes: 3760, + evictions: 0, + hit_count: 20, + miss_count: 4, + }, + recovery: { + current_as_source: 0, + current_as_target: 0, + throttle_time_in_millis: 0, + }, + bulk: { + total_operations: 0, + total_time_in_millis: 0, + total_size_in_bytes: 0, + avg_time_in_millis: 0, + avg_size_in_bytes: 0, + }, + }, + total: { + docs: { + count: 4, + deleted: 0, + }, + shard_stats: { + total_count: 1, + }, + store: { + size_in_bytes: 28413, + total_data_set_size_in_bytes: 28413, + reserved_in_bytes: 0, + }, + indexing: { + index_total: 0, + index_time_in_millis: 0, + index_current: 0, + index_failed: 0, + delete_total: 0, + delete_time_in_millis: 0, + delete_current: 0, + noop_update_total: 0, + is_throttled: false, + throttle_time_in_millis: 0, + }, + get: { + total: 0, + time_in_millis: 0, + exists_total: 0, + exists_time_in_millis: 0, + missing_total: 0, + missing_time_in_millis: 0, + current: 0, + }, + search: { + open_contexts: 0, + query_total: 24, + query_time_in_millis: 5, + query_current: 0, + fetch_total: 24, + fetch_time_in_millis: 0, + fetch_current: 0, + scroll_total: 0, + scroll_time_in_millis: 0, + scroll_current: 0, + suggest_total: 0, + suggest_time_in_millis: 0, + suggest_current: 0, + }, + merges: { + current: 0, + current_docs: 0, + current_size_in_bytes: 0, + total: 0, + total_time_in_millis: 0, + total_docs: 0, + total_size_in_bytes: 0, + total_stopped_time_in_millis: 0, + total_throttled_time_in_millis: 0, + total_auto_throttle_in_bytes: 20971520, + }, + refresh: { + total: 2, + total_time_in_millis: 0, + external_total: 2, + external_total_time_in_millis: 0, + listeners: 0, + }, + flush: { + total: 1, + periodic: 1, + total_time_in_millis: 0, + }, + warmer: { + current: 0, + total: 1, + total_time_in_millis: 0, + }, + query_cache: { + memory_size_in_bytes: 58, + total_count: 0, + hit_count: 0, + miss_count: 0, + cache_size: 0, + cache_count: 0, + evictions: 0, + }, + fielddata: { + memory_size_in_bytes: 608, + evictions: 0, + }, + completion: { + size_in_bytes: 0, + }, + segments: { + count: 4, + memory_in_bytes: 0, + terms_memory_in_bytes: 0, + stored_fields_memory_in_bytes: 0, + term_vectors_memory_in_bytes: 0, + norms_memory_in_bytes: 0, + points_memory_in_bytes: 0, + doc_values_memory_in_bytes: 0, + index_writer_memory_in_bytes: 0, + version_map_memory_in_bytes: 0, + fixed_bit_set_memory_in_bytes: 0, + max_unsafe_auto_id_timestamp: -1, + file_sizes: {}, + }, + translog: { + operations: 0, + size_in_bytes: 55, + uncommitted_operations: 0, + uncommitted_size_in_bytes: 55, + earliest_last_modified_age: 79289897, + }, + request_cache: { + memory_size_in_bytes: 3760, + evictions: 0, + hit_count: 20, + miss_count: 4, + }, + recovery: { + current_as_source: 0, + current_as_target: 0, + throttle_time_in_millis: 0, + }, + bulk: { + total_operations: 0, + total_time_in_millis: 0, + total_size_in_bytes: 0, + avg_time_in_millis: 0, + avg_size_in_bytes: 0, + }, + }, + }, }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/test_providers/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/test_providers/test_providers.tsx similarity index 100% rename from x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/test_providers/index.tsx rename to x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/test_providers/test_providers.tsx diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/unallowed_values/mock_unallowed_values.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/unallowed_values/mock_unallowed_values.ts new file mode 100644 index 0000000000000..393dd15ce9a20 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/mock/unallowed_values/mock_unallowed_values.ts @@ -0,0 +1,122 @@ +/* + * 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 mockUnallowedValuesResponse = [ + { + took: 1, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 3, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'event.category': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'an_invalid_category', + doc_count: 2, + }, + { + key: 'theory', + doc_count: 1, + }, + ], + }, + }, + status: 200, + }, + { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 4, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'event.kind': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, + status: 200, + }, + { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 4, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'event.outcome': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, + status: 200, + }, + { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 4, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'event.type': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, + status: 200, + }, +]; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/styles.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/styles.tsx index d54ea9d6316e2..6fbf130d01b8f 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/styles.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/styles.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiCode } from '@elastic/eui'; +import { EuiCode, EuiText } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import styled from 'styled-components'; @@ -21,3 +21,10 @@ export const CodeSuccess = styled(EuiCode)` export const CodeWarning = styled(EuiCode)` color: ${euiThemeVars.euiColorWarning}; `; + +export const FixedWidthLegendText = styled(EuiText)<{ + $width: number | undefined; +}>` + text-align: left; + ${({ $width }) => ($width != null ? `width: ${$width}px;` : '')} +`; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/translations.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/translations.ts index 771faa301cee3..53bedadd9361d 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/translations.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/translations.ts @@ -25,15 +25,6 @@ export const CHECKING = (index: string) => defaultMessage: 'Checking {index}', }); -export const COLLAPSE_BUTTON_LABEL = (collapsed: boolean) => - collapsed - ? i18n.translate('ecsDataQualityDashboard.collapseButtonLabelOpen', { - defaultMessage: 'Open', - }) - : i18n.translate('ecsDataQualityDashboard.collapseButtonLabelClosed', { - defaultMessage: 'Closed', - }); - export const COLD_DESCRIPTION = i18n.translate('ecsDataQualityDashboard.coldDescription', { defaultMessage: 'The index is no longer being updated and is queried infrequently. The information still needs to be searchable, but it’s okay if those queries are slower.', @@ -80,12 +71,6 @@ export const ECS_VERSION = i18n.translate('ecsDataQualityDashboard.ecsVersionSta defaultMessage: 'ECS version', }); -export const ERROR_LOADING_ECS_METADATA = (details: string) => - i18n.translate('ecsDataQualityDashboard.errorLoadingEcsMetadataLabel', { - values: { details }, - defaultMessage: 'Error loading ECS metadata: {details}', - }); - export const ERROR_LOADING_ECS_METADATA_TITLE = i18n.translate( 'ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingEcsMetadataTitle', { @@ -93,12 +78,6 @@ export const ERROR_LOADING_ECS_METADATA_TITLE = i18n.translate( } ); -export const ERROR_LOADING_ECS_VERSION = (details: string) => - i18n.translate('ecsDataQualityDashboard.errorLoadingEcsVersionLabel', { - values: { details }, - defaultMessage: 'Error loading ECS version: {details}', - }); - export const ERROR_LOADING_ECS_VERSION_TITLE = i18n.translate( 'ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingEcsVersionTitle', { @@ -214,6 +193,10 @@ export const SELECT_ONE_OR_MORE_ILM_PHASES: string = i18n.translate( } ); +export const INDEX_SIZE_TOOLTIP = i18n.translate('ecsDataQualityDashboard.indexSizeTooltip', { + defaultMessage: 'The size of the primary index (does not include replicas)', +}); + export const TECHNICAL_PREVIEW = i18n.translate('ecsDataQualityDashboard.technicalPreviewBadge', { defaultMessage: 'Technical preview', }); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/types.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/types.ts index 9edc76bbe6220..d51e908bd7b38 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/types.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/types.ts @@ -10,6 +10,7 @@ import type { IndicesGetMappingIndexMappingRecord, IndicesStatsIndicesStats, } from '@elastic/elasticsearch/lib/api/types'; +import type { Direction } from '@elastic/eui'; export interface Mappings { pattern: string; @@ -113,6 +114,7 @@ export interface PatternRollup { indices: number | undefined; pattern: string; results: Record | undefined; + sizeInBytes: number | undefined; stats: Record | null; } @@ -139,6 +141,7 @@ export interface IndexToCheck { export type OnCheckCompleted = ({ error, + formatBytes, formatNumber, indexName, partitionedFieldMetadata, @@ -146,6 +149,7 @@ export type OnCheckCompleted = ({ version, }: { error: string | null; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata | null; @@ -158,3 +162,15 @@ export interface ErrorSummary { indexName: string | null; pattern: string; } + +export interface SortConfig { + sort: { + direction: Direction; + field: string; + }; +} + +export interface SelectedIndex { + indexName: string; + pattern: string; +} diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_mappings/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_mappings/helpers.test.ts new file mode 100644 index 0000000000000..e1865c31c85df --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_mappings/helpers.test.ts @@ -0,0 +1,88 @@ +/* + * 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 { fetchMappings } from './helpers'; +import { mockMappingsResponse } from '../mock/mappings_response/mock_mappings_response'; + +describe('helpers', () => { + let originalFetch: typeof global['fetch']; + + beforeAll(() => { + originalFetch = global.fetch; + }); + + afterAll(() => { + global.fetch = originalFetch; + }); + + describe('fetchMappings', () => { + test('it returns the expected mappings', async () => { + const mockFetch = jest.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockMappingsResponse), + }); + global.fetch = mockFetch; + + const result = await fetchMappings({ + abortController: new AbortController(), + patternOrIndexName: 'auditbeat-custom-index-1', + }); + + expect(result).toEqual({ + 'auditbeat-custom-index-1': { + mappings: { + properties: { + '@timestamp': { type: 'date' }, + event: { properties: { category: { ignore_above: 1024, type: 'keyword' } } }, + host: { + properties: { + name: { + fields: { keyword: { ignore_above: 256, type: 'keyword' } }, + type: 'text', + }, + }, + }, + some: { + properties: { + field: { + fields: { keyword: { ignore_above: 256, type: 'keyword' } }, + type: 'text', + }, + }, + }, + source: { + properties: { + ip: { fields: { keyword: { ignore_above: 256, type: 'keyword' } }, type: 'text' }, + port: { type: 'long' }, + }, + }, + }, + }, + }, + }); + }); + + test('it throws the expected error when fetch fails', async () => { + const error = 'simulated error'; + const mockFetch = jest.fn().mockResolvedValue({ + ok: false, + statusText: error, + }); + + global.fetch = mockFetch; + + await expect( + fetchMappings({ + abortController: new AbortController(), + patternOrIndexName: 'auditbeat-custom-index-1', + }) + ).rejects.toThrowError( + 'Error loading mappings for auditbeat-custom-index-1: simulated error' + ); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.test.ts new file mode 100644 index 0000000000000..f25903adff823 --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.test.ts @@ -0,0 +1,511 @@ +/* + * 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 numeral from '@elastic/numeral'; + +import { + getTotalDocsCount, + getTotalIncompatible, + getTotalIndices, + getTotalIndicesChecked, + onPatternRollupUpdated, + updateResultOnCheckCompleted, +} from './helpers'; +import { auditbeatWithAllResults } from '../mock/pattern_rollup/mock_auditbeat_pattern_rollup'; +import { + mockPacketbeatPatternRollup, + packetbeatNoResults, + packetbeatWithSomeErrors, +} from '../mock/pattern_rollup/mock_packetbeat_pattern_rollup'; +import { PatternRollup } from '../types'; +import { EMPTY_STAT } from '../helpers'; +import { IndicesStatsIndicesStats } from '@elastic/elasticsearch/lib/api/types'; +import { mockPartitionedFieldMetadata } from '../mock/partitioned_field_metadata/mock_partitioned_field_metadata'; + +const defaultBytesFormat = '0,0.[0]b'; +const formatBytes = (value: number | undefined) => + value != null ? numeral(value).format(defaultBytesFormat) : EMPTY_STAT; + +const defaultNumberFormat = '0,0.[000]'; +const formatNumber = (value: number | undefined) => + value != null ? numeral(value).format(defaultNumberFormat) : EMPTY_STAT; + +const patternRollups: Record = { + 'auditbeat-*': auditbeatWithAllResults, // indices: 3 + 'packetbeat-*': mockPacketbeatPatternRollup, // indices: 2 +}; + +describe('helpers', () => { + let originalFetch: typeof global['fetch']; + + beforeAll(() => { + originalFetch = global.fetch; + }); + + afterAll(() => { + global.fetch = originalFetch; + }); + + describe('getTotalIndices', () => { + test('it returns the expected total when ALL `PatternRollup`s have an `indices`', () => { + expect(getTotalIndices(patternRollups)).toEqual(5); + }); + + test('it returns undefined when only SOME of the `PatternRollup`s have an `indices`', () => { + const someIndicesAreUndefined: Record = { + 'auditbeat-*': { + ...auditbeatWithAllResults, + indices: undefined, // <-- + }, + 'packetbeat-*': mockPacketbeatPatternRollup, // indices: 2 + }; + + expect(getTotalIndices(someIndicesAreUndefined)).toBeUndefined(); + }); + }); + + describe('getTotalDocsCount', () => { + test('it returns the expected total when ALL `PatternRollup`s have a `docsCount`', () => { + expect(getTotalDocsCount(patternRollups)).toEqual( + Number(auditbeatWithAllResults.docsCount) + Number(mockPacketbeatPatternRollup.docsCount) + ); + }); + + test('it returns undefined when only SOME of the `PatternRollup`s have a `docsCount`', () => { + const someIndicesAreUndefined: Record = { + 'auditbeat-*': { + ...auditbeatWithAllResults, + docsCount: undefined, // <-- + }, + 'packetbeat-*': mockPacketbeatPatternRollup, + }; + + expect(getTotalDocsCount(someIndicesAreUndefined)).toBeUndefined(); + }); + }); + + describe('getTotalIncompatible', () => { + test('it returns the expected total when ALL `PatternRollup`s have `results`', () => { + expect(getTotalIncompatible(patternRollups)).toEqual(4); + }); + + test('it returns the expected total when only SOME of the `PatternRollup`s have `results`', () => { + const someResultsAreUndefined: Record = { + 'auditbeat-*': auditbeatWithAllResults, + 'packetbeat-*': packetbeatNoResults, // <-- results is undefined + }; + + expect(getTotalIncompatible(someResultsAreUndefined)).toEqual(4); + }); + + test('it returns undefined when NONE of the `PatternRollup`s have `results`', () => { + const someResultsAreUndefined: Record = { + 'packetbeat-*': packetbeatNoResults, // <-- results is undefined + }; + + expect(getTotalIncompatible(someResultsAreUndefined)).toBeUndefined(); + }); + }); + + describe('getTotalIndicesChecked', () => { + test('it returns the expected total', () => { + expect(getTotalIndicesChecked(patternRollups)).toEqual(3); + }); + + test('it returns the expected total when errors have occurred', () => { + const someErrors: Record = { + 'auditbeat-*': auditbeatWithAllResults, // indices: 3 + 'packetbeat-*': packetbeatWithSomeErrors, // <-- indices: 2, but one has errors + }; + + expect(getTotalIndicesChecked(someErrors)).toEqual(4); + }); + }); + + describe('onPatternRollupUpdated', () => { + test('it returns a new collection with the updated rollup', () => { + const before: Record = { + 'auditbeat-*': auditbeatWithAllResults, + }; + + expect( + onPatternRollupUpdated({ + patternRollup: mockPacketbeatPatternRollup, + patternRollups: before, + }) + ).toEqual(patternRollups); + }); + }); + + describe('updateResultOnCheckCompleted', () => { + const packetbeatStats861: IndicesStatsIndicesStats = + mockPacketbeatPatternRollup.stats != null + ? mockPacketbeatPatternRollup.stats['.ds-packetbeat-8.6.1-2023.02.04-000001'] + : {}; + const packetbeatStats853: IndicesStatsIndicesStats = + mockPacketbeatPatternRollup.stats != null + ? mockPacketbeatPatternRollup.stats['.ds-packetbeat-8.5.3-2023.02.04-000001'] + : {}; + + test('it returns the updated rollups', () => { + expect( + updateResultOnCheckCompleted({ + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'packetbeat-*', + patternRollups: { + 'packetbeat-*': mockPacketbeatPatternRollup, + }, + }) + ).toEqual({ + 'packetbeat-*': { + docsCount: 3258632, + error: null, + ilmExplain: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536751379, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536751379, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536751809, + action: 'rollover', + action_time_millis: 1675536751809, + step: 'check-rollover-ready', + step_time_millis: 1675536751809, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + '.ds-packetbeat-8.5.3-2023.02.04-000001': { + index: '.ds-packetbeat-8.5.3-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536774084, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536774084, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536774416, + action: 'rollover', + action_time_millis: 1675536774416, + step: 'check-rollover-ready', + step_time_millis: 1675536774416, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + }, + ilmExplainPhaseCounts: { + hot: 2, + warm: 0, + cold: 0, + frozen: 0, + unmanaged: 0, + }, + indices: 2, + pattern: 'packetbeat-*', + results: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + docsCount: 1628343, + error: null, + ilmPhase: 'hot', + incompatible: 3, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + markdownComments: [ + '### .ds-packetbeat-8.6.1-2023.02.04-000001\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .ds-packetbeat-8.6.1-2023.02.04-000001 | 1,628,343 (50.0%) | 3 | `hot` | 697.7MB |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.6.1.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - .ds-packetbeat-8.6.1-2023.02.04-000001\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - .ds-packetbeat-8.6.1-2023.02.04-000001\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ], + pattern: 'packetbeat-*', + }, + }, + sizeInBytes: 1464758182, + stats: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': packetbeatStats861, + '.ds-packetbeat-8.5.3-2023.02.04-000001': packetbeatStats853, + }, + }, + }); + }); + + test('it returns the expected results when `patternRollup` does NOT have a `docsCount`', () => { + const noDocsCount = { + ...mockPacketbeatPatternRollup, + docsCount: undefined, // <-- + }; + + expect( + updateResultOnCheckCompleted({ + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'packetbeat-*', + patternRollups: { + 'packetbeat-*': noDocsCount, + }, + }) + ).toEqual({ + 'packetbeat-*': { + docsCount: undefined, // <-- + error: null, + ilmExplain: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536751379, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536751379, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536751809, + action: 'rollover', + action_time_millis: 1675536751809, + step: 'check-rollover-ready', + step_time_millis: 1675536751809, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + '.ds-packetbeat-8.5.3-2023.02.04-000001': { + index: '.ds-packetbeat-8.5.3-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536774084, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536774084, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536774416, + action: 'rollover', + action_time_millis: 1675536774416, + step: 'check-rollover-ready', + step_time_millis: 1675536774416, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + }, + ilmExplainPhaseCounts: { + hot: 2, + warm: 0, + cold: 0, + frozen: 0, + unmanaged: 0, + }, + indices: 2, + pattern: 'packetbeat-*', + results: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + docsCount: 1628343, + error: null, + ilmPhase: 'hot', + incompatible: 3, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + markdownComments: [ + '### .ds-packetbeat-8.6.1-2023.02.04-000001\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .ds-packetbeat-8.6.1-2023.02.04-000001 | 1,628,343 () | 3 | `hot` | 697.7MB |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.6.1.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - .ds-packetbeat-8.6.1-2023.02.04-000001\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - .ds-packetbeat-8.6.1-2023.02.04-000001\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ], + pattern: 'packetbeat-*', + }, + }, + sizeInBytes: 1464758182, + stats: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': packetbeatStats861, + '.ds-packetbeat-8.5.3-2023.02.04-000001': packetbeatStats853, + }, + }, + }); + }); + + test('it returns the expected results when `partitionedFieldMetadata` is null', () => { + expect( + updateResultOnCheckCompleted({ + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + partitionedFieldMetadata: null, // <-- + pattern: 'packetbeat-*', + patternRollups: { + 'packetbeat-*': mockPacketbeatPatternRollup, + }, + }) + ).toEqual({ + 'packetbeat-*': { + docsCount: 3258632, + error: null, + ilmExplain: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + index: '.ds-packetbeat-8.6.1-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536751379, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536751379, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536751809, + action: 'rollover', + action_time_millis: 1675536751809, + step: 'check-rollover-ready', + step_time_millis: 1675536751809, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + '.ds-packetbeat-8.5.3-2023.02.04-000001': { + index: '.ds-packetbeat-8.5.3-2023.02.04-000001', + managed: true, + policy: 'packetbeat', + index_creation_date_millis: 1675536774084, + time_since_index_creation: '25.26d', + lifecycle_date_millis: 1675536774084, + age: '25.26d', + phase: 'hot', + phase_time_millis: 1675536774416, + action: 'rollover', + action_time_millis: 1675536774416, + step: 'check-rollover-ready', + step_time_millis: 1675536774416, + phase_execution: { + policy: 'packetbeat', + version: 1, + modified_date_in_millis: 1675536751205, + }, + }, + }, + ilmExplainPhaseCounts: { + hot: 2, + warm: 0, + cold: 0, + frozen: 0, + unmanaged: 0, + }, + indices: 2, + pattern: 'packetbeat-*', + results: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + docsCount: 1628343, + error: null, + ilmPhase: 'hot', + incompatible: undefined, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + markdownComments: [], + pattern: 'packetbeat-*', + }, + }, + sizeInBytes: 1464758182, + stats: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': packetbeatStats861, + '.ds-packetbeat-8.5.3-2023.02.04-000001': packetbeatStats853, + }, + }, + }); + }); + + test('it returns the updated rollups when there is no `partitionedFieldMetadata`', () => { + const noIlmExplain = { + ...mockPacketbeatPatternRollup, + ilmExplain: null, + }; + + expect( + updateResultOnCheckCompleted({ + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'packetbeat-*', + patternRollups: { + 'packetbeat-*': noIlmExplain, + }, + }) + ).toEqual({ + 'packetbeat-*': { + docsCount: 3258632, + error: null, + ilmExplain: null, + ilmExplainPhaseCounts: { + hot: 2, + warm: 0, + cold: 0, + frozen: 0, + unmanaged: 0, + }, + indices: 2, + pattern: 'packetbeat-*', + results: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': { + docsCount: 1628343, + error: null, + ilmPhase: undefined, + incompatible: 3, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + markdownComments: [ + '### .ds-packetbeat-8.6.1-2023.02.04-000001\n', + '| Result | Index | Docs | Incompatible fields | ILM Phase | Size |\n|--------|-------|------|---------------------|-----------|------|\n| ❌ | .ds-packetbeat-8.6.1-2023.02.04-000001 | 1,628,343 (50.0%) | 3 | -- | 697.7MB |\n\n', + '### **Incompatible fields** `3` **Custom fields** `4` **ECS compliant fields** `2` **All fields** `9`\n', + "#### 3 incompatible fields, 0 fields with mappings in the same family\n\nFields are incompatible with ECS when index mappings, or the values of the fields in the index, don't conform to the Elastic Common Schema (ECS), version 8.6.1.\n\nIncompatible fields with mappings in the same family have exactly the same search behavior but may have different space usage or performance characteristics.\n\nWhen an incompatible field is not in the same family:\n❌ Detection engine rules referencing these fields may not match them correctly\n❌ Pages may not display some events or fields due to unexpected field mappings or values\n❌ Mappings or field values that don't comply with ECS are not supported\n", + '\n#### Incompatible field mappings - .ds-packetbeat-8.6.1-2023.02.04-000001\n\n\n| Field | ECS mapping type (expected) | Index mapping type (actual) | \n|-------|-----------------------------|-----------------------------|\n| host.name | `keyword` | `text` |\n| source.ip | `ip` | `text` |\n\n#### Incompatible field values - .ds-packetbeat-8.6.1-2023.02.04-000001\n\n\n| Field | ECS values (expected) | Document values (actual) | \n|-------|-----------------------|--------------------------|\n| event.category | `authentication`, `configuration`, `database`, `driver`, `email`, `file`, `host`, `iam`, `intrusion_detection`, `malware`, `network`, `package`, `process`, `registry`, `session`, `threat`, `vulnerability`, `web` | `an_invalid_category` (2), `theory` (1) |\n\n', + ], + pattern: 'packetbeat-*', + }, + }, + sizeInBytes: 1464758182, + stats: { + '.ds-packetbeat-8.6.1-2023.02.04-000001': packetbeatStats861, + '.ds-packetbeat-8.5.3-2023.02.04-000001': packetbeatStats853, + }, + }, + }); + }); + + test('it returns the unmodified rollups when `pattern` is not a member of `patternRollups`', () => { + const shouldNotBeModified: Record = { + 'packetbeat-*': mockPacketbeatPatternRollup, + }; + + expect( + updateResultOnCheckCompleted({ + error: null, + formatBytes, + formatNumber, + indexName: '.ds-packetbeat-8.6.1-2023.02.04-000001', + partitionedFieldMetadata: mockPartitionedFieldMetadata, + pattern: 'this-pattern-is-not-in-pattern-rollups', // <-- + patternRollups: shouldNotBeModified, + }) + ).toEqual(shouldNotBeModified); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.ts index 995ac8eac86c4..dbad0364904f5 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/helpers.ts @@ -8,7 +8,11 @@ import { getIndexDocsCountFromRollup } from '../data_quality_panel/data_quality_summary/summary_actions/check_all/helpers'; import { getIlmPhase } from '../data_quality_panel/pattern/helpers'; import { getAllIncompatibleMarkdownComments } from '../data_quality_panel/tabs/incompatible_tab/helpers'; -import { getTotalPatternIncompatible, getTotalPatternIndicesChecked } from '../helpers'; +import { + getSizeInBytes, + getTotalPatternIncompatible, + getTotalPatternIndicesChecked, +} from '../helpers'; import type { IlmPhase, PartitionedFieldMetadata, PatternRollup } from '../types'; export const getTotalIndices = ( @@ -19,7 +23,7 @@ export const getTotalIndices = ( // only return the total when all `PatternRollup`s have a `indices`: return allRollupsHaveIndices - ? allRollups.reduce((acc, { indices }) => acc + (indices ?? 0), 0) + ? allRollups.reduce((acc, { indices }) => acc + Number(indices), 0) : undefined; }; @@ -31,7 +35,21 @@ export const getTotalDocsCount = ( // only return the total when all `PatternRollup`s have a `docsCount`: return allRollupsHaveDocsCount - ? allRollups.reduce((acc, { docsCount }) => acc + (docsCount ?? 0), 0) + ? allRollups.reduce((acc, { docsCount }) => acc + Number(docsCount), 0) + : undefined; +}; + +export const getTotalSizeInBytes = ( + patternRollups: Record +): number | undefined => { + const allRollups = Object.values(patternRollups); + const allRollupsHaveSizeInBytes = allRollups.every(({ sizeInBytes }) => + Number.isInteger(sizeInBytes) + ); + + // only return the total when all `PatternRollup`s have a `sizeInBytes`: + return allRollupsHaveSizeInBytes + ? allRollups.reduce((acc, { sizeInBytes }) => acc + Number(sizeInBytes), 0) : undefined; }; @@ -69,6 +87,7 @@ export const onPatternRollupUpdated = ({ export const updateResultOnCheckCompleted = ({ error, + formatBytes, formatNumber, indexName, partitionedFieldMetadata, @@ -76,6 +95,7 @@ export const updateResultOnCheckCompleted = ({ patternRollups, }: { error: string | null; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata | null; @@ -85,7 +105,7 @@ export const updateResultOnCheckCompleted = ({ const patternRollup: PatternRollup | undefined = patternRollups[pattern]; if (patternRollup != null) { - const ilmExplain = patternRollup.ilmExplain ?? null; + const ilmExplain = patternRollup.ilmExplain; const ilmPhase: IlmPhase | undefined = ilmExplain != null ? getIlmPhase(ilmExplain[indexName]) : undefined; @@ -97,15 +117,19 @@ export const updateResultOnCheckCompleted = ({ const patternDocsCount = patternRollup.docsCount ?? 0; + const sizeInBytes = getSizeInBytes({ indexName, stats: patternRollup.stats }); + const markdownComments = partitionedFieldMetadata != null ? getAllIncompatibleMarkdownComments({ docsCount, + formatBytes, formatNumber, ilmPhase, indexName, partitionedFieldMetadata, patternDocsCount, + sizeInBytes, }) : []; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/index.tsx b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/index.tsx index da5745deaa3cd..1976b5e150de3 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/index.tsx +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_results_rollup/index.tsx @@ -17,6 +17,7 @@ import { getTotalIncompatible, getTotalIndices, getTotalIndicesChecked, + getTotalSizeInBytes, onPatternRollupUpdated, updateResultOnCheckCompleted, } from './helpers'; @@ -31,6 +32,7 @@ interface UseResultsRollup { totalIncompatible: number | undefined; totalIndices: number | undefined; totalIndicesChecked: number | undefined; + totalSizeInBytes: number | undefined; updatePatternIndexNames: ({ indexNames, pattern, @@ -58,6 +60,7 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll () => getTotalIndicesChecked(patternRollups), [patternRollups] ); + const totalSizeInBytes = useMemo(() => getTotalSizeInBytes(patternRollups), [patternRollups]); const updatePatternIndexNames = useCallback( ({ indexNames, pattern }: { indexNames: string[]; pattern: string }) => { @@ -72,12 +75,14 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll const onCheckCompleted: OnCheckCompleted = useCallback( ({ error, + formatBytes, formatNumber, indexName, partitionedFieldMetadata, pattern, }: { error: string | null; + formatBytes: (value: number | undefined) => string; formatNumber: (value: number | undefined) => string; indexName: string; partitionedFieldMetadata: PartitionedFieldMetadata | null; @@ -86,6 +91,7 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll setPatternRollups((current) => updateResultOnCheckCompleted({ error, + formatBytes, formatNumber, indexName, partitionedFieldMetadata, @@ -111,6 +117,7 @@ export const useResultsRollup = ({ ilmPhases, patterns }: Props): UseResultsRoll totalIncompatible, totalIndices, totalIndicesChecked, + totalSizeInBytes, updatePatternIndexNames, updatePatternRollup, }; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.test.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.test.ts new file mode 100644 index 0000000000000..2f80ba5e2cc7a --- /dev/null +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.test.ts @@ -0,0 +1,503 @@ +/* + * 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 { omit } from 'lodash/fp'; + +import { + fetchUnallowedValues, + getUnallowedValueCount, + getUnallowedValues, + isBucket, +} from './helpers'; +import { mockUnallowedValuesResponse } from '../mock/unallowed_values/mock_unallowed_values'; +import { UnallowedValueRequestItem, UnallowedValueSearchResult } from '../types'; + +describe('helpers', () => { + let originalFetch: typeof global['fetch']; + + beforeAll(() => { + originalFetch = global.fetch; + }); + + afterAll(() => { + global.fetch = originalFetch; + }); + + describe('isBucket', () => { + test('it returns true when the bucket is valid', () => { + expect( + isBucket({ + key: 'stop', + doc_count: 2, + }) + ).toBe(true); + }); + + test('it returns false when just `key` is missing', () => { + expect( + isBucket({ + doc_count: 2, + }) + ).toBe(false); + }); + + test('it returns false when just `key` has an incorrect type', () => { + expect( + isBucket({ + key: 1234, // <-- should be a string + doc_count: 2, + }) + ).toBe(false); + }); + + test('it returns false when just `doc_count` is missing', () => { + expect( + isBucket({ + key: 'stop', + }) + ).toBe(false); + }); + + test('it returns false when just `doc_count` has an incorrect type', () => { + expect( + isBucket({ + key: 'stop', + doc_count: 'foo', // <-- should be a number + }) + ).toBe(false); + }); + + test('it returns false when both `key` and `doc_count` are missing', () => { + expect(isBucket({})).toBe(false); + }); + + test('it returns false when both `key` and `doc_count` have incorrect types', () => { + expect( + isBucket({ + key: 1234, // <-- should be a string + doc_count: 'foo', // <-- should be a number + }) + ).toBe(false); + }); + + test('it returns false when `maybeBucket` is undefined', () => { + expect(isBucket(undefined)).toBe(false); + }); + }); + + describe('getUnallowedValueCount', () => { + test('it returns the expected count', () => { + expect( + getUnallowedValueCount({ + key: 'stop', + doc_count: 2, + }) + ).toEqual({ count: 2, fieldName: 'stop' }); + }); + }); + + describe('getUnallowedValues', () => { + const requestItems: UnallowedValueRequestItem[] = [ + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.category', + allowedValues: [ + 'authentication', + 'configuration', + 'database', + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ], + }, + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.kind', + allowedValues: [ + 'alert', + 'enrichment', + 'event', + 'metric', + 'state', + 'pipeline_error', + 'signal', + ], + }, + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.outcome', + allowedValues: ['failure', 'success', 'unknown'], + }, + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.type', + allowedValues: [ + 'access', + 'admin', + 'allowed', + 'change', + 'connection', + 'creation', + 'deletion', + 'denied', + 'end', + 'error', + 'group', + 'indicator', + 'info', + 'installation', + 'protocol', + 'start', + 'user', + ], + }, + ]; + + const searchResults: UnallowedValueSearchResult[] = [ + { + aggregations: { + 'event.category': { + buckets: [ + { + key: 'an_invalid_category', + doc_count: 2, + }, + { + key: 'theory', + doc_count: 1, + }, + ], + }, + }, + }, + { + aggregations: { + 'event.kind': { + buckets: [], + }, + }, + }, + { + aggregations: { + 'event.outcome': { + buckets: [], + }, + }, + }, + { + aggregations: { + 'event.type': { + buckets: [], + }, + }, + }, + ]; + + test('it returns the expected unallowed values', () => { + expect( + getUnallowedValues({ + requestItems, + searchResults, + }) + ).toEqual({ + 'event.category': [ + { count: 2, fieldName: 'an_invalid_category' }, + { count: 1, fieldName: 'theory' }, + ], + 'event.kind': [], + 'event.outcome': [], + 'event.type': [], + }); + }); + + test('it returns an empty index when `searchResults` is null', () => { + expect( + getUnallowedValues({ + requestItems, + searchResults: null, + }) + ).toEqual({}); + }); + + test('it returns an empty index when `searchResults` is not an array', () => { + expect( + getUnallowedValues({ + requestItems, + // @ts-expect-error + searchResults: 1234, + }) + ).toEqual({}); + }); + + test('it returns the expected results when `searchResults` does NOT have `aggregations`', () => { + const noAggregations: UnallowedValueSearchResult[] = searchResults.map((x) => + omit('aggregations', x) + ); + + expect( + getUnallowedValues({ + requestItems, + searchResults: noAggregations, + }) + ).toEqual({ + 'event.category': [], + 'event.kind': [], + 'event.outcome': [], + 'event.type': [], + }); + }); + + test('it returns the expected unallowed values when SOME buckets are invalid', () => { + const someInvalid: UnallowedValueSearchResult[] = [ + { + aggregations: { + 'event.category': { + buckets: [ + { + key: 'foo', + // @ts-expect-error + doc_count: 'this-is-an-invalid-bucket', // <-- invalid type, should be number + }, + { + key: 'bar', + doc_count: 1, + }, + ], + }, + }, + }, + { + aggregations: { + 'event.kind': { + buckets: [], + }, + }, + }, + { + aggregations: { + 'event.outcome': { + buckets: [], + }, + }, + }, + { + aggregations: { + 'event.type': { + buckets: [], + }, + }, + }, + ]; + + expect( + getUnallowedValues({ + requestItems, + searchResults: someInvalid, + }) + ).toEqual({ + 'event.category': [{ count: 1, fieldName: 'bar' }], + 'event.kind': [], + 'event.outcome': [], + 'event.type': [], + }); + }); + }); + + describe('fetchUnallowedValues', () => { + const requestItems: UnallowedValueRequestItem[] = [ + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.category', + allowedValues: [ + 'authentication', + 'configuration', + 'database', + 'driver', + 'email', + 'file', + 'host', + 'iam', + 'intrusion_detection', + 'malware', + 'network', + 'package', + 'process', + 'registry', + 'session', + 'threat', + 'vulnerability', + 'web', + ], + }, + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.kind', + allowedValues: [ + 'alert', + 'enrichment', + 'event', + 'metric', + 'state', + 'pipeline_error', + 'signal', + ], + }, + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.outcome', + allowedValues: ['failure', 'success', 'unknown'], + }, + { + indexName: 'auditbeat-custom-index-1', + indexFieldName: 'event.type', + allowedValues: [ + 'access', + 'admin', + 'allowed', + 'change', + 'connection', + 'creation', + 'deletion', + 'denied', + 'end', + 'error', + 'group', + 'indicator', + 'info', + 'installation', + 'protocol', + 'start', + 'user', + ], + }, + ]; + + test('it includes the expected content in the `fetch` request', async () => { + const mockFetch = jest.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockUnallowedValuesResponse), + }); + global.fetch = mockFetch; + const abortController = new AbortController(); + + await fetchUnallowedValues({ + abortController, + indexName: 'auditbeat-custom-index-1', + requestItems, + }); + + expect(mockFetch).toBeCalledWith( + '/internal/ecs_data_quality_dashboard/unallowed_field_values', + { + body: JSON.stringify(requestItems), + headers: { 'Content-Type': 'application/json', 'kbn-xsrf': 'xsrf' }, + method: 'POST', + signal: abortController.signal, + } + ); + }); + + test('it returns the expected unallowed values', async () => { + const mockFetch = jest.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockUnallowedValuesResponse), + }); + global.fetch = mockFetch; + + const result = await fetchUnallowedValues({ + abortController: new AbortController(), + indexName: 'auditbeat-custom-index-1', + requestItems, + }); + + expect(result).toEqual([ + { + _shards: { failed: 0, skipped: 0, successful: 1, total: 1 }, + aggregations: { + 'event.category': { + buckets: [ + { doc_count: 2, key: 'an_invalid_category' }, + { doc_count: 1, key: 'theory' }, + ], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + }, + }, + hits: { hits: [], max_score: null, total: { relation: 'eq', value: 3 } }, + status: 200, + timed_out: false, + took: 1, + }, + { + _shards: { failed: 0, skipped: 0, successful: 1, total: 1 }, + aggregations: { + 'event.kind': { buckets: [], doc_count_error_upper_bound: 0, sum_other_doc_count: 0 }, + }, + hits: { hits: [], max_score: null, total: { relation: 'eq', value: 4 } }, + status: 200, + timed_out: false, + took: 0, + }, + { + _shards: { failed: 0, skipped: 0, successful: 1, total: 1 }, + aggregations: { + 'event.outcome': { + buckets: [], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + }, + }, + hits: { hits: [], max_score: null, total: { relation: 'eq', value: 4 } }, + status: 200, + timed_out: false, + took: 0, + }, + { + _shards: { failed: 0, skipped: 0, successful: 1, total: 1 }, + aggregations: { + 'event.type': { buckets: [], doc_count_error_upper_bound: 0, sum_other_doc_count: 0 }, + }, + hits: { hits: [], max_score: null, total: { relation: 'eq', value: 4 } }, + status: 200, + timed_out: false, + took: 0, + }, + ]); + }); + + test('it throws the expected error when fetch fails', async () => { + const error = 'simulated error'; + const mockFetch = jest.fn().mockResolvedValue({ + ok: false, + statusText: error, + }); + global.fetch = mockFetch; + + await expect( + fetchUnallowedValues({ + abortController: new AbortController(), + indexName: 'auditbeat-custom-index-1', + requestItems, + }) + ).rejects.toThrowError( + 'Error loading unallowed values for index auditbeat-custom-index-1: simulated error' + ); + }); + }); +}); diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.ts b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.ts index 1fa8991ce9264..e1ee93b72b283 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.ts +++ b/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/use_unallowed_values/helpers.ts @@ -16,6 +16,7 @@ import type { const UNALLOWED_VALUES_API_ROUTE = '/internal/ecs_data_quality_dashboard/unallowed_field_values'; export const isBucket = (maybeBucket: unknown): maybeBucket is Bucket => + maybeBucket != null && typeof (maybeBucket as Bucket).key === 'string' && typeof (maybeBucket as Bucket).doc_count === 'number'; diff --git a/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts b/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts index 105e5cc6b1d84..be1a712c92312 100644 --- a/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts +++ b/x-pack/packages/kbn-securitysolution-data-table/common/types/session_view/index.ts @@ -6,7 +6,9 @@ */ export interface SessionViewConfig { + processIndex: string; sessionEntityId: string; + sessionStartTime: string; jumpToEntityId?: string; jumpToCursor?: string; investigatedAlertId?: string; diff --git a/x-pack/packages/ml/error_utils/README.md b/x-pack/packages/ml/error_utils/README.md new file mode 100644 index 0000000000000..d8f2837dffbd3 --- /dev/null +++ b/x-pack/packages/ml/error_utils/README.md @@ -0,0 +1,3 @@ +# @kbn/ml-error-utils + +Empty package generated by @kbn/generate diff --git a/x-pack/plugins/ml/common/util/errors/index.ts b/x-pack/packages/ml/error_utils/index.ts similarity index 65% rename from x-pack/plugins/ml/common/util/errors/index.ts rename to x-pack/packages/ml/error_utils/index.ts index f6566c98490da..9eac8a4d1c70b 100644 --- a/x-pack/plugins/ml/common/util/errors/index.ts +++ b/x-pack/packages/ml/error_utils/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -export { MLRequestFailure } from './request_error'; -export { extractErrorMessage, extractErrorProperties } from './process_errors'; +export { MLRequestFailure } from './src/request_error'; +export { extractErrorMessage, extractErrorProperties } from './src/process_errors'; export type { ErrorType, ErrorMessage, @@ -14,6 +14,8 @@ export type { EsErrorRootCause, MLErrorObject, MLHttpFetchError, + MLHttpFetchErrorBase, MLResponseError, -} from './types'; -export { isBoomError, isErrorString, isEsErrorBody, isMLResponseError } from './types'; + QueryErrorMessage, +} from './src/types'; +export { isBoomError, isErrorString, isEsErrorBody, isMLResponseError } from './src/types'; diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/native_connector_icons.ts b/x-pack/packages/ml/error_utils/jest.config.js similarity index 60% rename from x-pack/plugins/enterprise_search/public/assets/source_icons/native_connector_icons.ts rename to x-pack/packages/ml/error_utils/jest.config.js index c6f210a3ec8d4..f5da401040575 100644 --- a/x-pack/plugins/enterprise_search/public/assets/source_icons/native_connector_icons.ts +++ b/x-pack/packages/ml/error_utils/jest.config.js @@ -5,10 +5,8 @@ * 2.0. */ -import mongodb from './mongodb.svg'; -import mysql from './mysql.svg'; - -export const NATIVE_CONNECTOR_ICONS: Record = { - mongodb, - mysql, +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/packages/ml/error_utils'], }; diff --git a/x-pack/packages/ml/error_utils/kibana.jsonc b/x-pack/packages/ml/error_utils/kibana.jsonc new file mode 100644 index 0000000000000..7629766aca7a7 --- /dev/null +++ b/x-pack/packages/ml/error_utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-error-utils", + "owner": "@elastic/ml-ui" +} diff --git a/x-pack/packages/ml/error_utils/package.json b/x-pack/packages/ml/error_utils/package.json new file mode 100644 index 0000000000000..9f0e6c09ef578 --- /dev/null +++ b/x-pack/packages/ml/error_utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ml-error-utils", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/plugins/ml/common/util/errors/errors.test.ts b/x-pack/packages/ml/error_utils/src/process_errors.test.ts similarity index 95% rename from x-pack/plugins/ml/common/util/errors/errors.test.ts rename to x-pack/packages/ml/error_utils/src/process_errors.test.ts index 5b1e113d06f92..5624c2f0c7477 100644 --- a/x-pack/plugins/ml/common/util/errors/errors.test.ts +++ b/x-pack/packages/ml/error_utils/src/process_errors.test.ts @@ -7,7 +7,8 @@ import Boom from '@hapi/boom'; -import { extractErrorMessage, MLHttpFetchError, EsErrorBody } from '.'; +import { extractErrorMessage } from './process_errors'; +import { type MLHttpFetchError, type EsErrorBody } from './types'; describe('ML - error message utils', () => { describe('extractErrorMessage', () => { diff --git a/x-pack/plugins/ml/common/util/errors/process_errors.ts b/x-pack/packages/ml/error_utils/src/process_errors.ts similarity index 86% rename from x-pack/plugins/ml/common/util/errors/process_errors.ts rename to x-pack/packages/ml/error_utils/src/process_errors.ts index 0da2650fa5fd6..1a50fd6ce3494 100644 --- a/x-pack/plugins/ml/common/util/errors/process_errors.ts +++ b/x-pack/packages/ml/error_utils/src/process_errors.ts @@ -16,15 +16,19 @@ import { isMLResponseError, } from './types'; +/** + * Extract properties of the error object from within the response error + * coming from Kibana, Elasticsearch, and our own ML messages. + * + * @param {ErrorType} error + * @returns {MLErrorObject} + */ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own ML messages - // some responses contain raw es errors as part of a bulk response // e.g. if some jobs fail the action in a bulk request if (isEsErrorBody(error)) { return { - message: error.error.reason, + message: error.error.reason ?? '', statusCode: error.status, fullError: error, }; @@ -79,7 +83,7 @@ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { typeof error.body.attributes.body.error.root_cause[0] === 'object' && isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; + errObj.causedBy = error.body.attributes.body.error.root_cause[0].script as string; errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; } return errObj; @@ -103,8 +107,14 @@ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { }; }; +/** + * Extract only the error message within the response error + * coming from Kibana, Elasticsearch, and our own ML messages. + * + * @param {ErrorType} error + * @returns {string} + */ export const extractErrorMessage = (error: ErrorType): string => { - // extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages const errorObj = extractErrorProperties(error); return errorObj.message; }; diff --git a/x-pack/plugins/ml/common/util/errors/request_error.ts b/x-pack/packages/ml/error_utils/src/request_error.ts similarity index 75% rename from x-pack/plugins/ml/common/util/errors/request_error.ts rename to x-pack/packages/ml/error_utils/src/request_error.ts index 57d63e6cf54b8..a370129871323 100644 --- a/x-pack/plugins/ml/common/util/errors/request_error.ts +++ b/x-pack/packages/ml/error_utils/src/request_error.ts @@ -7,7 +7,22 @@ import { MLErrorObject, ErrorType } from './types'; +/** + * ML Request Failure + * + * @export + * @class MLRequestFailure + * @typedef {MLRequestFailure} + * @extends {Error} + */ export class MLRequestFailure extends Error { + /** + * Creates an instance of MLRequestFailure. + * + * @constructor + * @param {MLErrorObject} error + * @param {ErrorType} resp + */ constructor(error: MLErrorObject, resp: ErrorType) { super(error.message); Object.setPrototypeOf(this, new.target.prototype); diff --git a/x-pack/packages/ml/error_utils/src/types.ts b/x-pack/packages/ml/error_utils/src/types.ts new file mode 100644 index 0000000000000..b66c960b8c8c0 --- /dev/null +++ b/x-pack/packages/ml/error_utils/src/types.ts @@ -0,0 +1,196 @@ +/* + * 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 Boom from '@hapi/boom'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +/** + * Short hand type of estypes.ErrorCause. + * @typedef {EsErrorRootCause} + */ +export type EsErrorRootCause = estypes.ErrorCause; + +/** + * Short hand type of estypes.ErrorResponseBase. + * @typedef {EsErrorBody} + */ +export type EsErrorBody = estypes.ErrorResponseBase; + +/** + * ML Response error + * @export + * @interface MLResponseError + * @typedef {MLResponseError} + */ +export interface MLResponseError { + /** + * statusCode + * @type {number} + */ + statusCode: number; + /** + * error + * @type {string} + */ + error: string; + /** + * message + * @type {string} + */ + message: string; + /** + * Optional attributes + * @type {?{ + body: EsErrorBody; + }} + */ + attributes?: { + body: EsErrorBody; + }; +} + +/** + * Interface holding error message + * @export + * @interface ErrorMessage + * @typedef {ErrorMessage} + */ +export interface ErrorMessage { + /** + * message + * @type {string} + */ + message: string; +} + +/** + * To be used for client side errors related to search query bars. + */ +export interface QueryErrorMessage extends ErrorMessage { + /** + * query + * @type {string} + */ + query: string; +} + +/** + * ML Error Object + * @export + * @interface MLErrorObject + * @typedef {MLErrorObject} + */ +export interface MLErrorObject { + /** + * Optional causedBy + * @type {?string} + */ + causedBy?: string; + /** + * message + * @type {string} + */ + message: string; + /** + * Optional statusCode + * @type {?number} + */ + statusCode?: number; + /** + * Optional fullError + * @type {?EsErrorBody} + */ + fullError?: EsErrorBody; +} + +/** + * MLHttpFetchErrorBase + * @export + * @interface MLHttpFetchErrorBase + * @typedef {MLHttpFetchErrorBase} + * @template T + * @extends {IHttpFetchError} + */ +export interface MLHttpFetchErrorBase extends IHttpFetchError { + /** + * body + * @type {T} + */ + body: T; +} + +/** + * MLHttpFetchError + * @export + * @typedef {MLHttpFetchError} + */ +export type MLHttpFetchError = MLHttpFetchErrorBase; + +/** + * Union type of error types + * @export + * @typedef {ErrorType} + */ +export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; + +/** + * Type guard to check if error is of type EsErrorBody + * @export + * @param {unknown} error + * @returns {error is EsErrorBody} + */ +export function isEsErrorBody(error: unknown): error is EsErrorBody { + return isPopulatedObject(error, ['error']) && isPopulatedObject(error.error, ['reason']); +} + +/** + * Type guard to check if error is a string. + * @export + * @param {unknown} error + * @returns {error is string} + */ +export function isErrorString(error: unknown): error is string { + return typeof error === 'string'; +} + +/** + * Type guard to check if error is of type ErrorMessage. + * @export + * @param {unknown} error + * @returns {error is ErrorMessage} + */ +export function isErrorMessage(error: unknown): error is ErrorMessage { + return isPopulatedObject(error, ['message']) && typeof error.message === 'string'; +} + +/** + * Type guard to check if error is of type MLResponseError. + * @export + * @param {unknown} error + * @returns {error is MLResponseError} + */ +export function isMLResponseError(error: unknown): error is MLResponseError { + return ( + isPopulatedObject(error, ['body']) && + isPopulatedObject(error.body, ['message']) && + 'message' in error.body + ); +} + +/** + * Type guard to check if error is of type Boom. + * @export + * @param {unknown} error + * @returns {error is Boom.Boom} + */ +export function isBoomError(error: unknown): error is Boom.Boom { + return isPopulatedObject(error, ['isBoom']) && error.isBoom === true; +} diff --git a/x-pack/packages/ml/error_utils/tsconfig.json b/x-pack/packages/ml/error_utils/tsconfig.json new file mode 100644 index 0000000000000..de1c550b0e1ab --- /dev/null +++ b/x-pack/packages/ml/error_utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/ml-is-populated-object", + "@kbn/core-http-browser", + ] +} diff --git a/x-pack/packages/ml/route_utils/src/create_execution_context.ts b/x-pack/packages/ml/route_utils/src/create_execution_context.ts index 2aa12d3a13a9c..aa274fb199ed3 100644 --- a/x-pack/packages/ml/route_utils/src/create_execution_context.ts +++ b/x-pack/packages/ml/route_utils/src/create_execution_context.ts @@ -6,6 +6,7 @@ */ import type { CoreStart } from '@kbn/core/server'; +import type { KibanaExecutionContext } from '@kbn/core-execution-context-common'; /** * Creates an execution context to be passed on as part of ES queries. @@ -22,7 +23,7 @@ export function createExecutionContext( name: string, id?: string, type = 'application' -) { +): KibanaExecutionContext { const labels = coreStart.executionContext.getAsLabels(); const page = labels.page as string; return { diff --git a/x-pack/packages/ml/route_utils/tsconfig.json b/x-pack/packages/ml/route_utils/tsconfig.json index ece6bfd9e321a..d7a348f37f8e8 100644 --- a/x-pack/packages/ml/route_utils/tsconfig.json +++ b/x-pack/packages/ml/route_utils/tsconfig.json @@ -17,5 +17,6 @@ ], "kbn_references": [ "@kbn/core", + "@kbn/core-execution-context-common", ] } diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector.yaml index fbf14725db7ff..b3d42c3f47484 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector.yaml @@ -8,6 +8,15 @@ post: parameters: - $ref: '../components/headers/kbn_xsrf.yaml' - $ref: '../components/parameters/space_id.yaml' + - in: path + name: id + description: > + An UUID v1 or v4 identifier for the connector. If you omit this parameter, + an identifier is randomly generated. + required: true + schema: + type: string + example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: @@ -18,7 +27,7 @@ post: oneOf: - $ref: '../components/schemas/create_connector_request_cases_webhook.yaml' - $ref: '../components/schemas/create_connector_request_email.yaml' - - $ref: '../components/schemas/create_connector_request_index.yaml' + - $ref: '../components/schemas/create_connector_request_index.yaml' - $ref: '../components/schemas/create_connector_request_jira.yaml' - $ref: '../components/schemas/create_connector_request_opsgenie.yaml' - $ref: '../components/schemas/create_connector_request_pagerduty.yaml' diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 3c4b1a876c522..e09e3adf93140 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -92,7 +92,7 @@ jest.mock('uuid', () => ({ v4: () => 'uuidv4', })); -const defaultKibanaIndex = '.kibana'; +const kibanaIndices = ['.kibana']; const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const actionExecutor = actionExecutorMock.create(); @@ -141,7 +141,7 @@ beforeEach(() => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, preconfiguredActions: [], actionExecutor, executionEnqueuer, @@ -601,7 +601,7 @@ describe('create()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, preconfiguredActions: [], actionExecutor, executionEnqueuer, @@ -691,6 +691,70 @@ describe('create()', () => { }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); + + test('throws error when predefined id match a pre-configure action id', async () => { + const preDefinedId = 'mySuperRadTestPreconfiguredId'; + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + preconfiguredActions: [ + { + id: preDefinedId, + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await expect( + actionsClient.create({ + action: { + name: 'my name', + actionTypeId: 'my-action-type', + config: {}, + secrets: {}, + }, + options: { + id: preDefinedId, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"This mySuperRadTestPreconfiguredId already exist in preconfigured action."` + ); + }); }); describe('get()', () => { @@ -719,7 +783,7 @@ describe('get()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -780,7 +844,7 @@ describe('get()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -903,7 +967,7 @@ describe('get()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -979,7 +1043,7 @@ describe('getAll()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -1122,7 +1186,7 @@ describe('getAll()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -1205,7 +1269,7 @@ describe('getBulk()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -1342,7 +1406,7 @@ describe('getBulk()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -1402,7 +1466,7 @@ describe('getOAuthAccessToken()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -2623,7 +2687,7 @@ describe('isPreconfigured()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -2662,7 +2726,7 @@ describe('isPreconfigured()', () => { actionTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, - defaultKibanaIndex, + kibanaIndices, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 867fc4f9c7e47..3efae4952692a 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -106,11 +106,12 @@ interface Action extends ActionUpdate { export interface CreateOptions { action: Action; + options?: { id?: string }; } interface ConstructorOptions { logger: Logger; - defaultKibanaIndex: string; + kibanaIndices: string[]; scopedClusterClient: IScopedClusterClient; actionTypeRegistry: ActionTypeRegistry; unsecuredSavedObjectsClient: SavedObjectsClientContract; @@ -134,7 +135,7 @@ export interface UpdateOptions { export class ActionsClient { private readonly logger: Logger; - private readonly defaultKibanaIndex: string; + private readonly kibanaIndices: string[]; private readonly scopedClusterClient: IScopedClusterClient; private readonly unsecuredSavedObjectsClient: SavedObjectsClientContract; private readonly actionTypeRegistry: ActionTypeRegistry; @@ -153,7 +154,7 @@ export class ActionsClient { constructor({ logger, actionTypeRegistry, - defaultKibanaIndex, + kibanaIndices, scopedClusterClient, unsecuredSavedObjectsClient, preconfiguredActions, @@ -172,7 +173,7 @@ export class ActionsClient { this.actionTypeRegistry = actionTypeRegistry; this.unsecuredSavedObjectsClient = unsecuredSavedObjectsClient; this.scopedClusterClient = scopedClusterClient; - this.defaultKibanaIndex = defaultKibanaIndex; + this.kibanaIndices = kibanaIndices; this.preconfiguredActions = preconfiguredActions; this.actionExecutor = actionExecutor; this.executionEnqueuer = executionEnqueuer; @@ -191,8 +192,20 @@ export class ActionsClient { */ public async create({ action: { actionTypeId, name, config, secrets }, + options, }: CreateOptions): Promise { - const id = SavedObjectsUtils.generateId(); + const id = options?.id || SavedObjectsUtils.generateId(); + + if (this.preconfiguredActions.some((preconfiguredAction) => preconfiguredAction.id === id)) { + throw Boom.badRequest( + i18n.translate('xpack.actions.serverSideErrors.predefinedIdConnectorAlreadyExists', { + defaultMessage: 'This {id} already exist in preconfigured action.', + values: { + id, + }, + }) + ); + } try { await this.authorization.ensureAuthorized('create', actionTypeId); @@ -449,11 +462,7 @@ export class ActionsClient { isDeprecated: isConnectorDeprecated(preconfiguredAction), })), ].sort((a, b) => a.name.localeCompare(b.name)); - return await injectExtraFindData( - this.defaultKibanaIndex, - this.scopedClusterClient, - mergedResult - ); + return await injectExtraFindData(this.kibanaIndices, this.scopedClusterClient, mergedResult); } /** @@ -888,7 +897,7 @@ function actionFromSavedObject( } async function injectExtraFindData( - defaultKibanaIndex: string, + kibanaIndices: string[], scopedClusterClient: IScopedClusterClient, actionResults: ActionResult[] ): Promise { @@ -927,7 +936,7 @@ async function injectExtraFindData( }; } const aggregationResult = await scopedClusterClient.asInternalUser.search({ - index: defaultKibanaIndex, + index: kibanaIndices, body: { aggs, size: 0, diff --git a/x-pack/plugins/actions/server/lib/mustache_lambdas.test.ts b/x-pack/plugins/actions/server/lib/mustache_lambdas.test.ts new file mode 100644 index 0000000000000..6f67c4dd39ea8 --- /dev/null +++ b/x-pack/plugins/actions/server/lib/mustache_lambdas.test.ts @@ -0,0 +1,201 @@ +/* + * 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 dedent from 'dedent'; + +import { renderMustacheString } from './mustache_renderer'; + +describe('mustache lambdas', () => { + describe('FormatDate', () => { + it('date with defaults is successful', () => { + const timeStamp = '2022-11-29T15:52:44Z'; + const template = dedent` + {{#FormatDate}} {{timeStamp}} {{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none')).toEqual('2022-11-29 03:52pm'); + }); + + it('date with a time zone is successful', () => { + const timeStamp = '2022-11-29T15:52:44Z'; + const template = dedent` + {{#FormatDate}} {{timeStamp}} ; America/New_York {{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none')).toEqual('2022-11-29 10:52am'); + }); + + it('date with a format is successful', () => { + const timeStamp = '2022-11-29T15:52:44Z'; + const template = dedent` + {{#FormatDate}} {{timeStamp}} ;; dddd MMM Do YYYY HH:mm:ss.SSS {{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none')).toEqual( + 'Tuesday Nov 29th 2022 15:52:44.000' + ); + }); + + it('date with a format and timezone is successful', () => { + const timeStamp = '2022-11-29T15:52:44Z'; + const template = dedent` + {{#FormatDate}} {{timeStamp}};America/New_York;dddd MMM Do YYYY HH:mm:ss.SSS {{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none').trim()).toEqual( + 'Tuesday Nov 29th 2022 10:52:44.000' + ); + }); + + it('empty date produces error', () => { + const timeStamp = ''; + const template = dedent` + {{#FormatDate}} {{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none').trim()).toEqual( + 'error rendering mustache template "{{#FormatDate}} {{/FormatDate}}": date is empty' + ); + }); + + it('invalid date produces error', () => { + const timeStamp = 'this is not a d4t3'; + const template = dedent` + {{#FormatDate}}{{timeStamp}}{{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none').trim()).toEqual( + 'error rendering mustache template "{{#FormatDate}}{{timeStamp}}{{/FormatDate}}": invalid date "this is not a d4t3"' + ); + }); + + it('invalid timezone produces error', () => { + const timeStamp = '2023-04-10T23:52:39'; + const template = dedent` + {{#FormatDate}}{{timeStamp}};NotATime Zone!{{/FormatDate}} + `.trim(); + + expect(renderMustacheString(template, { timeStamp }, 'none').trim()).toEqual( + 'error rendering mustache template "{{#FormatDate}}{{timeStamp}};NotATime Zone!{{/FormatDate}}": unknown timeZone value "NotATime Zone!"' + ); + }); + + it('invalid format produces error', () => { + const timeStamp = '2023-04-10T23:52:39'; + const template = dedent` + {{#FormatDate}}{{timeStamp}};;garbage{{/FormatDate}} + `.trim(); + + // not clear how to force an error, it pretty much does something with + // ANY string + expect(renderMustacheString(template, { timeStamp }, 'none').trim()).toEqual( + 'gamrbamg2' // a => am/pm (so am here); e => day of week + ); + }); + }); + + describe('EvalMath', () => { + it('math is successful', () => { + const vars = { + context: { + a: { b: 1 }, + c: { d: 2 }, + }, + }; + const template = dedent` + {{#EvalMath}} 1 + 0 {{/EvalMath}} + {{#EvalMath}} 1 + context.a.b {{/EvalMath}} + {{#context}} + {{#EvalMath}} 1 + c.d {{/EvalMath}} + {{/context}} + `.trim(); + + const result = renderMustacheString(template, vars, 'none'); + expect(result).toEqual(`1\n2\n3\n`); + }); + + it('invalid expression produces error', () => { + const vars = { + context: { + a: { b: 1 }, + c: { d: 2 }, + }, + }; + const template = dedent` + {{#EvalMath}} ) 1 ++++ 0 ( {{/EvalMath}} + `.trim(); + + const result = renderMustacheString(template, vars, 'none'); + expect(result).toEqual( + `error rendering mustache template "{{#EvalMath}} ) 1 ++++ 0 ( {{/EvalMath}}": error evaluating tinymath expression ") 1 ++++ 0 (": Failed to parse expression. Expected "(", function, literal, or whitespace but ")" found.` + ); + }); + }); + + describe('ParseHJson', () => { + it('valid Hjson is successful', () => { + const vars = { + context: { + a: { b: 1 }, + c: { d: 2 }, + }, + }; + const hjson = ` + { + # specify rate in requests/second (because comments are helpful!) + rate: 1000 + + a: {{context.a}} + a_b: {{context.a.b}} + c: {{context.c}} + c_d: {{context.c.d}} + + # list items can be separated by lines, or commas, and trailing + # commas permitted + list: [ + 1 2 + 3 + 4,5,6, + ] + }`; + const template = dedent` + {{#ParseHjson}} ${hjson} {{/ParseHjson}} + `.trim(); + + const result = renderMustacheString(template, vars, 'none'); + expect(JSON.parse(result)).toMatchInlineSnapshot(` + Object { + "a": Object { + "b": 1, + }, + "a_b": 1, + "c": Object { + "d": 2, + }, + "c_d": 2, + "list": Array [ + "1 2", + 3, + 4, + 5, + 6, + ], + "rate": 1000, + } + `); + }); + + it('renders an error message on parse errors', () => { + const template = dedent` + {{#ParseHjson}} [1,2,3,,] {{/ParseHjson}} + `.trim(); + + const result = renderMustacheString(template, {}, 'none'); + expect(result).toMatch(/^error rendering mustache template .*/); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/lib/mustache_lambdas.ts b/x-pack/plugins/actions/server/lib/mustache_lambdas.ts new file mode 100644 index 0000000000000..62ba5621e0e1e --- /dev/null +++ b/x-pack/plugins/actions/server/lib/mustache_lambdas.ts @@ -0,0 +1,114 @@ +/* + * 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 * as tinymath from '@kbn/tinymath'; +import { parse as hjsonParse } from 'hjson'; + +import moment from 'moment-timezone'; + +type Variables = Record; + +const DefaultDateTimeZone = 'UTC'; +const DefaultDateFormat = 'YYYY-MM-DD hh:mma'; + +export function getMustacheLambdas(): Variables { + return getLambdas(); +} + +const TimeZoneSet = new Set(moment.tz.names()); + +type RenderFn = (text: string) => string; + +function getLambdas() { + return { + EvalMath: () => + // mustache invokes lamdas with `this` set to the current "view" (variables) + function (this: Variables, text: string, render: RenderFn) { + return evalMath(this, render(text.trim())); + }, + ParseHjson: () => + function (text: string, render: RenderFn) { + return parseHjson(render(text.trim())); + }, + FormatDate: () => + function (text: string, render: RenderFn) { + const dateString = render(text.trim()).trim(); + return formatDate(dateString); + }, + }; +} + +function evalMath(vars: Variables, o: unknown): string { + const expr = `${o}`; + try { + const result = tinymath.evaluate(expr, vars); + return `${result}`; + } catch (err) { + throw new Error(`error evaluating tinymath expression "${expr}": ${err.message}`); + } +} + +function parseHjson(o: unknown): string { + const hjsonObject = `${o}`; + let object: unknown; + + try { + object = hjsonParse(hjsonObject); + } catch (err) { + throw new Error(`error parsing Hjson "${hjsonObject}": ${err.message}`); + } + + return JSON.stringify(object); +} + +function formatDate(dateString: unknown): string { + const { date, timeZone, format } = splitDateString(`${dateString}`); + + if (date === '') { + throw new Error(`date is empty`); + } + + if (isNaN(new Date(date).valueOf())) { + throw new Error(`invalid date "${date}"`); + } + + let mDate: moment.Moment; + try { + mDate = moment(date); + if (!mDate.isValid()) { + throw new Error(`date is invalid`); + } + } catch (err) { + throw new Error(`error evaluating moment date "${date}": ${err.message}`); + } + + if (!TimeZoneSet.has(timeZone)) { + throw new Error(`unknown timeZone value "${timeZone}"`); + } + + try { + mDate.tz(timeZone); + } catch (err) { + throw new Error(`error evaluating moment timeZone "${timeZone}": ${err.message}`); + } + + try { + return mDate.format(format); + } catch (err) { + throw new Error(`error evaluating moment format "${format}": ${err.message}`); + } +} + +function splitDateString(dateString: string) { + const parts = dateString.split(';', 3).map((s) => s.trim()); + const [date = '', timeZone = '', format = ''] = parts; + return { + date, + timeZone: timeZone || DefaultDateTimeZone, + format: format || DefaultDateFormat, + }; +} diff --git a/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts b/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts index 964a793d8f81c..3a02ce0d1a983 100644 --- a/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts +++ b/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts @@ -58,6 +58,12 @@ describe('mustache_renderer', () => { expect(renderMustacheString('{{f.g}}', variables, escape)).toBe('3'); expect(renderMustacheString('{{f.h}}', variables, escape)).toBe(''); expect(renderMustacheString('{{i}}', variables, escape)).toBe('42,43,44'); + + if (escape === 'markdown') { + expect(renderMustacheString('{{i.asJSON}}', variables, escape)).toBe('\\[42,43,44\\]'); + } else { + expect(renderMustacheString('{{i.asJSON}}', variables, escape)).toBe('[42,43,44]'); + } }); } @@ -339,6 +345,11 @@ describe('mustache_renderer', () => { const expected = '1 - {"c":2,"d":[3,4]} -- 5,{"f":6,"g":7}'; expect(renderMustacheString('{{a}} - {{b}} -- {{e}}', deepVariables, 'none')).toEqual(expected); + + expect(renderMustacheString('{{e}}', deepVariables, 'none')).toEqual('5,{"f":6,"g":7}'); + expect(renderMustacheString('{{e.asJSON}}', deepVariables, 'none')).toEqual( + '[5,{"f":6,"g":7}]' + ); }); describe('converting dot variables', () => { diff --git a/x-pack/plugins/actions/server/lib/mustache_renderer.ts b/x-pack/plugins/actions/server/lib/mustache_renderer.ts index fc4381fa0c9c3..37713167e9a34 100644 --- a/x-pack/plugins/actions/server/lib/mustache_renderer.ts +++ b/x-pack/plugins/actions/server/lib/mustache_renderer.ts @@ -7,8 +7,10 @@ import Mustache from 'mustache'; import { isString, isPlainObject, cloneDeepWith, merge } from 'lodash'; +import { getMustacheLambdas } from './mustache_lambdas'; export type Escape = 'markdown' | 'slack' | 'json' | 'none'; + type Variables = Record; // return a rendered mustache template with no escape given the specified variables and escape @@ -25,11 +27,13 @@ export function renderMustacheStringNoEscape(string: string, variables: Variable // return a rendered mustache template given the specified variables and escape export function renderMustacheString(string: string, variables: Variables, escape: Escape): string { const augmentedVariables = augmentObjectVariables(variables); + const lambdas = getMustacheLambdas(); + const previousMustacheEscape = Mustache.escape; Mustache.escape = getEscape(escape); try { - return Mustache.render(`${string}`, augmentedVariables); + return Mustache.render(`${string}`, { ...lambdas, ...augmentedVariables }); } catch (err) { // log error; the mustache code does not currently leak variables return `error rendering mustache template "${string}": ${err.message}`; @@ -98,6 +102,9 @@ function addToStringDeep(object: unknown): void { // walk arrays, but don't add a toString() as mustache already does something if (Array.isArray(object)) { + // instead, add an asJSON() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (object as any).asJSON = () => JSON.stringify(object); object.forEach((element) => addToStringDeep(element)); return; } diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 63bea300af7f7..128c3efe50295 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -116,13 +116,16 @@ export interface PluginSetupContract { >( actionType: ActionType ): void; + registerSubActionConnectorType< Config extends ActionTypeConfig = ActionTypeConfig, Secrets extends ActionTypeSecrets = ActionTypeSecrets >( connector: SubActionConnectorType ): void; + isPreconfiguredConnector(connectorId: string): boolean; + getSubActionConnectorClass: () => IServiceAbstract; getCaseConnectorClass: () => IServiceAbstract; getActionsHealth: () => { hasPermanentEncryptionKey: boolean }; @@ -197,7 +200,6 @@ export class ActionsPlugin implements Plugin, plugins: ActionsPluginsSetup ): PluginSetupContract { - this.kibanaIndex = core.savedObjects.getKibanaIndex(); - this.licenseState = new LicenseState(plugins.licensing.license$); this.isESOCanEncrypt = plugins.encryptedSavedObjects.canEncrypt; @@ -295,17 +295,15 @@ export class ActionsPlugin implements Plugin( 'actions', - this.createRouteHandlerContext(core, this.kibanaIndex) + this.createRouteHandlerContext(core) ); if (usageCollection) { const eventLogIndex = this.eventLogService.getIndexPattern(); - const kibanaIndex = this.kibanaIndex; initializeActionsTelemetry( this.telemetryLogger, plugins.taskManager, core, - kibanaIndex, this.preconfiguredActions, eventLogIndex ); @@ -381,7 +379,6 @@ export class ActionsPlugin implements Plugin, - defaultKibanaIndex: string + core: CoreSetup ): IContextProvider => { const { actionTypeRegistry, @@ -622,7 +618,7 @@ export class ActionsPlugin implements Plugin { const [config, handler] = router.post.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector"`); + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector/{id?}"`); const createResult = { id: '1', @@ -86,6 +86,7 @@ describe('createActionRoute', () => { "name": "My name", "secrets": Object {}, }, + "options": undefined, }, ] `); diff --git a/x-pack/plugins/actions/server/routes/create.ts b/x-pack/plugins/actions/server/routes/create.ts index 369b421de41a3..c55661ce7b921 100644 --- a/x-pack/plugins/actions/server/routes/create.ts +++ b/x-pack/plugins/actions/server/routes/create.ts @@ -50,8 +50,13 @@ export const createActionRoute = ( ) => { router.post( { - path: `${BASE_ACTION_API_PATH}/connector`, + path: `${BASE_ACTION_API_PATH}/connector/{id?}`, validate: { + params: schema.maybe( + schema.object({ + id: schema.maybe(schema.string()), + }) + ), body: bodySchema, }, }, @@ -60,7 +65,7 @@ export const createActionRoute = ( const actionsClient = (await context.actions).getActionsClient(); const action = rewriteBodyReq(req.body); return res.ok({ - body: rewriteBodyRes(await actionsClient.create({ action })), + body: rewriteBodyRes(await actionsClient.create({ action, options: req.params })), }); }) ) diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts index a110320586750..5b38062265196 100644 --- a/x-pack/plugins/actions/server/saved_objects/index.ts +++ b/x-pack/plugins/actions/server/saved_objects/index.ts @@ -12,6 +12,7 @@ import type { } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { getOldestIdleActionTask } from '@kbn/task-manager-plugin/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { actionMappings, actionTaskParamsMappings, connectorTokenMappings } from './mappings'; import { getActionsMigrations } from './actions_migrations'; import { getActionTaskParamsMigrations } from './action_task_params_migrations'; @@ -34,6 +35,7 @@ export function setupSavedObjects( ) { savedObjects.registerType({ name: ACTION_SAVED_OBJECT_TYPE, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', @@ -72,6 +74,7 @@ export function setupSavedObjects( savedObjects.registerType({ name: ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', @@ -99,6 +102,7 @@ export function setupSavedObjects( savedObjects.registerType({ name: CONNECTOR_TOKEN_SAVED_OBJECT_TYPE, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'agnostic', mappings: connectorTokenMappings, diff --git a/x-pack/plugins/actions/server/usage/task.ts b/x-pack/plugins/actions/server/usage/task.ts index cd7534e203b88..7faf443019c6b 100644 --- a/x-pack/plugins/actions/server/usage/task.ts +++ b/x-pack/plugins/actions/server/usage/task.ts @@ -24,18 +24,10 @@ export function initializeActionsTelemetry( logger: Logger, taskManager: TaskManagerSetupContract, core: CoreSetup, - kibanaIndex: string, preconfiguredActions: PreConfiguredAction[], eventLogIndex: string ) { - registerActionsTelemetryTask( - logger, - taskManager, - core, - kibanaIndex, - preconfiguredActions, - eventLogIndex - ); + registerActionsTelemetryTask(logger, taskManager, core, preconfiguredActions, eventLogIndex); } export function scheduleActionsTelemetry(logger: Logger, taskManager: TaskManagerStartContract) { @@ -46,7 +38,6 @@ function registerActionsTelemetryTask( logger: Logger, taskManager: TaskManagerSetupContract, core: CoreSetup, - kibanaIndex: string, preconfiguredActions: PreConfiguredAction[], eventLogIndex: string ) { @@ -54,13 +45,7 @@ function registerActionsTelemetryTask( [TELEMETRY_TASK_TYPE]: { title: 'Actions usage fetch task', timeout: '5m', - createTaskRunner: telemetryTaskRunner( - logger, - core, - kibanaIndex, - preconfiguredActions, - eventLogIndex - ), + createTaskRunner: telemetryTaskRunner(logger, core, preconfiguredActions, eventLogIndex), }, }); } @@ -82,7 +67,6 @@ async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContra export function telemetryTaskRunner( logger: Logger, core: CoreSetup, - kibanaIndex: string, preconfiguredActions: PreConfiguredAction[], eventLogIndex: string ) { @@ -96,12 +80,17 @@ export function telemetryTaskRunner( }, ]) => client.asInternalUser ); + const getActionIndex = () => + core + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects.getIndexForType('action')); return { async run() { + const actionIndex = await getActionIndex(); const esClient = await getEsClient(); return Promise.all([ - getTotalCount(esClient, kibanaIndex, logger, preconfiguredActions), - getInUseTotalCount(esClient, kibanaIndex, logger, undefined, preconfiguredActions), + getTotalCount(esClient, actionIndex, logger, preconfiguredActions), + getInUseTotalCount(esClient, actionIndex, logger, undefined, preconfiguredActions), getExecutionsPerDayCount(esClient, eventLogIndex, logger), ]).then(([totalAggegations, totalInUse, totalExecutionsPerDay]) => { const hasErrors = diff --git a/x-pack/plugins/actions/tsconfig.json b/x-pack/plugins/actions/tsconfig.json index 77ef11e88bfe3..8c253cb644fee 100644 --- a/x-pack/plugins/actions/tsconfig.json +++ b/x-pack/plugins/actions/tsconfig.json @@ -33,7 +33,8 @@ "@kbn/logging-mocks", "@kbn/core-elasticsearch-client-server-mocks", "@kbn/safer-lodash-set", - "@kbn/core-http-server-mocks" + "@kbn/core-http-server-mocks", + "@kbn/tinymath", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/aiops/kibana.jsonc b/x-pack/plugins/aiops/kibana.jsonc index 019c77cc155fa..8c0e5a9bccee6 100644 --- a/x-pack/plugins/aiops/kibana.jsonc +++ b/x-pack/plugins/aiops/kibana.jsonc @@ -12,6 +12,7 @@ "data", "lens", "licensing", + "uiActions", "unifiedFieldList", ], "requiredBundles": [ diff --git a/x-pack/plugins/aiops/public/application/utils/error_utils.ts b/x-pack/plugins/aiops/public/application/utils/error_utils.ts deleted file mode 100644 index f1f1c34dd2959..0000000000000 --- a/x-pack/plugins/aiops/public/application/utils/error_utils.ts +++ /dev/null @@ -1,193 +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. - */ - -// TODO Consolidate with duplicate error utils file in -// `x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts` - -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; - -export interface WrappedError { - body: { - attributes: { - body: EsErrorBody; - }; - message: Boom.Boom; - }; - statusCode: number; -} - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface AiOpsResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface AiOpsErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface AiOpsHttpFetchError extends IHttpFetchError { - body: T; -} - -export type ErrorType = - | WrappedError - | AiOpsHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isAiOpsResponseError(error: any): error is AiOpsResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} - -export function isWrappedError(error: any): error is WrappedError { - return error && isBoomError(error.body?.message) === true; -} - -export const extractErrorProperties = (error: ErrorType): AiOpsErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own AiOps messages - - // some responses contain raw es errors as part of a bulk response - // e.g. if some jobs fail the action in a bulk request - - if (isEsErrorBody(error)) { - return { - message: error.error.reason, - statusCode: error.status, - fullError: error, - }; - } - - if (isErrorString(error)) { - return { - message: error, - }; - } - if (isWrappedError(error)) { - return error.body.message?.output?.payload; - } - - if (isBoomError(error)) { - return { - message: error.output.payload.message, - statusCode: error.output.payload.statusCode, - }; - } - - if (error?.body === undefined && !error?.message) { - return { - message: '', - }; - } - - if (typeof error.body === 'string') { - return { - message: error.body, - }; - } - - if (isAiOpsResponseError(error)) { - if ( - typeof error.body.attributes === 'object' && - typeof error.body.attributes.body?.error?.reason === 'string' - ) { - const errObj: AiOpsErrorObject = { - message: error.body.attributes.body.error.reason, - statusCode: error.body.statusCode, - fullError: error.body.attributes.body, - }; - if ( - typeof error.body.attributes.body.error.caused_by === 'object' && - (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || - typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') - ) { - errObj.causedBy = - error.body.attributes.body.error.caused_by?.caused_by?.reason || - error.body.attributes.body.error.caused_by?.reason; - } - if ( - Array.isArray(error.body.attributes.body.error.root_cause) && - typeof error.body.attributes.body.error.root_cause[0] === 'object' && - isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) - ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; - errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; - } - return errObj; - } else { - return { - message: error.body.message, - statusCode: error.body.statusCode, - }; - } - } - - if (isErrorMessage(error)) { - return { - message: error.message, - }; - } - - // If all else fail return an empty message instead of JSON.stringify - return { - message: '', - }; -}; - -export const extractErrorMessage = (error: ErrorType): string => { - // extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages - const errorObj = extractErrorProperties(error); - return errorObj.message; -}; diff --git a/x-pack/plugins/aiops/public/application/utils/url_state.ts b/x-pack/plugins/aiops/public/application/utils/url_state.ts new file mode 100644 index 0000000000000..9fdaa443f4c75 --- /dev/null +++ b/x-pack/plugins/aiops/public/application/utils/url_state.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { Filter, Query } from '@kbn/es-query'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage } from './search_utils'; + +const defaultSearchQuery = { + match_all: {}, +}; + +export interface AiOpsPageUrlState { + pageKey: 'AIOPS_INDEX_VIEWER'; + pageUrlState: AiOpsIndexBasedAppState; +} + +export interface AiOpsIndexBasedAppState { + searchString?: Query['query']; + searchQuery?: estypes.QueryDslQueryContainer; + searchQueryLanguage: SearchQueryLanguage; + filters?: Filter[]; +} + +export type AiOpsFullIndexBasedAppState = Required; + +export const getDefaultAiOpsListState = ( + overrides?: Partial +): AiOpsFullIndexBasedAppState => ({ + searchString: '', + searchQuery: defaultSearchQuery, + searchQueryLanguage: SEARCH_QUERY_LANGUAGE.KUERY, + filters: [], + ...overrides, +}); + +export const isFullAiOpsListState = (arg: unknown): arg is AiOpsFullIndexBasedAppState => { + return isPopulatedObject(arg, Object.keys(getDefaultAiOpsListState())); +}; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx index 28ff26ad7645a..dd054b88076ec 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_points_table.tsx @@ -12,11 +12,14 @@ import { EuiIcon, EuiInMemoryTable, EuiToolTip, + type DefaultItemAction, } from '@elastic/eui'; import React, { type FC, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; +import { type Filter, FilterStateStore } from '@kbn/es-query'; +import { useDataSource } from '../../hooks/use_data_source'; import { useCommonChartProps } from './use_common_chart_props'; import { type ChangePointAnnotation, @@ -33,13 +36,49 @@ export interface ChangePointsTableProps { onSelectionChange: (update: SelectedChangePoint[]) => void; } +function getFilterConfig( + index: string, + item: Required, + negate: boolean +): Filter { + return { + meta: { + disabled: false, + negate, + alias: null, + index, + key: `${item.group.name}_${item.group.value}`, + // @ts-ignore FilterMeta type definition misses the field property + field: item.group.name, + params: { + query: item.group.value, + }, + type: 'phrase', + }, + query: { + match_phrase: { + [item.group.name]: item.group.value, + }, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, + }; +} + export const ChangePointsTable: FC = ({ isLoading, annotations, fieldConfig, onSelectionChange, }) => { - const { fieldFormats } = useAiopsAppContext(); + const { + fieldFormats, + data: { + query: { filterManager }, + }, + } = useAiopsAppContext(); + const { dataView } = useDataSource(); const dateFormatter = useMemo(() => fieldFormats.deserialize({ id: 'date' }), [fieldFormats]); @@ -51,6 +90,8 @@ export const ChangePointsTable: FC = ({ }, }; + const hasActions = fieldConfig.splitField !== undefined; + const columns: Array> = [ { field: 'timestamp', @@ -126,6 +167,61 @@ export const ChangePointsTable: FC = ({ truncateText: false, sortable: true, }, + { + name: i18n.translate('xpack.aiops.changePointDetection.actionsColumn', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: i18n.translate( + 'xpack.aiops.changePointDetection.actions.filterForValueAction', + { + defaultMessage: 'Filter for value', + } + ), + description: i18n.translate( + 'xpack.aiops.changePointDetection.actions.filterForValueAction', + { + defaultMessage: 'Filter for value', + } + ), + icon: 'plusInCircle', + color: 'primary', + type: 'icon', + onClick: (item) => { + filterManager.addFilters( + getFilterConfig(dataView.id!, item as Required, false)! + ); + }, + isPrimary: true, + 'data-test-subj': 'aiopsChangePointFilterForValue', + }, + { + name: i18n.translate( + 'xpack.aiops.changePointDetection.actions.filterOutValueAction', + { + defaultMessage: 'Filter out value', + } + ), + description: i18n.translate( + 'xpack.aiops.changePointDetection.actions.filterOutValueAction', + { + defaultMessage: 'Filter out value', + } + ), + icon: 'minusInCircle', + color: 'primary', + type: 'icon', + onClick: (item) => { + filterManager.addFilters( + getFilterConfig(dataView.id!, item as Required, true)! + ); + }, + isPrimary: true, + 'data-test-subj': 'aiopsChangePointFilterForValue', + }, + ] as Array>, + }, ] : []), ]; @@ -155,6 +251,7 @@ export const ChangePointsTable: FC = ({ columns={columns} pagination={{ pageSizeOptions: [5, 10, 15] }} sorting={defaultSorting} + hasActions={hasActions} message={ isLoading ? ( = ({ const splitFieldCardinality = useSplitFieldCardinality(fieldConfig.splitField, combinedQuery); + const [isExpanded, setIsExpanded] = useState(true); + const { results: annotations, isLoading: annotationsLoading, progress, } = useChangePointResults(fieldConfig, requestParams, combinedQuery, splitFieldCardinality); - const accordionId = useGeneratedHtmlId({ prefix: 'fieldConfig' }); - return ( - - - - } - value={progress ?? 0} - max={100} - valueText - size="m" + + + + + !prevState)} + aria-label={i18n.translate('xpack.aiops.changePointDetection.expandConfigLabel', { + defaultMessage: 'Expand configuration', + })} /> - - - } - extraAction={ + + + + + } + value={progress ?? 0} + max={100} + valueText + size="m" + /> + + + + + + + + - } - paddingSize="s" - > + + + + {isExpanded ? ( = ({ splitFieldCardinality={splitFieldCardinality} onSelectionChange={onSelectionChange} /> - + ) : null} ); }; @@ -205,7 +224,25 @@ interface FieldsControlsProps { * Renders controls for fields selection and emits updates on change. */ export const FieldsControls: FC = ({ fieldConfig, onChange, children }) => { - const { splitFieldsOptions } = useChangePointDetectionContext(); + const { splitFieldsOptions, combinedQuery } = useChangePointDetectionContext(); + const { dataView } = useDataSource(); + const { data, uiSettings, fieldFormats, charts, fieldStats } = useAiopsAppContext(); + const timefilter = useTimefilter(); + // required in order to trigger state updates + useTimeRangeUpdates(); + const timefilterActiveBounds = timefilter.getActiveBounds(); + + const fieldStatsServices: FieldStatsServices = useMemo(() => { + return { + uiSettings, + dataViews: data.dataViews, + data, + fieldFormats, + charts, + }; + }, [uiSettings, data, fieldFormats, charts]); + + const FieldStatsFlyoutProvider = fieldStats!.FieldStatsFlyoutProvider; const onChangeFn = useCallback( (field: keyof FieldConfig, value: string) => { @@ -216,27 +253,41 @@ export const FieldsControls: FC = ({ fieldConfig, onChange, ); return ( - - - onChangeFn('fn', v)} /> - - - onChangeFn('metricField', v)} - /> - - {splitFieldsOptions.length > 0 ? ( - - onChangeFn('splitField', v!)} + + + + onChangeFn('fn', v)} /> + + + onChangeFn('metricField', v)} /> - ) : null} + {splitFieldsOptions.length > 0 ? ( + + onChangeFn('splitField', v!)} + /> + + ) : null} - {children} - + {children} + + ); }; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/function_picker.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/function_picker.tsx index 2e86961b80432..a24e71ac1cc9b 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/function_picker.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/function_picker.tsx @@ -33,7 +33,6 @@ export const FunctionPicker: FC = React.memo(({ value, onCh onChange={(id) => onChange(id)} isFullWidth buttonSize="compressed" - onClick={(e) => e.stopPropagation()} /> ); }); diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/metric_field_selector.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/metric_field_selector.tsx index 266e69b8d6f11..3b0d0017fb499 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/metric_field_selector.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/metric_field_selector.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import React, { FC, useCallback, useMemo } from 'react'; +import React, { type FC, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiComboBox, type EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; import { useChangePointDetectionContext } from './change_point_detection_context'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; interface MetricFieldSelectorProps { value: string; @@ -17,10 +18,19 @@ interface MetricFieldSelectorProps { export const MetricFieldSelector: FC = React.memo( ({ value, onChange }) => { + const { fieldStats } = useAiopsAppContext(); const { metricFieldOptions } = useChangePointDetectionContext(); + const { renderOption, closeFlyout } = fieldStats!.useFieldStatsTrigger(); + const options = useMemo(() => { - return metricFieldOptions.map((v) => ({ value: v.name, label: v.displayName })); + return metricFieldOptions.map((v) => { + return { + value: v.name, + label: v.displayName, + field: { id: v.name, type: v.type }, + }; + }); }, [metricFieldOptions]); const selection = options.filter((v) => v.value === value); @@ -29,28 +39,32 @@ export const MetricFieldSelector: FC = React.memo( (selectedOptions: EuiComboBoxOptionOption[]) => { const option = selectedOptions[0]; if (typeof option !== 'undefined') { - onChange(option.label); + onChange(option.value as string); } + closeFlyout(); }, - [onChange] + [onChange, closeFlyout] ); return ( - - e.stopPropagation()} - /> - + <> + + + + ); } ); diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/split_field_selector.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/split_field_selector.tsx index cc52a0cc5ee37..6d8540565015d 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/split_field_selector.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/split_field_selector.tsx @@ -8,6 +8,7 @@ import React, { FC, useMemo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiComboBox, type EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { useChangePointDetectionContext } from './change_point_detection_context'; interface SplitFieldSelectorProps { @@ -16,32 +17,37 @@ interface SplitFieldSelectorProps { } export const SplitFieldSelector: FC = React.memo(({ value, onChange }) => { + const { fieldStats } = useAiopsAppContext(); + const { renderOption, closeFlyout } = fieldStats!.useFieldStatsTrigger(); + const { splitFieldsOptions } = useChangePointDetectionContext(); - const options = useMemo>>(() => { + const options = useMemo(() => { return [ { - name: undefined, - displayName: i18n.translate('xpack.aiops.changePointDetection.notSelectedSplitFieldLabel', { + value: undefined, + label: i18n.translate('xpack.aiops.changePointDetection.notSelectedSplitFieldLabel', { defaultMessage: '--- Not selected ---', }), }, - ...splitFieldsOptions, - ].map((v) => ({ - value: v.name, - label: v.displayName, - })); + ...splitFieldsOptions.map((v) => ({ + value: v.name, + label: v.displayName, + ...(v.name ? { field: { id: v.name, type: v?.type } } : {}), + })), + ]; }, [splitFieldsOptions]); const selection = options.filter((v) => v.value === value); const onChangeCallback = useCallback( - (selectedOptions: Array>) => { + (selectedOptions: EuiComboBoxOptionOption[]) => { const option = selectedOptions[0]; - const newValue = option?.value; + const newValue = option?.value as string; onChange(newValue); + closeFlyout(); }, - [onChange] + [onChange, closeFlyout] ); return ( @@ -57,7 +63,8 @@ export const SplitFieldSelector: FC = React.memo(({ val onChange={onChangeCallback} isClearable data-test-subj="aiopsChangePointSplitField" - onClick={(e) => e.stopPropagation()} + // @ts-ignore + renderOption={renderOption} /> ); diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx index ce69ea9fea3ae..b30e3a7d1e6fb 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_app_state.tsx @@ -10,7 +10,6 @@ import { pick } from 'lodash'; import { EuiCallOut } from '@elastic/eui'; -import type { Filter, Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import type { SavedSearch } from '@kbn/discover-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; @@ -21,7 +20,6 @@ import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; -import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage } from '../../application/utils/search_utils'; import type { AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; import { AiopsAppContext } from '../../hooks/use_aiops_app_context'; import { DataSourceContext } from '../../hooks/use_data_source'; @@ -42,34 +40,6 @@ export interface ExplainLogRateSpikesAppStateProps { appDependencies: AiopsAppDependencies; } -const defaultSearchQuery = { - match_all: {}, -}; - -export interface AiOpsPageUrlState { - pageKey: 'AIOPS_INDEX_VIEWER'; - pageUrlState: AiOpsIndexBasedAppState; -} - -export interface AiOpsIndexBasedAppState { - searchString?: Query['query']; - searchQuery?: Query['query']; - searchQueryLanguage: SearchQueryLanguage; - filters?: Filter[]; -} - -export const getDefaultAiOpsListState = ( - overrides?: Partial -): Required => ({ - searchString: '', - searchQuery: defaultSearchQuery, - searchQueryLanguage: SEARCH_QUERY_LANGUAGE.KUERY, - filters: [], - ...overrides, -}); - -export const restorableDefaults = getDefaultAiOpsListState(); - export const ExplainLogRateSpikesAppState: FC = ({ dataView, savedSearch, diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx index 86082d64e0e27..1feb8548a23aa 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_page.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useEffect, useState, FC } from 'react'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiEmptyPrompt, EuiFlexGroup, @@ -28,6 +29,10 @@ import { useDataSource } from '../../hooks/use_data_source'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { SearchQueryLanguage } from '../../application/utils/search_utils'; import { useData } from '../../hooks/use_data'; +import { + getDefaultAiOpsListState, + type AiOpsPageUrlState, +} from '../../application/utils/url_state'; import { DocumentCountContent } from '../document_count_content/document_count_content'; import { SearchPanel } from '../search_panel'; @@ -35,7 +40,6 @@ import type { GroupTableItem } from '../spike_analysis_table/types'; import { useSpikeAnalysisTableRowContext } from '../spike_analysis_table/spike_analysis_table_row_provider'; import { PageHeader } from '../page_header'; -import { restorableDefaults, type AiOpsPageUrlState } from './explain_log_rate_spikes_app_state'; import { ExplainLogRateSpikesAnalysis } from './explain_log_rate_spikes_analysis'; function getDocumentCountStatsSplitLabel( @@ -66,7 +70,7 @@ export const ExplainLogRateSpikesPage: FC = () => { const [aiopsListState, setAiopsListState] = usePageUrlState( 'AIOPS_INDEX_VIEWER', - restorableDefaults + getDefaultAiOpsListState() ); const [globalState, setGlobalState] = useUrlState('_g'); @@ -80,7 +84,7 @@ export const ExplainLogRateSpikesPage: FC = () => { const setSearchParams = useCallback( (searchParams: { - searchQuery: Query['query']; + searchQuery: estypes.QueryDslQueryContainer; searchString: Query['query']; queryLanguage: SearchQueryLanguage; filters: Filter[]; @@ -112,6 +116,7 @@ export const ExplainLogRateSpikesPage: FC = () => { searchQuery, } = useData( { selectedDataView: dataView, selectedSavedSearch }, + 'explain_log_rage_spikes', aiopsListState, setGlobalState, currentSelectedSignificantTerm, diff --git a/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.ts b/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.ts new file mode 100644 index 0000000000000..b915626ade72c --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.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. + */ + +import { i18n } from '@kbn/i18n'; +import { + createAction, + ACTION_CATEGORIZE_FIELD, + type CategorizeFieldContext, +} from '@kbn/ui-actions-plugin/public'; +import type { CoreStart } from '@kbn/core/public'; +import { AiopsPluginStartDeps } from '../../types'; +import { showCategorizeFlyout } from './show_flyout'; + +export const categorizeFieldAction = (coreStart: CoreStart, plugins: AiopsPluginStartDeps) => + createAction({ + type: ACTION_CATEGORIZE_FIELD, + id: ACTION_CATEGORIZE_FIELD, + getDisplayName: () => + i18n.translate('xpack.aiops.categorizeFieldAction.displayName', { + defaultMessage: 'Categorize field', + }), + isCompatible: async ({ field }: CategorizeFieldContext) => { + return field.esTypes?.includes('text') === true; + }, + execute: async (context: CategorizeFieldContext) => { + const { field, dataView } = context; + showCategorizeFlyout(field, dataView, coreStart, plugins); + }, + }); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index c888694a7b0c3..6ee9af6beb405 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -5,48 +5,46 @@ * 2.0. */ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import type { TimefilterContract } from '@kbn/data-plugin/public'; import { useEuiBackgroundColor, - EuiButton, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, EuiInMemoryTable, EuiBasicTableColumn, EuiCode, EuiText, EuiTableSelectionType, + EuiHorizontalRule, + EuiSpacer, } from '@elastic/eui'; -import { useDiscoverLinks } from '../use_discover_links'; +import { DataViewField } from '@kbn/data-views-plugin/common'; +import { Filter } from '@kbn/es-query'; +import { useDiscoverLinks, createFilter, QueryMode, QUERY_MODE } from '../use_discover_links'; import { MiniHistogram } from '../../mini_histogram'; import { useEuiTheme } from '../../../hooks/use_eui_theme'; -import type { AiOpsIndexBasedAppState } from '../../explain_log_rate_spikes/explain_log_rate_spikes_app_state'; +import type { AiOpsFullIndexBasedAppState } from '../../../application/utils/url_state'; import type { EventRate, Category, SparkLinesPerCategory } from '../use_categorize_request'; import { useTableState } from './use_table_state'; - -const QUERY_MODE = { - INCLUDE: 'should', - EXCLUDE: 'must_not', -} as const; -export type QueryMode = typeof QUERY_MODE[keyof typeof QUERY_MODE]; +import { getLabels } from './labels'; +import { TableHeader } from './table_header'; interface Props { categories: Category[]; sparkLines: SparkLinesPerCategory; eventRate: EventRate; dataViewId: string; - selectedField: string | undefined; + selectedField: DataViewField | string | undefined; timefilter: TimefilterContract; - aiopsListState: Required; + aiopsListState: AiOpsFullIndexBasedAppState; pinnedCategory: Category | null; setPinnedCategory: (category: Category | null) => void; selectedCategory: Category | null; setSelectedCategory: (category: Category | null) => void; + onAddFilter?: (values: Filter, alias?: string) => void; + onClose?: () => void; + enableRowActions?: boolean; } export const CategoryTable: FC = ({ @@ -61,6 +59,9 @@ export const CategoryTable: FC = ({ setPinnedCategory, selectedCategory, setSelectedCategory, + onAddFilter, + onClose = () => {}, + enableRowActions = true, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); @@ -68,7 +69,25 @@ export const CategoryTable: FC = ({ const [selectedCategories, setSelectedCategories] = useState([]); const { onTableChange, pagination, sorting } = useTableState(categories ?? [], 'key'); + const labels = useMemo( + () => getLabels(onAddFilter !== undefined && onClose !== undefined), + [onAddFilter, onClose] + ); + const openInDiscover = (mode: QueryMode, category?: Category) => { + if ( + onAddFilter !== undefined && + selectedField !== undefined && + typeof selectedField !== 'string' + ) { + onAddFilter( + createFilter('', selectedField.name, selectedCategories, mode, category), + `Patterns - ${selectedField.name}` + ); + onClose(); + return; + } + const timefilterActiveBounds = timefilter.getActiveBounds(); if (timefilterActiveBounds === undefined || selectedField === undefined) { return; @@ -76,7 +95,7 @@ export const CategoryTable: FC = ({ openInDiscoverWithFilter( dataViewId, - selectedField, + typeof selectedField === 'string' ? selectedField : selectedField.name, selectedCategories, aiopsListState, timefilterActiveBounds, @@ -99,7 +118,7 @@ export const CategoryTable: FC = ({ name: i18n.translate('xpack.aiops.logCategorization.column.logRate', { defaultMessage: 'Log rate', }), - sortable: true, + sortable: false, width: '100px', render: (_, { key }) => { const sparkLine = sparkLines[key]; @@ -128,57 +147,39 @@ export const CategoryTable: FC = ({ defaultMessage: 'Examples', }), sortable: true, - style: { display: 'block' }, render: (examples: string[]) => ( -
    + <> {examples.map((e) => ( - <> - - - {e} - - - - + + + {e} + + ))} -
    + ), }, { - name: 'Actions', + name: i18n.translate('xpack.aiops.logCategorization.column.actions', { + defaultMessage: 'Actions', + }), + sortable: false, width: '60px', actions: [ { - name: i18n.translate('xpack.aiops.logCategorization.showInDiscover', { - defaultMessage: 'Show these in Discover', - }), - description: i18n.translate('xpack.aiops.logCategorization.showInDiscover', { - defaultMessage: 'Show these in Discover', - }), - icon: 'discoverApp', + name: labels.singleSelect.in, + description: labels.singleSelect.in, + icon: 'plusInCircle', type: 'icon', onClick: (category) => openInDiscover(QUERY_MODE.INCLUDE, category), }, { - name: i18n.translate('xpack.aiops.logCategorization.filterOutInDiscover', { - defaultMessage: 'Filter out in Discover', - }), - description: i18n.translate('xpack.aiops.logCategorization.filterOutInDiscover', { - defaultMessage: 'Filter out in Discover', - }), - icon: 'filter', + name: labels.singleSelect.out, + description: labels.singleSelect.out, + icon: 'minusInCircle', type: 'icon', onClick: (category) => openInDiscover(QUERY_MODE.EXCLUDE, category), }, - // Disabled for now - // { - // name: i18n.translate('xpack.aiops.logCategorization.openInDataViz', { - // defaultMessage: 'Open in data visualizer', - // }), - // icon: 'stats', - // type: 'icon', - // onClick: () => {}, - // }, ], }, ] as Array>; @@ -212,28 +213,15 @@ export const CategoryTable: FC = ({ return ( <> - {selectedCategories.length > 0 ? ( - <> - - - openInDiscover(QUERY_MODE.INCLUDE)}> - - - - - openInDiscover(QUERY_MODE.EXCLUDE)}> - - - - - - ) : null} + openInDiscover(queryMode)} + /> + + + compressed items={categories} @@ -245,22 +233,24 @@ export const CategoryTable: FC = ({ pagination={pagination} sorting={sorting} rowProps={(category) => { - return { - onClick: () => { - if (category.key === pinnedCategory?.key) { - setPinnedCategory(null); - } else { - setPinnedCategory(category); + return enableRowActions + ? { + onClick: () => { + if (category.key === pinnedCategory?.key) { + setPinnedCategory(null); + } else { + setPinnedCategory(category); + } + }, + onMouseEnter: () => { + setSelectedCategory(category); + }, + onMouseLeave: () => { + setSelectedCategory(null); + }, + style: getRowStyle(category), } - }, - onMouseEnter: () => { - setSelectedCategory(category); - }, - onMouseLeave: () => { - setSelectedCategory(null); - }, - style: getRowStyle(category), - }; + : undefined; }} /> diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/index.ts b/x-pack/plugins/aiops/public/components/log_categorization/category_table/index.ts index 65b93b2c1eb44..6fef3e174dfc6 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/index.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/index.ts @@ -6,4 +6,3 @@ */ export { CategoryTable } from './category_table'; -export type { QueryMode } from './category_table'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/labels.ts b/x-pack/plugins/aiops/public/components/log_categorization/category_table/labels.ts new file mode 100644 index 0000000000000..674f7b8da0b60 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/labels.ts @@ -0,0 +1,62 @@ +/* + * 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 function getLabels(isFlyout: boolean) { + const flyoutFilterIn = (single: boolean) => + i18n.translate('xpack.aiops.logCategorization.flyout.filterIn', { + defaultMessage: 'Filter for {values, plural, one {pattern} other {patterns}}', + values: { + values: single ? 1 : 2, + }, + }); + const flyoutFilterOut = (single: boolean) => + i18n.translate('xpack.aiops.logCategorization.flyout.filterOut', { + defaultMessage: 'Filter out {values, plural, one {pattern} other {patterns}}', + values: { + values: single ? 1 : 2, + }, + }); + + const aiopsFilterIn = (single: boolean) => + i18n.translate('xpack.aiops.logCategorization.filterIn', { + defaultMessage: 'Filter for {values, plural, one {pattern} other {patterns}} in Discover', + values: { + values: single ? 1 : 2, + }, + }); + const aiopsFilterOut = (single: boolean) => + i18n.translate('xpack.aiops.logCategorization.filterOut', { + defaultMessage: 'Filter out {values, plural, one {pattern} other {patterns}} in Discover', + values: { + values: single ? 1 : 2, + }, + }); + + return isFlyout + ? { + multiSelect: { + in: flyoutFilterIn(false), + out: flyoutFilterOut(false), + }, + singleSelect: { + in: flyoutFilterIn(true), + out: flyoutFilterOut(true), + }, + } + : { + multiSelect: { + in: aiopsFilterIn(false), + out: aiopsFilterOut(false), + }, + singleSelect: { + in: aiopsFilterIn(true), + out: aiopsFilterOut(true), + }, + }; +} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx new file mode 100644 index 0000000000000..735f7fd5630bd --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useEuiTheme } from '../../../hooks/use_eui_theme'; +import { getLabels } from './labels'; +import { QueryMode, QUERY_MODE } from '../use_discover_links'; + +interface Props { + categoriesCount: number; + selectedCategoriesCount: number; + labels: ReturnType; + openInDiscover: (mode: QueryMode) => void; +} + +export const TableHeader: FC = ({ + categoriesCount, + selectedCategoriesCount, + labels, + openInDiscover, +}) => { + const euiTheme = useEuiTheme(); + return ( + <> + + + + + {selectedCategoriesCount > 0 ? ( + <> + + + ) : null} + + + {selectedCategoriesCount > 0 ? ( + <> + + openInDiscover(QUERY_MODE.INCLUDE)} + iconType="plusInCircle" + iconSide="left" + > + {labels.multiSelect.in} + + + + openInDiscover(QUERY_MODE.EXCLUDE)} + iconType="minusInCircle" + iconSide="left" + > + {labels.multiSelect.out} + + + + ) : null} + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/document_count_chart.tsx b/x-pack/plugins/aiops/public/components/log_categorization/document_count_chart.tsx index 4ec9ddd68209e..7e02eabf01dbc 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/document_count_chart.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/document_count_chart.tsx @@ -34,7 +34,7 @@ export const DocumentCountChart: FC = ({ const chartPointsSplitLabel = i18n.translate( 'xpack.aiops.logCategorization.chartPointsSplitLabel', { - defaultMessage: 'Selected category', + defaultMessage: 'Selected pattern', } ); const chartPoints = useMemo(() => { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/index.ts b/x-pack/plugins/aiops/public/components/log_categorization/index.ts index 748a0f8486420..550add8802747 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/index.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/index.ts @@ -7,6 +7,7 @@ export type { LogCategorizationAppStateProps } from './log_categorization_app_state'; import { LogCategorizationAppState } from './log_categorization_app_state'; +export { categorizeFieldAction } from './categorize_field_actions'; // required for dynamic import using React.lazy() // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx b/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx index bf80b55fee37c..3707bb810dcb5 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx @@ -66,7 +66,7 @@ export const InformationText: FC = ({

    } @@ -80,7 +80,7 @@ export const InformationText: FC = ({

    } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx b/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx new file mode 100644 index 0000000000000..77ba11fd46cc5 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.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, { FC } from 'react'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingElastic, + EuiText, + EuiFlexGrid, + EuiSpacer, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface Props { + onClose: () => void; +} + +export const LoadingCategorization: FC = ({ onClose }) => ( + <> + + + + + + + + + +

    + +

    +
    +
    + + + + onClose()}>Cancel + + + +
    +
    +
    + +); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx new file mode 100644 index 0000000000000..176381f8b92de --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx @@ -0,0 +1,264 @@ +/* + * 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, { FC, useState, useEffect, useCallback, useRef } from 'react'; +import type { SavedSearch } from '@kbn/discover-plugin/public'; +import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiTitle, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiFlexGroup, + EuiFlexItem, + useEuiTheme, +} from '@elastic/eui'; + +import { buildEmptyFilter, Filter } from '@kbn/es-query'; + +import { usePageUrlState } from '@kbn/ml-url-state'; +import { useData } from '../../hooks/use_data'; +import { useCategorizeRequest } from './use_categorize_request'; +import type { EventRate, Category, SparkLinesPerCategory } from './use_categorize_request'; +import { CategoryTable } from './category_table'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { InformationText } from './information_text'; +import { createMergedEsQuery } from '../../application/utils/search_utils'; +import { SamplingMenu } from './sampling_menu'; +import { TechnicalPreviewBadge } from './technical_preview_badge'; +import { LoadingCategorization } from './loading_categorization'; +import { + type AiOpsPageUrlState, + getDefaultAiOpsListState, + isFullAiOpsListState, +} from '../../application/utils/url_state'; + +export interface LogCategorizationPageProps { + dataView: DataView; + savedSearch: SavedSearch | null; + selectedField: DataViewField; + onClose: () => void; +} + +const BAR_TARGET = 20; + +export const LogCategorizationFlyout: FC = ({ + dataView, + savedSearch, + selectedField, + onClose, +}) => { + const { + notifications: { toasts }, + data: { + query: { getState, filterManager }, + }, + uiSettings, + } = useAiopsAppContext(); + const { euiTheme } = useEuiTheme(); + + const mounted = useRef(false); + const { runCategorizeRequest, cancelRequest, randomSampler } = useCategorizeRequest(); + const [aiopsListState, setAiopsListState] = usePageUrlState( + 'AIOPS_INDEX_VIEWER', + getDefaultAiOpsListState() + ); + const [selectedCategory, setSelectedCategory] = useState(null); + const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch); + const [loading, setLoading] = useState(true); + const [eventRate, setEventRate] = useState([]); + const [pinnedCategory, setPinnedCategory] = useState(null); + const [data, setData] = useState<{ + categories: Category[]; + sparkLines: SparkLinesPerCategory; + } | null>(null); + + useEffect( + function cancelRequestOnLeave() { + mounted.current = true; + return () => { + mounted.current = false; + cancelRequest(); + }; + }, + [cancelRequest, mounted] + ); + + const { + documentStats, + timefilter, + earliest, + latest, + searchQueryLanguage, + searchString, + searchQuery, + intervalMs, + forceRefresh, + } = useData( + { selectedDataView: dataView, selectedSavedSearch }, + 'log_categorization', + aiopsListState, + undefined, + undefined, + undefined, + BAR_TARGET, + true + ); + + useEffect(() => { + const { filters, query } = getState(); + + setAiopsListState({ + ...aiopsListState, + searchQuery: createMergedEsQuery(query, filters, dataView, uiSettings), + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const loadCategories = useCallback(async () => { + const { title: index, timeFieldName: timeField } = dataView; + + if (selectedField === undefined || timeField === undefined) { + return; + } + + cancelRequest(); + + setLoading(true); + setData(null); + + try { + const { categories, sparkLinesPerCategory: sparkLines } = await runCategorizeRequest( + index, + selectedField.name, + timeField, + earliest, + latest, + searchQuery, + intervalMs + ); + + if (mounted.current === true) { + setData({ categories, sparkLines }); + } + } catch (error) { + toasts.addError(error, { + title: i18n.translate('xpack.aiops.logCategorization.errorLoadingCategories', { + defaultMessage: 'Error loading categories', + }), + }); + } + + if (mounted.current === true) { + setLoading(false); + } + }, [ + dataView, + selectedField, + cancelRequest, + runCategorizeRequest, + earliest, + latest, + searchQuery, + intervalMs, + toasts, + ]); + + const onAddFilter = useCallback( + (values: Filter, alias?: string) => { + const filter = buildEmptyFilter(false, dataView.id); + if (alias) { + filter.meta.alias = alias; + } + filter.query = values.query; + filterManager.addFilters([filter]); + }, + [dataView, filterManager] + ); + + useEffect(() => { + if (documentStats.documentCountStats?.buckets) { + randomSampler.setDocCount(documentStats.totalCount); + setEventRate( + Object.entries(documentStats.documentCountStats.buckets).map(([key, docCount]) => ({ + key: +key, + docCount, + })) + ); + setData(null); + loadCategories(); + } + }, [ + documentStats, + earliest, + latest, + searchQueryLanguage, + searchString, + searchQuery, + loadCategories, + randomSampler, + ]); + + return ( + <> + + + + +

    + +

    +
    +
    + + + + + + forceRefresh()} /> + +
    +
    + + {loading === true ? : null} + + + + {loading === false && + data !== null && + data.categories.length > 0 && + isFullAiOpsListState(aiopsListState) ? ( + + ) : null} + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index feb6ef9c9b31c..e4653d089e5a6 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -4,8 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React, { FC, useState, useEffect, useCallback, useMemo } from 'react'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiButton, EuiSpacer, @@ -21,14 +23,18 @@ import { import { Filter, Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useUrlState } from '@kbn/ml-url-state'; +import { usePageUrlState, useUrlState } from '@kbn/ml-url-state'; import { useDataSource } from '../../hooks/use_data_source'; import { useData } from '../../hooks/use_data'; import type { SearchQueryLanguage } from '../../application/utils/search_utils'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { + getDefaultAiOpsListState, + isFullAiOpsListState, + type AiOpsPageUrlState, +} from '../../application/utils/url_state'; -import { restorableDefaults } from '../explain_log_rate_spikes/explain_log_rate_spikes_app_state'; import { SearchPanel } from '../search_panel'; import { PageHeader } from '../page_header'; @@ -37,6 +43,7 @@ import { useCategorizeRequest } from './use_categorize_request'; import { CategoryTable } from './category_table'; import { DocumentCountChart } from './document_count_chart'; import { InformationText } from './information_text'; +import { SamplingMenu } from './sampling_menu'; const BAR_TARGET = 20; @@ -46,18 +53,23 @@ export const LogCategorizationPage: FC = () => { } = useAiopsAppContext(); const { dataView, savedSearch } = useDataSource(); - const { runCategorizeRequest, cancelRequest } = useCategorizeRequest(); - const [aiopsListState, setAiopsListState] = useState(restorableDefaults); + const { runCategorizeRequest, cancelRequest, randomSampler } = useCategorizeRequest(); + const [aiopsListState, setAiopsListState] = usePageUrlState( + 'AIOPS_INDEX_VIEWER', + getDefaultAiOpsListState() + ); const [globalState, setGlobalState] = useUrlState('_g'); const [selectedField, setSelectedField] = useState(); const [selectedCategory, setSelectedCategory] = useState(null); - const [categories, setCategories] = useState(null); const [selectedSavedSearch, setSelectedDataView] = useState(savedSearch); const [loading, setLoading] = useState(false); const [totalCount, setTotalCount] = useState(0); const [eventRate, setEventRate] = useState([]); const [pinnedCategory, setPinnedCategory] = useState(null); - const [sparkLines, setSparkLines] = useState({}); + const [data, setData] = useState<{ + categories: Category[]; + sparkLines: SparkLinesPerCategory; + } | null>(null); useEffect(() => { if (savedSearch) { @@ -76,7 +88,7 @@ export const LogCategorizationPage: FC = () => { const setSearchParams = useCallback( (searchParams: { - searchQuery: Query['query']; + searchQuery: estypes.QueryDslQueryContainer; searchString: Query['query']; queryLanguage: SearchQueryLanguage; filters: Filter[]; @@ -109,6 +121,7 @@ export const LogCategorizationPage: FC = () => { intervalMs, } = useData( { selectedDataView: dataView, selectedSavedSearch }, + 'log_categorization', aiopsListState, setGlobalState, undefined, @@ -150,20 +163,29 @@ export const LogCategorizationPage: FC = () => { useEffect(() => { if (documentStats.documentCountStats?.buckets) { + randomSampler.setDocCount(documentStats.totalCount); setEventRate( Object.entries(documentStats.documentCountStats.buckets).map(([key, docCount]) => ({ key: +key, docCount, })) ); - setCategories(null); + setData(null); setTotalCount(documentStats.totalCount); } - }, [documentStats, earliest, latest, searchQueryLanguage, searchString, searchQuery]); + }, [ + documentStats, + earliest, + latest, + searchQueryLanguage, + searchString, + searchQuery, + randomSampler, + ]); const loadCategories = useCallback(async () => { setLoading(true); - setCategories(null); + setData(null); const { title: index, timeFieldName: timeField } = dataView; if (selectedField === undefined || timeField === undefined) { @@ -183,8 +205,7 @@ export const LogCategorizationPage: FC = () => { intervalMs ); - setCategories(resp.categories); - setSparkLines(resp.sparkLinesPerCategory); + setData({ categories: resp.categories, sparkLines: resp.sparkLinesPerCategory }); } catch (error) { toasts.addError(error, { title: i18n.translate('xpack.aiops.logCategorization.errorLoadingCategories', { @@ -207,7 +228,7 @@ export const LogCategorizationPage: FC = () => { ]); const onFieldChange = (value: EuiComboBoxOptionOption[] | undefined) => { - setCategories(null); + setData(null); setSelectedField(value && value.length ? value[0].label : undefined); }; @@ -253,15 +274,17 @@ export const LogCategorizationPage: FC = () => { > ) : ( cancelRequest()}>Cancel )}
    - + + loadCategories()} /> +
    {eventRate.length ? ( @@ -271,7 +294,7 @@ export const LogCategorizationPage: FC = () => { eventRate={eventRate} pinnedCategory={pinnedCategory} selectedCategory={selectedCategory} - sparkLines={sparkLines} + sparkLines={data?.sparkLines ?? {}} totalCount={totalCount} documentCountStats={documentStats.documentCountStats} /> @@ -283,18 +306,21 @@ export const LogCategorizationPage: FC = () => { - {selectedField !== undefined && categories !== null && categories.length > 0 ? ( + {selectedField !== undefined && + data !== null && + data.categories.length > 0 && + isFullAiOpsListState(aiopsListState) ? ( n * 100); + +export const MIN_SAMPLER_PROBABILITY = 0.00001; +export const RANDOM_SAMPLER_STEP = MIN_SAMPLER_PROBABILITY * 100; +export const DEFAULT_PROBABILITY = 0.001; + +export const RANDOM_SAMPLER_OPTION = { + ON_AUTOMATIC: 'on_automatic', + ON_MANUAL: 'on_manual', + OFF: 'off', +} as const; + +export type RandomSamplerOption = typeof RANDOM_SAMPLER_OPTION[keyof typeof RANDOM_SAMPLER_OPTION]; +export type RandomSamplerProbability = number | null; + +export const RANDOM_SAMPLER_SELECT_OPTIONS: Array<{ + value: RandomSamplerOption; + text: string; + 'data-test-subj': string; +}> = [ + { + 'data-test-subj': 'aiopsRandomSamplerOptionOnAutomatic', + value: RANDOM_SAMPLER_OPTION.ON_AUTOMATIC, + text: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.onAutomaticLabel', { + defaultMessage: 'On - automatic', + }), + }, + { + 'data-test-subj': 'aiopsRandomSamplerOptionOnManual', + value: RANDOM_SAMPLER_OPTION.ON_MANUAL, + text: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.onManualLabel', { + defaultMessage: 'On - manual', + }), + }, + { + 'data-test-subj': 'aiopsRandomSamplerOptionOff', + value: RANDOM_SAMPLER_OPTION.OFF, + text: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.offLabel', { + defaultMessage: 'Off', + }), + }, +]; + +export class RandomSampler { + private docCount$ = new BehaviorSubject(0); + private mode$ = new BehaviorSubject(RANDOM_SAMPLER_OPTION.ON_AUTOMATIC); + private probability$ = new BehaviorSubject(DEFAULT_PROBABILITY); + private setRandomSamplerModeInStorage: (mode: RandomSamplerOption) => void; + private setRandomSamplerProbabilityInStorage: (prob: RandomSamplerProbability) => void; + + constructor( + randomSamplerMode: RandomSamplerOption, + setRandomSamplerMode: (mode: RandomSamplerOption) => void, + randomSamplerProbability: RandomSamplerProbability, + setRandomSamplerProbability: (prob: RandomSamplerProbability) => void + ) { + this.mode$.next(randomSamplerMode); + this.setRandomSamplerModeInStorage = setRandomSamplerMode; + this.probability$.next(randomSamplerProbability); + this.setRandomSamplerProbabilityInStorage = setRandomSamplerProbability; + } + + setDocCount(docCount: number) { + return this.docCount$.next(docCount); + } + + getDocCount() { + return this.docCount$.getValue(); + } + + public setMode(mode: RandomSamplerOption) { + this.setRandomSamplerModeInStorage(mode); + return this.mode$.next(mode); + } + + public getMode$() { + return this.mode$.asObservable(); + } + + public getMode() { + return this.mode$.getValue(); + } + + public setProbability(probability: RandomSamplerProbability) { + this.setRandomSamplerProbabilityInStorage(probability); + return this.probability$.next(probability); + } + + public getProbability$() { + return this.probability$.asObservable(); + } + + public getProbability() { + return this.probability$.getValue(); + } + + public createRandomSamplerWrapper() { + const mode = this.getMode(); + const probability = this.getProbability(); + + let prob = {}; + if (mode === RANDOM_SAMPLER_OPTION.ON_MANUAL) { + prob = { probability }; + } else if (mode === RANDOM_SAMPLER_OPTION.OFF) { + prob = { probability: 1 }; + } + + const wrapper = createRandomSamplerWrapper({ + ...prob, + totalNumDocs: this.getDocCount(), + }); + this.setProbability(wrapper.probability); + return wrapper; + } +} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler_range_slider.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler_range_slider.tsx new file mode 100644 index 0000000000000..4f2b62729ca46 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler_range_slider.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 { EuiButton, EuiFlexItem, EuiFormRow, EuiRange, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { isDefined } from '@kbn/ml-is-defined'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { roundToDecimalPlace } from '@kbn/ml-number-utils'; +import React, { useState } from 'react'; +import { + MIN_SAMPLER_PROBABILITY, + RANDOM_SAMPLER_PROBABILITIES, + RANDOM_SAMPLER_STEP, +} from './random_sampler'; + +export const RandomSamplerRangeSlider = ({ + samplingProbability, + setSamplingProbability, +}: { + samplingProbability?: number | null; + setSamplingProbability?: (value: number | null) => void; +}) => { + // Keep track of the input in sampling probability slider when mode is on - manual + // before 'Apply' is clicked + const [samplingProbabilityInput, setSamplingProbabilityInput] = useState(samplingProbability); + + const isInvalidSamplingProbabilityInput = + !isDefined(samplingProbabilityInput) || + isNaN(samplingProbabilityInput) || + samplingProbabilityInput < MIN_SAMPLER_PROBABILITY || + samplingProbabilityInput > 0.5; + + const inputValue = (samplingProbabilityInput ?? MIN_SAMPLER_PROBABILITY) * 100; + + return ( + + + + = 1 + ? roundToDecimalPlace(inputValue, 0) + : roundToDecimalPlace(inputValue, 3) + } + ticks={RANDOM_SAMPLER_PROBABILITIES.map((d) => ({ + value: d, + label: d === 0.001 || d >= 5 ? `${d}` : '', + }))} + isInvalid={isInvalidSamplingProbabilityInput} + onChange={(e) => { + const value = parseFloat((e.target as HTMLInputElement).value); + const prevValue = samplingProbabilityInput ? samplingProbabilityInput * 100 : value; + + if (value > 0 && value <= 1) { + setSamplingProbabilityInput(value / 100); + } else { + // Because the incremental step is very small (0.0001), + // every time user clicks the ^/∨ in the numerical input + // we need to make sure it rounds up or down to the next whole number + const nearestInt = value > prevValue ? Math.ceil(value) : Math.floor(value); + setSamplingProbabilityInput(nearestInt / 100); + } + }} + step={RANDOM_SAMPLER_STEP} + data-test-subj="dvRandomSamplerProbabilityRange" + append={ + { + if (setSamplingProbability && isDefined(samplingProbabilityInput)) { + setSamplingProbability(samplingProbabilityInput); + } + }} + > + + + } + /> + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx new file mode 100644 index 0000000000000..e4d18f6dbc260 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx @@ -0,0 +1,185 @@ +/* + * 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, { FC, useCallback, useMemo, useState } from 'react'; +import { + EuiFlexItem, + EuiPopover, + EuiPanel, + EuiSpacer, + EuiCallOut, + EuiSelect, + EuiFormRow, + EuiButtonEmpty, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import useObservable from 'react-use/lib/useObservable'; + +import { RandomSamplerRangeSlider } from './random_sampler_range_slider'; +import { + RandomSampler, + RandomSamplerOption, + RANDOM_SAMPLER_OPTION, + RANDOM_SAMPLER_SELECT_OPTIONS, +} from './random_sampler'; + +interface Props { + randomSampler: RandomSampler; + reload: () => void; +} + +export const SamplingMenu: FC = ({ randomSampler, reload }) => { + const [showSamplingOptionsPopover, setShowSamplingOptionsPopover] = useState(false); + + const samplingProbability = useObservable( + randomSampler.getProbability$(), + randomSampler.getProbability() + ); + const setSamplingProbability = useCallback( + (probability: number | null) => { + randomSampler.setProbability(probability); + reload(); + }, + [reload, randomSampler] + ); + + const randomSamplerPreference = useObservable(randomSampler.getMode$(), randomSampler.getMode()); + const setRandomSamplerPreference = useCallback( + (mode: RandomSamplerOption) => { + randomSampler.setMode(mode); + reload(); + }, + [randomSampler, reload] + ); + + const { calloutInfoMessage, buttonText } = useMemo(() => { + switch (randomSamplerPreference) { + case RANDOM_SAMPLER_OPTION.OFF: + return { + calloutInfoMessage: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.message', + { + defaultMessage: + 'Random sampling can be turned on to increase the speed of analysis, although some accuracy will be lost.', + } + ), + buttonText: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.button', + { + defaultMessage: 'No sampling', + } + ), + }; + case RANDOM_SAMPLER_OPTION.ON_AUTOMATIC: + return { + calloutInfoMessage: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onAutomaticCallout.message', + { + defaultMessage: + 'The pattern analysis will use random sampler aggregations. The probability is automatically set to balance accuracy and speed.', + } + ), + buttonText: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onAutomaticCallout.button', + { + defaultMessage: 'Auto sampling', + } + ), + }; + + case RANDOM_SAMPLER_OPTION.ON_MANUAL: + default: + return { + calloutInfoMessage: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onManualCallout.message', + { + defaultMessage: + 'The pattern analysis will use random sampler aggregations. A lower percentage probability increases performance, but some accuracy is lost.', + } + ), + buttonText: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onManualCallout.button', + { + defaultMessage: 'Manual sampling', + } + ), + }; + } + }, [randomSamplerPreference]); + + return ( + setShowSamplingOptionsPopover(!showSamplingOptionsPopover)} + iconSide="right" + iconType="arrowDown" + > + {buttonText} + + } + isOpen={showSamplingOptionsPopover} + closePopover={() => setShowSamplingOptionsPopover(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + + + + + setRandomSamplerPreference(e.target.value as RandomSamplerOption)} + /> + + + {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_MANUAL ? ( + + ) : null} + + {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_AUTOMATIC ? ( + + ) : null} + + + ); +}; + +const ProbabilityUsedMessage: FC<{ samplingProbability: number | null }> = ({ + samplingProbability, +}) => { + return samplingProbability !== null ? ( +
    + + + +
    + ) : null; +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx new file mode 100644 index 0000000000000..1a1f60a75f9e7 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx @@ -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 React from 'react'; +import { takeUntil, distinctUntilChanged, skip } from 'rxjs/operators'; +import { from } from 'rxjs'; +import { pick } from 'lodash'; +import type { CoreStart } from '@kbn/core/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; + +import { + toMountPoint, + wrapWithTheme, + KibanaContextProvider, +} from '@kbn/kibana-react-plugin/public'; +import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import { DatePickerContextProvider } from '@kbn/ml-date-picker'; +import { StorageContextProvider } from '@kbn/ml-local-storage'; +import type { AiopsPluginStartDeps } from '../../types'; +import { AiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { LogCategorizationFlyout } from './log_categorization_for_flyout'; +import { AIOPS_STORAGE_KEYS } from '../../types/storage'; + +const localStorage = new Storage(window.localStorage); + +export async function showCategorizeFlyout( + field: DataViewField, + dataView: DataView, + coreStart: CoreStart, + plugins: AiopsPluginStartDeps +): Promise { + const { http, theme, overlays, application, notifications, uiSettings } = coreStart; + + return new Promise(async (resolve, reject) => { + try { + const onFlyoutClose = () => { + flyoutSession.close(); + resolve(); + }; + + const appDependencies = { + notifications, + uiSettings, + http, + theme, + application, + ...plugins, + }; + const datePickerDeps = { + ...pick(appDependencies, ['data', 'http', 'notifications', 'theme', 'uiSettings']), + toMountPoint, + wrapWithTheme, + uiSettingsKeys: UI_SETTINGS, + }; + + const flyoutSession = overlays.openFlyout( + toMountPoint( + wrapWithTheme( + + + + + + + + + , + theme.theme$ + ) + ), + { + 'data-test-subj': 'aiopsCategorizeFlyout', + ownFocus: true, + closeButtonAriaLabel: 'aiopsCategorizeFlyout', + onClose: onFlyoutClose, + size: 'l', + } + ); + + // Close the flyout when user navigates out of the current plugin + application.currentAppId$ + .pipe(skip(1), takeUntil(from(flyoutSession.onClose)), distinctUntilChanged()) + .subscribe(() => { + flyoutSession.close(); + }); + } catch (error) { + reject(error); + } + }); +} diff --git a/x-pack/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts b/x-pack/plugins/aiops/public/components/log_categorization/technical_preview_badge/index.ts similarity index 75% rename from x-pack/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts rename to x-pack/plugins/aiops/public/components/log_categorization/technical_preview_badge/index.ts index 4f81fd1744795..86a309414943c 100644 --- a/x-pack/plugins/grokdebugger/public/components/grok_debugger/brace_imports.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/technical_preview_badge/index.ts @@ -5,6 +5,4 @@ * 2.0. */ -import 'brace/mode/json'; -import 'brace/mode/text'; -import 'brace/theme/textmate'; +export { TechnicalPreviewBadge } from './technical_preview_badge'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/technical_preview_badge/technical_preview_badge.tsx b/x-pack/plugins/aiops/public/components/log_categorization/technical_preview_badge/technical_preview_badge.tsx new file mode 100644 index 0000000000000..1d077eb636c5a --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/technical_preview_badge/technical_preview_badge.tsx @@ -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 React, { FC } from 'react'; + +import { EuiBetaBadge } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +export const TechnicalPreviewBadge: FC = () => { + return ( + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts index b687d5a0110d9..7889cc97280a5 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts @@ -6,30 +6,47 @@ */ import { cloneDeep, get } from 'lodash'; -import { useRef, useCallback } from 'react'; +import { useRef, useCallback, useMemo } from 'react'; import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/public'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { createRandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; +import { estypes } from '@elastic/elasticsearch'; +import { useStorage } from '@kbn/ml-local-storage'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { RandomSampler } from './sampling_menu'; +import { + type AiOpsKey, + type AiOpsStorageMapped, + AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE, + AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE, +} from '../../types/storage'; +import { RANDOM_SAMPLER_OPTION, DEFAULT_PROBABILITY } from './sampling_menu/random_sampler'; const CATEGORY_LIMIT = 1000; const EXAMPLE_LIMIT = 1; -interface CatResponse { - rawResponse: { - aggregations: { - categories: { - buckets: Array<{ - key: string; - doc_count: number; - hit: { hits: { hits: Array<{ _source: { message: string } }> } }; - sparkline: { buckets: Array<{ key_as_string: string; key: number; doc_count: number }> }; - }>; +interface CategoriesAgg { + categories: { + buckets: Array<{ + key: string; + doc_count: number; + hit: { hits: { hits: Array<{ _source: { message: string } }> } }; + sparkline: { + buckets: Array<{ key_as_string: string; key: number; doc_count: number }>; }; - }; + }>; }; } +interface CategoriesSampleAgg { + sample: CategoriesAgg; +} + +interface CatResponse { + rawResponse: estypes.SearchResponseBody; +} + export interface Category { key: string; count: number; @@ -45,10 +62,32 @@ export type EventRate = Array<{ export type SparkLinesPerCategory = Record>; export function useCategorizeRequest() { + const [randomSamplerMode, setRandomSamplerMode] = useStorage< + AiOpsKey, + AiOpsStorageMapped + >(AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE, RANDOM_SAMPLER_OPTION.ON_AUTOMATIC); + + const [randomSamplerProbability, setRandomSamplerProbability] = useStorage< + AiOpsKey, + AiOpsStorageMapped + >(AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE, DEFAULT_PROBABILITY); + const { data } = useAiopsAppContext(); const abortController = useRef(new AbortController()); + const randomSampler = useMemo( + () => + new RandomSampler( + randomSamplerMode, + setRandomSamplerMode, + randomSamplerProbability, + setRandomSamplerProbability + ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + const runCategorizeRequest = useCallback( ( index: string, @@ -59,16 +98,18 @@ export function useCategorizeRequest() { query: QueryDslQueryContainer, intervalMs?: number ): Promise<{ categories: Category[]; sparkLinesPerCategory: SparkLinesPerCategory }> => { + const { wrap, unwrap } = randomSampler.createRandomSamplerWrapper(); + return new Promise((resolve, reject) => { data.search .search, CatResponse>( - createCategoryRequest(index, field, timeField, from, to, query, intervalMs), + createCategoryRequest(index, field, timeField, from, to, query, wrap, intervalMs), { abortSignal: abortController.current.signal } ) .subscribe({ next: (result) => { if (isCompleteResponse(result)) { - resolve(processCategoryResults(result, field)); + resolve(processCategoryResults(result, field, unwrap)); } else if (isErrorResponse(result)) { reject(result); } else { @@ -86,7 +127,7 @@ export function useCategorizeRequest() { }); }); }, - [data.search] + [data.search, randomSampler] ); const cancelRequest = useCallback(() => { @@ -94,7 +135,7 @@ export function useCategorizeRequest() { abortController.current = new AbortController(); }, []); - return { runCategorizeRequest, cancelRequest }; + return { runCategorizeRequest, cancelRequest, randomSampler }; } function createCategoryRequest( @@ -104,6 +145,7 @@ function createCategoryRequest( from: number | undefined, to: number | undefined, queryIn: QueryDslQueryContainer, + wrap: ReturnType['wrap'], intervalMs?: number ) { const query = cloneDeep(queryIn); @@ -134,52 +176,64 @@ function createCategoryRequest( }, }, }); + + const aggs = { + categories: { + categorize_text: { + field, + size: CATEGORY_LIMIT, + }, + aggs: { + hit: { + top_hits: { + size: EXAMPLE_LIMIT, + sort: [timeField], + _source: field, + }, + }, + ...(intervalMs + ? { + sparkline: { + date_histogram: { + field: timeField, + fixed_interval: `${intervalMs}ms`, + }, + }, + } + : {}), + }, + }, + }; + return { params: { index, size: 0, body: { query, - aggs: { - categories: { - categorize_text: { - field, - size: CATEGORY_LIMIT, - }, - aggs: { - hit: { - top_hits: { - size: EXAMPLE_LIMIT, - sort: [timeField], - _source: field, - }, - }, - ...(intervalMs - ? { - sparkline: { - date_histogram: { - field: timeField, - fixed_interval: `${intervalMs}ms`, - }, - }, - } - : {}), - }, - }, - }, + aggs: wrap(aggs), }, }, }; } -function processCategoryResults(result: CatResponse, field: string) { +function processCategoryResults( + result: CatResponse, + field: string, + unwrap: ReturnType['unwrap'] +) { const sparkLinesPerCategory: SparkLinesPerCategory = {}; - - if (result.rawResponse.aggregations === undefined) { + const { aggregations } = result.rawResponse; + if (aggregations === undefined) { throw new Error('processCategoryResults failed, did not return aggregations.'); } + const { + categories: { buckets }, + } = unwrap( + aggregations as unknown as Record + ) as CategoriesAgg; - const categories: Category[] = result.rawResponse.aggregations.categories.buckets.map((b) => { + const categories: Category[] = buckets.map((b) => { sparkLinesPerCategory[b.key] = b.sparkline === undefined ? {} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts index 2bc63415f9b95..cb3b60268f438 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts @@ -10,10 +10,16 @@ import moment from 'moment'; import type { TimeRangeBounds } from '@kbn/data-plugin/common'; import { i18n } from '@kbn/i18n'; +import type { Filter } from '@kbn/es-query'; +import type { AiOpsIndexBasedAppState } from '../../application/utils/url_state'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import type { Category } from './use_categorize_request'; -import type { QueryMode } from './category_table'; -import type { AiOpsIndexBasedAppState } from '../explain_log_rate_spikes/explain_log_rate_spikes_app_state'; + +export const QUERY_MODE = { + INCLUDE: 'should', + EXCLUDE: 'must_not', +} as const; +export type QueryMode = typeof QUERY_MODE[keyof typeof QUERY_MODE]; export function useDiscoverLinks() { const { @@ -29,8 +35,6 @@ export function useDiscoverLinks() { mode: QueryMode, category?: Category ) => { - const selectedRows = category === undefined ? selection : [category]; - const _g = rison.encode({ time: { from: moment(timefilterActiveBounds.min?.valueOf()).toISOString(), @@ -39,35 +43,7 @@ export function useDiscoverLinks() { }); const _a = rison.encode({ - filters: [ - ...aiopsListState.filters, - { - query: { - bool: { - [mode]: selectedRows.map(({ key: query }) => ({ - match: { - [field]: { - auto_generate_synonyms_phrase_query: false, - fuzziness: 0, - operator: 'and', - query, - }, - }, - })), - }, - }, - meta: { - alias: i18n.translate('xpack.aiops.logCategorization.filterAliasLabel', { - defaultMessage: 'Categorization - {field}', - values: { - field, - }, - }), - index, - disabled: false, - }, - }, - ], + filters: [...aiopsListState.filters, createFilter(index, field, selection, mode, category)], index, interval: 'auto', query: { @@ -85,3 +61,39 @@ export function useDiscoverLinks() { return { openInDiscoverWithFilter }; } + +export function createFilter( + index: string, + field: string, + selection: Category[], + mode: QueryMode, + category?: Category +): Filter { + const selectedRows = category === undefined ? selection : [category]; + return { + query: { + bool: { + [mode]: selectedRows.map(({ key: query }) => ({ + match: { + [field]: { + auto_generate_synonyms_phrase_query: false, + fuzziness: 0, + operator: 'and', + query, + }, + }, + })), + }, + }, + meta: { + alias: i18n.translate('xpack.aiops.logCategorization.filterAliasLabel', { + defaultMessage: 'Categorization - {field}', + values: { + field, + }, + }), + index, + disabled: false, + }, + }; +} diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx index 1a5b7460b62fe..2d6d96251d303 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -38,6 +38,7 @@ import { useSpikeAnalysisTableRowContext } from './spike_analysis_table_row_prov import { FieldStatsPopover } from '../field_stats_popover'; import { useCopyToClipboardAction } from './use_copy_to_clipboard_action'; import { useViewInDiscoverAction } from './use_view_in_discover_action'; +import { useViewInLogPatternAnalysisAction } from './use_view_in_log_pattern_analysis_action'; const NARROW_COLUMN_WIDTH = '120px'; const ACTIONS_COLUMN_WIDTH = '60px'; @@ -95,6 +96,7 @@ export const SpikeAnalysisTable: FC = ({ const copyToClipBoardAction = useCopyToClipboardAction(); const viewInDiscoverAction = useViewInDiscoverAction(dataViewId); + const viewInLogPatternAnalysisAction = useViewInLogPatternAnalysisAction(dataViewId); const columns: Array> = [ { @@ -238,7 +240,7 @@ export const SpikeAnalysisTable: FC = ({ name: i18n.translate('xpack.aiops.spikeAnalysisTable.actionsColumnName', { defaultMessage: 'Actions', }), - actions: [viewInDiscoverAction, copyToClipBoardAction], + actions: [viewInDiscoverAction, viewInLogPatternAnalysisAction, copyToClipBoardAction], width: ACTIONS_COLUMN_WIDTH, valign: 'middle', }, diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx index b319db0088d4d..ca55b43907bd6 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table_groups.tsx @@ -40,6 +40,7 @@ import { useSpikeAnalysisTableRowContext } from './spike_analysis_table_row_prov import type { GroupTableItem } from './types'; import { useCopyToClipboardAction } from './use_copy_to_clipboard_action'; import { useViewInDiscoverAction } from './use_view_in_discover_action'; +import { useViewInLogPatternAnalysisAction } from './use_view_in_log_pattern_analysis_action'; const NARROW_COLUMN_WIDTH = '120px'; const EXPAND_COLUMN_WIDTH = '40px'; @@ -121,6 +122,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ const copyToClipBoardAction = useCopyToClipboardAction(); const viewInDiscoverAction = useViewInDiscoverAction(dataViewId); + const viewInLogPatternAnalysisAction = useViewInLogPatternAnalysisAction(dataViewId); const columns: Array> = [ { @@ -355,7 +357,7 @@ export const SpikeAnalysisGroupsTable: FC = ({ name: i18n.translate('xpack.aiops.spikeAnalysisTable.actionsColumnName', { defaultMessage: 'Actions', }), - actions: [viewInDiscoverAction, copyToClipBoardAction], + actions: [viewInDiscoverAction, viewInLogPatternAnalysisAction, copyToClipBoardAction], width: ACTIONS_COLUMN_WIDTH, valign: 'top', }, diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/table_action_button.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/table_action_button.tsx new file mode 100644 index 0000000000000..16c91f8d3851f --- /dev/null +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/table_action_button.tsx @@ -0,0 +1,62 @@ +/* + * 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, { type FC } from 'react'; + +import { EuiLink, EuiIcon, EuiText, EuiToolTip, type IconType } from '@elastic/eui'; + +interface TableActionButtonProps { + iconType: IconType; + dataTestSubjPostfix: string; + isDisabled: boolean; + label: string; + tooltipText?: string; + onClick: () => void; +} + +export const TableActionButton: FC = ({ + iconType, + dataTestSubjPostfix, + isDisabled, + label, + tooltipText, + onClick, +}) => { + const buttonContent = ( + <> + + {label} + + ); + + const unwrappedButton = !isDisabled ? ( + + {buttonContent} + + ) : ( + + {buttonContent} + + ); + + if (tooltipText) { + return {unwrappedButton}; + } + + return unwrappedButton; +}; diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.test.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.test.tsx index 82359b3d2b1aa..0984c76a4b170 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.test.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.test.tsx @@ -30,11 +30,19 @@ describe('useCopyToClipboardAction', () => { it('renders the action for a single significant term', async () => { execCommandMock.mockImplementationOnce(() => true); const { result } = renderHook(() => useCopyToClipboardAction()); - const { getByLabelText } = render((result.current as Action).render(significantTerms[0])); + const { findByText, getByTestId } = render( + (result.current as Action).render(significantTerms[0]) + ); - const button = getByLabelText('Copy field/value pair as KQL syntax to clipboard'); + const button = getByTestId('aiopsTableActionButtonCopyToClipboard enabled'); - expect(button).toBeInTheDocument(); + userEvent.hover(button); + + // The tooltip from EUI takes 250ms to appear, so we must + // use a `find*` query to asynchronously poll for it. + expect( + await findByText('Copy field/value pair as KQL syntax to clipboard') + ).toBeInTheDocument(); await act(async () => { await userEvent.click(button); @@ -50,12 +58,16 @@ describe('useCopyToClipboardAction', () => { it('renders the action for a group of items', async () => { execCommandMock.mockImplementationOnce(() => true); const groupTableItems = getGroupTableItems(finalSignificantTermGroups); - const { result } = renderHook(() => useCopyToClipboardAction()); - const { getByLabelText } = render((result.current as Action).render(groupTableItems[0])); + const { result } = renderHook(useCopyToClipboardAction); + const { findByText, getByText } = render((result.current as Action).render(groupTableItems[0])); + + const button = getByText('Copy to clipboard'); - const button = getByLabelText('Copy group items as KQL syntax to clipboard'); + userEvent.hover(button); - expect(button).toBeInTheDocument(); + // The tooltip from EUI takes 250ms to appear, so we must + // use a `find*` query to asynchronously poll for it. + expect(await findByText('Copy group items as KQL syntax to clipboard')).toBeInTheDocument(); await act(async () => { await userEvent.click(button); diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.tsx index e9924307c1e27..1b906eb56e988 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_copy_to_clipboard_action.tsx @@ -7,14 +7,22 @@ import React from 'react'; -import { EuiCopy, EuiToolTip, EuiButtonIcon } from '@elastic/eui'; +import { EuiCopy, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isSignificantTerm, type SignificantTerm } from '@kbn/ml-agg-utils'; +import { TableActionButton } from './table_action_button'; import { getTableItemAsKQL } from './get_table_item_as_kql'; import type { GroupTableItem, TableItemAction } from './types'; +const copyToClipboardButtonLabel = i18n.translate( + 'xpack.aiops.spikeAnalysisTable.linksMenu.copyToClipboardButtonLabel', + { + defaultMessage: 'Copy to clipboard', + } +); + const copyToClipboardSignificantTermMessage = i18n.translate( 'xpack.aiops.spikeAnalysisTable.linksMenu.copyToClipboardSignificantTermMessage', { @@ -37,7 +45,15 @@ export const useCopyToClipboardAction = (): TableItemAction => ({ return ( - {(copy) => } + {(copy) => ( + + )} ); diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_discover_action.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_discover_action.tsx index 5f30abb2f6cec..bd7741bb452bf 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_discover_action.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_discover_action.tsx @@ -7,14 +7,13 @@ import React, { useMemo } from 'react'; -import { EuiIcon, EuiToolTip } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; import type { SignificantTerm } from '@kbn/ml-agg-utils'; import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { TableActionButton } from './table_action_button'; import { getTableItemAsKQL } from './get_table_item_as_kql'; import type { GroupTableItem, TableItemAction } from './types'; @@ -83,19 +82,26 @@ export const useViewInDiscoverAction = (dataViewId?: string): TableItemAction => }; return { - name: () => ( - - - - ), - description: viewInDiscoverMessage, - type: 'button', - onClick: async (tableItem) => { - const openInDiscoverUrl = await generateDiscoverUrl(tableItem); - if (typeof openInDiscoverUrl === 'string') { - await application.navigateToUrl(openInDiscoverUrl); - } + render: (tableItem: SignificantTerm | GroupTableItem) => { + const tooltipText = discoverUrlError ? discoverUrlError : viewInDiscoverMessage; + + const clickHandler = async () => { + const openInDiscoverUrl = await generateDiscoverUrl(tableItem); + if (typeof openInDiscoverUrl === 'string') { + await application.navigateToUrl(openInDiscoverUrl); + } + }; + + return ( + + ); }, - enabled: () => discoverUrlError === undefined, }; }; diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_log_pattern_analysis_action.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_log_pattern_analysis_action.tsx new file mode 100644 index 0000000000000..9388cf147c8ff --- /dev/null +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/use_view_in_log_pattern_analysis_action.tsx @@ -0,0 +1,108 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import type { SignificantTerm } from '@kbn/ml-agg-utils'; + +import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; + +import { TableActionButton } from './table_action_button'; +import { getTableItemAsKQL } from './get_table_item_as_kql'; +import type { GroupTableItem, TableItemAction } from './types'; + +const viewInLogPatternAnalysisMessage = i18n.translate( + 'xpack.aiops.spikeAnalysisTable.linksMenu.viewInLogPatternAnalysis', + { + defaultMessage: 'View in Log Pattern Analysis', + } +); + +export const useViewInLogPatternAnalysisAction = (dataViewId?: string): TableItemAction => { + const { application, share, data } = useAiopsAppContext(); + + const mlLocator = useMemo(() => share.url.locators.get('ML_APP_LOCATOR'), [share.url.locators]); + + const generateLogPatternAnalysisUrl = async ( + groupTableItem: GroupTableItem | SignificantTerm + ) => { + if (mlLocator !== undefined) { + const searchString = getTableItemAsKQL(groupTableItem); + const ast = fromKueryExpression(searchString); + const searchQuery = toElasticsearchQuery(ast); + + const appState = { + AIOPS_INDEX_VIEWER: { + filters: data.query.filterManager.getFilters(), + // QueryDslQueryContainer type triggers an error as being + // not working with SerializableRecord, however, it works as expected. + searchQuery: searchQuery as unknown, + searchQueryLanguage: SEARCH_QUERY_LANGUAGE.KUERY, + searchString: getTableItemAsKQL(groupTableItem), + }, + } as SerializableRecord; + + return await mlLocator.getUrl({ + page: 'aiops/log_categorization', + pageState: { + index: dataViewId, + timeRange: data.query.timefilter.timefilter.getTime(), + appState, + }, + }); + } + }; + + const logPatternAnalysisUrlError = useMemo(() => { + if (!mlLocator) { + return i18n.translate('xpack.aiops.spikeAnalysisTable.mlLocatorMissingErrorMessage', { + defaultMessage: 'No locator for Log Pattern Analysis detected', + }); + } + if (!dataViewId) { + return i18n.translate( + 'xpack.aiops.spikeAnalysisTable.autoGeneratedLogPatternAnalysisLinkErrorMessage', + { + defaultMessage: + 'Unable to link to Log Pattern Analysis; no data view exists for this index', + } + ); + } + }, [dataViewId, mlLocator]); + + return { + render: (tableItem: SignificantTerm | GroupTableItem) => { + const message = logPatternAnalysisUrlError + ? logPatternAnalysisUrlError + : viewInLogPatternAnalysisMessage; + + const clickHandler = async () => { + const openInLogPatternAnalysisUrl = await generateLogPatternAnalysisUrl(tableItem); + if (typeof openInLogPatternAnalysisUrl === 'string') { + await application.navigateToUrl(openInLogPatternAnalysisUrl); + } + }; + + const isDisabled = logPatternAnalysisUrlError !== undefined; + + return ( + + ); + }, + }; +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts index 92ea33c18e2d3..ad2ef100924b3 100644 --- a/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts +++ b/x-pack/plugins/aiops/public/hooks/use_aiops_app_context.ts @@ -5,27 +5,32 @@ * 2.0. */ -import { createContext, useContext } from 'react'; +import { createContext, type FC, useContext } from 'react'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; - import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { - CoreStart, CoreSetup, + CoreStart, + ExecutionContextStart, HttpStart, IUiSettingsClient, ThemeServiceStart, } from '@kbn/core/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import { type EuiComboBoxProps } from '@elastic/eui/src/components/combo_box/combo_box'; +import { type DataView } from '@kbn/data-views-plugin/common'; +import type { FieldStatsProps, FieldStatsServices } from '@kbn/unified-field-list-plugin/public'; +import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; export interface AiopsAppDependencies { application: CoreStart['application']; data: DataPublicPluginStart; + executionContext: ExecutionContextStart; charts: ChartsPluginStart; fieldFormats: FieldFormatsStart; http: HttpStart; @@ -36,6 +41,19 @@ export interface AiopsAppDependencies { unifiedSearch: UnifiedSearchPublicPluginStart; share: SharePluginStart; lens: LensPublicStart; + // deps for unified field stats + fieldStats?: { + useFieldStatsTrigger: () => { + renderOption: EuiComboBoxProps['renderOption']; + closeFlyout: () => void; + }; + FieldStatsFlyoutProvider: FC<{ + dataView: DataView; + fieldStatsServices: FieldStatsServices; + timeRangeMs?: TimeRangeMs; + dslQuery?: FieldStatsProps['dslQuery']; + }>; + }; } export const AiopsAppContext = createContext(undefined); diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts index 82db675a94c4f..e1cf21b6e520f 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data.ts +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -8,14 +8,17 @@ import { useEffect, useMemo, useState } from 'react'; import { merge } from 'rxjs'; +import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { SignificantTerm } from '@kbn/ml-agg-utils'; import type { SavedSearch } from '@kbn/discover-plugin/public'; import type { Dictionary } from '@kbn/ml-url-state'; import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker'; +import { PLUGIN_ID } from '../../common'; + import type { DocumentStatsSearchStrategyParams } from '../get_document_stats'; -import type { AiOpsIndexBasedAppState } from '../components/explain_log_rate_spikes/explain_log_rate_spikes_app_state'; +import type { AiOpsIndexBasedAppState } from '../application/utils/url_state'; import { getEsQueryFromSavedSearch } from '../application/utils/search_utils'; import type { GroupTableItem } from '../components/spike_analysis_table/types'; @@ -31,19 +34,28 @@ export const useData = ( selectedDataView, selectedSavedSearch, }: { selectedDataView: DataView; selectedSavedSearch: SavedSearch | null }, + contextId: string, aiopsListState: AiOpsIndexBasedAppState, - onUpdate: (params: Dictionary) => void, + onUpdate?: (params: Dictionary) => void, selectedSignificantTerm?: SignificantTerm, selectedGroup?: GroupTableItem | null, - barTarget: number = DEFAULT_BAR_TARGET + barTarget: number = DEFAULT_BAR_TARGET, + readOnly: boolean = false ) => { const { + executionContext, uiSettings, data: { query: { filterManager }, }, } = useAiopsAppContext(); + useExecutionContext(executionContext, { + name: PLUGIN_ID, + type: 'application', + id: contextId, + }); + const [lastRefresh, setLastRefresh] = useState(0); /** Prepare required params to pass to search strategy **/ @@ -56,7 +68,7 @@ export const useData = ( }); if (searchData === undefined || aiopsListState.searchString !== '') { - if (aiopsListState.filters) { + if (aiopsListState.filters && readOnly === false) { const globalFilters = filterManager?.getGlobalFilters(); if (filterManager) filterManager.setFilters(aiopsListState.filters); @@ -151,8 +163,8 @@ export const useData = ( time: timefilter.getTime(), refreshInterval: timefilter.getRefreshInterval(), }); + setLastRefresh(Date.now()); } - setLastRefresh(Date.now()); }); // This listens just for an initial update of the timefilter to be switched on. @@ -180,5 +192,6 @@ export const useData = ( searchQueryLanguage, searchString, searchQuery, + forceRefresh: () => setLastRefresh(Date.now()), }; }; diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts index 9e4f50368a0b5..8cfaa074286d6 100644 --- a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -12,10 +12,10 @@ import { i18n } from '@kbn/i18n'; import type { ToastsStart } from '@kbn/core/public'; import { stringHash } from '@kbn/ml-string-hash'; import { createRandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { RANDOM_SAMPLER_SEED } from '../../common/constants'; -import { extractErrorProperties } from '../application/utils/error_utils'; import { DocumentCountStats, getDocumentCountStatsRequest, diff --git a/x-pack/plugins/aiops/public/plugin.ts b/x-pack/plugins/aiops/public/plugin.ts index 0232473a40f11..51a109d8c90f9 100755 --- a/x-pack/plugins/aiops/public/plugin.ts +++ b/x-pack/plugins/aiops/public/plugin.ts @@ -5,10 +5,45 @@ * 2.0. */ -import { Plugin } from '@kbn/core/public'; +import type { CoreStart, Plugin } from '@kbn/core/public'; +import { firstValueFrom } from 'rxjs'; + +import { + AiopsPluginSetup, + AiopsPluginSetupDeps, + AiopsPluginStart, + AiopsPluginStartDeps, +} from './types'; + +export class AiopsPlugin + implements Plugin +{ + public setup() { + return {}; + } + + public start(core: CoreStart, plugins: AiopsPluginStartDeps) { + // importing async to keep the aiops plugin size to a minimum + Promise.all([ + import('@kbn/ui-actions-plugin/public'), + import('./components/log_categorization'), + firstValueFrom(plugins.licensing.license$), + ]).then(([uiActionsImports, { categorizeFieldAction }, license]) => { + if (license.hasAtLeast('platinum')) { + const { ACTION_CATEGORIZE_FIELD, CATEGORIZE_FIELD_TRIGGER } = uiActionsImports; + if (plugins.uiActions.hasAction(ACTION_CATEGORIZE_FIELD)) { + plugins.uiActions.unregisterAction(ACTION_CATEGORIZE_FIELD); + } + + plugins.uiActions.addTriggerAction( + CATEGORIZE_FIELD_TRIGGER, + categorizeFieldAction(core, plugins) + ); + } + }); + + return {}; + } -export class AiopsPlugin implements Plugin { - public setup() {} - public start() {} public stop() {} } diff --git a/x-pack/plugins/aiops/public/types.ts b/x-pack/plugins/aiops/public/types.ts new file mode 100755 index 0000000000000..8441e36529f90 --- /dev/null +++ b/x-pack/plugins/aiops/public/types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { ExecutionContextStart } from '@kbn/core-execution-context-browser'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AiopsPluginSetupDeps {} + +export interface AiopsPluginStartDeps { + data: DataPublicPluginStart; + charts: ChartsPluginStart; + uiActions: UiActionsStart; + fieldFormats: FieldFormatsStart; + lens: LensPublicStart; + share: SharePluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; + storage: IStorageWrapper; + licensing: LicensingPluginStart; + executionContext: ExecutionContextStart; +} + +/** + * aiops plugin server setup contract + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AiopsPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AiopsPluginStart {} diff --git a/x-pack/plugins/aiops/public/types/storage.ts b/x-pack/plugins/aiops/public/types/storage.ts index bd10b2d25b0cd..e6a1f8d46730b 100644 --- a/x-pack/plugins/aiops/public/types/storage.ts +++ b/x-pack/plugins/aiops/public/types/storage.ts @@ -6,17 +6,34 @@ */ import { type FrozenTierPreference } from '@kbn/ml-date-picker'; +import { + type RandomSamplerOption, + type RandomSamplerProbability, +} from '../components/log_categorization/sampling_menu/random_sampler'; export const AIOPS_FROZEN_TIER_PREFERENCE = 'aiops.frozenDataTierPreference'; +export const AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE = 'aiops.randomSamplingModePreference'; +export const AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE = + 'aiops.randomSamplingProbabilityPreference'; export type AiOps = Partial<{ [AIOPS_FROZEN_TIER_PREFERENCE]: FrozenTierPreference; + [AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE]: RandomSamplerOption; + [AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE]: number; }> | null; export type AiOpsKey = keyof Exclude; export type AiOpsStorageMapped = T extends typeof AIOPS_FROZEN_TIER_PREFERENCE ? FrozenTierPreference | undefined + : T extends typeof AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE + ? RandomSamplerOption + : T extends typeof AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE + ? RandomSamplerProbability : null; -export const AIOPS_STORAGE_KEYS = [AIOPS_FROZEN_TIER_PREFERENCE] as const; +export const AIOPS_STORAGE_KEYS = [ + AIOPS_FROZEN_TIER_PREFERENCE, + AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE, + AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE, +] as const; diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index b9a6ff5408eda..3e152f8931409 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -12,45 +12,48 @@ "types/**/*" ], "kbn_references": [ + "@kbn/aiops-components", + "@kbn/aiops-utils", + "@kbn/charts-plugin", + "@kbn/config-schema", + "@kbn/core-elasticsearch-server", + "@kbn/core-execution-context-browser", "@kbn/core", - "@kbn/kibana-utils-plugin", - "@kbn/kibana-react-plugin", "@kbn/data-plugin", - "@kbn/unified-search-plugin", - "@kbn/charts-plugin", - "@kbn/discover-plugin", - "@kbn/lens-plugin", + "@kbn/data-views-plugin", "@kbn/datemath", - "@kbn/field-formats-plugin", - "@kbn/ml-agg-utils", - "@kbn/config-schema", - "@kbn/ml-is-populated-object", + "@kbn/discover-plugin", "@kbn/es-query", - "@kbn/share-plugin", - "@kbn/data-views-plugin", - "@kbn/saved-search-plugin", - "@kbn/i18n", - "@kbn/ml-string-hash", - "@kbn/ui-theme", + "@kbn/es-types", + "@kbn/field-formats-plugin", + "@kbn/field-types", "@kbn/i18n-react", - "@kbn/rison", - "@kbn/core-http-browser", - "@kbn/aiops-components", - "@kbn/aiops-utils", + "@kbn/i18n", + "@kbn/kibana-react-plugin", + "@kbn/kibana-utils-plugin", + "@kbn/lens-plugin", "@kbn/licensing-plugin", - "@kbn/field-types", "@kbn/logging", - "@kbn/core-elasticsearch-server", - "@kbn/es-types", - "@kbn/ml-url-state", - "@kbn/ml-local-storage", + "@kbn/ml-agg-utils", "@kbn/ml-date-picker", + "@kbn/ml-error-utils", + "@kbn/ml-is-defined", + "@kbn/ml-is-populated-object", "@kbn/ml-local-storage", + "@kbn/ml-number-utils", "@kbn/ml-query-utils", - "@kbn/ml-is-defined", + "@kbn/ml-random-sampler-utils", "@kbn/ml-route-utils", + "@kbn/ml-string-hash", + "@kbn/ml-url-state", + "@kbn/rison", + "@kbn/saved-search-plugin", + "@kbn/share-plugin", + "@kbn/ui-actions-plugin", + "@kbn/ui-theme", "@kbn/unified-field-list-plugin", - "@kbn/ml-random-sampler-utils", + "@kbn/unified-search-plugin", + "@kbn/utility-types", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts index 4456bd363a016..5aff2411e07f9 100644 --- a/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts +++ b/x-pack/plugins/alerting/common/alert_schema/field_maps/mapping_from_field_map.test.ts @@ -216,6 +216,9 @@ describe('mappingFromFieldMap', () => { flapping_history: { type: 'boolean', }, + maintenance_window_ids: { + type: 'keyword', + }, instance: { properties: { id: { @@ -278,6 +281,11 @@ describe('mappingFromFieldMap', () => { type: 'date_range', format: 'epoch_millis||strict_date_optional_time', }, + url: { + ignore_above: 2048, + index: false, + type: 'keyword', + }, uuid: { type: 'keyword', }, diff --git a/x-pack/plugins/alerting/common/alert_summary.ts b/x-pack/plugins/alerting/common/alert_summary.ts index b1563882d4f21..ed6cf325e20b1 100644 --- a/x-pack/plugins/alerting/common/alert_summary.ts +++ b/x-pack/plugins/alerting/common/alert_summary.ts @@ -29,6 +29,7 @@ export interface AlertSummary { errorMessages: Array<{ date: string; message: string }>; alerts: Record; executionDuration: ExecutionDuration; + revision: number; } export interface AlertStatus { @@ -38,4 +39,5 @@ export interface AlertStatus { actionGroupId?: string; activeStartDate?: string; flapping: boolean; + maintenanceWindowIds?: string[]; } diff --git a/x-pack/plugins/alerting/common/execution_log_types.ts b/x-pack/plugins/alerting/common/execution_log_types.ts index 2d5e34df8f766..7a35bea0df619 100644 --- a/x-pack/plugins/alerting/common/execution_log_types.ts +++ b/x-pack/plugins/alerting/common/execution_log_types.ts @@ -63,6 +63,7 @@ export interface IExecutionLog { rule_id: string; space_ids: string[]; rule_name: string; + maintenance_window_ids: string[]; } export interface IExecutionErrors { diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index f12abcde69660..1fa0806effdef 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -57,6 +57,13 @@ export interface AlertingFrameworkHealth { export const LEGACY_BASE_ALERT_API_PATH = '/api/alerts'; export const BASE_ALERTING_API_PATH = '/api/alerting'; export const INTERNAL_BASE_ALERTING_API_PATH = '/internal/alerting'; +export const INTERNAL_ALERTING_API_FIND_RULES_PATH = `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_find`; + +export const INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH = + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window` as const; +export const INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH = + `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/_active` as const; + export const ALERTS_FEATURE_ID = 'alerts'; export const MONITORING_HISTORY_LIMIT = 200; export const ENABLE_MAINTENANCE_WINDOWS = false; diff --git a/x-pack/plugins/alerting/common/maintenance_window.ts b/x-pack/plugins/alerting/common/maintenance_window.ts index 0392d0cdb3667..e41140f8fc918 100644 --- a/x-pack/plugins/alerting/common/maintenance_window.ts +++ b/x-pack/plugins/alerting/common/maintenance_window.ts @@ -46,6 +46,11 @@ export type MaintenanceWindow = MaintenanceWindowSOAttributes & { id: string; }; +export type MaintenanceWindowCreateBody = Omit< + MaintenanceWindowSOProperties, + 'events' | 'expirationDate' | 'enabled' | 'archived' +>; + export interface MaintenanceWindowClientContext { getModificationMetadata: () => Promise; savedObjectsClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 0bac3fd995a27..f58324f1d09ee 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -10,7 +10,7 @@ import type { SavedObjectAttributes, SavedObjectsResolveResponse, } from '@kbn/core/server'; -import type { KueryNode } from '@kbn/es-query'; +import type { Filter, KueryNode } from '@kbn/es-query'; import { RuleNotifyWhenType } from './rule_notify_when_type'; import { RuleSnooze } from './rule_snooze_type'; @@ -94,11 +94,12 @@ export interface AlertsFilterTimeframe extends SavedObjectAttributes { } export interface AlertsFilter extends SavedObjectAttributes { - query: null | { + query?: { kql: string; + filters: Filter[]; dsl?: string; // This fields is generated in the code by using "kql", therefore it's not optional but defined as optional to avoid modifying a lot of files in different plugins }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export type RuleActionAlertsFilterProperty = AlertsFilterTimeframe | RuleActionParam; @@ -191,10 +192,11 @@ export interface Rule { } export interface SanitizedAlertsFilter extends AlertsFilter { - query: null | { + query?: { kql: string; + filters: Filter[]; }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export type SanitizedRuleAction = Omit & { diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.json b/x-pack/plugins/alerting/docs/openapi/bundled.json index e7fbc15c77c01..becb387e84024 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.json +++ b/x-pack/plugins/alerting/docs/openapi/bundled.json @@ -613,59 +613,6 @@ "schema": { "type": "object", "properties": { - "alerting_framework_heath": { - "type": "object", - "description": "This property has a typo. Use `alerting_framework_health` instead.", - "deprecated": true, - "properties": { - "_deprecated": { - "type": "string", - "example": "This state property has a typo, use \"alerting_framework_health\" instead." - }, - "decryption_health": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "ok" - }, - "timestamp": { - "type": "string", - "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" - } - } - }, - "execution_health": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "ok" - }, - "timestamp": { - "type": "string", - "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" - } - } - }, - "read_health": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "ok" - }, - "timestamp": { - "type": "string", - "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" - } - } - } - } - }, "alerting_framework_health": { "type": "object", "description": "Three substates identify the health of the alerting framework: `decryption_health`, `execution_health`, and `read_health`.\n", @@ -3408,21 +3355,6 @@ "status": "ok", "timestamp": "2023-01-13T01:28:00.280Z" } - }, - "alerting_framework_heath": { - "_deprecated": "This state property has a typo, use \"alerting_framework_health\" instead.", - "decryption_health": { - "status": "ok", - "timestamp": "2023-01-13T01:28:00.280Z" - }, - "execution_health": { - "status": "ok", - "timestamp": "2023-01-13T01:28:00.280Z" - }, - "read_health": { - "status": "ok", - "timestamp": "2023-01-13T01:28:00.280Z" - } } } }, @@ -3568,4 +3500,4 @@ "apiKeyAuth": [] } ] -} \ No newline at end of file +} diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.yaml b/x-pack/plugins/alerting/docs/openapi/bundled.yaml index a82f2156f3581..d2b9eafb97e57 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.yaml +++ b/x-pack/plugins/alerting/docs/openapi/bundled.yaml @@ -370,44 +370,6 @@ paths: schema: type: object properties: - alerting_framework_heath: - type: object - description: This property has a typo. Use `alerting_framework_health` instead. - deprecated: true - properties: - _deprecated: - type: string - example: This state property has a typo, use "alerting_framework_health" instead. - decryption_health: - type: object - properties: - status: - type: string - example: ok - timestamp: - type: string - format: date-time - example: '2023-01-13T01:28:00.280Z' - execution_health: - type: object - properties: - status: - type: string - example: ok - timestamp: - type: string - format: date-time - example: '2023-01-13T01:28:00.280Z' - read_health: - type: object - properties: - status: - type: string - example: ok - timestamp: - type: string - format: date-time - example: '2023-01-13T01:28:00.280Z' alerting_framework_health: type: object description: | @@ -719,7 +681,7 @@ paths: summary: Mutes an alert. operationId: muteAlert description: | - You must have `all` privileges for the appropriate Kibana features, depending on the `consumer` and `rule_type_id` of the rule. For example, the **Management > Stack Rules** feature, **Analytics > Discover** and **Machine Learning** features, **Observability**, and **Security** features. If the rule has actions, you must also have `read` privileges for the **Management > Actions and Connectors** feature. + You must have `all` privileges for the appropriate Kibana features, depending on the `consumer` and `rule_type_id` of the rule. For example, the **Management > Stack Rules** feature, **Analytics > Discover** and **Machine Learning** features, **Observability**, and **Security** features. If the rule has actions, you must also have `read` privileges for the **Management > Actions and Connectors** feature. tags: - alerting parameters: @@ -745,7 +707,7 @@ paths: summary: Unmutes an alert. operationId: unmuteAlert description: | - You must have `all` privileges for the appropriate Kibana features, depending on the `consumer` and `rule_type_id` of the rule. For example, the **Management > Stack Rules** feature, **Analytics > Discover** and **Machine Learning** features, **Observability**, and **Security** features. If the rule has actions, you must also have `read` privileges for the **Management > Actions and Connectors** feature. + You must have `all` privileges for the appropriate Kibana features, depending on the `consumer` and `rule_type_id` of the rule. For example, the **Management > Stack Rules** feature, **Analytics > Discover** and **Machine Learning** features, **Observability**, and **Security** features. If the rule has actions, you must also have `read` privileges for the **Management > Actions and Connectors** feature. tags: - alerting parameters: @@ -2312,17 +2274,6 @@ components: read_health: status: ok timestamp: '2023-01-13T01:28:00.280Z' - alerting_framework_heath: - _deprecated: This state property has a typo, use "alerting_framework_health" instead. - decryption_health: - status: ok - timestamp: '2023-01-13T01:28:00.280Z' - execution_health: - status: ok - timestamp: '2023-01-13T01:28:00.280Z' - read_health: - status: ok - timestamp: '2023-01-13T01:28:00.280Z' get_rule_types_response: summary: Retrieve rule types associated with Kibana machine learning features value: diff --git a/x-pack/plugins/alerting/docs/openapi/components/examples/get_health_response.yaml b/x-pack/plugins/alerting/docs/openapi/components/examples/get_health_response.yaml index fcd334cc677cf..0c6101a1a70ec 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/examples/get_health_response.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/examples/get_health_response.yaml @@ -12,14 +12,3 @@ value: read_health: status: ok timestamp: '2023-01-13T01:28:00.280Z' - alerting_framework_heath: - _deprecated: "This state property has a typo, use \"alerting_framework_health\" instead." - decryption_health: - status: ok - timestamp: '2023-01-13T01:28:00.280Z' - execution_health: - status: ok - timestamp: '2023-01-13T01:28:00.280Z' - read_health: - status: ok - timestamp: '2023-01-13T01:28:00.280Z' \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml index 6934bddfa9580..cb2d541f86bed 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml @@ -17,44 +17,6 @@ get: schema: type: object properties: - alerting_framework_heath: - type: object - description: This property has a typo. Use `alerting_framework_health` instead. - deprecated: true - properties: - _deprecated: - type: string - example: "This state property has a typo, use \"alerting_framework_health\" instead." - decryption_health: - type: object - properties: - status: - type: string - example: ok - timestamp: - type: string - format: date-time - example: "2023-01-13T01:28:00.280Z" - execution_health: - type: object - properties: - status: - type: string - example: ok - timestamp: - type: string - format: date-time - example: "2023-01-13T01:28:00.280Z" - read_health: - type: object - properties: - status: - type: string - example: ok - timestamp: - type: string - format: date-time - example: "2023-01-13T01:28:00.280Z" alerting_framework_health: type: object description: > @@ -123,4 +85,4 @@ get: schema: $ref: '../components/schemas/401_response.yaml' servers: - - url: https://localhost:5601 \ No newline at end of file + - url: https://localhost:5601 diff --git a/x-pack/plugins/alerting/public/application/maintenance_windows.tsx b/x-pack/plugins/alerting/public/application/maintenance_windows.tsx index 88ac69cf82b66..4005ee739a12b 100644 --- a/x-pack/plugins/alerting/public/application/maintenance_windows.tsx +++ b/x-pack/plugins/alerting/public/application/maintenance_windows.tsx @@ -7,7 +7,7 @@ import React, { Suspense } from 'react'; import ReactDOM from 'react-dom'; -import { Router, Switch } from 'react-router-dom'; +import { Redirect, Router, Switch } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Route } from '@kbn/shared-ux-router'; import { CoreStart } from '@kbn/core/public'; @@ -23,6 +23,9 @@ const MaintenanceWindowsLazy: React.FC = React.lazy(() => import('../pages/maint const MaintenanceWindowsCreateLazy: React.FC = React.lazy( () => import('../pages/maintenance_windows/maintenance_window_create_page') ); +const MaintenanceWindowsEditLazy: React.FC = React.lazy( + () => import('../pages/maintenance_windows/maintenance_window_edit_page') +); const App = React.memo(() => { return ( @@ -38,6 +41,12 @@ const App = React.memo(() => { + + }> + + + + ); diff --git a/x-pack/plugins/alerting/public/config/paths.ts b/x-pack/plugins/alerting/public/config/paths.ts index 1fdfa48078c0a..7dc8b1b643f2a 100644 --- a/x-pack/plugins/alerting/public/config/paths.ts +++ b/x-pack/plugins/alerting/public/config/paths.ts @@ -12,12 +12,14 @@ export const paths = { alerting: { maintenanceWindows: `/${MAINTENANCE_WINDOWS_APP_ID}`, maintenanceWindowsCreate: '/create', + maintenanceWindowsEdit: '/edit/:maintenanceWindowId', }, }; export const AlertingDeepLinkId = { maintenanceWindows: MAINTENANCE_WINDOWS_APP_ID, maintenanceWindowsCreate: 'create', + maintenanceWindowsEdit: 'edit', }; export type IAlertingDeepLinkId = typeof AlertingDeepLinkId[keyof typeof AlertingDeepLinkId]; diff --git a/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx new file mode 100644 index 0000000000000..e6bd2a4071b27 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx @@ -0,0 +1,116 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useArchiveMaintenanceWindow } from './use_archive_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/archive', () => ({ + archiveMaintenanceWindow: jest.fn(), +})); + +const { archiveMaintenanceWindow } = jest.requireMock( + '../services/maintenance_windows_api/archive' +); + +const maintenanceWindow: MaintenanceWindow = { + title: 'archive', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useArchiveMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + archiveMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: true }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Archived maintenance window 'archive'") + ); + }); + + it('should call onError if api fails', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: true }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to archive maintenance window.') + ); + }); + + it('should call onSuccess if api succeeds (unarchive)', async () => { + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: false }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Unarchived maintenance window 'archive'") + ); + }); + + it('should call onError if api fails (unarchive)', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: false }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to unarchive maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts new file mode 100644 index 0000000000000..2bda74f83b9bf --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.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 { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { archiveMaintenanceWindow } from '../services/maintenance_windows_api/archive'; + +export function useArchiveMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = ({ + maintenanceWindowId, + archive, + }: { + maintenanceWindowId: string; + archive: boolean; + }) => { + return archiveMaintenanceWindow({ http, maintenanceWindowId, archive }); + }; + + return useMutation(mutationFn, { + onSuccess: (data, { archive }) => { + const archiveToast = i18n.translate('xpack.alerting.maintenanceWindowsArchiveSuccess', { + defaultMessage: "Archived maintenance window '{title}'", + values: { + title: data.title, + }, + }); + const unarchiveToast = i18n.translate('xpack.alerting.maintenanceWindowsUnarchiveSuccess', { + defaultMessage: "Unarchived maintenance window '{title}'", + values: { + title: data.title, + }, + }); + toasts.addSuccess(archive ? archiveToast : unarchiveToast); + }, + onError: (error, { archive }) => { + const archiveToast = i18n.translate('xpack.alerting.maintenanceWindowsArchiveFailure', { + defaultMessage: 'Failed to archive maintenance window.', + }); + const unarchiveToast = i18n.translate('xpack.alerting.maintenanceWindowsUnarchiveFailure', { + defaultMessage: 'Failed to unarchive maintenance window.', + }); + toasts.addDanger(archive ? archiveToast : unarchiveToast); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.test.tsx b/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.test.tsx index fb42a59db0fe2..def69ac94beb1 100644 --- a/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.test.tsx +++ b/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.test.tsx @@ -71,4 +71,19 @@ describe('useBreadcrumbs', () => { { text: 'Create' }, ]); }); + + test('set edit maintenance windows breadcrumbs', () => { + renderHook(() => useBreadcrumbs(AlertingDeepLinkId.maintenanceWindowsEdit), { + wrapper: appMockRenderer.AppWrapper, + }); + expect(mockSetBreadcrumbs).toHaveBeenCalledWith([ + { href: '/test', onClick: expect.any(Function), text: 'Stack Management' }, + { + href: AlertingDeepLinkId.maintenanceWindows, + onClick: expect.any(Function), + text: 'Maintenance Windows', + }, + { text: 'Edit' }, + ]); + }); }); diff --git a/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.ts index d33ce68bd0b1b..1553fc59e9176 100644 --- a/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/alerting/public/hooks/use_breadcrumbs.ts @@ -25,10 +25,17 @@ const breadcrumbTitle: Record = { defaultMessage: 'Create', } ), + [AlertingDeepLinkId.maintenanceWindowsEdit]: i18n.translate( + 'xpack.alerting.breadcrumbs.editMaintenanceWindowsLinkText', + { + defaultMessage: 'Edit', + } + ), }; const topLevelBreadcrumb: Record = { [AlertingDeepLinkId.maintenanceWindowsCreate]: AlertingDeepLinkId.maintenanceWindows, + [AlertingDeepLinkId.maintenanceWindowsEdit]: AlertingDeepLinkId.maintenanceWindows, }; function addClickHandlers( diff --git a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts index 08c01bb080055..e710595bc6180 100644 --- a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts @@ -23,12 +23,12 @@ export function useCreateMaintenanceWindow() { }; return useMutation(mutationFn, { - onSuccess: (variables: MaintenanceWindow) => { + onSuccess: (data) => { toasts.addSuccess( i18n.translate('xpack.alerting.maintenanceWindowsCreateSuccess', { defaultMessage: "Created maintenance window '{title}'", values: { - title: variables.title, + title: data.title, }, }) ); diff --git a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.tsx b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.tsx new file mode 100644 index 0000000000000..cdd188af84f8b --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.test.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 { renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFindMaintenanceWindows } from './use_find_maintenance_windows'; + +const mockAddDanger = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/find', () => ({ + findMaintenanceWindows: jest.fn(), +})); + +const { findMaintenanceWindows } = jest.requireMock('../services/maintenance_windows_api/find'); + +let appMockRenderer: AppMockRenderer; + +describe('useFindMaintenanceWindows', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + }); + + it('should call onError if api fails', async () => { + findMaintenanceWindows.mockRejectedValue('This is an error.'); + + renderHook(() => useFindMaintenanceWindows(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Unable to load maintenance windows.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts new file mode 100644 index 0000000000000..10b7f3402aca1 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts @@ -0,0 +1,51 @@ +/* + * 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 { useQuery } from '@tanstack/react-query'; +import { useKibana } from '../utils/kibana_react'; +import { findMaintenanceWindows } from '../services/maintenance_windows_api/find'; + +export const useFindMaintenanceWindows = () => { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const queryFn = () => { + return findMaintenanceWindows({ http }); + }; + + const onErrorFn = (error: Error) => { + if (error) { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsListFailure', { + defaultMessage: 'Unable to load maintenance windows.', + }) + ); + } + }; + + const { + isLoading, + data = [], + refetch, + } = useQuery({ + queryKey: ['findMaintenanceWindows'], + queryFn, + onError: onErrorFn, + refetchOnWindowFocus: false, + retry: false, + cacheTime: 0, + }); + + return { + maintenanceWindows: data, + isLoading, + refetch, + }; +}; diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx new file mode 100644 index 0000000000000..b80dbbae355bc --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx @@ -0,0 +1,110 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFinishAndArchiveMaintenanceWindow } from './use_finish_and_archive_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/finish', () => ({ + finishMaintenanceWindow: jest.fn(), +})); +jest.mock('../services/maintenance_windows_api/archive', () => ({ + archiveMaintenanceWindow: jest.fn(), +})); + +const { finishMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/finish'); +const { archiveMaintenanceWindow } = jest.requireMock( + '../services/maintenance_windows_api/archive' +); + +const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useFinishAndArchiveMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + finishMaintenanceWindow.mockResolvedValue(maintenanceWindow); + archiveMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith( + "Cancelled and archived running maintenance window 'test'" + ) + ); + }); + + it('should call onError if finish api fails', async () => { + finishMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel and archive maintenance window.') + ); + }); + + it('should call onError if archive api fails', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel and archive maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts new file mode 100644 index 0000000000000..d68bf2c89e379 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { finishMaintenanceWindow } from '../services/maintenance_windows_api/finish'; +import { archiveMaintenanceWindow } from '../services/maintenance_windows_api/archive'; + +export function useFinishAndArchiveMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = async (maintenanceWindowId: string) => { + await finishMaintenanceWindow({ http, maintenanceWindowId }); + return archiveMaintenanceWindow({ http, maintenanceWindowId, archive: true }); + }; + + return useMutation(mutationFn, { + onSuccess: (data) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedAndArchiveSuccess', { + defaultMessage: "Cancelled and archived running maintenance window '{title}'", + values: { + title: data.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure', { + defaultMessage: 'Failed to cancel and archive maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx new file mode 100644 index 0000000000000..ed534cb835c8d --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFinishMaintenanceWindow } from './use_finish_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/finish', () => ({ + finishMaintenanceWindow: jest.fn(), +})); + +const { finishMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/finish'); + +const maintenanceWindow: MaintenanceWindow = { + title: 'cancel', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useFinishMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + finishMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useFinishMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Cancelled running maintenance window 'cancel'") + ); + }); + + it('should call onError if api fails', async () => { + finishMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts new file mode 100644 index 0000000000000..7e8aafa1793ad --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { finishMaintenanceWindow } from '../services/maintenance_windows_api/finish'; + +export function useFinishMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = (maintenanceWindowId: string) => { + return finishMaintenanceWindow({ http, maintenanceWindowId }); + }; + + return useMutation(mutationFn, { + onSuccess: (data) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedSuccess', { + defaultMessage: "Cancelled running maintenance window '{title}'", + values: { + title: data.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedFailure', { + defaultMessage: 'Failed to cancel maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_get_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_get_maintenance_window.test.tsx new file mode 100644 index 0000000000000..eaef1f4fc4b99 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_get_maintenance_window.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 { renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useGetMaintenanceWindow } from './use_get_maintenance_window'; + +const mockAddDanger = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/get', () => ({ + getMaintenanceWindow: jest.fn(), +})); + +const { getMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/get'); + +let appMockRenderer: AppMockRenderer; + +describe('useGetMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + }); + + it('should call onError if api fails', async () => { + getMaintenanceWindow.mockRejectedValue(''); + + renderHook(() => useGetMaintenanceWindow('testId'), { + wrapper: appMockRenderer.AppWrapper, + }); + + await waitFor(() => expect(mockAddDanger).toBeCalledWith('Unable to get maintenance window.')); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_get_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_get_maintenance_window.ts new file mode 100644 index 0000000000000..1657ac9fbb30e --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_get_maintenance_window.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 { useQuery } from '@tanstack/react-query'; +import { useKibana } from '../utils/kibana_react'; +import { getMaintenanceWindow } from '../services/maintenance_windows_api/get'; +import { convertFromMaintenanceWindowToForm } from '../pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form'; + +export const useGetMaintenanceWindow = (maintenanceWindowId: string) => { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const queryFn = async () => { + const maintenanceWindow = await getMaintenanceWindow({ http, maintenanceWindowId }); + return convertFromMaintenanceWindowToForm(maintenanceWindow); + }; + + const onErrorFn = () => { + toasts.addDanger( + i18n.translate('xpack.alerting.getMaintenanceWindowFailure', { + defaultMessage: 'Unable to get maintenance window.', + }) + ); + }; + + const { isInitialLoading, isLoading, data, isError } = useQuery({ + queryKey: ['getMaintenanceWindow', maintenanceWindowId], + queryFn, + onError: onErrorFn, + refetchOnWindowFocus: false, + retry: false, + cacheTime: 0, + }); + + return { + maintenanceWindow: data, + isLoading: isLoading || isInitialLoading, + isError, + }; +}; diff --git a/x-pack/plugins/alerting/public/hooks/use_navigation.test.tsx b/x-pack/plugins/alerting/public/hooks/use_navigation.test.tsx index abf7a27f595a1..c4d2b7b71ef27 100644 --- a/x-pack/plugins/alerting/public/hooks/use_navigation.test.tsx +++ b/x-pack/plugins/alerting/public/hooks/use_navigation.test.tsx @@ -9,6 +9,7 @@ import { act, renderHook } from '@testing-library/react-hooks'; import { useCreateMaintenanceWindowNavigation, + useEditMaintenanceWindowsNavigation, useMaintenanceWindowsNavigation, } from './use_navigation'; import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; @@ -90,4 +91,21 @@ describe('useNavigation', () => { }); }); }); + + describe('useEditMaintenanceWindowNavigation', () => { + it('it calls navigateToEditMaintenanceWindow with correct arguments', () => { + const { result } = renderHook(() => useEditMaintenanceWindowsNavigation(), { + wrapper: appMockRenderer.AppWrapper, + }); + + act(() => { + result.current.navigateToEditMaintenanceWindows('1234'); + }); + + expect(mockNavigateTo).toHaveBeenCalledWith(APP_ID, { + deepLinkId: MAINTENANCE_WINDOWS_APP_ID, + path: '/edit/1234', + }); + }); + }); }); diff --git a/x-pack/plugins/alerting/public/hooks/use_navigation.ts b/x-pack/plugins/alerting/public/hooks/use_navigation.ts index 12f64dbdcad30..d1c3673b60abc 100644 --- a/x-pack/plugins/alerting/public/hooks/use_navigation.ts +++ b/x-pack/plugins/alerting/public/hooks/use_navigation.ts @@ -6,6 +6,7 @@ */ import { useCallback } from 'react'; +import { generatePath } from 'react-router-dom'; import type { NavigateToAppOptions } from '@kbn/core/public'; import { useKibana } from '../utils/kibana_react'; import { paths, APP_ID, MAINTENANCE_WINDOWS_APP_ID } from '../config'; @@ -53,3 +54,22 @@ export const useMaintenanceWindowsNavigation = () => { }), }; }; + +export const useEditMaintenanceWindowsNavigation = () => { + const { navigateTo, getAppUrl } = useNavigation(APP_ID); + const deepLinkId = MAINTENANCE_WINDOWS_APP_ID; + + return { + navigateToEditMaintenanceWindows: (maintenanceWindowId: string) => + navigateTo({ + path: generatePath(paths.alerting.maintenanceWindowsEdit, { maintenanceWindowId }), + deepLinkId, + }), + getEditMaintenanceWindowsUrl: (maintenanceWindowId: string, absolute?: boolean) => + getAppUrl({ + path: generatePath(paths.alerting.maintenanceWindowsEdit, { maintenanceWindowId }), + deepLinkId, + absolute, + }), + }; +}; diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx new file mode 100644 index 0000000000000..897b44295d8c0 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useUpdateMaintenanceWindow } from './use_update_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/update', () => ({ + updateMaintenanceWindow: jest.fn(), +})); + +const { updateMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/update'); + +const maintenanceWindow: MaintenanceWindow = { + title: 'updated', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useUpdateMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + updateMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useUpdateMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', maintenanceWindow }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Updated maintenance window 'updated'") + ); + }); + + it('should call onError if api fails', async () => { + updateMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useUpdateMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', maintenanceWindow }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to update maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts new file mode 100644 index 0000000000000..c7dd73724b6df --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.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 { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { updateMaintenanceWindow } from '../services/maintenance_windows_api/update'; + +export function useUpdateMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = ({ + maintenanceWindowId, + maintenanceWindow, + }: { + maintenanceWindowId: string; + maintenanceWindow: MaintenanceWindow; + }) => { + return updateMaintenanceWindow({ http, maintenanceWindowId, maintenanceWindow }); + }; + + return useMutation(mutationFn, { + onSuccess: (variables: MaintenanceWindow) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsUpdateSuccess', { + defaultMessage: "Updated maintenance window '{title}'", + values: { + title: variables.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsUpdateFailure', { + defaultMessage: 'Failed to update maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/center_justified_spinner.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/center_justified_spinner.test.tsx new file mode 100644 index 0000000000000..f3c6bacaa7917 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/center_justified_spinner.test.tsx @@ -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 React from 'react'; + +import { AppMockRenderer, createAppMockRenderer } from '../../../lib/test_utils'; +import { CenterJustifiedSpinner } from './center_justified_spinner'; + +describe('CenterJustifiedSpinner', () => { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + test('it renders', () => { + const result = appMockRenderer.render(); + + expect(result.getByTestId('center-justified-spinner')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/center_justified_spinner.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/center_justified_spinner.tsx new file mode 100644 index 0000000000000..72cf6345623e3 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/center_justified_spinner.tsx @@ -0,0 +1,23 @@ +/* + * 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, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiLoadingSpinnerSize } from '@elastic/eui/src/components/loading/loading_spinner'; + +interface Props { + size?: EuiLoadingSpinnerSize; +} + +export const CenterJustifiedSpinner: React.FunctionComponent = ({ size }) => ( + + + + + +); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx index df382e57c69d0..b2e97dac3389e 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx @@ -12,6 +12,11 @@ import { CreateMaintenanceWindowFormProps, CreateMaintenanceWindowForm, } from './create_maintenance_windows_form'; +import { useUiSetting } from '@kbn/kibana-react-plugin/public'; + +jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ + useUiSetting: jest.fn(), +})); const formProps: CreateMaintenanceWindowFormProps = { onCancel: jest.fn(), @@ -24,6 +29,7 @@ describe('CreateMaintenanceWindowForm', () => { beforeEach(() => { jest.clearAllMocks(); appMockRenderer = createAppMockRenderer(); + (useUiSetting as jest.Mock).mockReturnValue('America/New_York'); }); it('renders all form fields except the recurring form fields', async () => { @@ -33,6 +39,19 @@ describe('CreateMaintenanceWindowForm', () => { expect(result.getByTestId('date-field')).toBeInTheDocument(); expect(result.getByTestId('recurring-field')).toBeInTheDocument(); expect(result.queryByTestId('recurring-form')).not.toBeInTheDocument(); + expect(result.queryByTestId('timezone-field')).not.toBeInTheDocument(); + }); + + it('renders timezone field when the kibana setting is set to browser', async () => { + (useUiSetting as jest.Mock).mockReturnValue('Browser'); + + const result = appMockRenderer.render(); + + expect(result.getByTestId('title-field')).toBeInTheDocument(); + expect(result.getByTestId('date-field')).toBeInTheDocument(); + expect(result.getByTestId('recurring-field')).toBeInTheDocument(); + expect(result.queryByTestId('recurring-form')).not.toBeInTheDocument(); + expect(result.getByTestId('timezone-field')).toBeInTheDocument(); }); it('should initialize the form when no initialValue provided', () => { @@ -60,6 +79,7 @@ describe('CreateMaintenanceWindowForm', () => { title: 'test', startDate: '2023-03-24', endDate: '2023-03-26', + timezone: ['America/Los_Angeles'], recurring: true, }} /> @@ -71,10 +91,12 @@ describe('CreateMaintenanceWindowForm', () => { 'Press the down key to open a popover containing a calendar.' ); const recurringInput = within(result.getByTestId('recurring-field')).getByTestId('input'); + const timezoneInput = within(result.getByTestId('timezone-field')).getByTestId('input'); expect(titleInput).toHaveValue('test'); expect(dateInputs[0]).toHaveValue('03/24/2023 12:00 AM'); expect(dateInputs[1]).toHaveValue('03/26/2023 12:00 AM'); expect(recurringInput).toBeChecked(); + expect(timezoneInput).toHaveTextContent('America/Los_Angeles'); }); }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx index 6778907ade072..0dda6a8890529 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import moment from 'moment'; import { + FIELD_TYPES, Form, getUseField, useForm, @@ -14,7 +15,17 @@ import { UseMultiFields, } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; +import { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiConfirmModal, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiHorizontalRule, +} from '@elastic/eui'; +import { TIMEZONE_OPTIONS as UI_TIMEZONE_OPTIONS } from '@kbn/core-ui-settings-common'; import { FormProps, schema } from './schema'; import * as i18n from '../translations'; @@ -22,8 +33,10 @@ import { RecurringSchedule } from './recurring_schedule_form/recurring_schedule' import { SubmitButton } from './submit_button'; import { convertToRRule } from '../helpers/convert_to_rrule'; import { useCreateMaintenanceWindow } from '../../../hooks/use_create_maintenance_window'; +import { useUpdateMaintenanceWindow } from '../../../hooks/use_update_maintenance_window'; import { useUiSetting } from '../../../utils/kibana_react'; import { DatePickerRangeField } from './fields/date_picker_range_field'; +import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintenance_window'; const UseField = getUseField({ component: Field }); @@ -31,36 +44,61 @@ export interface CreateMaintenanceWindowFormProps { onCancel: () => void; onSuccess: () => void; initialValue?: FormProps; + maintenanceWindowId?: string; } -export const useTimeZone = (): string => { - const timeZone = useUiSetting('dateFormat:tz'); - return timeZone === 'Browser' ? moment.tz.guess() : timeZone; +const useDefaultTimezone = () => { + const kibanaTz: string = useUiSetting('dateFormat:tz'); + if (!kibanaTz || kibanaTz === 'Browser') { + return { defaultTimezone: moment.tz?.guess() ?? 'UTC', isBrowser: true }; + } + return { defaultTimezone: kibanaTz, isBrowser: false }; }; +const TIMEZONE_OPTIONS = UI_TIMEZONE_OPTIONS.map((n) => ({ label: n })) ?? [{ label: 'UTC' }]; export const CreateMaintenanceWindowForm = React.memo( - ({ onCancel, onSuccess, initialValue }) => { - const [defaultDateValue] = useState(moment().toISOString()); - const timezone = useTimeZone(); + ({ onCancel, onSuccess, initialValue, maintenanceWindowId }) => { + const [defaultStartDateValue] = useState(moment().toISOString()); + const [defaultEndDateValue] = useState(moment().add(30, 'minutes').toISOString()); + const [isModalVisible, setIsModalVisible] = useState(false); + const { defaultTimezone, isBrowser } = useDefaultTimezone(); - const { mutate: createMaintenanceWindow } = useCreateMaintenanceWindow(); + const isEditMode = initialValue !== undefined && maintenanceWindowId !== undefined; + const { mutate: createMaintenanceWindow, isLoading: isCreateLoading } = + useCreateMaintenanceWindow(); + const { mutate: updateMaintenanceWindow, isLoading: isUpdateLoading } = + useUpdateMaintenanceWindow(); + const { mutate: archiveMaintenanceWindow } = useArchiveMaintenanceWindow(); const submitMaintenanceWindow = useCallback( async (formData, isValid) => { if (isValid) { const startDate = moment(formData.startDate); const endDate = moment(formData.endDate); - await createMaintenanceWindow( - { - title: formData.title, - duration: endDate.diff(startDate), - rRule: convertToRRule(startDate, timezone, formData.recurringSchedule), - }, - { onSuccess } - ); + const maintenanceWindow = { + title: formData.title, + duration: endDate.diff(startDate), + rRule: convertToRRule( + startDate, + formData.timezone ? formData.timezone[0] : defaultTimezone, + formData.recurringSchedule + ), + }; + if (isEditMode) { + updateMaintenanceWindow({ maintenanceWindowId, maintenanceWindow }, { onSuccess }); + } else { + createMaintenanceWindow(maintenanceWindow, { onSuccess }); + } } }, - [createMaintenanceWindow, onSuccess, timezone] + [ + isEditMode, + maintenanceWindowId, + updateMaintenanceWindow, + createMaintenanceWindow, + onSuccess, + defaultTimezone, + ] ); const { form } = useForm({ @@ -75,10 +113,40 @@ export const CreateMaintenanceWindowForm = React.memo setIsModalVisible(false), []); + const showModal = useCallback(() => setIsModalVisible(true), []); + + const modal = useMemo(() => { + let m; + if (isModalVisible) { + m = ( + { + closeModal(); + archiveMaintenanceWindow( + { maintenanceWindowId: maintenanceWindowId!, archive: true }, + { onSuccess } + ); + }} + cancelButtonText={i18n.CANCEL} + confirmButtonText={i18n.ARCHIVE_TITLE} + defaultFocusedButton="confirm" + buttonColor="danger" + > +

    {i18n.ARCHIVE_CALLOUT_SUBTITLE}

    +
    + ); + } + return m; + }, [closeModal, archiveMaintenanceWindow, isModalVisible, maintenanceWindowId, onSuccess]); return (
    - + - - - - - - {(fields) => ( - - )} - - - - - - + + - - - {isRecurring ? : null} + > + {(fields) => } + + {showTimezone ? ( + + + {i18n.CREATE_FORM_TIMEZONE} + + ), + }, + }} + /> + + ) : null} + + + + + {isRecurring ? : null} + + {isEditMode ? ( + +

    {i18n.ARCHIVE_SUBTITLE}

    + + {i18n.ARCHIVE} + + {modal} +
    + ) : null} - + diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/button_group_field.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/button_group_field.tsx index 65c7e85b79c7a..2c286cb829783 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/button_group_field.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/button_group_field.tsx @@ -50,7 +50,6 @@ export const ButtonGroupField: React.FC = React.memo( {type === 'multi' ? ( = React.memo( /> ) : ( = React.memo( ({ field, showTimeSelect = true, ...rest }) => { + const [today] = useState(moment()); + const { setFieldValue } = useFormContext(); const [form] = useFormData({ watch: [field.path] }); @@ -42,7 +44,7 @@ export const DatePickerField: React.FC = React.memo( showTimeSelect={showTimeSelect} selected={selected} onChange={onChange} - minDate={selected} + minDate={today} fullWidth /> diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/date_picker_range_field.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/date_picker_range_field.tsx index 91b657500e23b..786ff9aba01db 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/date_picker_range_field.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/fields/date_picker_range_field.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import React, { useCallback } from 'react'; -import { Moment } from 'moment'; +import React, { useCallback, useState } from 'react'; +import moment, { Moment } from 'moment'; import { EuiDatePicker, EuiDatePickerRange, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; import { useFormData, @@ -24,18 +24,34 @@ interface DatePickerRangeFieldProps { export const DatePickerRangeField: React.FC = React.memo( ({ fields, showTimeSelect = true, ...rest }) => { + const [today] = useState(moment()); + const { setFieldValue } = useFormContext(); const [form] = useFormData({ watch: [fields.startDate.path, fields.endDate.path] }); const startDate = getSelected(form, fields.startDate.path); const endDate = getSelected(form, fields.endDate.path); - const onChange = useCallback( - (currentDate: Moment | null, path: string) => { + const onStartDateChange = useCallback( + (currentDate: Moment | null) => { + if (currentDate && currentDate.isAfter(endDate)) { + // if the current start date is ahead of the end date + // set the end date to the current start date + 30 min + const updatedEndDate = moment(currentDate).add(30, 'minutes'); + setFieldValue(fields.endDate.path, updatedEndDate); + } + // convert the moment date back into a string if it's not null + setFieldValue(fields.startDate.path, currentDate ? currentDate.toISOString() : currentDate); + }, + [setFieldValue, endDate, fields.endDate.path, fields.startDate.path] + ); + + const onEndDateChange = useCallback( + (currentDate: Moment | null) => { // convert the moment date back into a string if it's not null - setFieldValue(path, currentDate ? currentDate.toISOString() : currentDate); + setFieldValue(fields.endDate.path, currentDate ? currentDate.toISOString() : currentDate); }, - [setFieldValue] + [setFieldValue, fields.endDate.path] ); const isInvalid = startDate.isAfter(endDate); @@ -47,23 +63,23 @@ export const DatePickerRangeField: React.FC = React.m startDateControl={ date && onChange(date, fields.startDate.path)} + onChange={(date) => date && onStartDateChange(date)} startDate={startDate} endDate={endDate} aria-label="Start date" showTimeSelect={showTimeSelect} - minDate={startDate} + minDate={today} /> } endDateControl={ date && onChange(date, fields.endDate.path)} + onChange={(date) => date && onEndDateChange(date)} startDate={startDate} endDate={endDate} aria-label="End date" showTimeSelect={showTimeSelect} - minDate={startDate} + minDate={today} /> } fullWidth diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx new file mode 100644 index 0000000000000..ed3fcb0839441 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx @@ -0,0 +1,119 @@ +/* + * 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 moment from 'moment'; +import { AppMockRenderer, createAppMockRenderer } from '../../../lib/test_utils'; +import { MaintenanceWindowsList } from './maintenance_windows_list'; +import { MaintenanceWindowFindResponse } from '../types'; +import { MaintenanceWindowStatus } from '../../../../common'; + +describe('MaintenanceWindowsList', () => { + const date = moment('2023-04-05').toISOString(); + const endDate = moment('2023-04-05').add(1, 'month').toISOString(); + const items: MaintenanceWindowFindResponse[] = [ + { + id: '1', + total: 100, + title: 'Host maintenance', + enabled: true, + duration: 1, + expirationDate: date, + events: [], + rRule: { dtstart: date, tzid: 'UTC' }, + status: MaintenanceWindowStatus.Running, + eventStartTime: date, + eventEndTime: endDate, + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: date, + updatedAt: date, + }, + { + id: '2', + total: 0, + title: 'Server outage west coast', + enabled: true, + duration: 1, + expirationDate: date, + events: [], + rRule: { dtstart: date, tzid: 'UTC' }, + status: MaintenanceWindowStatus.Upcoming, + eventStartTime: date, + eventEndTime: endDate, + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: date, + updatedAt: date, + }, + { + id: '4', + total: 1000, + title: 'Monthly maintenance window', + enabled: true, + duration: 1, + expirationDate: date, + events: [], + rRule: { dtstart: date, tzid: 'UTC' }, + status: MaintenanceWindowStatus.Finished, + eventStartTime: date, + eventEndTime: endDate, + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: date, + updatedAt: date, + }, + { + id: '5', + total: 200, + title: 'Monthly maintenance window', + enabled: true, + duration: 1, + expirationDate: date, + events: [], + rRule: { dtstart: date, tzid: 'UTC' }, + status: MaintenanceWindowStatus.Archived, + eventStartTime: date, + eventEndTime: endDate, + createdBy: 'elastic', + updatedBy: 'elastic', + createdAt: date, + updatedAt: date, + }, + ]; + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + test('it renders', () => { + const result = appMockRenderer.render( + {}} loading={false} items={items} /> + ); + + expect(result.getAllByTestId('list-item')).toHaveLength(items.length); + + // check the title + expect(result.getAllByText('Host maintenance')).toHaveLength(1); + expect(result.getAllByText('Server outage west coast')).toHaveLength(1); + expect(result.getAllByText('Monthly maintenance window')).toHaveLength(2); + + // check the status + expect(result.getAllByText('Running')).toHaveLength(1); + expect(result.getAllByText('Upcoming')).toHaveLength(1); + expect(result.getAllByText('Finished')).toHaveLength(1); + expect(result.getAllByText('Archived')).toHaveLength(1); + + // check the startDate formatting + expect(result.getAllByText('04/05/23 00:00 AM')).toHaveLength(4); + + // check the endDate formatting + expect(result.getAllByText('05/05/23 00:00 AM')).toHaveLength(4); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx new file mode 100644 index 0000000000000..705219b9baa2a --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx @@ -0,0 +1,177 @@ +/* + * 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 { + formatDate, + EuiInMemoryTable, + EuiBasicTableColumn, + EuiFlexGroup, + EuiFlexItem, + SearchFilterConfig, + EuiBadge, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { MaintenanceWindowFindResponse, SortDirection } from '../types'; +import * as i18n from '../translations'; +import { useEditMaintenanceWindowsNavigation } from '../../../hooks/use_navigation'; +import { STATUS_DISPLAY, STATUS_SORT } from '../constants'; +import { UpcomingEventsPopover } from './upcoming_events_popover'; +import { MaintenanceWindowStatus } from '../../../../common'; +import { StatusFilter } from './status_filter'; +import { TableActionsPopover } from './table_actions_popover'; +import { useFinishMaintenanceWindow } from '../../../hooks/use_finish_maintenance_window'; +import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintenance_window'; +import { useFinishAndArchiveMaintenanceWindow } from '../../../hooks/use_finish_and_archive_maintenance_window'; + +interface MaintenanceWindowsListProps { + loading: boolean; + items: MaintenanceWindowFindResponse[]; + refreshData: () => void; +} + +const columns: Array> = [ + { + field: 'title', + name: i18n.NAME, + truncateText: true, + }, + { + field: 'status', + name: i18n.TABLE_STATUS, + render: (status: MaintenanceWindowStatus) => { + return ( + {STATUS_DISPLAY[status].label} + ); + }, + sortable: ({ status }) => STATUS_SORT[status], + }, + { + field: 'eventStartTime', + name: i18n.TABLE_START_TIME, + dataType: 'date', + render: (startDate: string, item: MaintenanceWindowFindResponse) => { + return ( + + {formatDate(startDate, 'MM/DD/YY HH:mm A')} + {item.events.length > 1 ? ( + + + + ) : null} + + ); + }, + sortable: true, + }, + { + field: 'eventEndTime', + name: i18n.TABLE_END_TIME, + dataType: 'date', + render: (endDate: string) => formatDate(endDate, 'MM/DD/YY HH:mm A'), + }, +]; + +const sorting = { + sort: { + field: 'status', + direction: SortDirection.asc, + }, +}; + +const rowProps = (item: MaintenanceWindowFindResponse) => ({ + className: item.status, + 'data-test-subj': 'list-item', +}); + +const search: { filters: SearchFilterConfig[] } = { + filters: [ + { + type: 'custom_component', + component: StatusFilter, + }, + ], +}; + +export const MaintenanceWindowsList = React.memo( + ({ loading, items, refreshData }) => { + const { euiTheme } = useEuiTheme(); + const { navigateToEditMaintenanceWindows } = useEditMaintenanceWindowsNavigation(); + const onEdit = useCallback( + (id) => navigateToEditMaintenanceWindows(id), + [navigateToEditMaintenanceWindows] + ); + const { mutate: finishMaintenanceWindow, isLoading: isLoadingFinish } = + useFinishMaintenanceWindow(); + const onCancel = useCallback( + (id) => finishMaintenanceWindow(id, { onSuccess: () => refreshData() }), + [finishMaintenanceWindow, refreshData] + ); + const { mutate: archiveMaintenanceWindow, isLoading: isLoadingArchive } = + useArchiveMaintenanceWindow(); + const onArchive = useCallback( + (id: string, archive: boolean) => + archiveMaintenanceWindow( + { maintenanceWindowId: id, archive }, + { onSuccess: () => refreshData() } + ), + [archiveMaintenanceWindow, refreshData] + ); + const { mutate: finishAndArchiveMaintenanceWindow, isLoading: isLoadingFinishAndArchive } = + useFinishAndArchiveMaintenanceWindow(); + const onCancelAndArchive = useCallback( + (id: string) => finishAndArchiveMaintenanceWindow(id, { onSuccess: () => refreshData() }), + [finishAndArchiveMaintenanceWindow, refreshData] + ); + + const tableCss = useMemo(() => { + return css` + .euiTableRow { + &.running { + background-color: ${euiTheme.colors.highlight}; + } + } + `; + }, [euiTheme.colors.highlight]); + + const actions: Array> = [ + { + name: '', + render: ({ status, id }: { status: MaintenanceWindowStatus; id: string }) => { + return ( + + ); + }, + }, + ]; + + return ( + + ); + } +); +MaintenanceWindowsList.displayName = 'MaintenanceWindowsList'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/page_header.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/page_header.tsx index 6c2d85afecd71..97602e9f6c972 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/page_header.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/page_header.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/react'; @@ -23,6 +23,15 @@ export const styles = { `, }; +export const ExperimentalBadge = React.memo(() => ( + +)); +ExperimentalBadge.displayName = 'ExperimentalBadge'; + interface TitleProps { title: string; description?: string; @@ -31,9 +40,16 @@ const Title = React.memo(({ title, description }) => { return ( - -

    {}

    -
    + + + +

    {}

    +
    +
    + + + +
    {description ? ( <> diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.tsx index 4b8b5cf8b0791..ea46cc55be84a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.tsx @@ -6,7 +6,15 @@ */ import React, { useMemo } from 'react'; import moment from 'moment'; -import { EuiFormLabel, EuiHorizontalRule, EuiSplitPanel } from '@elastic/eui'; +import { + EuiComboBox, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiHorizontalRule, + EuiSpacer, + EuiSplitPanel, +} from '@elastic/eui'; import { getUseField, useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { getWeekdayInfo } from '../../helpers/get_weekday_info'; @@ -28,9 +36,11 @@ import { FormProps } from '../schema'; const UseField = getUseField({ component: Field }); export const RecurringSchedule: React.FC = React.memo(() => { - const [{ startDate, recurringSchedule }] = useFormData({ + const [{ startDate, endDate, timezone, recurringSchedule }] = useFormData({ watch: [ 'startDate', + 'endDate', + 'timezone', 'recurringSchedule.frequency', 'recurringSchedule.interval', 'recurringSchedule.ends', @@ -103,14 +113,43 @@ export const RecurringSchedule: React.FC = React.memo(() => { }} /> {recurringSchedule?.ends === EndsOptions.ON_DATE ? ( - + <> + + + + + + {timezone ? ( + + + {i18n.CREATE_FORM_TIMEZONE} + + } + /> + + ) : null} + + ) : null} {recurringSchedule?.ends === EndsOptions.AFTER_X ? ( = { title: { type: FIELD_TYPES.TEXT, - label: i18n.CREATE_FORM_NAME, + label: i18n.NAME, validations: [ { validator: emptyField(i18n.CREATE_FORM_NAME_REQUIRED), @@ -46,6 +46,7 @@ export const schema: FormSchema = { }, startDate: {}, endDate: {}, + timezone: {}, recurring: { type: FIELD_TYPES.TOGGLE, label: i18n.CREATE_FORM_REPEAT, @@ -72,11 +73,7 @@ export const schema: FormSchema = { defaultValue: EndsOptions.NEVER, validations: [], }, - until: { - label: '', - defaultValue: moment().endOf('day').toISOString(), - validations: [], - }, + until: {}, count: { label: '', type: FIELD_TYPES.TEXT, diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/status_filter.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/status_filter.test.tsx new file mode 100644 index 0000000000000..f7cb8023323ef --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/status_filter.test.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 { Query } from '@elastic/eui'; +import { fireEvent } from '@testing-library/dom'; +import React from 'react'; + +import { AppMockRenderer, createAppMockRenderer } from '../../../lib/test_utils'; +import { StatusFilter } from './status_filter'; + +describe('StatusFilter', () => { + let appMockRenderer: AppMockRenderer; + const onChange = jest.fn(); + const query = Query.parse(''); + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + test('it renders', () => { + const result = appMockRenderer.render(); + + expect(result.getByTestId('status-filter-button')).toBeInTheDocument(); + }); + + test('it shows the popover', () => { + const result = appMockRenderer.render(); + + fireEvent.click(result.getByTestId('status-filter-button')); + expect(result.getByTestId('status-filter-running')).toBeInTheDocument(); + expect(result.getByTestId('status-filter-upcoming')).toBeInTheDocument(); + expect(result.getByTestId('status-filter-finished')).toBeInTheDocument(); + expect(result.getByTestId('status-filter-archived')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/status_filter.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/status_filter.tsx new file mode 100644 index 0000000000000..d9f7802050d8d --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/status_filter.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useCallback } from 'react'; +import { EuiFilterButton, EuiPopover, EuiFilterGroup, EuiFilterSelectItem } from '@elastic/eui'; +import { CustomComponentProps } from '@elastic/eui/src/components/search_bar/filters/custom_component_filter'; +import { STATUS_OPTIONS } from '../constants'; +import * as i18n from '../translations'; + +export const StatusFilter: React.FC = React.memo(({ query, onChange }) => { + const [selectedOptions, setSelectedOptions] = useState([]); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onFilterItemClick = useCallback( + (newOption: string) => () => { + const options = selectedOptions.includes(newOption) + ? selectedOptions.filter((option) => option !== newOption) + : [...selectedOptions, newOption]; + setSelectedOptions(options); + + let q = query.removeSimpleFieldClauses('status').removeOrFieldClauses('status'); + if (options.length > 0) { + q = options.reduce((acc, curr) => { + return acc.addOrFieldValue('status', curr, true, 'eq'); + }, q); + } + onChange?.(q); + }, + [query, onChange, selectedOptions] + ); + + const openPopover = useCallback(() => { + setIsPopoverOpen((prevIsOpen) => !prevIsOpen); + }, [setIsPopoverOpen]); + + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, [setIsPopoverOpen]); + + return ( + + 0} + numActiveFilters={selectedOptions.length} + numFilters={selectedOptions.length} + onClick={openPopover} + > + {i18n.TABLE_STATUS} + + } + > + <> + {STATUS_OPTIONS.map((status) => { + return ( + + {status.name} + + ); + })} + + + + ); +}); +StatusFilter.displayName = 'StatusFilter'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.test.tsx index c208312a78f2b..7f5906dfc7e00 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.test.tsx @@ -39,7 +39,7 @@ describe('SubmitButton', () => { it('it renders', async () => { const result = appMockRenderer.render( - + ); @@ -49,7 +49,7 @@ describe('SubmitButton', () => { it('it submits', async () => { const result = appMockRenderer.render( - + ); @@ -60,7 +60,7 @@ describe('SubmitButton', () => { it('it disables when submitting', async () => { const result = appMockRenderer.render( - + ); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.tsx index 1e48d96026e78..544c093539210 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/submit_button.tsx @@ -11,18 +11,23 @@ import { EuiButton } from '@elastic/eui'; import { useFormContext } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import * as i18n from '../translations'; -export const SubmitButton: React.FC = React.memo(() => { +interface SubmitButtonProps { + isLoading: boolean; + editMode?: boolean; +} + +export const SubmitButton: React.FC = React.memo(({ isLoading, editMode }) => { const { submit, isSubmitting } = useFormContext(); return ( - {i18n.CREATE_MAINTENANCE_WINDOW} + {editMode ? i18n.SAVE_MAINTENANCE_WINDOW : i18n.CREATE_MAINTENANCE_WINDOW} ); }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.test.tsx new file mode 100644 index 0000000000000..2b1a1057084f5 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.test.tsx @@ -0,0 +1,101 @@ +/* + * 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 { fireEvent } from '@testing-library/dom'; +import React from 'react'; + +import { AppMockRenderer, createAppMockRenderer } from '../../../lib/test_utils'; +import { TableActionsPopover } from './table_actions_popover'; +import { MaintenanceWindowStatus } from '../../../../common'; + +describe('TableActionsPopover', () => { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + test('it renders', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + + expect(result.getByTestId('table-actions-icon-button')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is running', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-cancel')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-cancel-and-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is upcoming', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is finished', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is archived', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-unarchive')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx new file mode 100644 index 0000000000000..4742ede93d53c --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx @@ -0,0 +1,230 @@ +/* + * 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 { + EuiButtonIcon, + EuiConfirmModal, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, +} from '@elastic/eui'; +import * as i18n from '../translations'; +import { MaintenanceWindowStatus } from '../../../../common'; + +interface TableActionsPopoverProps { + id: string; + status: MaintenanceWindowStatus; + onEdit: (id: string) => void; + onCancel: (id: string) => void; + onArchive: (id: string, archive: boolean) => void; + onCancelAndArchive: (id: string) => void; +} +type ModalType = 'cancel' | 'cancelAndArchive' | 'archive' | 'unarchive'; +type ActionType = ModalType | 'edit'; + +export const TableActionsPopover: React.FC = React.memo( + ({ id, status, onEdit, onCancel, onArchive, onCancelAndArchive }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [isModalVisible, setIsModalVisible] = useState(false); + const [modalType, setModalType] = useState(); + + const onButtonClick = useCallback(() => { + setIsPopoverOpen((open) => !open); + }, []); + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, []); + + const closeModal = useCallback(() => setIsModalVisible(false), []); + const showModal = useCallback((type: ModalType) => { + setModalType(type); + setIsModalVisible(true); + }, []); + + const modal = useMemo(() => { + const modals = { + cancel: { + props: { + title: i18n.CANCEL_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onCancel(id); + }, + cancelButtonText: i18n.CANCEL_MODAL_BUTTON, + confirmButtonText: i18n.CANCEL_MODAL_TITLE, + }, + subtitle: i18n.CANCEL_MODAL_SUBTITLE, + }, + cancelAndArchive: { + props: { + title: i18n.CANCEL_AND_ARCHIVE_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onCancelAndArchive(id); + }, + cancelButtonText: i18n.CANCEL_MODAL_BUTTON, + confirmButtonText: i18n.CANCEL_AND_ARCHIVE_MODAL_TITLE, + }, + subtitle: i18n.CANCEL_AND_ARCHIVE_MODAL_SUBTITLE, + }, + archive: { + props: { + title: i18n.ARCHIVE_TITLE, + onConfirm: () => { + closeModal(); + onArchive(id, true); + }, + cancelButtonText: i18n.CANCEL, + confirmButtonText: i18n.ARCHIVE_TITLE, + }, + subtitle: i18n.ARCHIVE_SUBTITLE, + }, + unarchive: { + props: { + title: i18n.UNARCHIVE_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onArchive(id, false); + }, + cancelButtonText: i18n.CANCEL, + confirmButtonText: i18n.UNARCHIVE_MODAL_TITLE, + }, + subtitle: i18n.UNARCHIVE_MODAL_SUBTITLE, + }, + }; + let m; + if (isModalVisible && modalType) { + const modalProps = modals[modalType]; + m = ( + +

    {modalProps.subtitle}

    +
    + ); + } + return m; + }, [id, modalType, isModalVisible, closeModal, onArchive, onCancel, onCancelAndArchive]); + + const items = useMemo(() => { + const menuItems = { + edit: ( + { + closePopover(); + onEdit(id); + }} + > + {i18n.TABLE_ACTION_EDIT} + + ), + cancel: ( + { + closePopover(); + showModal('cancel'); + }} + > + {i18n.TABLE_ACTION_CANCEL} + + ), + cancelAndArchive: ( + { + closePopover(); + showModal('cancelAndArchive'); + }} + > + {i18n.TABLE_ACTION_CANCEL_AND_ARCHIVE} + + ), + archive: ( + { + closePopover(); + showModal('archive'); + }} + > + {i18n.ARCHIVE} + + ), + unarchive: ( + { + closePopover(); + showModal('unarchive'); + }} + > + {i18n.TABLE_ACTION_UNARCHIVE} + + ), + }; + const statusMenuItemsMap: Record = { + running: ['edit', 'cancel', 'cancelAndArchive'], + upcoming: ['edit', 'archive'], + finished: ['edit', 'archive'], + archived: ['unarchive'], + }; + return statusMenuItemsMap[status].map((type) => menuItems[type]); + }, [id, status, onEdit, closePopover, showModal]); + + const button = useMemo( + () => ( + + ), + [onButtonClick] + ); + + return ( + <> + + + + + + + + {modal} + + ); + } +); +TableActionsPopover.displayName = 'TableActionsPopover'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/upcoming_events_popover.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/upcoming_events_popover.test.tsx new file mode 100644 index 0000000000000..574bb7d1f7549 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/upcoming_events_popover.test.tsx @@ -0,0 +1,67 @@ +/* + * 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 { fireEvent } from '@testing-library/react'; +import * as React from 'react'; +import { AppMockRenderer, createAppMockRenderer } from '../../../lib/test_utils'; +import { UpcomingEventsPopover } from './upcoming_events_popover'; +import { MaintenanceWindowStatus } from '../../../../common'; + +describe('rule_actions_popover', () => { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + it('renders the top 3 events', () => { + const result = appMockRenderer.render( + + ); + + const popoverButton = result.getByTestId('upcoming-events-icon-button'); + expect(popoverButton).toBeInTheDocument(); + fireEvent.click(popoverButton); + + expect(result.getByTestId('upcoming-events-popover-title')).toBeInTheDocument(); + expect(result.getByTestId('upcoming-events-popover-title')).toHaveTextContent( + 'Repeats every Friday' + ); + expect(result.getAllByTestId('upcoming-events-popover-item').length).toBe(3); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/upcoming_events_popover.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/upcoming_events_popover.tsx new file mode 100644 index 0000000000000..a20907a2e1236 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/upcoming_events_popover.tsx @@ -0,0 +1,127 @@ +/* + * 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 moment from 'moment'; +import { findIndex } from 'lodash'; +import { + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiIcon, + EuiPopover, + EuiPopoverTitle, + EuiSpacer, + EuiText, + formatDate, +} from '@elastic/eui'; +import * as i18n from '../translations'; +import { recurringSummary } from '../helpers/recurring_summary'; +import { getPresets } from '../helpers/get_presets'; +import { MaintenanceWindowFindResponse } from '../types'; +import { convertFromMaintenanceWindowToForm } from '../helpers/convert_from_maintenance_window_to_form'; + +interface UpcomingEventsPopoverProps { + maintenanceWindowFindResponse: MaintenanceWindowFindResponse; +} + +export const UpcomingEventsPopover: React.FC = React.memo( + ({ maintenanceWindowFindResponse }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const onButtonClick = useCallback(() => { + setIsPopoverOpen((open) => !open); + }, []); + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, []); + + const { startDate, recurringSchedule, topEvents, presets } = useMemo(() => { + const maintenanceWindow = convertFromMaintenanceWindowToForm(maintenanceWindowFindResponse); + const date = moment(maintenanceWindow.startDate); + const currentEventIndex = findIndex( + maintenanceWindowFindResponse.events, + (event) => + event.gte === maintenanceWindowFindResponse.eventStartTime && + event.lte === maintenanceWindowFindResponse.eventEndTime + ); + return { + startDate: date, + recurringSchedule: maintenanceWindow.recurringSchedule, + topEvents: maintenanceWindowFindResponse.events.slice( + currentEventIndex + 1, + currentEventIndex + 4 + ), + presets: getPresets(date), + }; + }, [maintenanceWindowFindResponse]); + + return ( + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + anchorPosition="downCenter" + > + + {i18n.CREATE_FORM_RECURRING_SUMMARY_PREFIX( + recurringSummary(startDate, recurringSchedule, presets) + )} + + + + + {i18n.UPCOMING} + + + + {topEvents.map((event, index) => ( + + + + + + + + {formatDate(event.gte, 'MM/DD/YY HH:mm A')} + + + + {index < topEvents.length - 1 ? ( + + ) : null} + + ))} + + + ); + } +); +UpcomingEventsPopover.displayName = 'UpcomingEventsPopover'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts index 2b06ff8fd41a5..27b10e804693d 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts @@ -7,6 +7,7 @@ import { invert, mapValues } from 'lodash'; import moment from 'moment'; import * as i18n from './translations'; +import { MaintenanceWindowStatus } from '../../../common'; // TODO - consolidate enum with backend export enum Frequency { @@ -105,3 +106,24 @@ export const ISO_WEEKDAYS_TO_RRULE: Record = { export const RRULE_WEEKDAYS_TO_ISO_WEEKDAYS = mapValues(invert(ISO_WEEKDAYS_TO_RRULE), (v) => Number(v) ); + +export const STATUS_DISPLAY = { + [MaintenanceWindowStatus.Running]: { color: 'primary', label: i18n.TABLE_STATUS_RUNNING }, + [MaintenanceWindowStatus.Upcoming]: { color: 'warning', label: i18n.TABLE_STATUS_UPCOMING }, + [MaintenanceWindowStatus.Finished]: { color: 'success', label: i18n.TABLE_STATUS_FINISHED }, + [MaintenanceWindowStatus.Archived]: { color: 'default', label: i18n.TABLE_STATUS_ARCHIVED }, +}; + +export const STATUS_SORT = { + [MaintenanceWindowStatus.Running]: 0, + [MaintenanceWindowStatus.Upcoming]: 1, + [MaintenanceWindowStatus.Finished]: 2, + [MaintenanceWindowStatus.Archived]: 3, +}; + +export const STATUS_OPTIONS = [ + { value: MaintenanceWindowStatus.Running, name: i18n.TABLE_STATUS_RUNNING }, + { value: MaintenanceWindowStatus.Upcoming, name: i18n.TABLE_STATUS_UPCOMING }, + { value: MaintenanceWindowStatus.Finished, name: i18n.TABLE_STATUS_FINISHED }, + { value: MaintenanceWindowStatus.Archived, name: i18n.TABLE_STATUS_ARCHIVED }, +]; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts new file mode 100644 index 0000000000000..c892f7db66729 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts @@ -0,0 +1,329 @@ +/* + * 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 moment from 'moment'; + +import { Frequency } from '../constants'; +import { RRuleFrequency } from '../types'; +import { convertFromMaintenanceWindowToForm } from './convert_from_maintenance_window_to_form'; + +describe('convertFromMaintenanceWindowToForm', () => { + const title = 'test'; + const today = '2023-03-22'; + const startDate = moment(today); + const endDate = moment(today).add(2, 'days'); + const duration = endDate.diff(startDate); + + test('should convert a maintenance window that is not recurring', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.YEARLY, + count: 1, + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: false, + }); + }); + + test('should convert a maintenance window that is recurring on a daily schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.DAILY, + interval: 1, + byweekday: ['WE'], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + byweekday: { 1: false, 2: false, 3: true, 4: false, 5: false, 6: false, 7: false }, + ends: 'never', + frequency: Frequency.DAILY, + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a daily schedule until', () => { + const until = moment(today).add(1, 'month').toISOString(); + + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.DAILY, + interval: 1, + byweekday: ['WE'], + until, + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + byweekday: { 1: false, 2: false, 3: true, 4: false, 5: false, 6: false, 7: false }, + ends: 'ondate', + until, + frequency: Frequency.DAILY, + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a daily schedule after x', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.DAILY, + interval: 1, + byweekday: ['WE'], + count: 3, + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + byweekday: { 1: false, 2: false, 3: true, 4: false, 5: false, 6: false, 7: false }, + ends: 'afterx', + count: 3, + frequency: Frequency.DAILY, + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a weekly schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.WEEKLY, + interval: 1, + byweekday: ['WE'], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + ends: 'never', + frequency: Frequency.WEEKLY, + byweekday: { 1: false, 2: false, 3: true, 4: false, 5: false, 6: false, 7: false }, + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a monthly schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.MONTHLY, + interval: 1, + byweekday: ['+4WE'], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + ends: 'never', + frequency: Frequency.MONTHLY, + bymonth: 'weekday', + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a yearly schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.YEARLY, + interval: 1, + bymonth: [3], + bymonthday: [22], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + ends: 'never', + frequency: Frequency.YEARLY, + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a custom daily schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.DAILY, + interval: 1, + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + customFrequency: Frequency.DAILY, + ends: 'never', + frequency: 'CUSTOM', + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a custom weekly schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.WEEKLY, + interval: 1, + byweekday: ['WE', 'TH'], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + byweekday: { 1: false, 2: false, 3: true, 4: true, 5: false, 6: false, 7: false }, + customFrequency: Frequency.WEEKLY, + ends: 'never', + frequency: 'CUSTOM', + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a custom monthly by day schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.MONTHLY, + interval: 1, + bymonthday: [22], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + bymonth: 'day', + customFrequency: Frequency.MONTHLY, + ends: 'never', + frequency: 'CUSTOM', + interval: 1, + }, + }); + }); + + test('should convert a maintenance window that is recurring on a custom yearly schedule', () => { + const maintenanceWindow = convertFromMaintenanceWindowToForm({ + title, + duration, + rRule: { + dtstart: startDate.toISOString(), + tzid: 'UTC', + freq: RRuleFrequency.YEARLY, + interval: 3, + bymonth: [3], + bymonthday: [22], + }, + }); + + expect(maintenanceWindow).toEqual({ + title, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + timezone: ['UTC'], + recurring: true, + recurringSchedule: { + customFrequency: Frequency.YEARLY, + ends: 'never', + frequency: 'CUSTOM', + interval: 3, + }, + }); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts new file mode 100644 index 0000000000000..954e6b8bd2658 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts @@ -0,0 +1,95 @@ +/* + * 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 moment from 'moment'; +import { has } from 'lodash'; +import { MaintenanceWindow } from '../types'; +import { EndsOptions, Frequency } from '../constants'; +import { FormProps, RecurringScheduleFormProps } from '../components/schema'; +import { getInitialByWeekday } from './get_initial_by_weekday'; +import { RRuleParams } from '../../../../common'; + +export const convertFromMaintenanceWindowToForm = ( + maintenanceWindow: MaintenanceWindow +): FormProps => { + const startDate = maintenanceWindow.rRule.dtstart; + const endDate = moment(startDate).add(maintenanceWindow.duration); + // maintenance window is considered recurring if interval is defined + const recurring = has(maintenanceWindow, 'rRule.interval'); + const form: FormProps = { + title: maintenanceWindow.title, + startDate, + endDate: endDate.toISOString(), + timezone: [maintenanceWindow.rRule.tzid], + recurring, + }; + if (!recurring) return form; + + const rRule = maintenanceWindow.rRule; + const isCustomFrequency = isCustom(rRule); + const frequency = rRule.freq?.toString() as Frequency; + const ends = rRule.until + ? EndsOptions.ON_DATE + : rRule.count + ? EndsOptions.AFTER_X + : EndsOptions.NEVER; + + const recurringSchedule: RecurringScheduleFormProps = { + frequency: isCustomFrequency ? 'CUSTOM' : frequency, + interval: rRule.interval, + ends, + }; + + if (isCustomFrequency) { + recurringSchedule.customFrequency = frequency; + } + + if (rRule.until) { + recurringSchedule.until = rRule.until; + } + if (rRule.count) { + recurringSchedule.count = rRule.count; + } + if (frequency !== Frequency.MONTHLY && rRule.byweekday) { + recurringSchedule.byweekday = getInitialByWeekday( + rRule.byweekday as string[], + moment(startDate) + ); + } + if (frequency === Frequency.MONTHLY) { + if (rRule.byweekday) { + recurringSchedule.bymonth = 'weekday'; + } else if (rRule.bymonthday) { + recurringSchedule.bymonth = 'day'; + } + } + + form.recurringSchedule = recurringSchedule; + + return form; +}; + +const isCustom = (rRule: RRuleParams) => { + const freq = rRule.freq?.toString() as Frequency; + // interval is greater than 1 + if (rRule.interval && rRule.interval > 1) { + return true; + } + // frequency is daily and no weekdays are selected + if (freq && freq === Frequency.DAILY && !rRule.byweekday) { + return true; + } + // frequency is weekly and there are multiple weekdays selected + if (freq && freq === Frequency.WEEKLY && rRule.byweekday && rRule.byweekday.length > 1) { + return true; + } + // frequency is monthly and by month day is selected + if (freq && freq === Frequency.MONTHLY && rRule.bymonthday) { + return true; + } + return false; +}; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts index 737bb47f4aa33..7eaecba5169b8 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts @@ -121,7 +121,7 @@ describe('convertToRRule', () => { tzid: 'UTC', freq: RRuleFrequency.YEARLY, interval: 1, - bymonth: [2], + bymonth: [3], bymonthday: [22], }); }); @@ -209,7 +209,7 @@ describe('convertToRRule', () => { tzid: 'UTC', freq: RRuleFrequency.YEARLY, interval: 3, - bymonth: [2], + bymonth: [3], bymonthday: [22], }); }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts index 0f20d1c335365..90706165c717b 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts @@ -6,20 +6,21 @@ */ import { Moment } from 'moment'; -import { RRule, RRuleFrequency, RRuleFrequencyMap } from '../types'; +import { RRuleFrequency, RRuleFrequencyMap } from '../types'; import { Frequency, ISO_WEEKDAYS_TO_RRULE } from '../constants'; import { getNthByWeekday } from './get_nth_by_weekday'; import { RecurringScheduleFormProps } from '../components/schema'; import { getPresets } from './get_presets'; +import { RRuleParams } from '../../../../common'; export const convertToRRule = ( startDate: Moment, timezone: string, recurringForm?: RecurringScheduleFormProps -): RRule => { +): RRuleParams => { const presets = getPresets(startDate); - const rRule: RRule = { + const rRule: RRuleParams = { dtstart: startDate.toISOString(), tzid: timezone, }; @@ -33,45 +34,44 @@ export const convertToRRule = ( count: 1, }; - if (recurringForm) { - let form = recurringForm; - if (recurringForm.frequency !== 'CUSTOM') { - form = { ...recurringForm, ...presets[recurringForm.frequency] }; - } - - const frequency = form.customFrequency ? form.customFrequency : (form.frequency as Frequency); - rRule.freq = RRuleFrequencyMap[frequency]; + let form = recurringForm; + if (recurringForm.frequency !== 'CUSTOM') { + form = { ...recurringForm, ...presets[recurringForm.frequency] }; + } - rRule.interval = form.interval; + const frequency = form.customFrequency ? form.customFrequency : (form.frequency as Frequency); + rRule.freq = RRuleFrequencyMap[frequency]; - if (form.until) { - rRule.until = form.until; - } + rRule.interval = form.interval; - if (form.count) { - rRule.count = form.count; - } + if (form.until) { + rRule.until = form.until; + } - if (form.byweekday) { - const byweekday = form.byweekday; - rRule.byweekday = Object.keys(byweekday) - .filter((k) => byweekday[k] === true) - .map((n) => ISO_WEEKDAYS_TO_RRULE[Number(n)]); - } + if (form.count) { + rRule.count = form.count; + } - if (form.bymonth) { - if (form.bymonth === 'day') { - rRule.bymonthday = [startDate.date()]; - } else if (form.bymonth === 'weekday') { - rRule.byweekday = [getNthByWeekday(startDate)]; - } - } + if (form.byweekday) { + const byweekday = form.byweekday; + rRule.byweekday = Object.keys(byweekday) + .filter((k) => byweekday[k] === true) + .map((n) => ISO_WEEKDAYS_TO_RRULE[Number(n)]); + } - if (frequency === Frequency.YEARLY) { - rRule.bymonth = [startDate.month()]; + if (form.bymonth) { + if (form.bymonth === 'day') { rRule.bymonthday = [startDate.date()]; + } else if (form.bymonth === 'weekday') { + rRule.byweekday = [getNthByWeekday(startDate)]; } } + if (frequency === Frequency.YEARLY) { + // rRule expects 1 based indexing for months + rRule.bymonth = [startDate.month() + 1]; + rRule.bymonthday = [startDate.date()]; + } + return rRule; }; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx index 80a35e4a02aee..fa9b54122562d 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx @@ -6,23 +6,32 @@ */ import React, { useCallback } from 'react'; -import { EuiPageHeader } from '@elastic/eui'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiPageHeader, + EuiPageHeaderSection, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; import { useKibana } from '../../utils/kibana_react'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { EmptyPrompt } from './components/empty_prompt'; import * as i18n from './translations'; import { useCreateMaintenanceWindowNavigation } from '../../hooks/use_navigation'; import { AlertingDeepLinkId } from '../../config'; +import { MaintenanceWindowsList } from './components/maintenance_windows_list'; +import { useFindMaintenanceWindows } from '../../hooks/use_find_maintenance_windows'; +import { CenterJustifiedSpinner } from './components/center_justified_spinner'; +import { ExperimentalBadge } from './components/page_header'; export const MaintenanceWindowsPage = React.memo(() => { const { docLinks } = useKibana().services; const { navigateToCreateMaintenanceWindow } = useCreateMaintenanceWindowNavigation(); - const { isLoading, maintenanceWindowsList } = { - isLoading: false, - maintenanceWindowsList: { total: 0 }, - }; - const { total } = maintenanceWindowsList || {}; + const { isLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows(); useBreadcrumbs(AlertingDeepLinkId.maintenanceWindows); @@ -30,17 +39,53 @@ export const MaintenanceWindowsPage = React.memo(() => { navigateToCreateMaintenanceWindow(); }, [navigateToCreateMaintenanceWindow]); + const refreshData = useCallback(() => refetch(), [refetch]); + + const showEmptyPrompt = !isLoading && maintenanceWindows.length === 0; + + if (isLoading) { + return ; + } + return ( <> - - - {!isLoading && total === 0 ? ( + + + + + +

    {i18n.MAINTENANCE_WINDOWS}

    +
    +
    + + + +
    + + +

    {i18n.MAINTENANCE_WINDOWS_DESCRIPTION}

    +
    +
    + {!showEmptyPrompt ? ( + + + {i18n.CREATE_NEW_BUTTON} + + + ) : null} +
    + {showEmptyPrompt ? ( - ) : null} + ) : ( + <> + + + + )} ); }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/maintenance_window_edit_page.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/maintenance_window_edit_page.tsx new file mode 100644 index 0000000000000..d8969988ee7d9 --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/maintenance_window_edit_page.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 React from 'react'; +import { useParams } from 'react-router-dom'; +import { EuiPageSection, EuiSpacer } from '@elastic/eui'; + +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useMaintenanceWindowsNavigation } from '../../hooks/use_navigation'; +import * as i18n from './translations'; +import { PageHeader } from './components/page_header'; +import { CreateMaintenanceWindowForm } from './components/create_maintenance_windows_form'; +import { AlertingDeepLinkId } from '../../config'; +import { useGetMaintenanceWindow } from '../../hooks/use_get_maintenance_window'; +import { CenterJustifiedSpinner } from './components/center_justified_spinner'; + +export const MaintenanceWindowsEditPage = React.memo(() => { + const { navigateToMaintenanceWindows } = useMaintenanceWindowsNavigation(); + + useBreadcrumbs(AlertingDeepLinkId.maintenanceWindowsEdit); + + const { maintenanceWindowId } = useParams<{ maintenanceWindowId: string }>(); + const { maintenanceWindow, isLoading, isError } = useGetMaintenanceWindow(maintenanceWindowId); + + if (isError) { + navigateToMaintenanceWindows(); + } + + if (!maintenanceWindow || isLoading) { + return ; + } + + return ( + + + + + + ); +}); +MaintenanceWindowsEditPage.displayName = 'MaintenanceWindowsEditPage'; +// eslint-disable-next-line import/no-default-export +export { MaintenanceWindowsEditPage as default }; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts index be8eb9e891bf0..65f24411a2a1a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts @@ -20,6 +20,13 @@ export const MAINTENANCE_WINDOWS_DESCRIPTION = i18n.translate( } ); +export const CREATE_NEW_BUTTON = i18n.translate( + 'xpack.alerting.maintenanceWindows.createNewButton', + { + defaultMessage: 'Create window', + } +); + export const EMPTY_PROMPT_BUTTON = i18n.translate( 'xpack.alerting.maintenanceWindows.emptyPrompt.button', { @@ -70,12 +77,9 @@ export const MAINTENANCE_WINDOWS_RETURN_LINK = i18n.translate( } ); -export const CREATE_FORM_NAME = i18n.translate( - 'xpack.alerting.maintenanceWindows.createForm.name', - { - defaultMessage: 'Name', - } -); +export const NAME = i18n.translate('xpack.alerting.maintenanceWindows.name', { + defaultMessage: 'Name', +}); export const CREATE_FORM_NAME_REQUIRED = i18n.translate( 'xpack.alerting.maintenanceWindows.createForm.nameFieldRequiredError', @@ -98,6 +102,13 @@ export const CREATE_FORM_SCHEDULE_INVALID = i18n.translate( } ); +export const CREATE_FORM_TIMEZONE = i18n.translate( + 'xpack.alerting.maintenanceWindows.createForm.timezone', + { + defaultMessage: 'Time zone', + } +); + export const CREATE_FORM_REPEAT = i18n.translate( 'xpack.alerting.maintenanceWindows.createForm.repeat', { @@ -369,3 +380,191 @@ export const CREATE_FORM_YEARLY_BY_MONTH_SUMMARY = (date: string) => export const CANCEL = i18n.translate('xpack.alerting.maintenanceWindows.createForm.cancel', { defaultMessage: 'Cancel', }); + +export const TABLE_ALERTS = i18n.translate('xpack.alerting.maintenanceWindows.table.alerts', { + defaultMessage: 'Alerts', +}); + +// Add this tooltip to the Alerts column heading on the MW table +export const TABLE_ALERTS_TOOLTIP = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.alertsTooltip', + { + defaultMessage: 'The total number of alerts created in the maintenance window.', + } +); + +export const TABLE_STATUS = i18n.translate('xpack.alerting.maintenanceWindows.table.status', { + defaultMessage: 'Status', +}); + +export const TABLE_STATUS_RUNNING = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.statusRunning', + { + defaultMessage: 'Running', + } +); + +export const TABLE_STATUS_UPCOMING = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.statusUpcoming', + { + defaultMessage: 'Upcoming', + } +); + +export const TABLE_STATUS_FINISHED = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.statusFinished', + { + defaultMessage: 'Finished', + } +); + +export const TABLE_STATUS_ARCHIVED = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.statusArchived', + { + defaultMessage: 'Archived', + } +); + +export const TABLE_START_TIME = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.startTime', + { + defaultMessage: 'Start time', + } +); + +export const TABLE_END_TIME = i18n.translate('xpack.alerting.maintenanceWindows.table.endTime', { + defaultMessage: 'End time', +}); + +export const TABLE_ACTION_EDIT = i18n.translate('xpack.alerting.maintenanceWindows.table.edit', { + defaultMessage: 'Edit', +}); + +export const EDIT_MAINTENANCE_WINDOW = i18n.translate( + 'xpack.alerting.maintenanceWindows.edit.maintenanceWindow', + { + defaultMessage: 'Edit maintenance window', + } +); + +export const SAVE_MAINTENANCE_WINDOW = i18n.translate( + 'xpack.alerting.maintenanceWindows.save.maintenanceWindow', + { + defaultMessage: 'Save maintenance window', + } +); + +export const TABLE_ACTION_CANCEL = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.cancel', + { + defaultMessage: 'Cancel', + } +); + +export const CANCEL_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.title', + { + defaultMessage: 'Cancel maintenance window', + } +); + +export const CANCEL_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.subtitle', + { + defaultMessage: + 'Rule notifications resume immediately. Running maintenance window events are canceled; upcoming events are unaffected.', + } +); + +export const CANCEL_MODAL_BUTTON = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.button', + { + defaultMessage: 'Keep running', + } +); + +export const TABLE_ACTION_CANCEL_AND_ARCHIVE = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.cancelAndArchive', + { + defaultMessage: 'Cancel and archive', + } +); + +export const CANCEL_AND_ARCHIVE_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelAndArchiveModal.title', + { + defaultMessage: 'Cancel and archive maintenance window', + } +); + +export const CANCEL_AND_ARCHIVE_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelAndArchiveModal.subtitle', + { + defaultMessage: + 'Rule notifications resume immediately. All running and upcoming maintenance window events are canceled and the window is queued for deletion.', + } +); + +export const ARCHIVE = i18n.translate('xpack.alerting.maintenanceWindows.archive', { + defaultMessage: 'Archive', +}); + +export const ARCHIVE_TITLE = i18n.translate('xpack.alerting.maintenanceWindows.archive.title', { + defaultMessage: 'Archive maintenance window', +}); + +export const ARCHIVE_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.archive.subtitle', + { + defaultMessage: + 'Upcoming maintenance window events are canceled and the window is queued for deletion.', + } +); + +export const TABLE_ACTION_UNARCHIVE = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.unarchive', + { + defaultMessage: 'Unarchive', + } +); + +export const UNARCHIVE_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.unarchiveModal.title', + { + defaultMessage: 'Unarchive maintenance window', + } +); + +export const UNARCHIVE_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.unarchiveModal.subtitle', + { + defaultMessage: 'Upcoming maintenance window events are scheduled.', + } +); + +export const ARCHIVE_CALLOUT_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.archiveCallout.subtitle', + { + defaultMessage: + 'The changes you have made here will not be saved. Are you sure you want to discard these unsaved changes and archive this maintenance window?', + } +); + +export const EXPERIMENTAL_LABEL = i18n.translate( + 'xpack.alerting.maintenanceWindows.badge.experimentalLabel', + { + defaultMessage: 'Technical preview', + } +); + +export const EXPERIMENTAL_DESCRIPTION = i18n.translate( + 'xpack.alerting.maintenanceWindows.badge.experimentalDescription', + { + defaultMessage: + 'This functionality is in technical preview and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.', + } +); + +export const UPCOMING = i18n.translate('xpack.alerting.maintenanceWindows.upcoming', { + defaultMessage: 'Upcoming', +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts index e2a3eb3e732cb..e6be4af6b8675 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts @@ -5,6 +5,11 @@ * 2.0. */ +import { + MaintenanceWindow as MaintenanceWindowServerSide, + MaintenanceWindowModificationMetadata, +} from '../../../common'; + export enum RRuleFrequency { YEARLY = 0, MONTHLY = 1, @@ -19,23 +24,14 @@ export const RRuleFrequencyMap = { '3': RRuleFrequency.DAILY, }; -export interface RecurringSchedule { - freq: RRuleFrequency; - interval: number; - until?: string; - count?: number; - byweekday?: string[]; - bymonthday?: number[]; - bymonth?: number[]; -} +export type MaintenanceWindow = Pick; -export type RRule = Partial & { - dtstart: string; - tzid: string; -}; +export type MaintenanceWindowFindResponse = MaintenanceWindowServerSide & + MaintenanceWindowModificationMetadata & { + total: number; + }; -export interface MaintenanceWindow { - title: string; - duration: number; - rRule: RRule; +export enum SortDirection { + asc = 'asc', + desc = 'desc', } diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts new file mode 100644 index 0000000000000..8f0e44eaf2eb9 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { archiveMaintenanceWindow } from './archive'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('archiveMaintenanceWindow', () => { + test('should call archive maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await archiveMaintenanceWindow({ + http, + maintenanceWindowId: '123', + archive: true, + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123/_archive", + Object { + "body": "{\\"archive\\":true}", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts new file mode 100644 index 0000000000000..fe07ebb04b38e --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts @@ -0,0 +1,35 @@ +/* + * 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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function archiveMaintenanceWindow({ + http, + maintenanceWindowId, + archive, +}: { + http: HttpSetup; + maintenanceWindowId: string; + archive: boolean; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}/_archive`, + { body: JSON.stringify({ archive }) } + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/create.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/create.ts index e4032edbef640..73323b257d3e2 100644 --- a/x-pack/plugins/alerting/public/services/maintenance_windows_api/create.ts +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/create.ts @@ -12,12 +12,12 @@ import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; const rewriteBodyRequest: RewriteResponseCase = ({ rRule, ...res }) => ({ ...res, - r_rule: { ...rRule }, + r_rule: rRule, }); const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ ...rest, - rRule: { ...rRule }, + rRule, }); export async function createMaintenanceWindow({ diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.test.ts new file mode 100644 index 0000000000000..de38f6de5af00 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.test.ts @@ -0,0 +1,83 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindowFindResponse } from '../../pages/maintenance_windows/types'; +import { findMaintenanceWindows } from './find'; +import { MaintenanceWindowStatus } from '../../../common'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('findMaintenanceWindows', () => { + test('should call find maintenance windows api', async () => { + const apiResponse = { + data: [ + { + id: '1', + title: 'test', + enabled: true, + duration: 1, + expiration_date: '2023-03-23T19:16:21.293Z', + events: [], + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + status: MaintenanceWindowStatus.Upcoming, + event_start_time: '2023-03-23T19:16:21.293Z', + event_end_time: '2023-03-23T19:16:21.293Z', + created_by: null, + updated_by: null, + created_at: '2023-03-23T19:16:21.293Z', + updated_at: '2023-03-23T19:16:21.293Z', + total: 1000, + }, + ], + total: 1, + }; + http.get.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindowFindResponse[] = [ + { + id: '1', + title: 'test', + enabled: true, + duration: 1, + expirationDate: '2023-03-23T19:16:21.293Z', + events: [], + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + status: MaintenanceWindowStatus.Upcoming, + eventStartTime: '2023-03-23T19:16:21.293Z', + eventEndTime: '2023-03-23T19:16:21.293Z', + createdBy: null, + updatedBy: null, + createdAt: '2023-03-23T19:16:21.293Z', + updatedAt: '2023-03-23T19:16:21.293Z', + total: 1000, + }, + ]; + + const result = await findMaintenanceWindows({ http }); + expect(result).toEqual(maintenanceWindow); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/_find", + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.ts new file mode 100644 index 0000000000000..e54028d30dfa8 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/find.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindowFindResponse } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes = (results: { + data: Array>; + total: number; +}): MaintenanceWindowFindResponse[] => { + return results.data.map((item) => transform(item)); +}; + +const transform: RewriteRequestCase = ({ + expiration_date: expirationDate, + r_rule: rRule, + event_start_time: eventStartTime, + event_end_time: eventEndTime, + created_by: createdBy, + updated_by: updatedBy, + created_at: createdAt, + updated_at: updatedAt, + ...rest +}) => ({ + ...rest, + expirationDate, + rRule, + eventStartTime, + eventEndTime, + createdBy, + updatedBy, + createdAt, + updatedAt, +}); + +export async function findMaintenanceWindows({ + http, +}: { + http: HttpSetup; +}): Promise { + const res = await http.get<{ + data: Array>; + total: number; + }>(`${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/_find`); + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts new file mode 100644 index 0000000000000..a67b7246a64f5 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts @@ -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 { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { finishMaintenanceWindow } from './finish'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('finishMaintenanceWindow', () => { + test('should call finish maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await finishMaintenanceWindow({ + http, + maintenanceWindowId: '123', + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123/_finish", + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts new file mode 100644 index 0000000000000..910fad0bee1c3 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function finishMaintenanceWindow({ + http, + maintenanceWindowId, +}: { + http: HttpSetup; + maintenanceWindowId: string; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}/_finish` + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/get.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/get.test.ts new file mode 100644 index 0000000000000..f19d77e55b405 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/get.test.ts @@ -0,0 +1,51 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { getMaintenanceWindow } from './get'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('getMaintenanceWindow', () => { + test('should call get maintenance windows api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.get.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await getMaintenanceWindow({ http, maintenanceWindowId: '123' }); + expect(result).toEqual(maintenanceWindow); + expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123", + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/get.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/get.ts new file mode 100644 index 0000000000000..d353f2b45124b --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/get.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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function getMaintenanceWindow({ + http, + maintenanceWindowId, +}: { + http: HttpSetup; + maintenanceWindowId: string; +}): Promise { + const res = await http.get>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}` + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/update.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/update.test.ts new file mode 100644 index 0000000000000..4c46ac4519928 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/update.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { updateMaintenanceWindow } from './update'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('updateMaintenanceWindow', () => { + test('should call update maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await updateMaintenanceWindow({ + http, + maintenanceWindowId: '123', + maintenanceWindow, + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123", + Object { + "body": "{\\"title\\":\\"test\\",\\"duration\\":1,\\"r_rule\\":{\\"dtstart\\":\\"2023-03-23T19:16:21.293Z\\",\\"tzid\\":\\"America/New_York\\",\\"freq\\":3,\\"interval\\":1,\\"byweekday\\":[\\"TH\\"]}}", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/update.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/update.ts new file mode 100644 index 0000000000000..c8328c23e825d --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/update.ts @@ -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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase, RewriteResponseCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRequest: RewriteResponseCase = ({ rRule, ...res }) => ({ + ...res, + r_rule: rRule, +}); + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function updateMaintenanceWindow({ + http, + maintenanceWindowId, + maintenanceWindow, +}: { + http: HttpSetup; + maintenanceWindowId: string; + maintenanceWindow: MaintenanceWindow; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}`, + { body: JSON.stringify(rewriteBodyRequest(maintenanceWindow)) } + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index 1fff89d527d5a..36877db2726c6 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -7,7 +7,7 @@ import { v4 as uuidV4 } from 'uuid'; import { get, isEmpty } from 'lodash'; -import { ALERT_INSTANCE_ID } from '@kbn/rule-data-utils'; +import { ALERT_INSTANCE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import { CombinedSummarizedAlerts } from '../types'; import { AlertInstanceMeta, @@ -277,7 +277,9 @@ export class Alert< } return !summarizedAlerts.all.data.some( - (alert) => get(alert, ALERT_INSTANCE_ID) === this.getId() + (alert) => + get(alert, ALERT_INSTANCE_ID) === this.getId() || + get(alert, ALERT_RULE_UUID) === this.getId() ); } } diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts index 3fb1877756d15..e17a13e0b78d6 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.test.ts @@ -15,6 +15,7 @@ import { ruleRunMetricsStoreMock } from '../lib/rule_run_metrics_store.mock'; import { getAlertsForNotification, processAlerts } from '../lib'; import { logAlerts } from '../task_runner/log_alerts'; import { DEFAULT_FLAPPING_SETTINGS } from '../../common/rules_settings'; +import { schema } from '@kbn/config-schema'; const scheduleActions = jest.fn(); const replaceState = jest.fn(() => ({ scheduleActions })); @@ -86,6 +87,9 @@ const ruleType: jest.Mocked = { producer: 'alerts', cancelAlertsOnRuleTimeout: true, ruleTaskTimeout: '5m', + validate: { + params: schema.any(), + }, }; const testAlert1 = { @@ -236,6 +240,7 @@ describe('Legacy Alerts Client', () => { shouldLogAndScheduleActionsForAlerts: true, flappingSettings: DEFAULT_FLAPPING_SETTINGS, notifyWhen: RuleNotifyWhen.CHANGE, + maintenanceWindowIds: ['window-id1', 'window-id2'], }); expect(processAlerts).toHaveBeenCalledWith({ @@ -284,6 +289,7 @@ describe('Legacy Alerts Client', () => { ruleRunMetricsStore, canSetRecoveryContext: false, shouldPersistAlerts: true, + maintenanceWindowIds: ['window-id1', 'window-id2'], }); expect(alertsClient.getProcessedAlerts('active')).toEqual({ diff --git a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts index 8fce6782cd2f1..b06ca4ffada79 100644 --- a/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/legacy_alerts_client.ts @@ -117,6 +117,7 @@ export class LegacyAlertsClient< shouldLogAndScheduleActionsForAlerts, flappingSettings, notifyWhen, + maintenanceWindowIds, }: { eventLogger: AlertingEventLogger; ruleLabel: string; @@ -124,6 +125,7 @@ export class LegacyAlertsClient< ruleRunMetricsStore: RuleRunMetricsStore; flappingSettings: RulesSettingsFlappingProperties; notifyWhen: RuleNotifyWhenType | null; + maintenanceWindowIds?: string[]; }) { const { newAlerts: processedAlertsNew, @@ -176,6 +178,7 @@ export class LegacyAlertsClient< ruleRunMetricsStore, canSetRecoveryContext: this.options.ruleType.doesSetRecoveryContext ?? false, shouldPersistAlerts: shouldLogAndScheduleActionsForAlerts, + maintenanceWindowIds, }); } diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts index 4949d958ce4f0..333623588aa64 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.test.ts @@ -24,6 +24,7 @@ import { v4 as uuidv4 } from 'uuid'; import { RecoveredActionGroup } from '../../common'; import { RegistryRuleType } from '../rule_type_registry'; import { AlertingAuthorizationFilterType } from './alerting_authorization_kuery'; +import { schema } from '@kbn/config-schema'; const ruleTypeRegistry = ruleTypeRegistryMock.create(); const features: jest.Mocked = featuresPluginMock.createStart(); @@ -196,6 +197,9 @@ beforeEach(() => { return { state: {} }; }, producer: 'myApp', + validate: { + params: schema.any(), + }, })); features.getKibanaFeatures.mockReturnValue([ myAppFeature, diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index a2f0a08b7512b..6cee7568692cb 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -74,7 +74,7 @@ export const plugin = (initContext: PluginInitializerContext) => new AlertingPlu export const config: PluginConfigDescriptor = { schema: configSchema, - deprecations: ({ renameFromRoot }) => [ + deprecations: ({ renameFromRoot, deprecate }) => [ renameFromRoot('xpack.alerts.healthCheck', 'xpack.alerting.healthCheck', { level: 'warning' }), renameFromRoot( 'xpack.alerts.invalidateApiKeysTask.interval', @@ -89,5 +89,9 @@ export const config: PluginConfigDescriptor = { renameFromRoot('xpack.alerting.defaultRuleTaskTimeout', 'xpack.alerting.rules.run.timeout', { level: 'warning', }), + deprecate('maxEphemeralActionsPerAlert', 'a future version', { + level: 'warning', + message: `Configuring "xpack.alerting.maxEphemeralActionsPerAlert" is deprecated and will be removed in a future version. Remove this setting to increase action execution resiliency.`, + }), ], }; diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts index dbcb75a7816e7..45f8d84fd4a98 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts @@ -42,6 +42,7 @@ describe('alertSummaryFromEventLog', () => { "lastRun": undefined, "muteAll": false, "name": "rule-name", + "revision": 0, "ruleTypeId": "123", "status": "OK", "statusEndDate": "2020-06-18T01:00:00.000Z", @@ -88,6 +89,7 @@ describe('alertSummaryFromEventLog', () => { "lastRun": undefined, "muteAll": true, "name": "rule-name-2", + "revision": 0, "ruleTypeId": "456", "status": "OK", "statusEndDate": "2020-06-18T03:00:00.000Z", diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts index 7c3df21a3281d..d316b1b5bf01c 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts @@ -40,6 +40,7 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) average: 0, valuesWithTimestamp: {}, }, + revision: rule.revision, }; const alerts = new Map(); @@ -86,6 +87,10 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) status.flapping = true; } + if (event?.kibana?.alert?.maintenance_window_ids?.length) { + status.maintenanceWindowIds = event.kibana.alert.maintenance_window_ids as string[]; + } + switch (action) { case EVENT_LOG_ACTIONS.newInstance: status.activeStartDate = timeStamp; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts index ff62ddaea7ff4..00a9bf7221ef3 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts @@ -15,6 +15,7 @@ const createAlertingEventLoggerMock = () => { setRuleName: jest.fn(), setExecutionSucceeded: jest.fn(), setExecutionFailed: jest.fn(), + setMaintenanceWindowIds: jest.fn(), logTimeout: jest.fn(), logAlert: jest.fn(), logAction: jest.fn(), diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 90c3645e151e5..2b6075cd49f49 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -27,6 +27,7 @@ import { import { RuleRunMetrics } from '../rule_run_metrics_store'; import { EVENT_LOG_ACTIONS } from '../../plugin'; import { TaskRunnerTimerSpan } from '../../task_runner/task_runner_timer'; +import { schema } from '@kbn/config-schema'; const mockNow = '2020-01-01T02:00:00.000Z'; const eventLogger = eventLoggerMock.create(); @@ -42,6 +43,9 @@ const ruleType: jest.Mocked = { executor: jest.fn(), producer: 'alerts', ruleTaskTimeout: '1m', + validate: { + params: schema.any(), + }, }; const context: RuleContextOpts = { @@ -51,6 +55,7 @@ const context: RuleContextOpts = { spaceId: 'test-space', executionId: 'abcd-efgh-ijklmnop', taskScheduledAt: new Date('2020-01-01T00:00:00.000Z'), + ruleRevision: 0, }; const contextWithScheduleDelay = { ...context, taskScheduleDelay: 7200000 }; @@ -68,6 +73,7 @@ const alert = { duration: '2343252346', }, flapping: false, + maintenanceWindowIds: ['window-id1', 'window-id2'], }; const action = { @@ -279,6 +285,51 @@ describe('AlertingEventLogger', () => { }); }); + describe('setMaintenanceWindowIds()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => + alertingEventLogger.setMaintenanceWindowIds([]) + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => + alertingEventLogger.setMaintenanceWindowIds([]) + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + it('should update event maintenance window IDs correctly', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setMaintenanceWindowIds([]); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + maintenance_window_ids: [], + }, + }, + }); + + alertingEventLogger.setMaintenanceWindowIds(['test-id-1', 'test-id-2']); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + maintenance_window_ids: ['test-id-1', 'test-id-2'], + }, + }, + }); + }); + }); + describe('logTimeout()', () => { test('should throw error if alertingEventLogger has not been initialized', () => { expect(() => alertingEventLogger.logTimeout()).toThrowErrorMatchingInlineSnapshot( @@ -1068,6 +1119,7 @@ describe('createAlertRecord', () => { expect(record.kibana?.alert?.rule?.rule_type_id).toEqual(contextWithName.ruleType.id); expect(record.kibana?.alert?.rule?.consumer).toEqual(contextWithName.consumer); expect(record.kibana?.alert?.rule?.execution?.uuid).toEqual(contextWithName.executionId); + expect(record.kibana?.alert?.maintenance_window_ids).toEqual(alert.maintenanceWindowIds); expect(record.kibana?.alerting?.instance_id).toEqual(alert.id); expect(record.kibana?.alerting?.action_group_id).toEqual(alert.group); expect(record.kibana?.saved_objects).toEqual([ diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index f84936e7ea6e4..3af760488a259 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -30,6 +30,7 @@ export interface RuleContextOpts { executionId: string; taskScheduledAt: Date; ruleName?: string; + ruleRevision?: number; } type RuleContext = RuleContextOpts & { @@ -50,6 +51,7 @@ interface AlertOpts { group?: string; state?: AlertInstanceState; flapping: boolean; + maintenanceWindowIds?: string[]; } interface ActionOpts { @@ -137,6 +139,14 @@ export class AlertingEventLogger { updateEvent(this.event, { message, outcome: 'success', alertingOutcome: 'success' }); } + public setMaintenanceWindowIds(maintenanceWindowIds: string[]) { + if (!this.isInitialized || !this.event) { + throw new Error('AlertingEventLogger not initialized'); + } + + updateEvent(this.event, { maintenanceWindowIds }); + } + public setExecutionFailed(message: string, errorMessage: string) { if (!this.isInitialized || !this.event) { throw new Error('AlertingEventLogger not initialized'); @@ -256,6 +266,8 @@ export function createAlertRecord(context: RuleContextOpts, alert: AlertOpts) { ], ruleName: context.ruleName, flapping: alert.flapping, + maintenanceWindowIds: alert.maintenanceWindowIds, + ruleRevision: context.ruleRevision, }); } @@ -286,6 +298,7 @@ export function createActionExecuteRecord(context: RuleContextOpts, action: Acti ], ruleName: context.ruleName, alertSummary: action.alertSummary, + ruleRevision: context.ruleRevision, }); } @@ -312,6 +325,7 @@ export function createExecuteTimeoutRecord(context: RuleContextOpts) { }, ], ruleName: context.ruleName, + ruleRevision: context.ruleRevision, }); } @@ -324,6 +338,7 @@ export function initializeExecuteRecord(context: RuleContext) { spaceId: context.spaceId, executionId: context.executionId, action: EVENT_LOG_ACTIONS.execute, + ruleRevision: context.ruleRevision, task: { scheduled: context.taskScheduledAt.toISOString(), scheduleDelay: Millis2Nanos * context.taskScheduleDelay, @@ -349,11 +364,22 @@ interface UpdateEventOpts { reason?: string; metrics?: RuleRunMetrics; timings?: TaskRunnerTimings; + maintenanceWindowIds?: string[]; } export function updateEvent(event: IEvent, opts: UpdateEventOpts) { - const { message, outcome, error, ruleName, status, reason, metrics, timings, alertingOutcome } = - opts; + const { + message, + outcome, + error, + ruleName, + status, + reason, + metrics, + timings, + alertingOutcome, + maintenanceWindowIds, + } = opts; if (!event) { throw new Error('Cannot update event because it is not initialized.'); } @@ -429,4 +455,10 @@ export function updateEvent(event: IEvent, opts: UpdateEventOpts) { ...timings, }; } + + if (maintenanceWindowIds) { + event.kibana = event.kibana || {}; + event.kibana.alert = event.kibana.alert || {}; + event.kibana.alert.maintenance_window_ids = maintenanceWindowIds; + } } diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index 185aa098a49d7..6b18d1aac93dd 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -8,6 +8,9 @@ import { createAlertEventLogRecordObject } from './create_alert_event_log_record_object'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { RecoveredActionGroup } from '../types'; +import { schema } from '@kbn/config-schema'; + +const MAINTENANCE_WINDOW_IDS = ['test-1', 'test-2']; describe('createAlertEventLogRecordObject', () => { const ruleType: jest.Mocked = { @@ -20,6 +23,9 @@ describe('createAlertEventLogRecordObject', () => { recoveryActionGroup: RecoveredActionGroup, executor: jest.fn(), producer: 'alerts', + validate: { + params: schema.any(), + }, }; test('created alert event "execute-start"', async () => { @@ -30,6 +36,7 @@ describe('createAlertEventLogRecordObject', () => { ruleType, consumer: 'rule-consumer', action: 'execute-start', + ruleRevision: 0, timestamp: '1970-01-01T00:00:00.000Z', task: { scheduled: '1970-01-01T00:00:00.000Z', @@ -44,6 +51,7 @@ describe('createAlertEventLogRecordObject', () => { }, ], spaceId: 'default', + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, }) ).toStrictEqual({ '@timestamp': '1970-01-01T00:00:00.000Z', @@ -59,8 +67,10 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, + maintenance_window_ids: MAINTENANCE_WINDOW_IDS, }, saved_objects: [ { @@ -99,6 +109,7 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'message text here', namespace: 'default', + ruleRevision: 0, state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -113,6 +124,7 @@ describe('createAlertEventLogRecordObject', () => { }, ], spaceId: 'default', + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, }) ).toStrictEqual({ event: { @@ -130,8 +142,10 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, + maintenance_window_ids: MAINTENANCE_WINDOW_IDS, }, alerting: { action_group_id: 'group 1', @@ -172,6 +186,7 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'action execution start', namespace: 'default', + ruleRevision: 0, state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -196,6 +211,7 @@ describe('createAlertEventLogRecordObject', () => { ongoing: 3, recovered: 1, }, + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, }) ).toStrictEqual({ event: { @@ -213,8 +229,10 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, + maintenance_window_ids: MAINTENANCE_WINDOW_IDS, }, alerting: { action_group_id: 'group 1', diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index f987c297d0217..251df68e5267f 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -42,6 +42,8 @@ interface CreateAlertEventLogRecordParams { ongoing: number; recovered: number; }; + maintenanceWindowIds?: string[]; + ruleRevision?: number; } export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecordParams): Event { @@ -60,6 +62,8 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor flapping, alertUuid, alertSummary, + maintenanceWindowIds, + ruleRevision, } = params; const alerting = params.instanceId || group || alertSummary @@ -92,8 +96,10 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor kibana: { alert: { ...(flapping !== undefined ? { flapping } : {}), + ...(maintenanceWindowIds ? { maintenance_window_ids: maintenanceWindowIds } : {}), ...(alertUuid ? { uuid: alertUuid } : {}), rule: { + revision: ruleRevision, rule_type_id: ruleType.id, ...(consumer ? { consumer } : {}), ...(executionId diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts index 6a57baaacef27..bb38fb7a98bfa 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts @@ -270,7 +270,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -283,6 +283,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -477,7 +478,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -490,6 +491,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -684,7 +686,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -697,6 +699,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -774,7 +777,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -861,7 +864,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -880,6 +883,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -958,6 +964,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -981,6 +988,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1022,7 +1030,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1112,7 +1120,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1131,6 +1139,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1209,6 +1220,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1232,6 +1244,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1273,7 +1286,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1355,7 +1368,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1374,6 +1387,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1452,6 +1468,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1475,6 +1492,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1516,7 +1534,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1603,7 +1621,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1622,6 +1640,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1700,6 +1721,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '61bb867b-661a-471f-bf92-23471afa10b3', @@ -1723,6 +1745,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts index b65499de20d45..44e1f7bbe98c1 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts @@ -41,6 +41,7 @@ const NUMBER_OF_NEW_ALERTS_FIELD = 'kibana.alert.rule.execution.metrics.alert_co const NUMBER_OF_RECOVERED_ALERTS_FIELD = 'kibana.alert.rule.execution.metrics.alert_counts.recovered'; const EXECUTION_UUID_FIELD = 'kibana.alert.rule.execution.uuid'; +const MAINTENANCE_WINDOW_IDS_FIELD = 'kibana.alert.maintenance_window_ids'; const Millis2Nanos = 1000 * 1000; @@ -82,7 +83,8 @@ interface IExecutionUuidAggBucket extends estypes.AggregationsStringTermsBucketK numActiveAlerts: estypes.AggregationsMaxAggregate; numRecoveredAlerts: estypes.AggregationsMaxAggregate; numNewAlerts: estypes.AggregationsMaxAggregate; - outcomeAndMessage: estypes.AggregationsTopHitsAggregate; + outcomeMessageAndMaintenanceWindow: estypes.AggregationsTopHitsAggregate; + maintenanceWindowIds: estypes.AggregationsTopHitsAggregate; }; actionExecution: { actionOutcomes: IActionExecution; @@ -401,7 +403,7 @@ export function getExecutionLogAggregation({ field: DURATION_FIELD, }, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -414,6 +416,7 @@ export function getExecutionLogAggregation({ SPACE_ID_FIELD, RULE_NAME_FIELD, ALERTING_OUTCOME_FIELD, + MAINTENANCE_WINDOW_IDS_FIELD, ], }, }, @@ -485,20 +488,30 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio const actionExecutionError = actionExecutionOutcomes.find((subBucket) => subBucket?.key === 'failure')?.doc_count ?? 0; - const outcomeAndMessage = bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source ?? {}; - let status = outcomeAndMessage.kibana?.alerting?.outcome ?? ''; + const outcomeMessageAndMaintenanceWindow = + bucket?.ruleExecution?.outcomeMessageAndMaintenanceWindow?.hits?.hits[0]?._source ?? {}; + let status = outcomeMessageAndMaintenanceWindow.kibana?.alerting?.outcome ?? ''; if (isEmpty(status)) { - status = outcomeAndMessage.event?.outcome ?? ''; + status = outcomeMessageAndMaintenanceWindow.event?.outcome ?? ''; } - const outcomeMessage = outcomeAndMessage.message ?? ''; - const outcomeErrorMessage = outcomeAndMessage.error?.message ?? ''; + const outcomeMessage = outcomeMessageAndMaintenanceWindow.message ?? ''; + const outcomeErrorMessage = outcomeMessageAndMaintenanceWindow.error?.message ?? ''; const message = status === 'failure' ? `${outcomeMessage} - ${outcomeErrorMessage}` : outcomeMessage; - const version = outcomeAndMessage.kibana?.version ?? ''; - - const ruleId = outcomeAndMessage ? outcomeAndMessage?.rule?.id ?? '' : ''; - const spaceIds = outcomeAndMessage ? outcomeAndMessage?.kibana?.space_ids ?? [] : []; - const ruleName = outcomeAndMessage ? outcomeAndMessage.rule?.name ?? '' : ''; + const version = outcomeMessageAndMaintenanceWindow.kibana?.version ?? ''; + + const ruleId = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow?.rule?.id ?? '' + : ''; + const spaceIds = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow?.kibana?.space_ids ?? [] + : []; + const maintenanceWindowIds = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow.kibana?.alert?.maintenance_window_ids ?? [] + : []; + const ruleName = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow.rule?.name ?? '' + : ''; return { id: bucket?.key ?? '', timestamp: bucket?.ruleExecution?.executeStartTime.value_as_string ?? '', @@ -520,6 +533,7 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio rule_id: ruleId, space_ids: spaceIds, rule_name: ruleName, + maintenance_window_ids: maintenanceWindowIds, }; } diff --git a/x-pack/plugins/alerting/server/lib/license_state.test.ts b/x-pack/plugins/alerting/server/lib/license_state.test.ts index 535033851b3e0..02b01bbd54f2d 100644 --- a/x-pack/plugins/alerting/server/lib/license_state.test.ts +++ b/x-pack/plugins/alerting/server/lib/license_state.test.ts @@ -72,6 +72,9 @@ describe('getLicenseCheckForRuleType', () => { minimumLicenseRequired: 'gold', isExportable: true, recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + validate: { + params: { validate: (params) => params }, + }, }; beforeEach(() => { @@ -207,6 +210,9 @@ describe('ensureLicenseForRuleType()', () => { minimumLicenseRequired: 'gold', isExportable: true, recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + validate: { + params: { validate: (params) => params }, + }, }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/lib/validate_rule_type_params.ts b/x-pack/plugins/alerting/server/lib/validate_rule_type_params.ts index b791b05499263..0f152bb6f641b 100644 --- a/x-pack/plugins/alerting/server/lib/validate_rule_type_params.ts +++ b/x-pack/plugins/alerting/server/lib/validate_rule_type_params.ts @@ -17,7 +17,7 @@ export function validateRuleTypeParams( } try { - return validator.validate(params); + return validator.validate(params as Params); } catch (err) { throw Boom.badRequest(`params invalid: ${err.message}`); } diff --git a/x-pack/plugins/alerting/server/maintenance_window_client.mock.ts b/x-pack/plugins/alerting/server/maintenance_window_client.mock.ts index 9d0157d8e2139..cda36a816f41c 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client.mock.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client.mock.ts @@ -17,7 +17,7 @@ const createMaintenanceWindowClientMock = () => { find: jest.fn(), get: jest.fn(), archive: jest.fn(), - getActiveMaintenanceWindows: jest.fn(), + getActiveMaintenanceWindows: jest.fn().mockResolvedValue([]), finish: jest.fn(), delete: jest.fn(), }; diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.test.ts index 944c78b7fe774..c0a61f7097f80 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.test.ts @@ -69,6 +69,16 @@ describe('getMaintenanceWindowDateAndStatus', () => { expect(result.eventEndTime).toEqual('2023-03-25T01:00:00.000Z'); expect(result.status).toEqual('archived'); + result = getMaintenanceWindowDateAndStatus({ + events: [], + dateToCompare: new Date(), + expirationDate: moment().subtract(1, 'minute').toDate(), + }); + + expect(result.eventStartTime).toEqual(null); + expect(result.eventEndTime).toEqual(null); + expect(result.status).toEqual('archived'); + jest.useFakeTimers().setSystemTime(new Date('2023-03-28T00:30:00.000Z')); result = getMaintenanceWindowDateAndStatus({ events, diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.ts b/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.ts index 73a5cea9ff671..4cd1e3322134b 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/get_maintenance_window_date_and_status.ts @@ -30,12 +30,15 @@ export const getMaintenanceWindowDateAndStatus = ({ dateToCompare: Date; expirationDate: Date; }): MaintenanceWindowDateAndStatus => { - // No events, status is finished + // No events, status is finished or archived if (!events.length) { + const status = moment.utc(expirationDate).isBefore(dateToCompare) + ? MaintenanceWindowStatus.Archived + : MaintenanceWindowStatus.Finished; return { eventStartTime: null, eventEndTime: null, - status: MaintenanceWindowStatus.Finished, + status, }; } diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/get_active_maintenance_windows.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/get_active_maintenance_windows.ts index 5b9478a427645..004e68bbc5c7d 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/get_active_maintenance_windows.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/get_active_maintenance_windows.ts @@ -29,7 +29,7 @@ export interface MaintenanceWindowAggregationResult { export interface ActiveParams { start?: string; - interval: string; + interval?: string; } export async function getActiveMaintenanceWindows( @@ -40,7 +40,7 @@ export async function getActiveMaintenanceWindows( const { start, interval } = params; const startDate = start ? new Date(start) : new Date(); - const duration = parseDuration(interval); + const duration = interval ? parseDuration(interval) : 0; const endDate = moment.utc(startDate).add(duration, 'ms').toDate(); const startDateISO = startDate.toISOString(); diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts index 47991a11f2bb1..481557d9d47a6 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts @@ -9,7 +9,7 @@ import moment from 'moment-timezone'; import { RRule } from 'rrule'; import { update } from './update'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; -import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server'; +import { SavedObject } from '@kbn/core/server'; import { MaintenanceWindowClientContext, MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, @@ -72,14 +72,14 @@ describe('MaintenanceWindowClient - update', () => { id: 'test-id', } as unknown as SavedObject); - savedObjectsClient.update.mockResolvedValueOnce({ + savedObjectsClient.create.mockResolvedValueOnce({ attributes: { ...mockMaintenanceWindow, ...updatedAttributes, ...updatedMetadata, }, id: 'test-id', - } as unknown as SavedObjectsUpdateResponse); + } as unknown as SavedObject); jest.useFakeTimers().setSystemTime(new Date(secondTimestamp)); @@ -92,9 +92,8 @@ describe('MaintenanceWindowClient - update', () => { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, 'test-id' ); - expect(savedObjectsClient.update).toHaveBeenLastCalledWith( + expect(savedObjectsClient.create).toHaveBeenLastCalledWith( MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - 'test-id', { ...updatedAttributes, events: [ @@ -104,7 +103,7 @@ describe('MaintenanceWindowClient - update', () => { expirationDate: moment(new Date(secondTimestamp)).tz('UTC').add(1, 'year').toISOString(), ...updatedMetadata, }, - { version: '123' } + { id: 'test-id' } ); // Only these 3 properties are worth asserting since the rest come from mocks expect(result).toEqual( @@ -141,25 +140,24 @@ describe('MaintenanceWindowClient - update', () => { id: 'test-id', } as unknown as SavedObject); - savedObjectsClient.update.mockResolvedValue({ + savedObjectsClient.create.mockResolvedValue({ attributes: { ...mockMaintenanceWindow, ...updatedAttributes, ...updatedMetadata, }, id: 'test-id', - } as unknown as SavedObjectsUpdateResponse); + } as unknown as SavedObject); // Update without changing duration or rrule await update(mockContext, { id: 'test-id' }); // Events keep the previous modified events, but adds on the new events - expect(savedObjectsClient.update).toHaveBeenLastCalledWith( + expect(savedObjectsClient.create).toHaveBeenLastCalledWith( MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - 'test-id', expect.objectContaining({ events: [...modifiedEvents, expect.any(Object), expect.any(Object), expect.any(Object)], }), - { version: '123' } + { id: 'test-id' } ); // Update with changing rrule @@ -173,16 +171,15 @@ describe('MaintenanceWindowClient - update', () => { }, }); // All events are regenerated - expect(savedObjectsClient.update).toHaveBeenLastCalledWith( + expect(savedObjectsClient.create).toHaveBeenLastCalledWith( MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - 'test-id', expect.objectContaining({ events: [ { gte: '2023-03-26T00:00:00.000Z', lte: '2023-03-26T01:00:00.000Z' }, { gte: '2023-04-01T23:00:00.000Z', lte: '2023-04-02T00:00:00.000Z' }, ], }), - { version: '123' } + { id: 'test-id' } ); }); diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.ts index 4722e820867d4..17a5d2f573ee9 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.ts @@ -51,14 +51,11 @@ async function updateWithOCC( const { id, title, enabled, duration, rRule } = params; try { - const { - attributes, - version, - id: fetchedId, - } = await savedObjectsClient.get( - MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - id - ); + const { attributes, id: fetchedId } = + await savedObjectsClient.get( + MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + id + ); if (moment.utc(attributes.expirationDate).isBefore(new Date())) { throw Boom.badRequest('Cannot edit archived maintenance windows'); @@ -77,29 +74,32 @@ async function updateWithOCC( events = mergeEvents({ oldEvents: attributes.events, newEvents: events }); } - const result = await savedObjectsClient.update( + await savedObjectsClient.delete(MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, fetchedId); + + const updatedAttributes = { + ...attributes, + ...(title ? { title } : {}), + ...(rRule ? { rRule } : {}), + ...(typeof duration === 'number' ? { duration } : {}), + ...(typeof enabled === 'boolean' ? { enabled } : {}), + expirationDate, + events, + ...modificationMetadata, + }; + + // We are deleting and then creating rather than updating because SO.update + // performs a partial update on the rRule, we would need to null out all of the fields + // that are removed from a new rRule if that were the case. + const result = await savedObjectsClient.create( MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - fetchedId, - { - ...attributes, - title, - enabled: typeof enabled === 'boolean' ? enabled : attributes.enabled, - expirationDate, - duration, - rRule, - events, - ...modificationMetadata, - }, + updatedAttributes, { - version, + id: fetchedId, } ); return getMaintenanceWindowFromRaw({ - attributes: { - ...attributes, - ...result.attributes, - }, + attributes: result.attributes, id: result.id, }); } catch (e) { diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts index ed4b8575b9810..5101ec8609108 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -71,6 +71,9 @@ const sampleRuleType: RuleType = { async executor() { return { state: {} }; }, + validate: { + params: { validate: (params) => params }, + }, }; describe('Alerting Plugin', () => { diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 2902848b6dea5..f73b7070ce55b 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -131,6 +131,7 @@ export interface PluginSetupContract { RecoveryActionGroupId > ): void; + getSecurityHealth: () => Promise; getConfig: () => AlertingRulesConfig; frameworkAlerts: PublicFrameworkAlertsService; @@ -219,7 +220,6 @@ export class AlertingPlugin { core: CoreSetup, plugins: AlertingPluginsSetup ): PluginSetupContract { - const kibanaIndex = core.savedObjects.getKibanaIndex(); this.kibanaBaseUrl = core.http.basePath.publicBaseUrl; this.licenseState = new LicenseState(plugins.licensing.license$); this.security = plugins.security; @@ -284,13 +284,7 @@ export class AlertingPlugin { core.getStartServices().then(([_, { taskManager }]) => taskManager) ); const eventLogIndex = this.eventLogService.getIndexPattern(); - initializeAlertingTelemetry( - this.telemetryLogger, - core, - plugins.taskManager, - kibanaIndex, - eventLogIndex - ); + initializeAlertingTelemetry(this.telemetryLogger, core, plugins.taskManager, eventLogIndex); } // Usage counter for telemetry @@ -502,6 +496,10 @@ export class AlertingPlugin { return rulesSettingsClientFactory!.create(request); }; + const getMaintenanceWindowClientWithRequest = (request: KibanaRequest) => { + return maintenanceWindowClientFactory!.create(request); + }; + taskRunnerFactory.initialize({ logger, data: plugins.data, @@ -528,6 +526,7 @@ export class AlertingPlugin { actionsConfigMap: getActionsConfigMap(this.config.rules.run.actions), usageCounter: this.usageCounter, getRulesSettingsClientWithRequest, + getMaintenanceWindowClientWithRequest, }); this.eventLogService!.registerSavedObjectProvider('alert', (request) => { diff --git a/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts b/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts index fa30b0ff8d2ed..f22d7d2055b09 100644 --- a/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts +++ b/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts @@ -19,6 +19,17 @@ const ruleActionSchema = schema.object({ id: schema.string(), params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), uuid: schema.maybe(schema.string()), + frequency: schema.maybe( + schema.object({ + summary: schema.boolean(), + throttle: schema.nullable(schema.string()), + notifyWhen: schema.oneOf([ + schema.literal('onActionGroupChange'), + schema.literal('onActiveAlert'), + schema.literal('onThrottleInterval'), + ]), + }) + ), }); const operationsSchema = schema.arrayOf( diff --git a/x-pack/plugins/alerting/server/routes/create_rule.test.ts b/x-pack/plugins/alerting/server/routes/create_rule.test.ts index 4285c5a986b2b..edd1d10d06236 100644 --- a/x-pack/plugins/alerting/server/routes/create_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/create_rule.test.ts @@ -56,6 +56,7 @@ describe('createRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, timeframe: { days: [1], @@ -92,7 +93,7 @@ describe('createRuleRoute', () => { id: mockedAlert.actions[0].id, params: mockedAlert.actions[0].params, alerts_filter: { - query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql }, + query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql, filters: [] }, timeframe: mockedAlert.actions[0].alertsFilter?.timeframe!, }, }, @@ -167,6 +168,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -263,6 +265,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -360,6 +363,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -457,6 +461,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index 50bd9ad387d7d..04b18da1a1b0c 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -20,7 +20,7 @@ import { RuleTypeParams, AlertingRequestHandlerContext, BASE_ALERTING_API_PATH, - INTERNAL_BASE_ALERTING_API_PATH, + INTERNAL_ALERTING_API_FIND_RULES_PATH, } from '../types'; import { trackLegacyTerminology } from './lib/track_legacy_terminology'; @@ -136,7 +136,7 @@ const buildFindRulesRoute = ({ }) ) ); - if (path === `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_find`) { + if (path === INTERNAL_ALERTING_API_FIND_RULES_PATH) { router.post( { path, @@ -205,7 +205,7 @@ export const findInternalRulesRoute = ( buildFindRulesRoute({ excludeFromPublicApi: false, licenseState, - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_find`, + path: INTERNAL_ALERTING_API_FIND_RULES_PATH, router, usageCounter, }); diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts index 3ee2b0d1816ba..f6e1c3417a42f 100644 --- a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts @@ -48,6 +48,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: ['namespace'], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -71,6 +72,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: ['namespace'], + maintenance_window_ids: ['test-id-1'], }, ], }; diff --git a/x-pack/plugins/alerting/server/routes/get_rule.test.ts b/x-pack/plugins/alerting/server/routes/get_rule.test.ts index 17481e538ed86..a672de9cbf320 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.test.ts @@ -49,6 +49,7 @@ describe('getRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, timeframe: { days: [1], diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts index a6b0e88bbf5af..f7fe1a3406e9c 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts @@ -38,6 +38,7 @@ describe('getRuleAlertSummaryRoute', () => { status: 'OK', errorMessages: [], alerts: {}, + revision: 0, executionDuration: { average: 1, valuesWithTimestamp: { diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts index eb22a6429809a..a0dd57da558eb 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts @@ -49,6 +49,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: ['namespace'], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -72,6 +73,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: ['namespace'], + maintenance_window_ids: ['test-id-1'], }, ], }; diff --git a/x-pack/plugins/alerting/server/routes/health.test.ts b/x-pack/plugins/alerting/server/routes/health.test.ts index b525122e3d00c..92150cc910c70 100644 --- a/x-pack/plugins/alerting/server/routes/health.test.ts +++ b/x-pack/plugins/alerting/server/routes/health.test.ts @@ -155,22 +155,6 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alerting_framework_heath: { - // Legacy: pre-v8.0 typo - _deprecated: 'This state property has a typo, use "alerting_framework_health" instead.', - decryption_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - execution_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - read_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - }, alerting_framework_health: { decryption_health: { status: HealthStatus.OK, @@ -213,22 +197,6 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alerting_framework_heath: { - // Legacy: pre-v8.0 typo - _deprecated: 'This state property has a typo, use "alerting_framework_health" instead.', - decryption_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - execution_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - read_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - }, alerting_framework_health: { decryption_health: { status: HealthStatus.OK, @@ -271,22 +239,6 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alerting_framework_heath: { - // Legacy: pre-v8.0 typo - _deprecated: 'This state property has a typo, use "alerting_framework_health" instead.', - decryption_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - execution_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - read_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - }, alerting_framework_health: { decryption_health: { status: HealthStatus.OK, @@ -329,22 +281,6 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alerting_framework_heath: { - // Legacy: pre-v8.0 typo - _deprecated: 'This state property has a typo, use "alerting_framework_health" instead.', - decryption_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - execution_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - read_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - }, alerting_framework_health: { decryption_health: { status: HealthStatus.OK, @@ -387,22 +323,6 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alerting_framework_heath: { - // Legacy: pre-v8.0 typo - _deprecated: 'This state property has a typo, use "alerting_framework_health" instead.', - decryption_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - execution_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - read_health: { - status: HealthStatus.OK, - timestamp: currentDate, - }, - }, alerting_framework_health: { decryption_health: { status: HealthStatus.OK, diff --git a/x-pack/plugins/alerting/server/routes/health.ts b/x-pack/plugins/alerting/server/routes/health.ts index 4d3934911221e..7adceeacd0484 100644 --- a/x-pack/plugins/alerting/server/routes/health.ts +++ b/x-pack/plugins/alerting/server/routes/health.ts @@ -30,13 +30,6 @@ const rewriteBodyRes: RewriteResponseCase = ({ execution_health: alertingFrameworkHealth.executionHealth, read_health: alertingFrameworkHealth.readHealth, }, - alerting_framework_heath: { - // Legacy: pre-v8.0 typo - _deprecated: 'This state property has a typo, use "alerting_framework_health" instead.', - decryption_health: alertingFrameworkHealth.decryptionHealth, - execution_health: alertingFrameworkHealth.executionHealth, - read_health: alertingFrameworkHealth.readHealth, - }, }); export const healthRoute = ( diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index 9a0b8be33db42..4c13090f80bd3 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -53,6 +53,7 @@ import { deleteMaintenanceWindowRoute } from './maintenance_window/delete_mainte import { findMaintenanceWindowsRoute } from './maintenance_window/find_maintenance_windows'; import { archiveMaintenanceWindowRoute } from './maintenance_window/archive_maintenance_window'; import { finishMaintenanceWindowRoute } from './maintenance_window/finish_maintenance_window'; +import { activeMaintenanceWindowsRoute } from './maintenance_window/active_maintenance_windows'; export interface RouteOptions { router: IRouter; @@ -108,4 +109,5 @@ export function defineRoutes(opts: RouteOptions) { findMaintenanceWindowsRoute(router, licenseState); archiveMaintenanceWindowRoute(router, licenseState); finishMaintenanceWindowRoute(router, licenseState); + activeMaintenanceWindowsRoute(router, licenseState); } diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts index 672ccee369aac..a78fcd7f86f65 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts @@ -47,6 +47,7 @@ describe('getAlertInstanceSummaryRoute', () => { average: 0, valuesWithTimestamp: {}, }, + revision: 0, }; it('gets alert instance summary', async () => { diff --git a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts index 3e561168aee48..9d6f89e070c3a 100644 --- a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts +++ b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts @@ -29,13 +29,20 @@ export const actionsSchema = schema.arrayOf( uuid: schema.maybe(schema.string()), alerts_filter: schema.maybe( schema.object({ - query: schema.nullable( + query: schema.maybe( schema.object({ kql: schema.string(), + filters: schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + state$: schema.maybe(schema.object({ store: schema.string() })), + }) + ), dsl: schema.maybe(schema.string()), }) ), - timeframe: schema.nullable( + timeframe: schema.maybe( schema.object({ days: schema.arrayOf( schema.oneOf([ diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts index b79e5a91ff381..61dc9282bbfa1 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts @@ -27,6 +27,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], @@ -42,7 +43,7 @@ describe('rewrite Actions', () => { ).toEqual([ { alerts_filter: { - query: { dsl: '{test:1}', kql: 'test:1s' }, + query: { dsl: '{test:1}', kql: 'test:1s', filters: [] }, timeframe: { days: [1, 2, 3], hours: { end: '15:00', start: '00:00' }, @@ -77,6 +78,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], @@ -104,6 +106,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts index d5d0ba1739355..7a348e583ac6c 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts @@ -43,7 +43,7 @@ const sampleRule: SanitizedRule & { activeSnoozes?: string[] } = notifyWhen: 'onThrottleInterval', throttle: '1m', }, - alertsFilter: { timeframe: null, query: { kql: 'test:1', dsl: '{}' } }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, }, ], scheduledTaskId: 'xyz456', diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.test.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.test.ts new file mode 100644 index 0000000000000..70ebcde490a65 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.test.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 { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from '../_mock_handler_arguments'; +import { maintenanceWindowClientMock } from '../../maintenance_window_client.mock'; +import { activeMaintenanceWindowsRoute } from './active_maintenance_windows'; +import { getMockMaintenanceWindow } from '../../maintenance_window_client/methods/test_helpers'; +import { MaintenanceWindowStatus } from '../../../common'; +import { rewriteMaintenanceWindowRes } from '../lib'; + +const maintenanceWindowClient = maintenanceWindowClientMock.create(); + +jest.mock('../../lib/license_api_access', () => ({ + verifyApiAccess: jest.fn(), +})); + +const mockMaintenanceWindows = [ + { + ...getMockMaintenanceWindow(), + eventStartTime: new Date().toISOString(), + eventEndTime: new Date().toISOString(), + status: MaintenanceWindowStatus.Running, + id: 'test-id1', + }, + { + ...getMockMaintenanceWindow(), + eventStartTime: new Date().toISOString(), + eventEndTime: new Date().toISOString(), + status: MaintenanceWindowStatus.Running, + id: 'test-id2', + }, +]; + +describe('activeMaintenanceWindowsRoute', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('should get the currently active maintenance windows', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + activeMaintenanceWindowsRoute(router, licenseState); + + maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce( + mockMaintenanceWindows + ); + const [config, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments({ maintenanceWindowClient }, { body: {} }); + + expect(config.path).toEqual('/internal/alerting/rules/maintenance_window/_active'); + expect(config.options?.tags?.[0]).toEqual('access:read-maintenance-window'); + + await handler(context, req, res); + + expect(maintenanceWindowClient.getActiveMaintenanceWindows).toHaveBeenCalled(); + expect(res.ok).toHaveBeenLastCalledWith({ + body: mockMaintenanceWindows.map((data) => rewriteMaintenanceWindowRes(data)), + }); + }); + + test('ensures the license allows for getting active maintenance windows', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + activeMaintenanceWindowsRoute(router, licenseState); + + maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce( + mockMaintenanceWindows + ); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments({ maintenanceWindowClient }, { body: {} }); + await handler(context, req, res); + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + test('ensures the license check prevents for getting active maintenance windows', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + activeMaintenanceWindowsRoute(router, licenseState); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('Failure'); + }); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments({ maintenanceWindowClient }, { body: {} }); + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: Failure]`); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.ts new file mode 100644 index 0000000000000..8bd3f7d3e0b49 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/active_maintenance_windows.ts @@ -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 { IRouter } from '@kbn/core/server'; +import { ILicenseState } from '../../lib'; +import { verifyAccessAndContext, rewriteMaintenanceWindowRes } from '../lib'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH, +} from '../../types'; +import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; + +export const activeMaintenanceWindowsRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH, + validate: {}, + options: { + tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`], + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const maintenanceWindowClient = (await context.alerting).getMaintenanceWindowClient(); + const result = await maintenanceWindowClient.getActiveMaintenanceWindows({}); + + return res.ok({ + body: result.map((maintenanceWindow) => rewriteMaintenanceWindowRes(maintenanceWindow)), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/archive_maintenance_window.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/archive_maintenance_window.ts index 123f374f79b05..e46bc07463e2f 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/archive_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/archive_maintenance_window.ts @@ -9,7 +9,10 @@ import { IRouter } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../../lib'; import { verifyAccessAndContext, rewritePartialMaintenanceBodyRes } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; const paramSchema = schema.object({ @@ -26,7 +29,7 @@ export const archiveMaintenanceWindowRoute = ( ) => { router.post( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/{id}/_archive`, + path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/{id}/_archive`, validate: { params: paramSchema, body: bodySchema, diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/create_maintenance_window.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/create_maintenance_window.ts index a74147d15890c..d26f8494e1061 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/create_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/create_maintenance_window.ts @@ -14,8 +14,11 @@ import { RewriteRequestCase, rewriteMaintenanceWindowRes, } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; -import { MaintenanceWindowSOProperties, MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; +import { MaintenanceWindowCreateBody, MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; const bodySchema = schema.object({ title: schema.string(), @@ -23,11 +26,6 @@ const bodySchema = schema.object({ r_rule: rRuleSchema, }); -type MaintenanceWindowCreateBody = Omit< - MaintenanceWindowSOProperties, - 'events' | 'expirationDate' | 'enabled' | 'archived' ->; - export const rewriteQueryReq: RewriteRequestCase = ({ r_rule: rRule, ...rest @@ -42,7 +40,7 @@ export const createMaintenanceWindowRoute = ( ) => { router.post( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window`, + path: INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, validate: { body: bodySchema, }, diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/delete_maintenance_window.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/delete_maintenance_window.ts index 2415dbe74b53d..c9ea00ef170de 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/delete_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/delete_maintenance_window.ts @@ -9,7 +9,10 @@ import { IRouter } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../../lib'; import { verifyAccessAndContext } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; const paramSchema = schema.object({ @@ -22,7 +25,7 @@ export const deleteMaintenanceWindowRoute = ( ) => { router.delete( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/{id}`, + path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/{id}`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/find_maintenance_windows.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/find_maintenance_windows.ts index b581a011630a9..e9262b3e51079 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/find_maintenance_windows.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/find_maintenance_windows.ts @@ -8,7 +8,10 @@ import { IRouter } from '@kbn/core/server'; import { ILicenseState } from '../../lib'; import { verifyAccessAndContext, rewriteMaintenanceWindowRes } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; export const findMaintenanceWindowsRoute = ( @@ -17,7 +20,7 @@ export const findMaintenanceWindowsRoute = ( ) => { router.get( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/_find`, + path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/_find`, validate: {}, options: { tags: [`access:${MAINTENANCE_WINDOW_API_PRIVILEGES.READ_MAINTENANCE_WINDOW}`], diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/finish_maintenance_window.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/finish_maintenance_window.ts index 2cd5ff9ba0994..0cb663043d57a 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/finish_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/finish_maintenance_window.ts @@ -9,7 +9,10 @@ import { IRouter } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../../lib'; import { verifyAccessAndContext, rewritePartialMaintenanceBodyRes } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; const paramSchema = schema.object({ @@ -22,7 +25,7 @@ export const finishMaintenanceWindowRoute = ( ) => { router.post( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/{id}/_finish`, + path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/{id}/_finish`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/get_maintenance_window.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/get_maintenance_window.ts index dc01beeef148a..b92281373817d 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/get_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/get_maintenance_window.ts @@ -9,7 +9,10 @@ import { IRouter } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../../lib'; import { verifyAccessAndContext, rewriteMaintenanceWindowRes } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; import { MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; const paramSchema = schema.object({ @@ -22,7 +25,7 @@ export const getMaintenanceWindowRoute = ( ) => { router.get( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/{id}`, + path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/{id}`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.ts index 7778b4d621359..5e63624587152 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.ts @@ -14,7 +14,10 @@ import { RewriteRequestCase, rewritePartialMaintenanceBodyRes, } from '../lib'; -import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../../types'; +import { + AlertingRequestHandlerContext, + INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, +} from '../../types'; import { MaintenanceWindowSOProperties, MAINTENANCE_WINDOW_API_PRIVILEGES } from '../../../common'; const paramSchema = schema.object({ @@ -49,7 +52,7 @@ export const updateMaintenanceWindowRoute = ( ) => { router.post( { - path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/{id}`, + path: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/{id}`, validate: { body: bodySchema, params: paramSchema, diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts index 4dfe1c00145e8..5a4b3a19c0d7c 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -53,8 +53,8 @@ describe('updateRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, - timeframe: null, }, }, ], @@ -123,9 +123,9 @@ describe('updateRuleRoute', () => { "alertsFilter": Object { "query": Object { "dsl": "{\\"must\\": {\\"term\\": { \\"name\\": \\"test\\" }}}", + "filters": Array [], "kql": "name:test", }, - "timeframe": null, }, "group": "default", "id": "2", diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index c30fd1d5a8ae4..428600f561ff7 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -15,6 +15,7 @@ import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock'; import { alertsServiceMock } from './alerts_service/alerts_service.mock'; +import { schema } from '@kbn/config-schema'; const logger = loggingSystemMock.create().get(); let mockedLicenseState: jest.Mocked; @@ -62,6 +63,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: schema.any(), + }, }); expect(registry.has('foo')).toEqual(true); }); @@ -83,6 +87,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); @@ -116,6 +123,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); @@ -140,6 +150,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); @@ -167,6 +180,9 @@ describe('Create Lifecycle', () => { executor: jest.fn(), producer: 'alerts', defaultScheduleInterval: 'foobar', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); @@ -193,6 +209,9 @@ describe('Create Lifecycle', () => { executor: jest.fn(), producer: 'alerts', defaultScheduleInterval: '10s', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register(ruleType); @@ -219,6 +238,9 @@ describe('Create Lifecycle', () => { executor: jest.fn(), producer: 'alerts', defaultScheduleInterval: '10s', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry({ ...ruleTypeRegistryParams, @@ -255,6 +277,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); @@ -284,6 +309,9 @@ describe('Create Lifecycle', () => { producer: 'alerts', minimumLicenseRequired: 'basic', isExportable: true, + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register(ruleType); @@ -317,6 +345,9 @@ describe('Create Lifecycle', () => { producer: 'alerts', minimumLicenseRequired: 'basic', isExportable: true, + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register(ruleType); @@ -354,6 +385,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); @@ -380,6 +414,9 @@ describe('Create Lifecycle', () => { executor: jest.fn(), producer: 'alerts', ruleTaskTimeout: '20m', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register(ruleType); @@ -412,6 +449,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register(ruleType); @@ -435,6 +475,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); expect(() => registry.register({ @@ -451,6 +494,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }) ).toThrowErrorMatchingInlineSnapshot(`"Rule type \\"test\\" is already registered."`); }); @@ -475,6 +521,9 @@ describe('Create Lifecycle', () => { context: 'test', mappings: { fieldMap: { field: { type: 'keyword', required: false } } }, }, + validate: { + params: { validate: (params) => params }, + }, }); expect(alertsService.register).toHaveBeenCalledWith({ @@ -499,6 +548,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: schema.any(), + }, }); expect(alertsService.register).not.toHaveBeenCalled(); @@ -522,6 +574,9 @@ describe('Create Lifecycle', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); const ruleType = registry.get('test'); expect(ruleType).toMatchInlineSnapshot(` @@ -552,6 +607,11 @@ describe('Create Lifecycle', () => { "id": "recovered", "name": "Recovered", }, + "validate": Object { + "params": Object { + "validate": [Function], + }, + }, } `); }); @@ -589,6 +649,9 @@ describe('Create Lifecycle', () => { minimumLicenseRequired: 'basic', executor: jest.fn(), producer: 'alerts', + validate: { + params: schema.any(), + }, }); const result = registry.list(); expect(result).toMatchInlineSnapshot(` @@ -689,6 +752,9 @@ describe('Create Lifecycle', () => { minimumLicenseRequired: 'basic', executor: jest.fn(), producer: 'alerts', + validate: { + params: schema.any(), + }, }); const result = registry.getAllTypes(); expect(result).toEqual(['test']); @@ -715,6 +781,9 @@ describe('Create Lifecycle', () => { isExportable: true, minimumLicenseRequired: 'basic', recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + validate: { + params: schema.any(), + }, }); }); @@ -750,6 +819,9 @@ function ruleTypeWithVariables( return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; if (!context && !state) return baseAlert; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts index 7ee9d323c7627..6605d9c22a72b 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts @@ -24,7 +24,15 @@ describe('addGeneratedActionValues()', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:testValue' }, + query: { + kql: 'test:testValue', + filters: [ + { + meta: { key: 'foo', params: { query: 'bar' } }, + query: { match_phrase: { foo: 'bar ' } }, + }, + ], + }, timeframe: { days: [1, 2], hours: { start: '08:00', end: '17:00' }, @@ -41,14 +49,17 @@ describe('addGeneratedActionValues()', () => { test('adds DSL', async () => { const actionWithGeneratedValues = addGeneratedActionValues([mockAction]); expect(actionWithGeneratedValues[0].alertsFilter?.query?.dsl).toBe( - '{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}}' + '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}},{"match_phrase":{"foo":"bar "}}],"should":[],"must_not":[]}}' ); }); test('throws error if KQL is not valid', async () => { expect(() => addGeneratedActionValues([ - { ...mockAction, alertsFilter: { query: { kql: 'foo:bar:1' }, timeframe: null } }, + { + ...mockAction, + alertsFilter: { query: { kql: 'foo:bar:1', filters: [] } }, + }, ]) ).toThrowErrorMatchingInlineSnapshot('"Error creating DSL query: invalid KQL"'); }); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts index c3cd721698594..71cbf01ea1a4e 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts @@ -6,7 +6,7 @@ */ import { v4 } from 'uuid'; -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { buildEsQuery, Filter } from '@kbn/es-query'; import Boom from '@hapi/boom'; import { NormalizedAlertAction, NormalizedAlertActionWithGeneratedValues } from '..'; @@ -14,9 +14,11 @@ export function addGeneratedActionValues( actions: NormalizedAlertAction[] = [] ): NormalizedAlertActionWithGeneratedValues[] { return actions.map(({ uuid, alertsFilter, ...action }) => { - const generateDSL = (kql: string) => { + const generateDSL = (kql: string, filters: Filter[]) => { try { - return JSON.stringify(toElasticsearchQuery(fromKueryExpression(kql))); + return JSON.stringify( + buildEsQuery(undefined, [{ query: kql, language: 'kuery' }], filters) + ); } catch (e) { throw Boom.badRequest(`Error creating DSL query: invalid KQL`); } @@ -29,13 +31,12 @@ export function addGeneratedActionValues( ? { alertsFilter: { ...alertsFilter, - timeframe: alertsFilter.timeframe || null, - query: !alertsFilter.query - ? null - : { - kql: alertsFilter.query.kql, - dsl: generateDSL(alertsFilter.query.kql), - }, + query: alertsFilter.query + ? { + ...alertsFilter.query, + dsl: generateDSL(alertsFilter.query.kql, alertsFilter.query.filters), + } + : undefined, }, } : {}), diff --git a/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts b/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts index bf8d10a341597..9648f23dc05d2 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts @@ -43,6 +43,7 @@ export const recoverRuleAlerts = async ( const event = createAlertEventLogRecordObject({ ruleId: id, ruleName: attributes.name, + ruleRevision: attributes.revision, ruleType: context.ruleTypeRegistry.get(attributes.alertTypeId), consumer: attributes.consumer, instanceId: alertId, diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts index 8689113ca7907..a0aa3286f1f6d 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts @@ -14,6 +14,7 @@ import { injectReferencesIntoActions } from '../../common'; import { transformToNotifyWhen } from './transform_to_notify_when'; import { transformFromLegacyActions } from './transform_legacy_actions'; import { LegacyIRuleActionsAttributes, legacyRuleActionsSavedObjectType } from './types'; +import { transformToAlertThrottle } from './transform_to_alert_throttle'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -136,7 +137,9 @@ export const formatLegacyActions = async ( return { ...rule, actions: [...rule.actions, ...legacyRuleActions], - throttle: (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions', + throttle: transformToAlertThrottle( + (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions' + ), notifyWhen: transformToNotifyWhen(ruleThrottle), // muteAll property is disregarded in further rule processing in Security Solution when legacy actions are present. // So it should be safe to set it as false, so it won't be displayed to user as w/o actions see transformFromAlertThrottle method diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts index bf67bb5a97191..50848be43a73f 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/migrate_legacy_actions.test.ts @@ -44,6 +44,9 @@ const ruleType: jest.Mocked = { cancelAlertsOnRuleTimeout: true, ruleTaskTimeout: '5m', getSummarizedAlerts: jest.fn(), + validate: { + params: { validate: (params) => params }, + }, }; const context = { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts index b23798294b300..cea32ce3e1913 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts @@ -67,7 +67,7 @@ describe('transformFromLegacyActions', () => { (transformToNotifyWhen as jest.Mock).mockReturnValueOnce(null); const actions = transformFromLegacyActions(legacyActionsAttr, references); - expect(actions[0].frequency?.notifyWhen).toBe('onThrottleInterval'); + expect(actions[0].frequency?.notifyWhen).toBe('onActiveAlert'); }); it('should return transformed legacy actions', () => { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts index 485a32f781695..1218e96a8dfd7 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts @@ -11,6 +11,7 @@ import type { SavedObjectReference } from '@kbn/core/server'; import { RawRuleAction } from '../../../types'; import { transformToNotifyWhen } from './transform_to_notify_when'; import { LegacyIRuleActionsAttributes } from './types'; +import { transformToAlertThrottle } from './transform_to_alert_throttle'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -50,8 +51,8 @@ export const transformFromLegacyActions = ( actionTypeId, frequency: { summary: true, - notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onThrottleInterval', - throttle: legacyActionsAttr.ruleThrottle, + notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onActiveAlert', + throttle: transformToAlertThrottle(legacyActionsAttr.ruleThrottle), }, }, ]; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts new file mode 100644 index 0000000000000..f52742009503a --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.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 { transformToAlertThrottle } from './transform_to_alert_throttle'; + +describe('transformToAlertThrottle', () => { + it('should return null when throttle is null OR no_actions', () => { + expect(transformToAlertThrottle(null)).toBeNull(); + expect(transformToAlertThrottle('rule')).toBeNull(); + expect(transformToAlertThrottle('no_actions')).toBeNull(); + }); + it('should return same value for other throttle values', () => { + expect(transformToAlertThrottle('1h')).toBe('1h'); + expect(transformToAlertThrottle('1m')).toBe('1m'); + expect(transformToAlertThrottle('1d')).toBe('1d'); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts new file mode 100644 index 0000000000000..7f6ecee900e2f --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.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. + */ + +/** + * Given a throttle from a "security_solution" rule this will transform it into an "alerting" "throttle" + * on their saved object. + * @params throttle The throttle from a "security_solution" rule + * @returns The "alerting" throttle + */ +export const transformToAlertThrottle = (throttle: string | null | undefined): string | null => { + if (throttle == null || throttle === 'rule' || throttle === 'no_actions') { + return null; + } else { + return throttle; + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts index fbf59b2953d9d..229c009df3eec 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts @@ -26,6 +26,9 @@ describe('validateActions', () => { cancelAlertsOnRuleTimeout: true, ruleTaskTimeout: '5m', getSummarizedAlerts: jest.fn(), + validate: { + params: { validate: (params) => params }, + }, }; const data = { @@ -42,7 +45,7 @@ describe('validateActions', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '10:00', end: '17:00' }, timezone: 'UTC' }, }, }, @@ -197,7 +200,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '30:00', end: '17:00' }, timezone: 'UTC' }, }, }, @@ -234,7 +237,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, // @ts-ignore timeframe: { days: [1], hours: { start: '10:00', end: '17:00' } }, }, @@ -258,7 +261,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { // @ts-ignore days: [0, 8], diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts index 357f624a815cd..b86e30556f090 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts @@ -154,7 +154,6 @@ export async function validateActions( ) { actionWithInvalidTimeframe.push(action); } - // alertsFilter time range filter's start time can't be before end time if (alertsFilter.timeframe.hours) { if ( validateHours(alertsFilter.timeframe.hours.start) || diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts index dfe4ecc952bf6..640f3c3f324b3 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts @@ -7,7 +7,7 @@ import pMap from 'p-map'; import Boom from '@hapi/boom'; -import { cloneDeep, omit } from 'lodash'; +import { cloneDeep } from 'lodash'; import { AlertConsumers } from '@kbn/rule-data-utils'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { @@ -503,11 +503,11 @@ async function updateRuleAttributesAndParamsInMemory action?.frequency - )?.frequency; - if (rule.attributes.consumer === AlertConsumers.SIEM && firstFrequency) { - ruleActions.actions = ruleActions.actions.map((action) => omit(action, 'frequency')); - if (!attributes.notifyWhen) { - attributes.notifyWhen = firstFrequency.notifyWhen; - attributes.throttle = firstFrequency.throttle; - } - } - break; } case 'snoozeSchedule': { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/create.ts b/x-pack/plugins/alerting/server/rules_client/methods/create.ts index 4bf83043f99af..fd83a7b9b92e1 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/create.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/create.ts @@ -6,8 +6,6 @@ */ import Semver from 'semver'; import Boom from '@hapi/boom'; -import { omit } from 'lodash'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { SavedObjectsUtils } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; import { parseDuration } from '../../../common/parse_duration'; @@ -88,7 +86,7 @@ export async function create( // Throws an error if alert type isn't registered const ruleType = context.ruleTypeRegistry.get(data.alertTypeId); - const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate?.params); + const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params); const username = await context.getUserName(); let createdAPIKey = null; @@ -111,17 +109,6 @@ export async function create( throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; - if (data.consumer === AlertConsumers.SIEM && firstFrequency) { - data.actions = data.actions.map((action) => omit(action, 'frequency')); - if (!data.notifyWhen) { - data.notifyWhen = firstFrequency.notifyWhen; - data.throttle = firstFrequency.throttle; - } - } - await withSpan({ name: 'validateActions', type: 'rules' }, () => validateActions(context, ruleType, data, allowMissingConnectorSecrets) ); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update.ts b/x-pack/plugins/alerting/server/rules_client/methods/update.ts index 4703cd5b92b28..f68b41067ddae 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update.ts @@ -6,9 +6,8 @@ */ import Boom from '@hapi/boom'; -import { isEqual, omit } from 'lodash'; +import { isEqual } from 'lodash'; import { SavedObject } from '@kbn/core/server'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import type { ShouldIncrementRevision } from './bulk_edit'; import { PartialRule, @@ -186,19 +185,8 @@ async function updateAlert( const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId); - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; - if (attributes.consumer === AlertConsumers.SIEM && firstFrequency) { - data.actions = data.actions.map((action) => omit(action, 'frequency')); - if (!attributes.notifyWhen) { - attributes.notifyWhen = firstFrequency.notifyWhen; - attributes.throttle = firstFrequency.throttle; - } - } - // Validate - const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate?.params); + const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params); await validateActions(context, ruleType, data, allowMissingConnectorSecrets); // Throw error if schedule interval is less than the minimum and we are enforcing it diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts index 74808613f869d..dbe04ec420000 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_delete.test.ts @@ -27,6 +27,7 @@ import { returnedRule2, siemRule1, } from './test_helpers'; +import { schema } from '@kbn/config-schema'; import { migrateLegacyActions } from '../lib'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { @@ -174,6 +175,9 @@ describe('bulkDelete', () => { return { state: {} }; }, producer: 'alerts', + validate: { + params: schema.any(), + }, }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index c9e9302fb0146..3598ad31fd390 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -210,6 +210,9 @@ describe('bulkEdit()', () => { return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); (migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock); @@ -686,6 +689,9 @@ describe('bulkEdit()', () => { }, producer: 'alerts', getSummarizedAlerts: jest.fn().mockResolvedValue({}), + validate: { + params: { validate: (params) => params }, + }, }); const existingAction = { frequency: { @@ -700,7 +706,8 @@ describe('bulkEdit()', () => { alertsFilter: { query: { kql: 'name:test', - dsl: '{"bool":{"should":[{"match":{"name":"test"}}],"minimum_should_match":1}}', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"name":"test"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', + filters: [], }, timeframe: { days: [1], @@ -719,7 +726,7 @@ describe('bulkEdit()', () => { id: '2', params: {}, uuid: '222', - alertsFilter: { query: { kql: 'test:1', dsl: 'test' } }, + alertsFilter: { query: { kql: 'test:1', dsl: 'test', filters: [] } }, }; unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ @@ -738,8 +745,7 @@ describe('bulkEdit()', () => { actionRef: 'action_1', uuid: '222', alertsFilter: { - query: { kql: 'test:1', dsl: 'test' }, - timeframe: null, + query: { kql: 'test:1', dsl: 'test', filters: [] }, }, }, ], @@ -796,10 +802,10 @@ describe('bulkEdit()', () => { uuid: '222', alertsFilter: { query: { - dsl: '{"bool":{"should":[{"match":{"test":"1"}}],"minimum_should_match":1}}', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', kql: 'test:1', + filters: [], }, - timeframe: null, }, }, ], @@ -829,8 +835,8 @@ describe('bulkEdit()', () => { query: { dsl: 'test', kql: 'test:1', + filters: [], }, - timeframe: null, }, }, ], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index 2be00b0fc5352..aedf130e1f846 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -1233,6 +1233,9 @@ describe('create()', () => { extractReferences: extractReferencesFn, injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ params: ruleParams, @@ -1414,6 +1417,9 @@ describe('create()', () => { extractReferences: extractReferencesFn, injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ params: ruleParams, @@ -2679,6 +2685,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const createdAttributes = { ...data, @@ -2747,6 +2756,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ schedule: { interval: '1s' } }); @@ -2781,6 +2793,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -2870,6 +2885,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -2916,6 +2934,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -2975,6 +2996,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -3052,6 +3076,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -3243,6 +3270,9 @@ describe('create()', () => { injectReferences: jest.fn(), }, getSummarizedAlerts: jest.fn().mockResolvedValue({}), + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -3292,6 +3322,9 @@ describe('create()', () => { extractReferences: jest.fn(), injectReferences: jest.fn(), }, + validate: { + params: { validate: (params) => params }, + }, })); const data = getMockData({ @@ -3310,7 +3343,7 @@ describe('create()', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, }, }, ], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index c09b491dbbdcc..b135f9b0ee23d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -100,6 +100,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: true, + revision: 0, scheduledTaskId: '1', actions: [ { @@ -224,6 +225,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -277,6 +279,7 @@ describe('disable()', () => { }, params: { alertId: '1', + revision: 0, }, ownerId: null, }); @@ -296,6 +299,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -333,6 +337,7 @@ describe('disable()', () => { uuid: 'uuid-1', rule: { consumer: 'myApp', + revision: 0, rule_type_id: '123', }, }, @@ -379,6 +384,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -425,6 +431,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: '1', updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', @@ -517,6 +524,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: null, updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', @@ -565,6 +573,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: null, updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts index 3573508d891cd..dadfd3204204a 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts @@ -19,6 +19,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { RegistryRuleType } from '../../rule_type_registry'; +import { schema } from '@kbn/config-schema'; import { enabledRule1, enabledRule2, siemRule1, siemRule2 } from './test_helpers'; import { formatLegacyActions } from '../lib'; @@ -370,6 +371,9 @@ describe('find()', () => { return { state: {} }; }, producer: 'myApp', + validate: { + params: schema.any(), + }, })); ruleTypeRegistry.get.mockImplementationOnce(() => ({ id: '123', @@ -387,6 +391,9 @@ describe('find()', () => { extractReferences: jest.fn(), injectReferences: injectReferencesFn, }, + validate: { + params: schema.any(), + }, })); unsecuredSavedObjectsClient.find.mockResolvedValue({ total: 2, @@ -570,6 +577,9 @@ describe('find()', () => { return { state: {} }; }, producer: 'myApp', + validate: { + params: schema.any(), + }, })); ruleTypeRegistry.get.mockImplementationOnce(() => ({ id: '123', @@ -587,6 +597,9 @@ describe('find()', () => { extractReferences: jest.fn(), injectReferences: injectReferencesFn, }, + validate: { + params: schema.any(), + }, })); unsecuredSavedObjectsClient.find.mockResolvedValue({ total: 2, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts index 1ca6e664fc359..eb9a8a22523ee 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts @@ -232,6 +232,9 @@ describe('get()', () => { extractReferences: jest.fn(), injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -355,6 +358,9 @@ describe('get()', () => { extractReferences: jest.fn(), injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts index 212977a8381c2..dbfcc5f3fc017 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts @@ -189,6 +189,7 @@ describe('getAlertSummary()', () => { "lastRun": "2019-02-12T21:01:32.479Z", "muteAll": false, "name": "rule-name", + "revision": 0, "ruleTypeId": "123", "status": "Active", "statusEndDate": "2019-02-12T21:01:22.479Z", diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index cebe9fa963971..33fb19c40f7fd 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -133,7 +133,7 @@ const aggregateResults = { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -242,7 +242,7 @@ const aggregateResults = { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -261,6 +261,9 @@ const aggregateResults = { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -389,6 +392,7 @@ describe('getExecutionLogForRule()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -412,6 +416,7 @@ describe('getExecutionLogForRule()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -725,6 +730,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -748,6 +754,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts index f036e2cb02298..337c09e087243 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts @@ -116,6 +116,9 @@ export function getBeforeSetup( return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, })); rulesClientParams.getEventLogClient.mockResolvedValue( eventLogClient ?? eventLogClientMock.create() diff --git a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts index bb22b5f6d0d53..37372752d53a3 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts @@ -284,6 +284,9 @@ describe('resolve()', () => { extractReferences: jest.fn(), injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ @@ -417,6 +420,9 @@ describe('resolve()', () => { extractReferences: jest.fn(), injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index b76d0607f22a8..1ffe43cc088ca 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -171,6 +171,14 @@ describe('update()', () => { return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, + }); + (migrateLegacyActions as jest.Mock).mockResolvedValue({ + hasLegacyActions: false, + resultedActions: [], + resultedReferences: [], }); (migrateLegacyActions as jest.Mock).mockResolvedValue({ hasLegacyActions: false, @@ -749,6 +757,9 @@ describe('update()', () => { extractReferences: extractReferencesFn, injectReferences: injectReferencesFn, }, + validate: { + params: { validate: (params) => params }, + }, })); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', @@ -1643,6 +1654,9 @@ describe('update()', () => { return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ id: alertId, diff --git a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts index 7f1a26b0e4940..fcde2f6e4f444 100644 --- a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts @@ -359,6 +359,9 @@ beforeEach(() => { return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, })); ruleTypeRegistry.get.mockReturnValue({ @@ -373,6 +376,9 @@ beforeEach(() => { return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); rulesClient = new RulesClient(rulesClientParams); diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts index 807a1475db9bb..ba9f2ac76077e 100644 --- a/x-pack/plugins/alerting/server/saved_objects/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -13,6 +13,7 @@ import type { } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { alertMappings } from './mappings'; import { rulesSettingsMappings } from './rules_settings_mappings'; import { maintenanceWindowMappings } from './maintenance_window_mapping'; @@ -78,6 +79,7 @@ export function setupSavedObjects( ) { savedObjects.registerType({ name: 'alert', + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', @@ -108,6 +110,7 @@ export function setupSavedObjects( savedObjects.registerType({ name: 'api_key_pending_invalidation', + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'agnostic', mappings: { @@ -124,6 +127,7 @@ export function setupSavedObjects( savedObjects.registerType({ name: RULES_SETTINGS_SAVED_OBJECT_TYPE, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'single', mappings: rulesSettingsMappings, @@ -131,6 +135,7 @@ export function setupSavedObjects( savedObjects.registerType({ name: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', mappings: maintenanceWindowMappings, diff --git a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts index af57c644dd0aa..e517d37739337 100644 --- a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts @@ -55,6 +55,9 @@ describe('isRuleExportable', () => { isExportable: true, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); expect( isRuleExportable( @@ -111,6 +114,9 @@ describe('isRuleExportable', () => { isExportable: false, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); expect( isRuleExportable( @@ -170,6 +176,9 @@ describe('isRuleExportable', () => { isExportable: false, executor: jest.fn(), producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }); expect( isRuleExportable( diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index de3e05dae82ce..03e1ba5976a62 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -21,6 +21,7 @@ import { RuleTypeParams, RuleTypeState, SanitizedRule, + GetViewInAppRelativeUrlFnOpts, } from '../types'; import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; import { alertingEventLoggerMock } from '../lib/alerting_event_logger/alerting_event_logger.mock'; @@ -31,6 +32,7 @@ import { AlertInstanceState, AlertInstanceContext } from '../../common'; import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server'; import sinon from 'sinon'; import { mockAAD } from './fixtures'; +import { schema } from '@kbn/config-schema'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), @@ -69,6 +71,9 @@ const ruleType: NormalizedRuleType< executor: jest.fn(), producer: 'alerts', getSummarizedAlerts: getSummarizedAlertsMock, + validate: { + params: schema.any(), + }, }; const rule = { id: '1', @@ -80,6 +85,7 @@ const rule = { contextVal: 'My other {{context.value}} goes here', stateVal: 'My other {{state.value}} goes here', }, + schedule: { interval: '1m' }, notifyWhen: 'onActiveAlert', actions: [ { @@ -116,6 +122,7 @@ const defaultExecutionParams = { ruleLabel: 'rule-label', request: {} as KibanaRequest, alertingEventLogger, + previousStartedAt: null, taskInstance: { params: { spaceId: 'test1', alertId: '1' }, } as unknown as ConcreteTaskInstance, @@ -940,7 +947,7 @@ describe('Execution Handler', () => { message: 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, - alertsFilter: { query: { kql: 'test:1', dsl: '{}' } }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, }, ], }, @@ -1325,7 +1332,7 @@ describe('Execution Handler', () => { 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, alertsFilter: { - query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}' }, + query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}', filters: [] }, }, }, ], @@ -1344,7 +1351,7 @@ describe('Execution Handler', () => { spaceId: 'test1', excludedAlertInstanceIds: ['foo'], alertsFilter: { - query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}' }, + query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}', filters: [] }, }, }); expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); @@ -1385,7 +1392,7 @@ describe('Execution Handler', () => { }, params: {}, alertsFilter: { - query: { kql: 'kibana.alert.instance.id:1', dsl: '{}' }, + query: { kql: 'kibana.alert.instance.id:1', dsl: '{}', filters: [] }, }, }, ], @@ -1405,7 +1412,7 @@ describe('Execution Handler', () => { spaceId: 'test1', excludedAlertInstanceIds: ['foo'], alertsFilter: { - query: { kql: 'kibana.alert.instance.id:1', dsl: '{}' }, + query: { kql: 'kibana.alert.instance.id:1', dsl: '{}', filters: [] }, }, }); expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledWith([ @@ -1447,6 +1454,25 @@ describe('Execution Handler', () => { ], } as unknown as SanitizedRule; + const summaryRuleWithUrl = { + ...rule, + actions: [ + { + id: '1', + group: null, + actionTypeId: 'test', + frequency: { + summary: true, + notifyWhen: 'onActiveAlert', + throttle: null, + }, + params: { + val: 'rule url: {{rule.url}}', + }, + }, + ], + } as unknown as SanitizedRule; + it('populates the rule.url in the action params when the base url and rule id are specified', async () => { const execParams = { ...defaultExecutionParams, @@ -1474,6 +1500,55 @@ describe('Execution Handler', () => { `); }); + it('populates the rule.url with start and stop time when available', async () => { + clock.reset(); + clock.tick(90000); + getSummarizedAlertsMock.mockResolvedValue({ + new: { + count: 2, + data: [ + mockAAD, + { + ...mockAAD, + '@timestamp': '2022-12-07T15:45:41.4672Z', + alert: { instance: { id: 'all' } }, + }, + ], + }, + ongoing: { count: 0, data: [] }, + recovered: { count: 0, data: [] }, + }); + const execParams = { + ...defaultExecutionParams, + ruleType: { + ...ruleType, + getViewInAppRelativeUrl: (opts: GetViewInAppRelativeUrlFnOpts) => + `/app/test/rule/${opts.rule.id}?start=${opts.start ?? 0}&end=${opts.end ?? 0}`, + }, + rule: summaryRuleWithUrl, + taskRunnerContext: { + ...defaultExecutionParams.taskRunnerContext, + kibanaBaseUrl: 'http://localhost:12345', + }, + }; + + const executionHandler = new ExecutionHandler(generateExecutionParams(execParams)); + await executionHandler.run(generateAlert({ id: 1 })); + + expect(injectActionParamsMock.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "actionParams": Object { + "val": "rule url: http://localhost:12345/s/test1/app/test/rule/1?start=30000&end=90000", + }, + "actionTypeId": "test", + "ruleId": "1", + "spaceId": "test1", + }, + ] + `); + }); + it('populates the rule.url without the space specifier when the spaceId is the string "default"', async () => { const execParams = { ...defaultExecutionParams, diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index 18c06c0b36f56..083225de1f371 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -40,6 +40,7 @@ import { import { generateActionHash, getSummaryActionsFromTaskState, + getSummaryActionTimeBounds, isActionOnInterval, isSummaryAction, isSummaryActionOnInterval, @@ -91,6 +92,7 @@ export class ExecutionHandler< private actionsClient: PublicMethodsOf; private ruleTypeActionGroups?: Map; private mutedAlertIdsSet: Set = new Set(); + private previousStartedAt: Date | null; constructor({ rule, @@ -104,6 +106,7 @@ export class ExecutionHandler< ruleConsumer, executionId, ruleLabel, + previousStartedAt, actionsClient, }: ExecutionHandlerOptions< Params, @@ -130,6 +133,7 @@ export class ExecutionHandler< this.ruleTypeActionGroups = new Map( ruleType.actionGroups.map((actionGroup) => [actionGroup.id, actionGroup.name]) ); + this.previousStartedAt = previousStartedAt; this.mutedAlertIdsSet = new Set(rule.mutedInstanceIds); } @@ -205,6 +209,11 @@ export class ExecutionHandler< ruleRunMetricsStore.incrementNumberOfTriggeredActionsByConnectorType(actionTypeId); if (isSummaryAction(action) && summarizedAlerts) { + const { start, end } = getSummaryActionTimeBounds( + action, + this.rule.schedule, + this.previousStartedAt + ); const actionToRun = { ...action, params: injectActionParams({ @@ -221,7 +230,7 @@ export class ExecutionHandler< actionsPlugin, actionTypeId, kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, - ruleUrl: this.buildRuleUrl(spaceId), + ruleUrl: this.buildRuleUrl(spaceId, start, end), }), }), }; @@ -419,13 +428,13 @@ export class ExecutionHandler< return alert.getScheduledActionOptions()?.actionGroup || this.ruleType.recoveryActionGroup.id; } - private buildRuleUrl(spaceId: string): string | undefined { + private buildRuleUrl(spaceId: string, start?: number, end?: number): string | undefined { if (!this.taskRunnerContext.kibanaBaseUrl) { return; } const relativePath = this.ruleType.getViewInAppRelativeUrl - ? this.ruleType.getViewInAppRelativeUrl({ rule: this.rule }) + ? this.ruleType.getViewInAppRelativeUrl({ rule: this.rule, start, end }) : `${triggersActionsRoute}${getRuleDetailsRoute(this.rule.id)}`; try { diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index dacddea634fa4..9be9064908fcb 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -156,6 +156,9 @@ export const ruleType: jest.Mocked = { cancelAlertsOnRuleTimeout: true, ruleTaskTimeout: '5m', autoRecoverAlerts: true, + validate: { + params: { validate: (params) => params }, + }, }; export const mockRunNowResponse = { @@ -233,7 +236,13 @@ export const mockTaskInstance = () => ({ ownerId: null, }); -export const generateAlertOpts = ({ action, group, state, id }: GeneratorParams = {}) => { +export const generateAlertOpts = ({ + action, + group, + state, + id, + maintenanceWindowIds = [], +}: GeneratorParams = {}) => { id = id ?? '1'; let message: string = ''; switch (action) { @@ -255,6 +264,7 @@ export const generateAlertOpts = ({ action, group, state, id }: GeneratorParams state, ...(group ? { group } : {}), flapping: false, + maintenanceWindowIds, }; }; @@ -399,6 +409,7 @@ export const mockAAD = { execution: { uuid: 'c35db7cc-5bf7-46ea-b43f-b251613a5b72' }, name: 'test-rule', producer: 'infrastructure', + revision: 0, rule_type_id: 'metrics.alert.threshold', uuid: '0de91960-7643-11ed-b719-bb9db8582cb6', tags: [], diff --git a/x-pack/plugins/alerting/server/task_runner/log_alerts.test.ts b/x-pack/plugins/alerting/server/task_runner/log_alerts.test.ts index ffeb5db16fed3..18379740e5feb 100644 --- a/x-pack/plugins/alerting/server/task_runner/log_alerts.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/log_alerts.test.ts @@ -362,4 +362,87 @@ describe('logAlerts', () => { uuid: expect.any(String), }); }); + + test('should correctly set maintenance window in ruleRunMetricsStore and call alertingEventLogger.logAlert', () => { + jest.clearAllMocks(); + const MAINTENANCE_WINDOW_IDS = ['window-id-1', 'window-id-2']; + + logAlerts({ + logger, + alertingEventLogger, + newAlerts: { + '4': new Alert<{}, {}, DefaultActionGroupId>('4'), + }, + activeAlerts: { + '1': new Alert<{}, {}, DefaultActionGroupId>('1'), + '4': new Alert<{}, {}, DefaultActionGroupId>('4'), + }, + recoveredAlerts: { + '7': new Alert<{}, {}, DefaultActionGroupId>('7'), + '8': new Alert<{}, {}, DefaultActionGroupId>('8'), + }, + ruleLogPrefix: `test-rule-type-id:123: 'test rule'`, + ruleRunMetricsStore, + canSetRecoveryContext: false, + shouldPersistAlerts: true, + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, + }); + + expect(ruleRunMetricsStore.getNumberOfNewAlerts()).toEqual(1); + expect(ruleRunMetricsStore.getNumberOfActiveAlerts()).toEqual(2); + expect(ruleRunMetricsStore.getNumberOfRecoveredAlerts()).toEqual(2); + + expect(alertingEventLogger.logAlert).toHaveBeenCalledTimes(5); + + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith(1, { + action: 'recovered-instance', + id: '7', + message: "test-rule-type-id:123: 'test rule' alert '7' has recovered", + state: {}, + flapping: false, + group: undefined, + uuid: expect.any(String), + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith(2, { + action: 'recovered-instance', + id: '8', + message: "test-rule-type-id:123: 'test rule' alert '8' has recovered", + state: {}, + flapping: false, + group: undefined, + uuid: expect.any(String), + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith(3, { + action: 'new-instance', + id: '4', + message: "test-rule-type-id:123: 'test rule' created new alert: '4'", + state: {}, + flapping: false, + group: undefined, + uuid: expect.any(String), + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith(4, { + action: 'active-instance', + id: '1', + message: "test-rule-type-id:123: 'test rule' active alert: '1' in actionGroup: 'undefined'", + state: {}, + flapping: false, + group: undefined, + uuid: expect.any(String), + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith(5, { + action: 'active-instance', + id: '4', + message: "test-rule-type-id:123: 'test rule' active alert: '4' in actionGroup: 'undefined'", + state: {}, + flapping: false, + group: undefined, + uuid: expect.any(String), + maintenanceWindowIds: MAINTENANCE_WINDOW_IDS, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/log_alerts.ts b/x-pack/plugins/alerting/server/task_runner/log_alerts.ts index a939ad511da34..8c1427b12b123 100644 --- a/x-pack/plugins/alerting/server/task_runner/log_alerts.ts +++ b/x-pack/plugins/alerting/server/task_runner/log_alerts.ts @@ -28,6 +28,7 @@ export interface LogAlertsParams< ruleRunMetricsStore: RuleRunMetricsStore; canSetRecoveryContext: boolean; shouldPersistAlerts: boolean; + maintenanceWindowIds?: string[]; } export function logAlerts< @@ -45,6 +46,7 @@ export function logAlerts< ruleRunMetricsStore, canSetRecoveryContext, shouldPersistAlerts, + maintenanceWindowIds, }: LogAlertsParams) { const newAlertIds = Object.keys(newAlerts); const activeAlertIds = Object.keys(activeAlerts); @@ -104,6 +106,7 @@ export function logAlerts< message, state, flapping: recoveredAlerts[id].getFlapping(), + maintenanceWindowIds, }); } @@ -121,6 +124,7 @@ export function logAlerts< message, state, flapping: activeAlerts[id].getFlapping(), + maintenanceWindowIds, }); } @@ -138,6 +142,7 @@ export function logAlerts< message, state, flapping: activeAlerts[id].getFlapping(), + maintenanceWindowIds, }); } } diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts index 244110472cec2..872672801b52c 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.test.ts @@ -14,8 +14,12 @@ import { isSummaryAction, isSummaryActionOnInterval, isSummaryActionThrottled, + getSummaryActionTimeBounds, } from './rule_action_helper'; +const now = '2021-05-13T12:33:37.000Z'; +Date.now = jest.fn().mockReturnValue(new Date(now)); + const mockOldAction: RuleAction = { id: '1', group: 'default', @@ -309,4 +313,60 @@ describe('rule_action_helper', () => { ).toBe(false); }); }); + + describe('getSummaryActionTimeBounds', () => { + test('returns undefined start and end action is not summary action', () => { + expect(getSummaryActionTimeBounds(mockAction, { interval: '1m' }, null)).toEqual({ + start: undefined, + end: undefined, + }); + }); + + test('returns start and end for summary action with throttle', () => { + const { start, end } = getSummaryActionTimeBounds( + mockSummaryAction, + { interval: '1m' }, + null + ); + expect(end).toEqual(1620909217000); + expect(end).toEqual(new Date(now).valueOf()); + expect(start).toEqual(1620822817000); + // start is end - throttle interval (1d) + expect(start).toEqual(new Date('2021-05-12T12:33:37.000Z').valueOf()); + }); + + test('returns start and end for summary action without throttle with previousStartedAt', () => { + const { start, end } = getSummaryActionTimeBounds( + { + ...mockSummaryAction, + frequency: { summary: true, notifyWhen: 'onActiveAlert', throttle: null }, + }, + { interval: '1m' }, + new Date('2021-05-13T12:31:57.000Z') + ); + + expect(end).toEqual(1620909217000); + expect(end).toEqual(new Date(now).valueOf()); + expect(start).toEqual(1620909117000); + // start is previous started at time + expect(start).toEqual(new Date('2021-05-13T12:31:57.000Z').valueOf()); + }); + + test('returns start and end for summary action without throttle without previousStartedAt', () => { + const { start, end } = getSummaryActionTimeBounds( + { + ...mockSummaryAction, + frequency: { summary: true, notifyWhen: 'onActiveAlert', throttle: null }, + }, + { interval: '1m' }, + null + ); + + expect(end).toEqual(1620909217000); + expect(end).toEqual(new Date(now).valueOf()); + expect(start).toEqual(1620909157000); + // start is end - schedule interval (1m) + expect(start).toEqual(new Date('2021-05-13T12:32:37.000Z').valueOf()); + }); + }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts index 2918d346511ca..5150170a52bcd 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts @@ -7,6 +7,7 @@ import { Logger } from '@kbn/logging'; import { + IntervalSchedule, parseDuration, RuleAction, RuleNotifyWhenTypeValues, @@ -99,3 +100,32 @@ export const getSummaryActionsFromTaskState = ({ } }, {}); }; + +export const getSummaryActionTimeBounds = ( + action: RuleAction, + ruleSchedule: IntervalSchedule, + previousStartedAt: Date | null +): { start?: number; end?: number } => { + if (!isSummaryAction(action)) { + return { start: undefined, end: undefined }; + } + let startDate: Date; + const now = Date.now(); + + if (isActionOnInterval(action)) { + // If action is throttled, set time bounds using throttle interval + const throttleMills = parseDuration(action.frequency!.throttle!); + startDate = new Date(now - throttleMills); + } else { + // If action is not throttled, set time bounds to previousStartedAt - now + // If previousStartedAt is null, use the rule schedule interval + if (previousStartedAt) { + startDate = previousStartedAt; + } else { + const scheduleMillis = parseDuration(ruleSchedule.interval); + startDate = new Date(now - scheduleMillis); + } + } + + return { start: startDate.valueOf(), end: now.valueOf() }; +}; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index d99bc21d5d3d4..216cf19373cfa 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -16,6 +16,7 @@ import { RuleExecutionStatusWarningReasons, Rule, RuleAction, + MaintenanceWindow, } from '../types'; import { ConcreteTaskInstance, isUnrecoverableError } from '@kbn/task-manager-plugin/server'; import { TaskRunnerContext } from './task_runner_factory'; @@ -77,7 +78,9 @@ import { SharePluginStart } from '@kbn/share-plugin/server'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { rulesSettingsClientMock } from '../rules_settings_client.mock'; +import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; +import { getMockMaintenanceWindow } from '../maintenance_window_client/methods/test_helpers'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -130,6 +133,7 @@ describe('Task Runner', () => { dataViewsServiceFactory: jest.fn().mockResolvedValue(dataViewPluginMocks.createStartContract()), } as DataViewsServerPluginStart; const alertsService = alertsServiceMock.create(); + const maintenanceWindowClient = maintenanceWindowClientMock.create(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -167,6 +171,7 @@ describe('Task Runner', () => { }, }, getRulesSettingsClientWithRequest: jest.fn().mockReturnValue(rulesSettingsClientMock.create()), + getMaintenanceWindowClientWithRequest: jest.fn().mockReturnValue(maintenanceWindowClient), }; const ephemeralTestParams: Array< @@ -203,6 +208,7 @@ describe('Task Runner', () => { }); savedObjectsService.getScopedClient.mockReturnValue(services.savedObjectsClient); elasticsearchService.client.asScoped.mockReturnValue(services.scopedClusterClient); + maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValue([]); taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockReturnValue(rulesClient); taskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( actionsClient @@ -217,6 +223,9 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.getRulesSettingsClientWithRequest.mockReturnValue( rulesSettingsClientMock.create() ); + taskRunnerFactoryInitializerParams.getMaintenanceWindowClientWithRequest.mockReturnValue( + maintenanceWindowClient + ); mockedRuleTypeSavedObject.monitoring!.run.history = []; mockedRuleTypeSavedObject.monitoring!.run.calculated_metrics.success_ratio = 0; @@ -602,6 +611,104 @@ describe('Task Runner', () => { } ); + test('skips alert notification if there are active maintenance windows', async () => { + taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); + taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); + ruleType.executor.mockImplementation( + async ({ + services: executorServices, + }: RuleExecutorOptions< + RuleTypeParams, + RuleTypeState, + AlertInstanceState, + AlertInstanceContext, + string + >) => { + executorServices.alertFactory.create('1').scheduleActions('default'); + return { state: {} }; + } + ); + const taskRunner = new TaskRunner( + ruleType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams, + inMemoryMetrics + ); + expect(AlertingEventLogger).toHaveBeenCalledTimes(1); + rulesClient.getAlertFromRaw.mockReturnValue(mockedRuleTypeSavedObject as Rule); + maintenanceWindowClient.getActiveMaintenanceWindows.mockResolvedValueOnce([ + { + ...getMockMaintenanceWindow(), + id: 'test-id-1', + } as MaintenanceWindow, + { + ...getMockMaintenanceWindow(), + id: 'test-id-2', + } as MaintenanceWindow, + ]); + + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); + await taskRunner.run(); + expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0); + + expect(logger.debug).toHaveBeenCalledTimes(7); + expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); + expect(logger.debug).nthCalledWith( + 2, + `rule test:1: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + ); + expect(logger.debug).nthCalledWith( + 3, + `no scheduling of actions for rule test:1: '${RULE_NAME}': has active maintenance windows test-id-1,test-id-2.` + ); + expect(logger.debug).nthCalledWith( + 4, + 'deprecated ruleRunStatus for test:1: {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + ); + expect(logger.debug).nthCalledWith( + 5, + 'ruleRunStatus for test:1: {"outcome":"succeeded","outcomeOrder":0,"outcomeMsg":null,"warning":null,"alertsCount":{"active":1,"new":1,"recovered":0,"ignored":0}}' + ); + expect(logger.debug).nthCalledWith( + 6, + 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":0,"numberOfGeneratedActions":0,"numberOfActiveAlerts":1,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":1,"hasReachedAlertLimit":false,"triggeredActionsStatus":"complete"}' + ); + expect(logger.debug).nthCalledWith( + 7, + 'Updating rule task for test rule with id 1 - {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"} - {"outcome":"succeeded","outcomeOrder":0,"outcomeMsg":null,"warning":null,"alertsCount":{"active":1,"new":1,"recovered":0,"ignored":0}}' + ); + + const maintenanceWindowIds = ['test-id-1', 'test-id-2']; + + testAlertingEventLogCalls({ + activeAlerts: 1, + newAlerts: 1, + status: 'active', + logAlert: 2, + maintenanceWindowIds, + }); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 1, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.newInstance, + group: 'default', + state: { start: DATE_1970, duration: '0' }, + maintenanceWindowIds, + }) + ); + expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( + 2, + generateAlertOpts({ + action: EVENT_LOG_ACTIONS.activeInstance, + group: 'default', + state: { start: DATE_1970, duration: '0' }, + maintenanceWindowIds, + }) + ); + + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); + }); + test.each(ephemeralTestParams)( 'skips firing actions for active alert if alert is muted %s', async (nameExtension, customTaskRunnerFactoryInitializerParams, enqueueFunction) => { @@ -3009,6 +3116,7 @@ describe('Task Runner', () => { errorMessage = 'GENERIC ERROR MESSAGE', executionStatus = 'succeeded', setRuleName = true, + maintenanceWindowIds, logAlert = 0, logAction = 0, hasReachedAlertLimit = false, @@ -3022,6 +3130,7 @@ describe('Task Runner', () => { generatedActions?: number; executionStatus?: 'succeeded' | 'failed' | 'not-reached'; setRuleName?: boolean; + maintenanceWindowIds?: string[]; logAlert?: number; logAction?: number; errorReason?: string; @@ -3036,6 +3145,11 @@ describe('Task Runner', () => { expect(alertingEventLogger.setRuleName).not.toHaveBeenCalled(); } expect(alertingEventLogger.getStartAndDuration).toHaveBeenCalled(); + if (maintenanceWindowIds?.length) { + expect(alertingEventLogger.setMaintenanceWindowIds).toHaveBeenCalledWith( + maintenanceWindowIds + ); + } if (status === 'error') { expect(alertingEventLogger.done).toHaveBeenCalledWith({ metrics: null, 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 45d22a8fbf2ac..b4ee45eea902b 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -47,6 +47,7 @@ import { parseDuration, RawAlertInstance, RuleLastRunOutcomeOrderMap, + MaintenanceWindow, } from '../../common'; import { NormalizedRuleType, UntypedNormalizedRuleType } from '../rule_type_registry'; import { getEsErrorMessage } from '../lib/errors'; @@ -315,6 +316,25 @@ export class TaskRunner< }); const rulesSettingsClient = this.context.getRulesSettingsClientWithRequest(fakeRequest); const flappingSettings = await rulesSettingsClient.flapping().get(); + const maintenanceWindowClient = this.context.getMaintenanceWindowClientWithRequest(fakeRequest); + + let activeMaintenanceWindows: MaintenanceWindow[] = []; + try { + activeMaintenanceWindows = await maintenanceWindowClient.getActiveMaintenanceWindows({ + interval: rule.schedule.interval, + }); + } catch (err) { + this.logger.error( + `error getting active maintenance window for ${ruleTypeId}:${ruleId} ${err.message}` + ); + } + + const maintenanceWindowIds = activeMaintenanceWindows.map( + (maintenanceWindow) => maintenanceWindow.id + ); + if (maintenanceWindowIds.length) { + this.alertingEventLogger.setMaintenanceWindowIds(maintenanceWindowIds); + } const { updatedRuleTypeState } = await this.timer.runWithTimer( TaskRunnerTimerSpan.RuleTypeRun, @@ -397,6 +417,7 @@ export class TaskRunner< }, logger: this.logger, flappingSettings, + ...(maintenanceWindowIds.length ? { maintenanceWindowIds } : {}), }) ); @@ -444,6 +465,7 @@ export class TaskRunner< shouldLogAndScheduleActionsForAlerts: this.shouldLogAndScheduleActionsForAlerts(), flappingSettings, notifyWhen, + maintenanceWindowIds, }); }); @@ -458,6 +480,7 @@ export class TaskRunner< ruleConsumer: this.ruleConsumer!, executionId: this.executionId, ruleLabel, + previousStartedAt: previousStartedAt ? new Date(previousStartedAt) : null, alertingEventLogger: this.alertingEventLogger, actionsClient: await this.context.actionsPlugin.getActionsClientWithRequest(fakeRequest), }); @@ -469,6 +492,10 @@ export class TaskRunner< if (isRuleSnoozed(rule)) { this.logger.debug(`no scheduling of actions for rule ${ruleLabel}: rule is snoozed.`); + } else if (maintenanceWindowIds.length) { + this.logger.debug( + `no scheduling of actions for rule ${ruleLabel}: has active maintenance windows ${maintenanceWindowIds}.` + ); } else if (!this.shouldLogAndScheduleActionsForAlerts()) { this.logger.debug( `no scheduling of actions for rule ${ruleLabel}: rule execution has been cancelled.` @@ -547,7 +574,7 @@ export class TaskRunner< this.alertingEventLogger.start(); return await loadRule({ - paramValidator: this.ruleType.validate?.params, + paramValidator: this.ruleType.validate.params, ruleId, spaceId, context: this.context, @@ -691,7 +718,7 @@ export class TaskRunner< schedule = asOk( ( await loadRule({ - paramValidator: this.ruleType.validate?.params, + paramValidator: this.ruleType.validate.params, ruleId, spaceId, context: this.context, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index c02ebe647e697..b43149fc05bcf 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -54,6 +54,7 @@ import { SharePluginStart } from '@kbn/share-plugin/server'; import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { rulesSettingsClientMock } from '../rules_settings_client.mock'; +import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; jest.mock('uuid', () => ({ @@ -143,6 +144,9 @@ describe('Task Runner Cancel', () => { }, }, getRulesSettingsClientWithRequest: jest.fn().mockReturnValue(rulesSettingsClientMock.create()), + getMaintenanceWindowClientWithRequest: jest + .fn() + .mockReturnValue(maintenanceWindowClientMock.create()), }; beforeEach(() => { @@ -173,6 +177,9 @@ describe('Task Runner Cancel', () => { taskRunnerFactoryInitializerParams.getRulesSettingsClientWithRequest.mockReturnValue( rulesSettingsClientMock.create() ); + taskRunnerFactoryInitializerParams.getMaintenanceWindowClientWithRequest.mockReturnValue( + maintenanceWindowClientMock.create() + ); rulesClient.getAlertFromRaw.mockReturnValue(mockedRuleTypeSavedObject as Rule); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index 61ec6f18a1138..69480563c355e 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -30,7 +30,9 @@ import { SharePluginStart } from '@kbn/share-plugin/server'; import { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { rulesSettingsClientMock } from '../rules_settings_client.mock'; +import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; +import { schema } from '@kbn/config-schema'; const inMemoryMetrics = inMemoryMetricsMock.create(); const executionContext = executionContextServiceMock.createSetupContract(); @@ -57,6 +59,9 @@ const ruleType: UntypedNormalizedRuleType = { }, executor: jest.fn(), producer: 'alerts', + validate: { + params: schema.any(), + }, }; let fakeTimer: sinon.SinonFakeTimers; @@ -120,6 +125,9 @@ describe('Task Runner Factory', () => { }, }, getRulesSettingsClientWithRequest: jest.fn().mockReturnValue(rulesSettingsClientMock.create()), + getMaintenanceWindowClientWithRequest: jest + .fn() + .mockReturnValue(maintenanceWindowClientMock.create()), }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index ced3da1ef346d..0299f07ab8de4 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -32,6 +32,7 @@ import { AlertInstanceContext, RulesClientApi, RulesSettingsClientApi, + MaintenanceWindowClientApi, } from '../types'; import { TaskRunner } from './task_runner'; import { NormalizedRuleType } from '../rule_type_registry'; @@ -65,6 +66,7 @@ export interface TaskRunnerContext { cancelAlertsOnRuleTimeout: boolean; usageCounter?: UsageCounter; getRulesSettingsClientWithRequest(request: KibanaRequest): RulesSettingsClientApi; + getMaintenanceWindowClientWithRequest(request: KibanaRequest): MaintenanceWindowClientApi; } export class TaskRunnerFactory { diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts index a240105f7cab2..a8889bc620c83 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts @@ -182,10 +182,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"1\\" exists", - } - `); + Object { + "message": "Value \\"1\\" exists", + } + `); }); test('alertName is passed to templates', () => { @@ -212,10 +212,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"alert-name\\" exists", - } - `); + Object { + "message": "Value \\"alert-name\\" exists", + } + `); }); test('tags is passed to templates', () => { @@ -242,10 +242,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"tag-A,tag-B\\" exists", - } - `); + Object { + "message": "Value \\"tag-A,tag-B\\" exists", + } + `); }); test('undefined tags is passed to templates', () => { @@ -271,10 +271,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"\\" is undefined and renders as empty string", - } - `); + Object { + "message": "Value \\"\\" is undefined and renders as empty string", + } + `); }); test('empty tags is passed to templates', () => { @@ -301,10 +301,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"\\" is an empty array and renders as empty string", - } - `); + Object { + "message": "Value \\"\\" is an empty array and renders as empty string", + } + `); }); test('spaceId is passed to templates', () => { @@ -331,10 +331,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"spaceId-A\\" exists", - } - `); + Object { + "message": "Value \\"spaceId-A\\" exists", + } + `); }); test('alertInstanceId is passed to templates', () => { @@ -361,10 +361,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"2\\" exists", - } - `); + Object { + "message": "Value \\"2\\" exists", + } + `); }); test('alertActionGroup is passed to templates', () => { @@ -391,10 +391,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"action-group\\" exists", - } - `); + Object { + "message": "Value \\"action-group\\" exists", + } + `); }); test('alertActionGroupName is passed to templates', () => { @@ -421,10 +421,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"Action Group\\" exists", - } - `); + Object { + "message": "Value \\"Action Group\\" exists", + } + `); }); test('rule variables are passed to templates', () => { @@ -451,10 +451,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"1\\", \\"alert-name\\", \\"spaceId-A\\" and \\"tag-A,tag-B\\" exist", - } - `); + Object { + "message": "Value \\"1\\", \\"alert-name\\", \\"spaceId-A\\" and \\"tag-A,tag-B\\" exist", + } + `); }); test('rule alert variables are passed to templates', () => { @@ -482,10 +482,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"2\\", \\"action-group\\", \\"uuid-1\\" and \\"Action Group\\" exist", - } - `); + Object { + "message": "Value \\"2\\", \\"action-group\\", \\"uuid-1\\" and \\"Action Group\\" exist", + } + `); }); test('date is passed to templates', () => { @@ -613,10 +613,10 @@ describe('transformActionParams', () => { flapping: true, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"true\\" exists", - } - `); + Object { + "message": "Value \\"true\\" exists", + } + `); }); }); @@ -626,13 +626,13 @@ describe('transformSummaryActionParams', () => { new: { count: 1, data: [mockAAD] }, ongoing: { count: 0, data: [] }, recovered: { count: 0, data: [] }, - all: { count: 0, data: [] }, + all: { count: 1, data: [mockAAD] }, }, rule: { id: '1', name: 'test-rule', tags: ['test-tag'], - params: {}, + params: { foo: 'bar', fooBar: true }, } as SanitizedRule, ruleTypeId: 'rule-type-id', actionId: 'action-id', @@ -657,6 +657,33 @@ describe('transformSummaryActionParams', () => { `); }); + test('renders aliased context values', () => { + const actionParams = { + message: + 'Value "{{context.alerts}}", "{{context.results_link}}" and "{{context.rule}}" exist', + }; + + const result = transformSummaryActionParams({ ...params, actionParams }); + expect(result).toMatchInlineSnapshot(` + Object { + "message": "Value \\"{\\"@timestamp\\":\\"2022-12-07T15:38:43.472Z\\",\\"event\\":{\\"kind\\":\\"signal\\",\\"action\\":\\"active\\"},\\"kibana\\":{\\"version\\":\\"8.7.0\\",\\"space_ids\\":[\\"default\\"],\\"alert\\":{\\"instance\\":{\\"id\\":\\"*\\"},\\"uuid\\":\\"2d3e8fe5-3e8b-4361-916e-9eaab0bf2084\\",\\"status\\":\\"active\\",\\"workflow_status\\":\\"open\\",\\"reason\\":\\"system.cpu is 90% in the last 1 min for all hosts. Alert when > 50%.\\",\\"time_range\\":{\\"gte\\":\\"2022-01-01T12:00:00.000Z\\"},\\"start\\":\\"2022-12-07T15:23:13.488Z\\",\\"duration\\":{\\"us\\":100000},\\"flapping\\":false,\\"rule\\":{\\"category\\":\\"Metric threshold\\",\\"consumer\\":\\"alerts\\",\\"execution\\":{\\"uuid\\":\\"c35db7cc-5bf7-46ea-b43f-b251613a5b72\\"},\\"name\\":\\"test-rule\\",\\"producer\\":\\"infrastructure\\",\\"revision\\":0,\\"rule_type_id\\":\\"metrics.alert.threshold\\",\\"uuid\\":\\"0de91960-7643-11ed-b719-bb9db8582cb6\\",\\"tags\\":[]}}}}\\", \\"http://ruleurl\\" and \\"{\\"foo\\":\\"bar\\",\\"foo_bar\\":true,\\"name\\":\\"test-rule\\",\\"id\\":\\"1\\"}\\" exist", + } + `); + }); + + test('renders aliased state values', () => { + const actionParams = { + message: 'Value "{{state.signals_count}}" exists', + }; + + const result = transformSummaryActionParams({ ...params, actionParams }); + expect(result).toMatchInlineSnapshot(` + Object { + "message": "Value \\"1\\" exists", + } + `); + }); + test('renders alerts values', () => { const actionParams = { message: diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts index 9f037d39b75d1..b4a3727863169 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts @@ -6,6 +6,7 @@ */ import { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; +import { mapKeys, snakeCase } from 'lodash/fp'; import { RuleActionParams, AlertInstanceState, @@ -143,6 +144,19 @@ export function transformSummaryActionParams({ const variables = { kibanaBaseUrl, date: new Date().toISOString(), + // For backwards compatibility with security solutions rules + context: { + alerts: alerts.all.data ?? [], + results_link: ruleUrl, + rule: mapKeys(snakeCase, { + ...rule.params, + name: rule.name, + id: rule.id, + }), + }, + state: { + signals_count: alerts.all.count ?? 0, + }, rule: { params: rule.params, id: rule.id, diff --git a/x-pack/plugins/alerting/server/task_runner/types.ts b/x-pack/plugins/alerting/server/task_runner/types.ts index f2d04812115a8..4a1343add9249 100644 --- a/x-pack/plugins/alerting/server/task_runner/types.ts +++ b/x-pack/plugins/alerting/server/task_runner/types.ts @@ -85,6 +85,7 @@ export interface ExecutionHandlerOptions< ruleConsumer: string; executionId: string; ruleLabel: string; + previousStartedAt: Date | null; actionsClient: PublicMethodsOf; } diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 1a23f623a54d4..420025b1cd007 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -23,6 +23,7 @@ import { import type { PublicMethodsOf } from '@kbn/utility-types'; import { SharePluginStart } from '@kbn/share-plugin/server'; import { type FieldMap } from '@kbn/alerts-as-data-utils'; +import { Filter } from '@kbn/es-query'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { RulesClient } from './rules_client'; @@ -118,6 +119,7 @@ export interface RuleExecutorOptions< state: State; namespace?: string; flappingSettings: RulesSettingsFlappingProperties; + maintenanceWindowIds?: string[]; } export interface RuleParamsAndRefs { @@ -136,7 +138,7 @@ export type ExecutorType< ) => Promise<{ state: State }>; export interface RuleTypeParamsValidator { - validate: (object: unknown) => Params; + validate: (object: Partial) => Params; validateMutatedParams?: (mutatedOject: Params, origObject?: Params) => Params; } @@ -168,6 +170,9 @@ export interface CombinedSummarizedAlerts extends SummarizedAlerts { export type GetSummarizedAlertsFn = (opts: GetSummarizedAlertsFnOpts) => Promise; export interface GetViewInAppRelativeUrlFnOpts { rule: Omit, 'viewInAppRelativeUrl'>; + // Optional time bounds + start?: number; + end?: number; } export type GetViewInAppRelativeUrlFn = ( opts: GetViewInAppRelativeUrlFnOpts @@ -231,8 +236,8 @@ export interface RuleType< > { id: string; name: string; - validate?: { - params?: RuleTypeParamsValidator; + validate: { + params: RuleTypeParamsValidator; }; actionGroups: Array>; defaultActionGroupId: ActionGroup['id']; @@ -281,11 +286,12 @@ export type UntypedRuleType = RuleType< >; export interface RawAlertsFilter extends AlertsFilter { - query: null | { + query?: { kql: string; + filters: Filter[]; dsl: string; }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export interface RawRuleAction extends SavedObjectAttributes { diff --git a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts index d16b76d91afa9..58a449d5f7004 100644 --- a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts +++ b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts @@ -157,7 +157,7 @@ describe('kibana index telemetry', () => { const telemetry = await getTotalCountAggregations({ esClient, - kibanaIndex: 'test', + alertIndex: 'test', logger, }); @@ -228,7 +228,7 @@ describe('kibana index telemetry', () => { const telemetry = await getTotalCountAggregations({ esClient, - kibanaIndex: 'test', + alertIndex: 'test', logger, }); @@ -333,7 +333,7 @@ describe('kibana index telemetry', () => { const telemetry = await getTotalCountInUse({ esClient, - kibanaIndex: 'test', + alertIndex: 'test', logger, }); @@ -357,7 +357,7 @@ describe('kibana index telemetry', () => { const telemetry = await getTotalCountInUse({ esClient, - kibanaIndex: 'test', + alertIndex: 'test', logger, }); diff --git a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts index 0c6d01016c313..a1055aa075521 100644 --- a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts +++ b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts @@ -25,7 +25,7 @@ import { parseSimpleRuleTypeBucket } from './parse_simple_rule_type_bucket'; interface Opts { esClient: ElasticsearchClient; - kibanaIndex: string; + alertIndex: string; logger: Logger; } @@ -57,12 +57,12 @@ interface GetTotalCountInUseResults { export async function getTotalCountAggregations({ esClient, - kibanaIndex, + alertIndex, logger, }: Opts): Promise { try { const query = { - index: kibanaIndex, + index: alertIndex, size: 0, body: { query: { @@ -400,12 +400,12 @@ export async function getTotalCountAggregations({ export async function getTotalCountInUse({ esClient, - kibanaIndex, + alertIndex, logger, }: Opts): Promise { try { const query = { - index: kibanaIndex, + index: alertIndex, size: 0, body: { query: { diff --git a/x-pack/plugins/alerting/server/usage/task.ts b/x-pack/plugins/alerting/server/usage/task.ts index e17d17dc6f4f5..9c6a3607535ee 100644 --- a/x-pack/plugins/alerting/server/usage/task.ts +++ b/x-pack/plugins/alerting/server/usage/task.ts @@ -29,10 +29,9 @@ export function initializeAlertingTelemetry( logger: Logger, core: CoreSetup, taskManager: TaskManagerSetupContract, - kibanaIndex: string, eventLogIndex: string ) { - registerAlertingTelemetryTask(logger, core, taskManager, kibanaIndex, eventLogIndex); + registerAlertingTelemetryTask(logger, core, taskManager, eventLogIndex); } export function scheduleAlertingTelemetry(logger: Logger, taskManager?: TaskManagerStartContract) { @@ -45,20 +44,13 @@ function registerAlertingTelemetryTask( logger: Logger, core: CoreSetup, taskManager: TaskManagerSetupContract, - kibanaIndex: string, eventLogIndex: string ) { taskManager.registerTaskDefinitions({ [TELEMETRY_TASK_TYPE]: { title: 'Alerting usage fetch task', timeout: '5m', - createTaskRunner: telemetryTaskRunner( - logger, - core, - kibanaIndex, - eventLogIndex, - taskManager.index - ), + createTaskRunner: telemetryTaskRunner(logger, core, eventLogIndex, taskManager.index), }, }); } @@ -80,7 +72,6 @@ async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContra export function telemetryTaskRunner( logger: Logger, core: CoreSetup, - kibanaIndex: string, eventLogIndex: string, taskManagerIndex: string ) { @@ -94,13 +85,18 @@ export function telemetryTaskRunner( }, ]) => client.asInternalUser ); + const getAlertIndex = () => + core + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects.getIndexForType('alert')); return { async run() { const esClient = await getEsClient(); + const alertIndex = await getAlertIndex(); return Promise.all([ - getTotalCountAggregations({ esClient, kibanaIndex, logger }), - getTotalCountInUse({ esClient, kibanaIndex, logger }), + getTotalCountAggregations({ esClient, alertIndex, logger }), + getTotalCountInUse({ esClient, alertIndex, logger }), getExecutionsPerDayCount({ esClient, eventLogIndex, logger }), getExecutionTimeoutsPerDayCount({ esClient, eventLogIndex, logger }), getFailedAndUnrecognizedTasksPerDay({ esClient, taskManagerIndex, logger }), diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index 3cf3904d3942b..200b8e3c2a79e 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -51,6 +51,7 @@ "@kbn/core-doc-links-server-mocks", "@kbn/doc-links", "@kbn/core-saved-objects-utils-server", + "@kbn/core-ui-settings-common", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/apm/common/agent_name.ts b/x-pack/plugins/apm/common/agent_name.ts index 21cecfcf348f7..7782cc044e950 100644 --- a/x-pack/plugins/apm/common/agent_name.ts +++ b/x-pack/plugins/apm/common/agent_name.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { AgentName } from '../typings/es_schemas/ui/fields/agent'; +import { + AgentName, + OpenTelemetryAgentName, +} from '../typings/es_schemas/ui/fields/agent'; import { ServerlessType } from './serverless'; /* @@ -47,8 +50,11 @@ export const AGENT_NAMES: AgentName[] = [ ...OPEN_TELEMETRY_AGENT_NAMES, ]; -export const isOpenTelemetryAgentName = (agentName: AgentName) => - OPEN_TELEMETRY_AGENT_NAMES.includes(agentName); +export function isOpenTelemetryAgentName( + agentName: string +): agentName is OpenTelemetryAgentName { + return OPEN_TELEMETRY_AGENT_NAMES.includes(agentName as AgentName); +} export const JAVA_AGENT_NAMES: AgentName[] = ['java', 'opentelemetry/java']; diff --git a/x-pack/plugins/apm/common/data_source.ts b/x-pack/plugins/apm/common/data_source.ts index b951677a8cb65..9282fb372ac72 100644 --- a/x-pack/plugins/apm/common/data_source.ts +++ b/x-pack/plugins/apm/common/data_source.ts @@ -13,7 +13,8 @@ type AnyApmDocumentType = | ApmDocumentType.TransactionMetric | ApmDocumentType.TransactionEvent | ApmDocumentType.ServiceDestinationMetric - | ApmDocumentType.ServiceSummaryMetric; + | ApmDocumentType.ServiceSummaryMetric + | ApmDocumentType.ErrorEvent; export interface ApmDataSource< TDocumentType extends AnyApmDocumentType = AnyApmDocumentType diff --git a/x-pack/plugins/apm/common/dependencies.ts b/x-pack/plugins/apm/common/dependencies.ts index 6228d4b9d279d..812bb33342ba5 100644 --- a/x-pack/plugins/apm/common/dependencies.ts +++ b/x-pack/plugins/apm/common/dependencies.ts @@ -13,14 +13,14 @@ import { } from './es_fields/apm'; import { environmentQuery } from './utils/environment_query'; -export const kueryBarPlaceholder = i18n.translate( - 'xpack.apm.dependencies.kueryBarPlaceholder', +export const unifiedSearchBarPlaceholder = i18n.translate( + 'xpack.apm.dependencies.unifiedSearchBarPlaceholder', { defaultMessage: `Search dependency metrics (e.g. span.destination.service.resource:elasticsearch)`, } ); -export const getKueryBarBoolFilter = ({ +export const getSearchBarBoolFilter = ({ dependencyName, environment, }: { diff --git a/x-pack/plugins/apm/common/document_type.ts b/x-pack/plugins/apm/common/document_type.ts index 333b9f69e0d0f..92a17c3125a96 100644 --- a/x-pack/plugins/apm/common/document_type.ts +++ b/x-pack/plugins/apm/common/document_type.ts @@ -11,6 +11,7 @@ export enum ApmDocumentType { TransactionEvent = 'transactionEvent', ServiceDestinationMetric = 'serviceDestinationMetric', ServiceSummaryMetric = 'serviceSummaryMetric', + ErrorEvent = 'error', } export type ApmServiceTransactionDocumentType = diff --git a/x-pack/plugins/apm/common/es_fields/__snapshots__/es_fields.test.ts.snap b/x-pack/plugins/apm/common/es_fields/__snapshots__/es_fields.test.ts.snap index deb363ab5a21b..9dfb15ed9cb05 100644 --- a/x-pack/plugins/apm/common/es_fields/__snapshots__/es_fields.test.ts.snap +++ b/x-pack/plugins/apm/common/es_fields/__snapshots__/es_fields.test.ts.snap @@ -90,6 +90,8 @@ exports[`Error ERROR_LOG_MESSAGE 1`] = `undefined`; exports[`Error ERROR_PAGE_URL 1`] = `undefined`; +exports[`Error ERROR_TYPE 1`] = `undefined`; + exports[`Error EVENT_NAME 1`] = `undefined`; exports[`Error EVENT_OUTCOME 1`] = `undefined`; @@ -156,10 +158,10 @@ exports[`Error LABEL_GC 1`] = `undefined`; exports[`Error LABEL_NAME 1`] = `undefined`; -exports[`Error LABEL_TYPE 1`] = `undefined`; - exports[`Error LABEL_TELEMETRY_AUTO_VERSION 1`] = `undefined`; +exports[`Error LABEL_TYPE 1`] = `undefined`; + exports[`Error METRIC_CGROUP_MEMORY_LIMIT_BYTES 1`] = `undefined`; exports[`Error METRIC_CGROUP_MEMORY_USAGE_BYTES 1`] = `undefined`; @@ -417,6 +419,8 @@ exports[`Span ERROR_LOG_MESSAGE 1`] = `undefined`; exports[`Span ERROR_PAGE_URL 1`] = `undefined`; +exports[`Span ERROR_TYPE 1`] = `undefined`; + exports[`Span EVENT_NAME 1`] = `undefined`; exports[`Span EVENT_OUTCOME 1`] = `"unknown"`; @@ -479,10 +483,10 @@ exports[`Span LABEL_GC 1`] = `undefined`; exports[`Span LABEL_NAME 1`] = `undefined`; -exports[`Span LABEL_TYPE 1`] = `undefined`; - exports[`Span LABEL_TELEMETRY_AUTO_VERSION 1`] = `undefined`; +exports[`Span LABEL_TYPE 1`] = `undefined`; + exports[`Span METRIC_CGROUP_MEMORY_LIMIT_BYTES 1`] = `undefined`; exports[`Span METRIC_CGROUP_MEMORY_USAGE_BYTES 1`] = `undefined`; @@ -740,6 +744,8 @@ exports[`Transaction ERROR_LOG_MESSAGE 1`] = `undefined`; exports[`Transaction ERROR_PAGE_URL 1`] = `undefined`; +exports[`Transaction ERROR_TYPE 1`] = `undefined`; + exports[`Transaction EVENT_NAME 1`] = `undefined`; exports[`Transaction EVENT_OUTCOME 1`] = `"unknown"`; @@ -812,10 +818,10 @@ exports[`Transaction LABEL_GC 1`] = `undefined`; exports[`Transaction LABEL_NAME 1`] = `undefined`; -exports[`Transaction LABEL_TYPE 1`] = `undefined`; - exports[`Transaction LABEL_TELEMETRY_AUTO_VERSION 1`] = `undefined`; +exports[`Transaction LABEL_TYPE 1`] = `undefined`; + exports[`Transaction METRIC_CGROUP_MEMORY_LIMIT_BYTES 1`] = `undefined`; exports[`Transaction METRIC_CGROUP_MEMORY_USAGE_BYTES 1`] = `undefined`; diff --git a/x-pack/plugins/apm/common/es_fields/apm.ts b/x-pack/plugins/apm/common/es_fields/apm.ts index 9a1dd15f94a75..141be32365956 100644 --- a/x-pack/plugins/apm/common/es_fields/apm.ts +++ b/x-pack/plugins/apm/common/es_fields/apm.ts @@ -109,6 +109,7 @@ export const ERROR_EXC_MESSAGE = 'error.exception.message'; // only to be used i export const ERROR_EXC_HANDLED = 'error.exception.handled'; // only to be used in es queries, since error.exception is now an array export const ERROR_EXC_TYPE = 'error.exception.type'; export const ERROR_PAGE_URL = 'error.page.url'; +export const ERROR_TYPE = 'error.type'; // METRICS export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; diff --git a/x-pack/plugins/apm/common/mobile_types.ts b/x-pack/plugins/apm/common/mobile_types.ts new file mode 100644 index 0000000000000..a450909c51d2f --- /dev/null +++ b/x-pack/plugins/apm/common/mobile_types.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 enum MobilePropertyType { + Device = 'device', + NetworkConnectionType = 'netConnectionType', + OsVersion = 'osVersion', + AppVersion = 'appVersion', +} + +export type MobilePropertyNctType = MobilePropertyType.NetworkConnectionType; + +export type MobilePropertyDeviceOsAppVersionType = + | MobilePropertyType.Device + | MobilePropertyType.OsVersion + | MobilePropertyType.AppVersion; diff --git a/x-pack/plugins/apm/common/rules/apm_rule_types.ts b/x-pack/plugins/apm/common/rules/apm_rule_types.ts index b5a640b2d72af..d1009cebc70da 100644 --- a/x-pack/plugins/apm/common/rules/apm_rule_types.ts +++ b/x-pack/plugins/apm/common/rules/apm_rule_types.ts @@ -15,6 +15,14 @@ import type { import type { ActionGroup } from '@kbn/alerting-plugin/common'; import { formatDurationFromTimeUnitChar } from '@kbn/observability-plugin/common'; import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from '../ml_constants'; +import { + ERROR_GROUP_ID, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_NAME, + TRANSACTION_TYPE, +} from '../es_fields/apm'; +import { getEnvironmentLabel } from '../environment_filter_values'; export const APM_SERVER_FEATURE_ID = 'apm'; @@ -40,29 +48,66 @@ const THRESHOLD_MET_GROUP: ActionGroup = { }), }; +const getFieldNameLabel = (field: string): string => { + switch (field) { + case SERVICE_NAME: + return 'service'; + case SERVICE_ENVIRONMENT: + return 'env'; + case TRANSACTION_TYPE: + return 'type'; + case TRANSACTION_NAME: + return 'name'; + case ERROR_GROUP_ID: + return 'error key'; + default: + return field; + } +}; + +export const getFieldValueLabel = ( + field: string, + fieldValue: string +): string => { + return field === SERVICE_ENVIRONMENT + ? getEnvironmentLabel(fieldValue) + : fieldValue; +}; + +const formatGroupByFields = (groupByFields: Record): string => { + const groupByFieldLabels = Object.keys(groupByFields).map( + (field) => + `${getFieldNameLabel(field)}: ${getFieldValueLabel( + field, + groupByFields[field] + )}` + ); + return groupByFieldLabels.join(', '); +}; + export function formatErrorCountReason({ threshold, measured, - serviceName, windowSize, windowUnit, + groupByFields, }: { threshold: number; measured: number; - serviceName: string; windowSize: number; windowUnit: string; + groupByFields: Record; }) { return i18n.translate('xpack.apm.alertTypes.errorCount.reason', { - defaultMessage: `Error count is {measured} in the last {interval} for {serviceName}. Alert when > {threshold}.`, + defaultMessage: `Error count is {measured} in the last {interval} for {group}. Alert when > {threshold}.`, values: { threshold, measured, - serviceName, interval: formatDurationFromTimeUnitChar( windowSize, windowUnit as TimeUnitChar ), + group: formatGroupByFields(groupByFields), }, }); } @@ -70,19 +115,19 @@ export function formatErrorCountReason({ export function formatTransactionDurationReason({ threshold, measured, - serviceName, asDuration, aggregationType, windowSize, windowUnit, + groupByFields, }: { threshold: number; measured: number; - serviceName: string; asDuration: AsDuration; aggregationType: string; windowSize: number; windowUnit: string; + groupByFields: Record; }) { let aggregationTypeFormatted = aggregationType.charAt(0).toUpperCase() + aggregationType.slice(1); @@ -90,16 +135,16 @@ export function formatTransactionDurationReason({ aggregationTypeFormatted = aggregationTypeFormatted + '.'; return i18n.translate('xpack.apm.alertTypes.transactionDuration.reason', { - defaultMessage: `{aggregationType} latency is {measured} in the last {interval} for {serviceName}. Alert when > {threshold}.`, + defaultMessage: `{aggregationType} latency is {measured} in the last {interval} for {group}. Alert when > {threshold}.`, values: { threshold: asDuration(threshold), measured: asDuration(measured), - serviceName, aggregationType: aggregationTypeFormatted, interval: formatDurationFromTimeUnitChar( windowSize, windowUnit as TimeUnitChar ), + group: formatGroupByFields(groupByFields), }, }); } @@ -107,28 +152,28 @@ export function formatTransactionDurationReason({ export function formatTransactionErrorRateReason({ threshold, measured, - serviceName, asPercent, windowSize, windowUnit, + groupByFields, }: { threshold: number; measured: number; - serviceName: string; asPercent: AsPercent; windowSize: number; windowUnit: string; + groupByFields: Record; }) { return i18n.translate('xpack.apm.alertTypes.transactionErrorRate.reason', { - defaultMessage: `Failed transactions is {measured} in the last {interval} for {serviceName}. Alert when > {threshold}.`, + defaultMessage: `Failed transactions is {measured} in the last {interval} for {group}. Alert when > {threshold}.`, values: { threshold: asPercent(threshold, 100), measured: asPercent(measured, 100), - serviceName, interval: formatDurationFromTimeUnitChar( windowSize, windowUnit as TimeUnitChar ), + group: formatGroupByFields(groupByFields), }, }); } diff --git a/x-pack/plugins/apm/common/rules/default_action_message.ts b/x-pack/plugins/apm/common/rules/default_action_message.ts index 503bc1ca3cd26..e8bde279f55ee 100644 --- a/x-pack/plugins/apm/common/rules/default_action_message.ts +++ b/x-pack/plugins/apm/common/rules/default_action_message.ts @@ -25,7 +25,8 @@ export const transactionDurationMessage = i18n.translate( defaultMessage: `\\{\\{alertName\\}\\} alert is firing because of the following conditions: - Service name: \\{\\{context.serviceName\\}\\} -- Type: \\{\\{context.transactionType\\}\\} +- Transaction type: \\{\\{context.transactionType\\}\\} +- Transaction name: \\{\\{context.transactionName\\}\\} - Environment: \\{\\{context.environment\\}\\} - Latency threshold: \\{\\{context.threshold\\}\\}ms - Latency observed: \\{\\{context.triggerValue\\}\\} over the last \\{\\{context.interval\\}\\}`, diff --git a/x-pack/plugins/apm/common/rules/schema.ts b/x-pack/plugins/apm/common/rules/schema.ts index 58a5b40da41f2..656f6efbe4b25 100644 --- a/x-pack/plugins/apm/common/rules/schema.ts +++ b/x-pack/plugins/apm/common/rules/schema.ts @@ -15,11 +15,14 @@ export const errorCountParamsSchema = schema.object({ threshold: schema.number(), serviceName: schema.maybe(schema.string()), environment: schema.string(), + groupBy: schema.maybe(schema.arrayOf(schema.string())), + errorGroupingKey: schema.maybe(schema.string()), }); export const transactionDurationParamsSchema = schema.object({ serviceName: schema.maybe(schema.string()), transactionType: schema.maybe(schema.string()), + transactionName: schema.maybe(schema.string()), windowSize: schema.number(), windowUnit: schema.string(), threshold: schema.number(), @@ -29,6 +32,7 @@ export const transactionDurationParamsSchema = schema.object({ schema.literal(AggregationType.P99), ]), environment: schema.string(), + groupBy: schema.maybe(schema.arrayOf(schema.string())), }); export const anomalyParamsSchema = schema.object({ @@ -50,8 +54,10 @@ export const transactionErrorRateParamsSchema = schema.object({ windowUnit: schema.string(), threshold: schema.number(), transactionType: schema.maybe(schema.string()), + transactionName: schema.maybe(schema.string()), serviceName: schema.maybe(schema.string()), environment: schema.string(), + groupBy: schema.maybe(schema.arrayOf(schema.string())), }); type ErrorCountParamsType = TypeOf; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/generate_mobile.data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/generate_mobile.data.ts new file mode 100644 index 0000000000000..bf25bf83d8aaa --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/generate_mobile.data.ts @@ -0,0 +1,392 @@ +/* + * 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 { apm, timerange } from '@kbn/apm-synthtrace-client'; + +export function generateMobileData({ from, to }: { from: number; to: number }) { + const range = timerange(from, to); + + const galaxy10 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Samsung', + modelIdentifier: 'SM-G973F', + modelName: 'Galaxy S10', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + const galaxy7 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '1.2' }) + .deviceInfo({ + manufacturer: 'Samsung', + modelIdentifier: 'SM-G930F', + modelName: 'Galaxy S7', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ + type: 'cell', + subType: 'edge', + carrierName: 'M1 Limited', + carrierMNC: '03', + carrierICC: 'SG', + carrierMCC: '525', + }); + + const huaweiP2 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '1.1' }) + .deviceInfo({ + manufacturer: 'Huawei', + modelIdentifier: 'HUAWEI P2-0000', + modelName: 'HuaweiP2', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '20.24.184.101', + cityName: 'Singapore', + continentName: 'Asia', + countryIsoCode: 'SG', + countryName: 'Singapore', + location: { coordinates: [103.8554, 1.3036], type: 'Point' }, + }) + .setNetworkConnection({ + type: 'cell', + subType: 'edge', + carrierName: 'Osaka Gas Business Create Co., Ltd.', + carrierMNC: '17', + carrierICC: 'JP', + carrierMCC: '440', + }); + + const pixel7 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Google', + modelIdentifier: 'Pixel 7', + modelName: 'Pixel 7', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + const pixel7Pro = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Google', + modelIdentifier: 'Pixel 7 Pro', + modelName: 'Pixel 7 Pro', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + const pixel8 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Google', + modelIdentifier: 'Pixel 8', + modelName: 'Pixel 8', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + return range.interval('1m').generator((timestamp) => { + galaxy10.startNewSession(); + galaxy7.startNewSession(); + huaweiP2.startNewSession(); + pixel7.startNewSession(); + pixel7Pro.startNewSession(); + pixel8.startNewSession(); + return [ + galaxy10 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(500) + .success() + .children( + galaxy10 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + galaxy10 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + galaxy10 + .transaction('Second View - View Appearing', 'Android Activity') + .timestamp(10000 + timestamp) + .duration(300) + .failure() + .children( + galaxy10 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/second', + }) + .duration(400) + .success() + .timestamp(10000 + timestamp + 250) + ), + huaweiP2 + .transaction('Start View - View Appearing', 'huaweiP2 Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + huaweiP2 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + huaweiP2 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + galaxy7 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + galaxy7 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + galaxy7 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + pixel7 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + pixel7 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + pixel7 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + pixel8 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + pixel8 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + pixel8 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + pixel7Pro + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + pixel7Pro + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + pixel7Pro + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + ]; + }); +} diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/mobile_overview_with_most_used_charts.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/mobile_overview_with_most_used_charts.cy.ts new file mode 100644 index 0000000000000..6a05a9f0fcb1f --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/mobile_overview_with_most_used_charts.cy.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 url from 'url'; +import moment from 'moment/moment'; +import { synthtrace } from '../../../../synthtrace'; +import { generateMobileData } from './generate_mobile.data'; + +const start = Date.now() - 1000; +const end = Date.now(); + +const rangeFrom = new Date(start).toISOString(); +const rangeTo = new Date(end).toISOString(); + +const apiRequestsToIntercept = [ + { + endpoint: '/internal/apm/mobile-services/synth-android/most_used_charts?*', + aliasName: 'mostUsedChartRequest', + }, +]; + +const aliasNames = apiRequestsToIntercept.map( + ({ aliasName }) => `@${aliasName}` +); + +const apmMobileServiceOverview = url.format({ + pathname: 'app/apm/mobile-services/synth-android', + query: { + rangeFrom, + rangeTo, + }, +}); +describe('Mobile Service overview page', () => { + before(() => { + synthtrace.index( + generateMobileData({ + from: new Date(start).getTime(), + to: new Date(end).getTime(), + }) + ); + }); + + after(() => { + synthtrace.clean(); + }); + + describe('Mobile service overview with charts', () => { + beforeEach(() => { + cy.loginAsEditorUser(); + cy.visitKibana(apmMobileServiceOverview); + apiRequestsToIntercept.map(({ endpoint, aliasName }) => { + cy.intercept('GET', endpoint).as(aliasName); + }); + }); + + describe('accessing android service page', () => { + it('shows the most used charts', () => { + cy.wait(aliasNames); + + cy.getByTestSubj('mostUsedChart-device').should('exist'); + cy.getByTestSubj('mostUsedChart-netConnectionType').should('exist'); + cy.getByTestSubj('mostUsedChart-osVersion').should('exist'); + cy.getByTestSubj('mostUsedChart-appVersion').should('exist'); + }); + + it('shows No results found, when no data is present', () => { + cy.wait(aliasNames); + + const timeStart = moment(start).subtract(5, 'm').toISOString(); + const timeEnd = moment(end).subtract(5, 'm').toISOString(); + + cy.selectAbsoluteTimeRange(timeStart, timeEnd); + + cy.contains('Update').click(); + + cy.wait(aliasNames); + + cy.expectAPIsToHaveBeenCalledWith({ + apisIntercepted: aliasNames, + value: `start=${encodeURIComponent( + new Date(timeStart).toISOString() + )}&end=${encodeURIComponent(new Date(timeEnd).toISOString())}`, + }); + cy.getByTestSubj('mostUsedNoResultsFound').should('exist'); + }); + }); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/service_and_mobile_overview.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/service_and_mobile_overview.cy.ts index ccede29d7e3e9..fec0dbbe36686 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/service_and_mobile_overview.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/service_overview/service_and_mobile_overview.cy.ts @@ -82,9 +82,6 @@ describe('Service overview page', () => { cy.visitKibana(apmServiceOverview); cy.location().should((loc) => { expect(loc.pathname).to.eq('/app/apm/services/synth-go-1/overview'); - expect(loc.search).to.eq( - `?comparisonEnabled=true&environment=ENVIRONMENT_ALL&kuery=&latencyAggregationType=avg&rangeFrom=${rangeFrom}&rangeTo=${rangeTo}&serviceGroup=&offset=1d&transactionType=request` - ); }); }); }); @@ -105,9 +102,6 @@ describe('Service overview page', () => { expect(loc.pathname).to.eq( '/app/apm/mobile-services/synth-ios/overview' ); - expect(loc.search).to.eq( - `?comparisonEnabled=true&environment=ENVIRONMENT_ALL&kuery=&latencyAggregationType=avg&rangeFrom=${rangeFrom}&rangeTo=${rangeTo}&serviceGroup=&offset=1d&transactionType=request` - ); }); }); }); @@ -128,9 +122,6 @@ describe('Service overview page', () => { expect(loc.pathname).to.eq( '/app/apm/mobile-services/synth-android/overview' ); - expect(loc.search).to.eq( - `?comparisonEnabled=true&environment=ENVIRONMENT_ALL&kuery=&latencyAggregationType=avg&rangeFrom=${rangeFrom}&rangeTo=${rangeTo}&serviceGroup=&offset=1d&transactionType=request` - ); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts index 3fdeff2e406a8..88d4408d33152 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/power_user/storage_explorer/storage_explorer.cy.ts @@ -140,12 +140,6 @@ describe('Storage Explorer', () => { }); }); - it('when clicking the refresh button', () => { - cy.wait(mainAliasNames); - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); - }); - it('when selecting a different time range and clicking the update button', () => { cy.wait(mainAliasNames); @@ -155,9 +149,6 @@ describe('Storage Explorer', () => { ); cy.contains('Update').click(); cy.wait(mainAliasNames); - - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); }); it('with the correct lifecycle phase when changing the lifecycle phase', () => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts index 2ae901be32fbf..5451a0df2d845 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/deep_links.cy.ts @@ -17,6 +17,8 @@ describe('APM deep links', () => { cy.contains('APM / Service groups'); cy.contains('APM / Traces'); cy.contains('APM / Service Map'); + cy.contains('APM / Dependencies'); + cy.contains('APM / Settings'); // navigates to home page // Force click because welcome screen changes @@ -43,5 +45,15 @@ describe('APM deep links', () => { // navigates to service maps cy.contains('APM / Service Map').click({ force: true }); cy.url().should('include', '/apm/service-map'); + + cy.getByTestSubj('nav-search-input').type('APM'); + // navigates to dependencies page + cy.contains('APM / Dependencies').click({ force: true }); + cy.url().should('include', '/apm/dependencies/inventory'); + + cy.getByTestSubj('nav-search-input').type('APM'); + // navigates to settings page + cy.contains('APM / Settings').click({ force: true }); + cy.url().should('include', '/apm/settings/general-settings'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts index 8714fe8f07873..c09547abcf7c8 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/error_details.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; import url from 'url'; import { synthtrace } from '../../../../synthtrace'; import { checkA11y } from '../../../support/commands'; @@ -12,14 +13,6 @@ import { generateData } from './generate_data'; const start = '2021-10-10T00:00:00.000Z'; const end = '2021-10-10T00:15:00.000Z'; -const errorDetailsPageHref = url.format({ - pathname: - '/app/apm/services/opbeans-java/errors/0000000000000000000000000Error%200', - query: { - rangeFrom: start, - rangeTo: end, - }, -}); describe('Error details', () => { beforeEach(() => { @@ -41,18 +34,29 @@ describe('Error details', () => { }); it('has no detectable a11y violations on load', () => { + const errorGroupingKey = getErrorGroupingKey('Error 1'); + const errorGroupingKeyShort = errorGroupingKey.slice(0, 5); + const errorDetailsPageHref = url.format({ + pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, + query: { + rangeFrom: start, + rangeTo: end, + }, + }); + cy.visitKibana(errorDetailsPageHref); - cy.contains('Error group 00000'); + cy.contains(`Error group ${errorGroupingKeyShort}`); // set skipFailures to true to not fail the test when there are accessibility failures checkA11y({ skipFailures: true }); }); describe('when error has no occurrences', () => { it('shows zero occurrences', () => { + const errorGroupingKey = getErrorGroupingKey('Error foo bar'); + cy.visitKibana( url.format({ - pathname: - '/app/apm/services/opbeans-java/errors/0000000000000000000000000Error%201', + pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, query: { rangeFrom: start, rangeTo: end, @@ -65,9 +69,19 @@ describe('Error details', () => { }); describe('when error has data', () => { + const errorGroupingKey = getErrorGroupingKey('Error 1'); + const errorGroupingKeyShort = errorGroupingKey.slice(0, 5); + const errorDetailsPageHref = url.format({ + pathname: `/app/apm/services/opbeans-java/errors/${errorGroupingKey}`, + query: { + rangeFrom: start, + rangeTo: end, + }, + }); + it('shows errors distribution chart', () => { cy.visitKibana(errorDetailsPageHref); - cy.contains('Error group 00000'); + cy.contains(`Error group ${errorGroupingKeyShort}`); cy.getByTestSubj('errorDistribution').contains('Error occurrences'); }); @@ -89,7 +103,6 @@ describe('Error details', () => { describe('when clicking on related transaction sample', () => { it('should redirects to the transaction details page', () => { cy.visitKibana(errorDetailsPageHref); - cy.contains('Error group 00000'); cy.contains('a', 'GET /apple 🍎').click(); cy.url().should('include', 'opbeans-java/transactions/view'); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts index 8ac95d509d0bd..a64dc157d4037 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/errors/errors_page.cy.ts @@ -30,6 +30,8 @@ describe('Errors page', () => { describe('when data is loaded', () => { before(() => { + synthtrace.clean(); + synthtrace.index( generateData({ from: new Date(start).getTime(), @@ -79,16 +81,16 @@ describe('Errors page', () => { cy.contains('div', 'Error 1'); }); - it('clicking on type adds a filter in the kuerybar', () => { + it('clicking on type adds a filter in the searchbar', () => { cy.visitKibana(javaServiceErrorsPageHref); - cy.getByTestSubj('headerFilterKuerybar') + cy.getByTestSubj('apmUnifiedSearchBar') .invoke('val') .should('be.empty'); // `force: true` because Cypress says the element is 0x0 cy.contains('exception 0').click({ force: true, }); - cy.getByTestSubj('headerFilterKuerybar') + cy.getByTestSubj('apmUnifiedSearchBar') .its('length') .should('be.gt', 0); cy.get('table') @@ -142,38 +144,28 @@ describe('Check detailed statistics API with multiple errors', () => { cy.visitKibana(`${javaServiceErrorsPageHref}&pageSize=10`); cy.wait('@errorsMainStatistics'); cy.get('.euiPagination__list').children().should('have.length', 5); + + let requestedGroupIdsPage1: string[]; + cy.wait('@errorsDetailedStatistics').then((payload) => { - expect(payload.request.body.groupIds).eql( - JSON.stringify([ - '0000000000000000000000000Error 0', - '0000000000000000000000000Error 1', - '0000000000000000000000000Error 2', - '0000000000000000000000000Error 3', - '0000000000000000000000000Error 4', - '0000000000000000000000000Error 5', - '0000000000000000000000000Error 6', - '0000000000000000000000000Error 7', - '0000000000000000000000000Error 8', - '0000000000000000000000000Error 9', - ]) - ); + cy.get('[data-test-subj="errorGroupId"]').each(($el, index) => { + const displayedGroupId = $el.text(); + requestedGroupIdsPage1 = JSON.parse(payload.request.body.groupIds); + + const requestedGroupId = requestedGroupIdsPage1[index].slice(0, 5); + expect(displayedGroupId).eq(requestedGroupId); + + expect(requestedGroupIdsPage1).to.have.length(10); + }); }); cy.getByTestSubj('pagination-button-1').click(); + + // expect that the requested groupIds on page 2 are different from page 1 cy.wait('@errorsDetailedStatistics').then((payload) => { - expect(payload.request.body.groupIds).eql( - JSON.stringify([ - '000000000000000000000000Error 10', - '000000000000000000000000Error 11', - '000000000000000000000000Error 12', - '000000000000000000000000Error 13', - '000000000000000000000000Error 14', - '000000000000000000000000Error 15', - '000000000000000000000000Error 16', - '000000000000000000000000Error 17', - '000000000000000000000000Error 18', - '000000000000000000000000Error 19', - ]) - ); + const requestedGroupIdsPage2 = JSON.parse(payload.request.body.groupIds); + + expect(requestedGroupIdsPage1[0]).not.eq(requestedGroupIdsPage2[0]); + expect(requestedGroupIdsPage2).to.have.length(10); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/mobile/generate_data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/mobile/generate_data.ts new file mode 100644 index 0000000000000..5b12bd58b76be --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/mobile/generate_data.ts @@ -0,0 +1,221 @@ +/* + * 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 { apm, timerange } from '@kbn/apm-synthtrace-client'; + +export const SERVICE_VERSIONS = ['2.3', '1.2', '1.1']; + +export function generateMobileData({ from, to }: { from: number; to: number }) { + const galaxy10 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: SERVICE_VERSIONS[0] }) + .deviceInfo({ + manufacturer: 'Samsung', + modelIdentifier: 'SM-G973F', + modelName: 'Galaxy S10', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + const galaxy7 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: SERVICE_VERSIONS[1] }) + .deviceInfo({ + manufacturer: 'Samsung', + modelIdentifier: 'SM-G930F', + modelName: 'Galaxy S7', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ + type: 'cell', + subType: 'edge', + carrierName: 'M1 Limited', + carrierMNC: '03', + carrierICC: 'SG', + carrierMCC: '525', + }); + + const huaweiP2 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: SERVICE_VERSIONS[2] }) + .deviceInfo({ + manufacturer: 'Huawei', + modelIdentifier: 'HUAWEI P2-0000', + modelName: 'HuaweiP2', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '20.24.184.101', + cityName: 'Singapore', + continentName: 'Asia', + countryIsoCode: 'SG', + countryName: 'Singapore', + location: { coordinates: [103.8554, 1.3036], type: 'Point' }, + }) + .setNetworkConnection({ + type: 'cell', + subType: 'edge', + carrierName: 'Osaka Gas Business Create Co., Ltd.', + carrierMNC: '17', + carrierICC: 'JP', + carrierMCC: '440', + }); + + return timerange(from, to) + .interval('5m') + .rate(1) + .generator((timestamp) => { + galaxy10.startNewSession(); + galaxy7.startNewSession(); + huaweiP2.startNewSession(); + return [ + galaxy10 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(500) + .success() + .children( + galaxy10 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + galaxy10 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + galaxy10 + .transaction('Second View - View Appearing', 'Android Activity') + .timestamp(10000 + timestamp) + .duration(300) + .failure() + .children( + galaxy10 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/second', + }) + .duration(400) + .success() + .timestamp(10000 + timestamp + 250) + ), + huaweiP2 + .transaction('Start View - View Appearing', 'huaweiP2 Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + huaweiP2 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + huaweiP2 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + galaxy7 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + galaxy7 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + galaxy7 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + ]; + }); +} diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/mobile/mobile_transactions.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/mobile/mobile_transactions.cy.ts new file mode 100644 index 0000000000000..85cf055507f3b --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/mobile/mobile_transactions.cy.ts @@ -0,0 +1,68 @@ +/* + * 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 { synthtrace } from '../../../../synthtrace'; +import { generateMobileData } from './generate_data'; + +const start = '2021-10-10T00:00:00.000Z'; +const end = '2021-10-10T00:15:00.000Z'; + +const mobileTransactionsPageHref = url.format({ + pathname: '/app/apm/mobile-services/synth-android/transactions', + query: { + rangeFrom: start, + rangeTo: end, + }, +}); + +describe('Mobile transactions page', () => { + beforeEach(() => { + cy.loginAsViewerUser(); + }); + + describe('when data is loaded', () => { + before(() => { + synthtrace.index( + generateMobileData({ + from: new Date(start).getTime(), + to: new Date(end).getTime(), + }) + ); + }); + + after(() => { + synthtrace.clean(); + }); + + describe('when click on tab shows correct table', () => { + it('shows version tab', () => { + cy.visitKibana(mobileTransactionsPageHref); + cy.getByTestSubj('apmAppVersionTab') + .click() + .should('have.attr', 'aria-selected', 'true'); + cy.url().should('include', 'mobileSelectedTab=app_version_tab'); + }); + + it('shows OS version tab', () => { + cy.visitKibana(mobileTransactionsPageHref); + cy.getByTestSubj('apmOsVersionTab') + .click() + .should('have.attr', 'aria-selected', 'true'); + cy.url().should('include', 'mobileSelectedTab=os_version_tab'); + }); + + it('shows devices tab', () => { + cy.visitKibana(mobileTransactionsPageHref); + cy.getByTestSubj('apmDevicesTab') + .click() + .should('have.attr', 'aria-selected', 'true'); + cy.url().should('include', 'mobileSelectedTab=devices_tab'); + }); + }); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts index e689e126d4bfd..8e30f2784eb34 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/header_filters/header_filters.cy.ts @@ -45,7 +45,7 @@ describe('Service inventory - header filters', () => { cy.contains('Services'); cy.contains('opbeans-node'); cy.contains('service 1'); - cy.getByTestSubj('headerFilterKuerybar') + cy.getByTestSubj('apmUnifiedSearchBar') .type(`service.name: "${specialServiceName}"`) .type('{enter}'); cy.contains('service 1'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts index 2d40c690a8c92..032409ec56d40 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_inventory/service_inventory.cy.ts @@ -103,12 +103,6 @@ describe('Service inventory', () => { }); }); - it('when clicking the refresh button', () => { - cy.wait(mainAliasNames); - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); - }); - it('when selecting a different time range and clicking the update button', () => { cy.wait(mainAliasNames); @@ -118,9 +112,6 @@ describe('Service inventory', () => { ); cy.contains('Update').click(); cy.wait(mainAliasNames); - - cy.contains('Refresh').click(); - cy.wait(mainAliasNames); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts index d693148010c7e..2970aa3887e95 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/errors_table.cy.ts @@ -50,12 +50,12 @@ describe('Errors table', () => { it('clicking on type adds a filter in the kuerybar and navigates to errors page', () => { cy.visitKibana(serviceOverviewHref); - cy.getByTestSubj('headerFilterKuerybar').invoke('val').should('be.empty'); + cy.getByTestSubj('apmUnifiedSearchBar').invoke('val').should('be.empty'); // `force: true` because Cypress says the element is 0x0 cy.contains('Exception').click({ force: true, }); - cy.getByTestSubj('headerFilterKuerybar').its('length').should('be.gt', 0); + cy.getByTestSubj('apmUnifiedSearchBar').its('length').should('be.gt', 0); cy.get('table').find('td:contains("Exception")').should('have.length', 1); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts index 8a25024506696..05fe508092ff4 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/header_filters.cy.ts @@ -117,7 +117,7 @@ describe('Service overview - header filters', () => { }); }); - describe('Filtering by kuerybar', () => { + describe('Filtering by searchbar', () => { beforeEach(() => { cy.loginAsViewerUser(); }); @@ -129,13 +129,23 @@ describe('Service overview - header filters', () => { }) ); cy.contains('opbeans-java'); - cy.getByTestSubj('headerFilterKuerybar').type('transaction.n'); + cy.getByTestSubj('apmUnifiedSearchBar').type('transaction.n'); cy.contains('transaction.name'); - cy.getByTestSubj('suggestionContainer').find('li').first().click(); - cy.getByTestSubj('headerFilterKuerybar').type(':'); - cy.getByTestSubj('suggestionContainer').find('li').first().click(); - cy.getByTestSubj('headerFilterKuerybar').type('{enter}'); - cy.url().should('include', '&kuery=transaction.name'); + cy.getByTestSubj( + 'autocompleteSuggestion-field-transaction.name-' + ).click(); + cy.getByTestSubj('apmUnifiedSearchBar').type(':'); + cy.getByTestSubj('autoCompleteSuggestionText').should('have.length', 1); + cy.getByTestSubj( + Cypress.$.escapeSelector( + 'autocompleteSuggestion-value-"GET-/api/product"-' + ) + ).click(); + cy.getByTestSubj('apmUnifiedSearchBar').type('{enter}'); + cy.url().should( + 'include', + '&kuery=transaction.name%20:%22GET%20%2Fapi%2Fproduct%22%20' + ); }); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts index bce3da42d5a3f..ec511a0725aac 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/service_overview/time_comparison.cy.ts @@ -121,7 +121,7 @@ describe('Service overview: Time Comparison', () => { '2021-10-20T00:00:00.000Z' ); - cy.getByTestSubj('superDatePickerApplyTimeButton').click(); + cy.getByTestSubj('querySubmitButton').click(); cy.getByTestSubj('comparisonSelect').should('have.value', '864000000ms'); cy.getByTestSubj('comparisonSelect').should( diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts index 60b36b10ee4a3..0ec3c16e8658b 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/transaction_details/span_links.cy.ts @@ -52,7 +52,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerInternalOnlyIds.spanAId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('2 incoming'); cy.contains('0 outgoing'); @@ -66,7 +66,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerExternalOnlyIds.spanBId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('1 incoming'); cy.contains('1 outgoing'); @@ -80,7 +80,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerConsumerIds.transactionCId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('1 incoming'); cy.contains('1 outgoing'); @@ -94,7 +94,7 @@ describe('Span links', () => { cy.contains('1 Span link'); cy.getByTestSubj( `spanLinksBadge_${ids.producerConsumerIds.spanCId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('1 Span link found'); cy.contains('1 incoming'); cy.contains('0 outgoing'); @@ -108,7 +108,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerMultipleIds.transactionDId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('0 incoming'); cy.contains('2 outgoing'); @@ -122,7 +122,7 @@ describe('Span links', () => { cy.contains('2 Span links'); cy.getByTestSubj( `spanLinksBadge_${ids.producerMultipleIds.spanEId}` - ).realHover(); + ).trigger('mouseover'); cy.contains('2 Span links found'); cy.contains('0 incoming'); cy.contains('2 outgoing'); diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx index 09ca441cd26e9..f95f117cedc5b 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/error_count_rule_type/index.tsx @@ -7,13 +7,15 @@ import { i18n } from '@kbn/i18n'; import { defaults, omit } from 'lodash'; -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ForLastExpression, TIME_UNITS, } from '@kbn/triggers-actions-ui-plugin/public'; +import { EuiFormRow } from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; import { asInteger } from '../../../../../common/utils/formatters'; import { useFetcher } from '../../../../hooks/use_fetcher'; @@ -21,11 +23,19 @@ import { createCallApmApi } from '../../../../services/rest/create_call_apm_api' import { ChartPreview } from '../../ui_components/chart_preview'; import { EnvironmentField, + ErrorGroupingKeyField, IsAboveField, ServiceField, } from '../../utils/fields'; import { AlertMetadata, getIntervalAndTimeRange } from '../../utils/helper'; import { ApmRuleParamsContainer } from '../../ui_components/apm_rule_params_container'; +import { APMRuleGroupBy } from '../../ui_components/apm_rule_group_by'; +import { + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_NAME, + ERROR_GROUP_ID, +} from '../../../../../common/es_fields/apm'; export interface RuleParams { windowSize?: number; @@ -33,6 +43,8 @@ export interface RuleParams { threshold?: number; serviceName?: string; environment?: string; + groupBy?: string[] | undefined; + errorGroupingKey?: string; } interface Props { @@ -74,6 +86,7 @@ export function ErrorCountRuleType(props: Props) { query: { environment: params.environment, serviceName: params.serviceName, + errorGroupingKey: params.errorGroupingKey, interval, start, end, @@ -88,9 +101,17 @@ export function ErrorCountRuleType(props: Props) { params.windowUnit, params.environment, params.serviceName, + params.errorGroupingKey, ] ); + const onGroupByChange = useCallback( + (group: string[] | null) => { + setRuleParams('groupBy', group ?? []); + }, + [setRuleParams] + ); + const fields = [ , @@ -106,6 +128,12 @@ export function ErrorCountRuleType(props: Props) { onChange={(value) => setRuleParams('environment', value)} serviceName={params.serviceName} />, + setRuleParams('errorGroupingKey', value)} + serviceName={params.serviceName} + />, + ); + const groupAlertsBy = ( + <> + + + + + + ); + return ( {} } }, } as unknown as Partial); +interface Args { + ruleParams: RuleParams; + metadata?: AlertMetadata; +} + export default { title: 'alerting/TransactionDurationRuleType', component: TransactionDurationRuleType, @@ -32,16 +39,48 @@ export default { ], }; -export const Example: Story = () => { - const [params, setParams] = useState({ +export const CreatingInApmServiceOverview: Story = ({ + ruleParams, + metadata, +}) => { + const [params, setParams] = useState(ruleParams); + + function setRuleParams(property: string, value: any) { + setParams({ ...params, [property]: value }); + } + + return ( + {}} + /> + ); +}; + +CreatingInApmServiceOverview.args = { + ruleParams: { aggregationType: AggregationType.Avg, environment: 'testEnvironment', serviceName: 'testServiceName', threshold: 1500, transactionType: 'testTransactionType', + transactionName: 'GET /api/customer/:id', windowSize: 5, windowUnit: 'm', - }); + }, + metadata: { + environment: ENVIRONMENT_ALL.value, + serviceName: undefined, + }, +}; + +export const CreatingInStackManagement: Story = ({ + ruleParams, + metadata, +}) => { + const [params, setParams] = useState(ruleParams); function setRuleParams(property: string, value: any) { setParams({ ...params, [property]: value }); @@ -50,8 +89,20 @@ export const Example: Story = () => { return ( {}} /> ); }; + +CreatingInStackManagement.args = { + ruleParams: { + aggregationType: AggregationType.Avg, + environment: 'testEnvironment', + threshold: 1500, + windowSize: 5, + windowUnit: 'm', + }, + metadata: undefined, +}; diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx index a94cad8767369..ea1a5d8138f18 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_duration_rule_type/index.tsx @@ -8,13 +8,15 @@ import { EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { defaults, map, omit } from 'lodash'; -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ForLastExpression, TIME_UNITS, } from '@kbn/triggers-actions-ui-plugin/public'; +import { EuiFormRow } from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; import { AggregationType } from '../../../../../common/rules/apm_rule_types'; import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; @@ -30,19 +32,29 @@ import { IsAboveField, ServiceField, TransactionTypeField, + TransactionNameField, } from '../../utils/fields'; import { AlertMetadata, getIntervalAndTimeRange } from '../../utils/helper'; import { ApmRuleParamsContainer } from '../../ui_components/apm_rule_params_container'; import { PopoverExpression } from '../../ui_components/popover_expression'; +import { APMRuleGroupBy } from '../../ui_components/apm_rule_group_by'; +import { + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_NAME, + TRANSACTION_TYPE, +} from '../../../../../common/es_fields/apm'; export interface RuleParams { aggregationType: AggregationType; environment: string; - serviceName: string; threshold: number; - transactionType: string; + transactionType?: string; + transactionName?: string; + serviceName?: string; windowSize: number; windowUnit: string; + groupBy?: string[] | undefined; } const TRANSACTION_ALERT_AGGREGATION_TYPES: Record = { @@ -105,6 +117,7 @@ export function TransactionDurationRuleType(props: Props) { environment: params.environment, serviceName: params.serviceName, transactionType: params.transactionType, + transactionName: params.transactionName, interval, start, end, @@ -119,6 +132,7 @@ export function TransactionDurationRuleType(props: Props) { params.environment, params.serviceName, params.transactionType, + params.transactionName, params.windowSize, params.windowUnit, ] @@ -142,6 +156,13 @@ export function TransactionDurationRuleType(props: Props) { /> ); + const onGroupByChange = useCallback( + (group: string[] | null) => { + setRuleParams('groupBy', group ?? []); + }, + [setRuleParams] + ); + const fields = [ { if (value !== params.serviceName) { setRuleParams('serviceName', value); - setRuleParams('transactionType', ''); + setRuleParams('transactionType', undefined); + setRuleParams('transactionName', undefined); setRuleParams('environment', ENVIRONMENT_ALL.value); } }} @@ -164,6 +186,11 @@ export function TransactionDurationRuleType(props: Props) { onChange={(value) => setRuleParams('environment', value)} serviceName={params.serviceName} />, + setRuleParams('transactionName', value)} + serviceName={params.serviceName} + />, , ]; + const groupAlertsBy = ( + <> + + + + + + ); + return ( diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.stories.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.stories.tsx new file mode 100644 index 0000000000000..cd94439db0389 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.stories.tsx @@ -0,0 +1,105 @@ +/* + * 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 { Story } from '@storybook/react'; +import React, { ComponentType, useState } from 'react'; +import { CoreStart } from '@kbn/core/public'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { RuleParams, TransactionErrorRateRuleType } from '.'; +import { AlertMetadata } from '../../utils/helper'; +import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; + +const KibanaReactContext = createKibanaReactContext({ + notifications: { toasts: { add: () => {} } }, +} as unknown as Partial); + +interface Args { + ruleParams: RuleParams; + metadata?: AlertMetadata; +} + +export default { + title: 'alerting/TransactionErrorRateRuleType', + component: TransactionErrorRateRuleType, + decorators: [ + (StoryComponent: ComponentType) => { + return ( + +
    + +
    +
    + ); + }, + ], +}; + +export const CreatingInApmServiceOverview: Story = ({ + ruleParams, + metadata, +}) => { + const [params, setParams] = useState(ruleParams); + + function setRuleParams(property: string, value: any) { + setParams({ ...params, [property]: value }); + } + + return ( + {}} + /> + ); +}; + +CreatingInApmServiceOverview.args = { + ruleParams: { + environment: 'testEnvironment', + serviceName: 'testServiceName', + threshold: 1500, + transactionType: 'testTransactionType', + transactionName: 'GET /api/customer/:id', + windowSize: 5, + windowUnit: 'm', + }, + metadata: { + environment: ENVIRONMENT_ALL.value, + serviceName: undefined, + }, +}; + +export const CreatingInStackManagement: Story = ({ + ruleParams, + metadata, +}) => { + const [params, setParams] = useState(ruleParams); + + function setRuleParams(property: string, value: any) { + setParams({ ...params, [property]: value }); + } + + return ( + {}} + /> + ); +}; + +CreatingInStackManagement.args = { + ruleParams: { + environment: 'testEnvironment', + threshold: 1500, + windowSize: 5, + windowUnit: 'm', + }, + metadata: undefined, +}; diff --git a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.tsx b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.tsx index f9cfd6a511ef2..6f4f36b84778d 100644 --- a/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/rule_types/transaction_error_rate_rule_type/index.tsx @@ -6,13 +6,16 @@ */ import { defaults, omit } from 'lodash'; -import React, { useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useEffect } from 'react'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ForLastExpression, TIME_UNITS, } from '@kbn/triggers-actions-ui-plugin/public'; +import { EuiFormRow } from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; import { asPercent } from '../../../../../common/utils/formatters'; import { useFetcher } from '../../../../hooks/use_fetcher'; @@ -23,20 +26,30 @@ import { IsAboveField, ServiceField, TransactionTypeField, + TransactionNameField, } from '../../utils/fields'; import { AlertMetadata, getIntervalAndTimeRange } from '../../utils/helper'; import { ApmRuleParamsContainer } from '../../ui_components/apm_rule_params_container'; +import { APMRuleGroupBy } from '../../ui_components/apm_rule_group_by'; +import { + SERVICE_NAME, + SERVICE_ENVIRONMENT, + TRANSACTION_TYPE, + TRANSACTION_NAME, +} from '../../../../../common/es_fields/apm'; -interface RuleParams { +export interface RuleParams { windowSize?: number; windowUnit?: string; threshold?: number; serviceName?: string; transactionType?: string; + transactionName?: string; environment?: string; + groupBy?: string[] | undefined; } -interface Props { +export interface Props { ruleParams: RuleParams; metadata?: AlertMetadata; setRuleParams: (key: string, value: any) => void; @@ -78,6 +91,7 @@ export function TransactionErrorRateRuleType(props: Props) { environment: params.environment, serviceName: params.serviceName, transactionType: params.transactionType, + transactionName: params.transactionName, interval, start, end, @@ -89,6 +103,7 @@ export function TransactionErrorRateRuleType(props: Props) { }, [ params.transactionType, + params.transactionName, params.environment, params.serviceName, params.windowSize, @@ -96,13 +111,21 @@ export function TransactionErrorRateRuleType(props: Props) { ] ); + const onGroupByChange = useCallback( + (group: string[] | null) => { + setRuleParams('groupBy', group ?? []); + }, + [setRuleParams] + ); + const fields = [ { if (value !== params.serviceName) { setRuleParams('serviceName', value); - setRuleParams('transactionType', ''); + setRuleParams('transactionType', undefined); + setRuleParams('transactionName', undefined); setRuleParams('environment', ENVIRONMENT_ALL.value); } }} @@ -117,6 +140,11 @@ export function TransactionErrorRateRuleType(props: Props) { onChange={(value) => setRuleParams('environment', value)} serviceName={params.serviceName} />, + setRuleParams('transactionName', value)} + serviceName={params.serviceName} + />, ); + const groupAlertsBy = ( + <> + + + + + + ); + return ( void; + errorOptions?: string[]; +} + +export function APMRuleGroupBy({ + options, + fields, + preSelectedOptions, + onChange, + errorOptions, +}: Props) { + const handleChange = useCallback( + (selectedOptions: Array<{ label: string }>) => { + const groupByOption = selectedOptions.map((option) => option.label); + onChange([...new Set(preSelectedOptions.concat(groupByOption))]); + }, + [onChange, preSelectedOptions] + ); + + const getPreSelectedOptions = () => { + return preSelectedOptions.map((field) => ({ + label: field, + color: 'lightgray', + disabled: true, + })); + }; + + const getUserSelectedOptions = (groupBy: string[] | undefined) => { + return (groupBy ?? []) + .filter((group) => !preSelectedOptions.includes(group)) + .map((field) => ({ + label: field, + color: errorOptions?.includes(field) ? 'danger' : undefined, + })); + }; + + const selectedOptions = [ + ...getPreSelectedOptions(), + ...getUserSelectedOptions(options.groupBy), + ]; + + return ( + ({ label: field }))} + onChange={handleChange} + isClearable={false} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/apm_rule_params_container/index.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/apm_rule_params_container/index.tsx index 57d27c6cb6e31..b651fb29a824f 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/apm_rule_params_container/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/apm_rule_params_container/index.tsx @@ -24,6 +24,7 @@ interface Props { setRuleProperty: (key: string, value: any) => void; defaultParams: Record; fields: React.ReactNode[]; + groupAlertsBy?: React.ReactNode; chartPreview?: React.ReactNode; minimumWindowSize?: MinimumWindowSize; } @@ -31,6 +32,7 @@ interface Props { export function ApmRuleParamsContainer(props: Props) { const { fields, + groupAlertsBy, setRuleParams, defaultParams, chartPreview, @@ -72,6 +74,7 @@ export function ApmRuleParamsContainer(props: Props) { {chartPreview} + {groupAlertsBy} ); } diff --git a/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx b/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx index a385bfaa4d95e..826a28b06c659 100644 --- a/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx +++ b/x-pack/plugins/apm/public/components/alerting/utils/fields.tsx @@ -9,9 +9,11 @@ import { EuiFieldNumber } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { + ERROR_GROUP_ID, SERVICE_ENVIRONMENT, SERVICE_NAME, TRANSACTION_TYPE, + TRANSACTION_NAME, } from '../../../../common/es_fields/apm'; import { ENVIRONMENT_ALL, @@ -39,7 +41,7 @@ export function ServiceField({ > void; + serviceName?: string; +}) { + const label = i18n.translate('xpack.apm.alerting.fields.transaction.name', { + defaultMessage: 'Name', + }); + + return ( + + + + ); +} + export function TransactionTypeField({ currentValue, onChange, @@ -113,7 +155,7 @@ export function TransactionTypeField({ return ( void; + serviceName?: string; +}) { + const label = i18n.translate('xpack.apm.alerting.fields.error.group.id', { + defaultMessage: 'Error grouping key', + }); + return ( + + + + ); +} + export function IsAboveField({ value, unit, diff --git a/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx index b4ae13619abb0..ff569f369ba4e 100644 --- a/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/dependencies_inventory/index.tsx @@ -8,27 +8,26 @@ import { EuiSpacer } from '@elastic/eui'; import React from 'react'; import { - getKueryBarBoolFilter, - kueryBarPlaceholder, + unifiedSearchBarPlaceholder, + getSearchBarBoolFilter, } from '../../../../common/dependencies'; -import { useApmParams } from '../../../hooks/use_apm_params'; import { SearchBar } from '../../shared/search_bar/search_bar'; import { DependenciesInventoryTable } from './dependencies_inventory_table'; +import { useApmParams } from '../../../hooks/use_apm_params'; export function DependenciesInventory() { const { query: { environment }, } = useApmParams('/dependencies/inventory'); - const kueryBarBoolFilter = getKueryBarBoolFilter({ + const searchBarBoolFilter = getSearchBarBoolFilter({ environment, }); - return ( <> diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx index 360d753b5abec..2ea165fb63702 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx @@ -110,7 +110,11 @@ function ErrorGroupList({ width: `${unit * 6}px`, render: (_, { groupId }) => { return ( - + {groupId.slice(0, 5) || NOT_AVAILABLE_LABEL} ); diff --git a/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx b/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx index 3039b69d094a9..7d41b8cf3ee01 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexGroupProps, @@ -14,30 +13,27 @@ import { } from '@elastic/eui'; import React from 'react'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; -import { ApmDatePicker } from '../../shared/date_picker/apm_date_picker'; -import { KueryBar } from '../../shared/kuery_bar'; import { TimeComparison } from '../../shared/time_comparison'; import { TransactionTypeSelect } from '../../shared/transaction_type_select'; import { MobileFilters } from './service_overview/filters'; +import { UnifiedSearchBar } from '../../shared/unified_search_bar'; interface Props { hidden?: boolean; - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTimeComparison?: boolean; showTransactionTypeSelector?: boolean; showMobileFilters?: boolean; - kueryBarPlaceholder?: string; - kueryBarBoolFilter?: QueryDslQueryContainer[]; + searchBarPlaceholder?: string; } export function MobileSearchBar({ hidden = false, - showKueryBar = true, + showUnifiedSearchBar = true, showTimeComparison = false, showTransactionTypeSelector = false, showMobileFilters = false, - kueryBarBoolFilter, - kueryBarPlaceholder, + searchBarPlaceholder, }: Props) { const { isSmall, isMedium, isLarge, isXl, isXXXL } = useBreakpoints(); @@ -66,17 +62,11 @@ export function MobileSearchBar({ )} - {showKueryBar && ( + {showUnifiedSearchBar && ( - + )} - - -
    diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx index 9ddfb179f52ae..df322ea7fe372 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/index.tsx @@ -28,13 +28,7 @@ import { useTimeRange } from '../../../../hooks/use_time_range'; import { useApmRouter } from '../../../../hooks/use_apm_router'; import { ServiceOverviewThroughputChart } from '../../service_overview/service_overview_throughput_chart'; import { TransactionsTable } from '../../../shared/transactions_table'; -import { - DEVICE_MODEL_IDENTIFIER, - HOST_OS_VERSION, - NETWORK_CONNECTION_TYPE, - SERVICE_VERSION, -} from '../../../../../common/es_fields/apm'; -import { MostUsedChart } from './most_used_chart'; +import { MostUsedCharts } from './most_used_charts'; import { GeoMap } from './geo_map'; import { FailedTransactionRateChart } from '../../../shared/charts/failed_transaction_rate_chart'; import { ServiceOverviewDependenciesTable } from '../../service_overview/service_overview_dependencies_table'; @@ -66,6 +60,7 @@ export function MobileServiceOverview() { netConnectionType, offset, comparisonEnabled, + transactionType, }, } = useApmParams('/mobile-services/{serviceName}/overview'); @@ -198,73 +193,16 @@ export function MobileServiceOverview() { - - {/* Device */} - - - - {/* NCT */} - - - - - {/* OS version */} - - - - {/* App version */} - - - - + + + diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap deleted file mode 100644 index bc2ecef25d310..0000000000000 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap +++ /dev/null @@ -1,105 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Most used chart with Lens gets lens attributes 1`] = ` -Object { - "references": Array [ - Object { - "id": "apm_static_index_pattern_id", - "name": "indexpattern-datasource-layer-host-os-version", - "type": "index-pattern", - }, - ], - "state": Object { - "datasourceStates": Object { - "formBased": Object { - "layers": Object { - "host-os-version": Object { - "columnOrder": Array [ - "termsColumn", - "countColumn", - ], - "columns": Object { - "countColumn": Object { - "dataType": "number", - "isBucketed": false, - "label": "Count of records", - "operationType": "count", - "scale": "ratio", - "sourceField": "___records___", - }, - "termsColumn": Object { - "dataType": "string", - "isBucketed": true, - "label": "Top 5 values of host.os.version", - "operationType": "terms", - "params": Object { - "orderBy": Object { - "columnId": "countColumn", - "type": "column", - }, - "orderDirection": "desc", - "size": 5, - }, - "scale": "ordinal", - "sourceField": "host.os.version", - }, - }, - }, - }, - }, - }, - "filters": Array [ - Object { - "meta": Object {}, - "query": Object { - "term": Object { - "processor.event": "transaction", - }, - }, - }, - Object { - "meta": Object {}, - "query": Object { - "term": Object { - "service.name": "opbeans-swift", - }, - }, - }, - Object { - "meta": Object {}, - "query": Object { - "term": Object { - "transaction.type": "request", - }, - }, - }, - ], - "query": Object { - "language": "kuery", - "query": "", - }, - "visualization": Object { - "layers": Array [ - Object { - "categoryDisplay": "default", - "layerId": "host-os-version", - "layerType": "data", - "legendDisplay": "hide", - "legendPosition": "bottom", - "metrics": Array [ - "countColumn", - ], - "nestedLegend": false, - "numberDisplay": "percent", - "primaryGroups": Array [ - "termsColumn", - ], - }, - ], - "shape": "donut", - }, - }, - "title": "most-used-host-os-version", - "visualizationType": "lnsPie", -} -`; diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/get_lens_attributes.ts b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/get_lens_attributes.ts deleted file mode 100644 index c311fa561f81c..0000000000000 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/get_lens_attributes.ts +++ /dev/null @@ -1,117 +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'; -import { - CountIndexPatternColumn, - TermsIndexPatternColumn, - PersistedIndexPatternLayer, - PieVisualizationState, - TypedLensByValueInput, -} from '@kbn/lens-plugin/public'; -import type { Filter } from '@kbn/es-query'; -import { APM_STATIC_DATA_VIEW_ID } from '../../../../../../common/data_view_constants'; -import { MostUsedMetricTypes } from '.'; - -const BUCKET_SIZE = 5; - -export function getLensAttributes({ - metric, - filters, - kuery = '', -}: { - metric: MostUsedMetricTypes; - filters: Filter[]; - kuery?: string; -}): TypedLensByValueInput['attributes'] { - const metricId = metric.replaceAll('.', '-'); - - const columnA = 'termsColumn'; - const columnB = 'countColumn'; - - const dataLayer: PersistedIndexPatternLayer = { - columnOrder: [columnA, columnB], - columns: { - [columnA]: { - label: i18n.translate( - 'xpack.apm.serviceOverview.lensFlyout.topValues', - { - defaultMessage: 'Top {BUCKET_SIZE} values of {metric}', - values: { - BUCKET_SIZE, - metric, - }, - } - ), - dataType: 'string', - operationType: 'terms', - scale: 'ordinal', - sourceField: metric, - isBucketed: true, - params: { - size: BUCKET_SIZE, - orderBy: { - type: 'column', - columnId: columnB, - }, - orderDirection: 'desc', - }, - } as TermsIndexPatternColumn, - [columnB]: { - label: i18n.translate( - 'xpack.apm.serviceOverview.lensFlyout.countRecords', - { - defaultMessage: 'Count of records', - } - ), - dataType: 'number', - operationType: 'count', - scale: 'ratio', - isBucketed: false, - sourceField: '___records___', - } as CountIndexPatternColumn, - }, - }; - - return { - title: `most-used-${metricId}`, - visualizationType: 'lnsPie', - references: [ - { - type: 'index-pattern', - id: APM_STATIC_DATA_VIEW_ID, - name: `indexpattern-datasource-layer-${metricId}`, - }, - ], - state: { - visualization: { - shape: 'donut', - layers: [ - { - layerId: metricId, - primaryGroups: [columnA], - metrics: [columnB], - categoryDisplay: 'default', - legendDisplay: 'hide', - nestedLegend: false, - numberDisplay: 'percent', - layerType: 'data', - legendPosition: 'bottom', - }, - ], - } as PieVisualizationState, - datasourceStates: { - formBased: { - layers: { - [metricId]: dataLayer, - }, - }, - }, - filters, - query: { language: 'kuery', query: kuery }, - }, - }; -} diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/index.tsx deleted file mode 100644 index 53f9d41cd15b1..0000000000000 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/index.tsx +++ /dev/null @@ -1,123 +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 { EuiTitle, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; -import React, { useMemo, useCallback } from 'react'; -import type { Filter } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ApmPluginStartDeps } from '../../../../../plugin'; -import { getLensAttributes } from './get_lens_attributes'; -import { - DEVICE_MODEL_IDENTIFIER, - HOST_OS_VERSION, - NETWORK_CONNECTION_TYPE, - SERVICE_VERSION, -} from '../../../../../../common/es_fields/apm'; - -export type MostUsedMetricTypes = - | typeof DEVICE_MODEL_IDENTIFIER - | typeof SERVICE_VERSION - | typeof HOST_OS_VERSION - | typeof NETWORK_CONNECTION_TYPE; - -export function MostUsedChart({ - title, - start, - end, - kuery, - filters, - metric, -}: { - title: React.ReactNode; - start: string; - end: string; - kuery?: string; - filters: Filter[]; - metric: MostUsedMetricTypes; -}) { - const { services } = useKibana(); - const { - lens: { EmbeddableComponent, navigateToPrefilledEditor, canUseEditor }, - } = services; - - const lensAttributes = useMemo( - () => - getLensAttributes({ - kuery, - filters, - metric, - }), - [kuery, filters, metric] - ); - - const openInLens = useCallback(() => { - if (lensAttributes) { - navigateToPrefilledEditor( - { - id: `dataVisualizer-${metric}`, - timeRange: { - from: start, - to: end, - }, - attributes: lensAttributes, - }, - { - openInNewTab: true, - } - ); - } - }, [navigateToPrefilledEditor, lensAttributes, start, end, metric]); - - const getOpenInLensAction = () => { - return { - id: 'openInLens', - type: 'link', - getDisplayName() { - return i18n.translate('xpack.apm.serviceOverview.openInLens', { - defaultMessage: 'Open in Lens', - }); - }, - getIconType() { - return 'visArea'; - }, - async isCompatible() { - return true; - }, - async execute() { - openInLens(); - return; - }, - }; - }; - - return ( - - - -

    {title}

    -
    - -
    - - - -
    - ); -} diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/most_used_chart.test.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/most_used_chart.test.tsx deleted file mode 100644 index 49e0cefc6ddf9..0000000000000 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_chart/most_used_chart.test.tsx +++ /dev/null @@ -1,102 +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 { render } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { CoreStart } from '@kbn/core/public'; -import React, { ReactNode } from 'react'; -import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; -import { getLensAttributes } from './get_lens_attributes'; -import { MostUsedChart, MostUsedMetricTypes } from '.'; -import { HOST_OS_VERSION } from '../../../../../../common/es_fields/apm'; - -const mockEmbeddableComponent = jest.fn(); - -function Wrapper({ children }: { children?: ReactNode }) { - const KibanaReactContext = createKibanaReactContext({ - lens: { - EmbeddableComponent: mockEmbeddableComponent.mockReturnValue( -
    - ), - canUseEditor: jest.fn(() => true), - navigateToPrefilledEditor: jest.fn(), - }, - } as Partial); - - return ( - - - {children} - - - ); -} - -const renderOptions = { wrapper: Wrapper }; - -describe('Most used chart with Lens', () => { - const props = { - metric: HOST_OS_VERSION as MostUsedMetricTypes, - filters: [ - { - meta: {}, - query: { - term: { - 'processor.event': 'transaction', - }, - }, - }, - { - meta: {}, - query: { - term: { - 'service.name': 'opbeans-swift', - }, - }, - }, - { - meta: {}, - query: { - term: { - 'transaction.type': 'request', - }, - }, - }, - ], - }; - test('gets lens attributes', () => { - expect(getLensAttributes(props)).toMatchSnapshot(); - }); - - test('Renders most used chart with Lens', () => { - const start = '2022-10-30T20%3A52%3A47.080Z'; - const end = '2022-10-31T20%3A52%3A47.080Z'; - - render( - , - renderOptions - ); - - expect(mockEmbeddableComponent).toHaveBeenCalledTimes(1); - expect(mockEmbeddableComponent.mock.calls[0][0]).toEqual( - expect.objectContaining({ - timeRange: { - from: start, - to: end, - }, - attributes: getLensAttributes(props), - }) - ); - }); -}); diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_charts/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_charts/index.tsx new file mode 100644 index 0000000000000..3e428266bee95 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_charts/index.tsx @@ -0,0 +1,122 @@ +/* + * 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, { useRef } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexGroupProps, + useResizeObserver, +} from '@elastic/eui'; +import { SunburstChart } from './sunburst_chart'; +import { useBreakpoints } from '../../../../../hooks/use_breakpoints'; +import { APIReturnType } from '../../../../../services/rest/create_call_apm_api'; +import { useFetcher } from '../../../../../hooks/use_fetcher'; +import { MobilePropertyType } from '../../../../../../common/mobile_types'; + +type MostUsedCharts = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/most_used_charts'>['mostUsedCharts'][0]; + +const MOST_USED_CHARTS: Array<{ key: MostUsedCharts['key']; label: string }> = [ + { + key: MobilePropertyType.Device, + label: i18n.translate('xpack.apm.mobile.charts.device', { + defaultMessage: 'Devices', + }), + }, + { + key: MobilePropertyType.NetworkConnectionType, + label: i18n.translate('xpack.apm.mobile.charts.nct', { + defaultMessage: 'Network Connection Type', + }), + }, + { + key: MobilePropertyType.OsVersion, + label: i18n.translate('xpack.apm.mobile.charts.osVersion', { + defaultMessage: 'OS version', + }), + }, + { + key: MobilePropertyType.AppVersion, + label: i18n.translate('xpack.apm.mobile.charts.appVersion', { + defaultMessage: 'App version', + }), + }, +]; +export function MostUsedCharts({ + start, + end, + kuery, + environment, + transactionType, + serviceName, +}: { + start: string; + end: string; + kuery: string; + environment: string; + transactionType?: string; + serviceName: string; +}) { + const { isLarge } = useBreakpoints(); + const resizeRef = useRef(null); + const dimensions = useResizeObserver(resizeRef.current); + const groupDirection: EuiFlexGroupProps['direction'] = isLarge + ? 'column' + : 'row'; + const { data = { mostUsedCharts: [] }, status } = useFetcher( + (callApmApi) => { + return callApmApi( + 'GET /internal/apm/mobile-services/{serviceName}/most_used_charts', + { + params: { + path: { serviceName }, + query: { + start, + end, + environment, + kuery, + transactionType, + }, + }, + } + ); + }, + [start, end, environment, kuery, serviceName, transactionType] + ); + + const chartWidth = isLarge + ? dimensions.width + : dimensions.width / MOST_USED_CHARTS.length; + + return ( +
    + + {MOST_USED_CHARTS.map(({ key, label }) => { + const chartData = + data?.mostUsedCharts.find((chart) => chart.key === key)?.options || + []; + return ( +
    + +
    + ); + })} +
    +
    + ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_charts/sunburst_chart.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_charts/sunburst_chart.tsx new file mode 100644 index 0000000000000..166c0985130f5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/most_used_charts/sunburst_chart.tsx @@ -0,0 +1,166 @@ +/* + * 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 { + Chart, + Partition, + PartitionLayout, + Datum, + PartialTheme, + Settings, +} from '@elastic/charts'; + +import { + EuiFlexItem, + euiPaletteColorBlindBehindText, + EuiTitle, + EuiIcon, + EuiText, + EuiSpacer, + EuiProgress, + useEuiFontSize, +} from '@elastic/eui'; +import { IconChartDonut } from '@kbn/chart-icons'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { ChartContainer } from '../../../../shared/charts/chart_container'; +import { FETCH_STATUS } from '../../../../../hooks/use_fetcher'; + +const theme: PartialTheme = { + chartMargins: { top: 0, left: 0, bottom: 0, right: 0 }, + partition: { + minFontSize: 5, + idealFontSizeJump: 1.1, + outerSizeRatio: 1, + emptySizeRatio: 0.3, + circlePadding: 3, + }, +}; + +export function SunburstChart({ + data, + label, + chartKey, + fetchStatus, + chartWidth, +}: { + data?: Array<{ key: string | number; docCount: number }>; + label?: string; + chartKey: string; + fetchStatus: FETCH_STATUS; + chartWidth: number; +}) { + const colors = euiPaletteColorBlindBehindText({ sortBy: 'natural' }); + const isDataAvailable = data && data.length > 0; + const isLoading = fetchStatus === FETCH_STATUS.LOADING; + + // The loader needs to be wrapped inside a div with fixed height to avoid layout shift + const ProgressLoader = ( +
    + {isLoading && ( + + )} +
    + ); + + return ( + + +

    + {label} +

    +
    + {ProgressLoader} + + + {isDataAvailable ? ( + + + Number(d.docCount)} + valueGetter="percent" + layers={[ + { + groupByRollup: (d: Datum) => d.key, + nodeLabel: (d: Datum) => d, + fillLabel: { + fontWeight: 100, + maximizeFontSize: true, + valueFont: { + fontWeight: 900, + }, + }, + shape: { + fillColor: (_, sortIndex) => { + return colors[sortIndex]; + }, + }, + }, + ]} + /> + + ) : ( + + )} + +
    + ); +} + +const noResultsFoundStyle = css({ + height: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', +}); +export function NoResultsFound() { + const noResultsFoundText = i18n.translate( + 'xpack.apm.mobile.charts.noResultsFound', + { + defaultMessage: 'No results found', + } + ); + return ( +
    + + + +

    {noResultsFoundText}

    +
    +
    + ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx index be6963994c639..ce06e04683af9 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx @@ -16,11 +16,11 @@ import { useHistory } from 'react-router-dom'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useApmParams } from '../../../../hooks/use_apm_params'; import { useTimeRange } from '../../../../hooks/use_time_range'; -import { TransactionsTable } from '../../../shared/transactions_table'; import { replace } from '../../../shared/links/url_helpers'; import { getKueryWithMobileFilters } from '../../../../../common/utils/get_kuery_with_mobile_filters'; import { MobileTransactionCharts } from './transaction_charts'; import { MobileTreemap } from '../charts/mobile_treemap'; +import { TransactionOverviewTabs } from './transaction_overview_tabs'; export function MobileTransactionOverview() { const { @@ -37,6 +37,7 @@ export function MobileTransactionOverview() { kuery, offset, comparisonEnabled, + mobileSelectedTab, }, } = useApmParams('/mobile-services/{serviceName}/transactions'); @@ -88,15 +89,14 @@ export function MobileTransactionOverview() { /> - diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/app_version_tab.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/app_version_tab.tsx new file mode 100644 index 0000000000000..deafdeb59d3c5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/app_version_tab.tsx @@ -0,0 +1,61 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { TabContentProps } from '.'; +import { isPending } from '../../../../../hooks/use_fetcher'; +import { StatsList } from './stats_list'; +import { SERVICE_VERSION } from '../../../../../../common/es_fields/apm'; +import { useMobileStatisticsFetcher } from './use_mobile_statistics_fetcher'; + +function AppVersionTab({ + environment, + kuery, + start, + end, + comparisonEnabled, + offset, +}: TabContentProps) { + const { + mainStatistics, + mainStatisticsStatus, + detailedStatistics, + detailedStatisticsStatus, + } = useMobileStatisticsFetcher({ + field: SERVICE_VERSION, + environment, + kuery, + start, + end, + comparisonEnabled, + offset, + }); + + return ( + + ); +} + +export const appVersionTab = { + dataTestSubj: 'apmAppVersionTab', + key: 'app_version_tab', + label: i18n.translate( + 'xpack.apm.mobile.transactions.overview.tabs.app.version', + { + defaultMessage: 'App version', + } + ), + component: AppVersionTab, +}; diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/devices_tab.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/devices_tab.tsx new file mode 100644 index 0000000000000..4d2f18b046709 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/devices_tab.tsx @@ -0,0 +1,58 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { TabContentProps } from '.'; +import { isPending } from '../../../../../hooks/use_fetcher'; +import { StatsList } from './stats_list'; +import { useMobileStatisticsFetcher } from './use_mobile_statistics_fetcher'; +import { DEVICE_MODEL_IDENTIFIER } from '../../../../../../common/es_fields/apm'; + +function DevicesTab({ + environment, + kuery, + start, + end, + comparisonEnabled, + offset, +}: TabContentProps) { + const { + mainStatistics, + mainStatisticsStatus, + detailedStatistics, + detailedStatisticsStatus, + } = useMobileStatisticsFetcher({ + field: DEVICE_MODEL_IDENTIFIER, + environment, + kuery, + start, + end, + comparisonEnabled, + offset, + }); + + return ( + + ); +} + +export const devicesTab = { + dataTestSubj: 'apmDevicesTab', + key: 'devices_tab', + label: i18n.translate('xpack.apm.mobile.transactions.overview.tabs.devices', { + defaultMessage: 'Devices', + }), + component: DevicesTab, +}; diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/index.tsx new file mode 100644 index 0000000000000..c986f5903b7b5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/index.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { EuiSpacer, EuiTabs, EuiTab } from '@elastic/eui'; +import { push } from '../../../../shared/links/url_helpers'; +import { transactionsTab } from './transactions_tab'; +import { osVersionTab } from './os_version_tab'; +import { appVersionTab } from './app_version_tab'; +import { devicesTab } from './devices_tab'; + +export interface TabContentProps { + agentName?: string; + environment: string; + start: string; + end: string; + kuery: string; + comparisonEnabled: boolean; + offset?: string; + mobileSelectedTab?: string; +} + +const tabs = [transactionsTab, appVersionTab, osVersionTab, devicesTab]; + +export function TransactionOverviewTabs({ + agentName, + environment, + start, + end, + kuery, + comparisonEnabled, + offset, + mobileSelectedTab, +}: TabContentProps) { + const history = useHistory(); + + const { component: TabContent } = + tabs.find((tab) => tab.key === mobileSelectedTab) ?? transactionsTab; + return ( + <> + + {tabs.map(({ dataTestSubj, key, label }) => ( + { + push(history, { + query: { + mobileSelectedTab: key, + }, + }); + }} + > + {label} + + ))} + + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/os_version_tab.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/os_version_tab.tsx new file mode 100644 index 0000000000000..6eee1f01aae9f --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/os_version_tab.tsx @@ -0,0 +1,61 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { TabContentProps } from '.'; +import { isPending } from '../../../../../hooks/use_fetcher'; +import { StatsList } from './stats_list'; +import { useMobileStatisticsFetcher } from './use_mobile_statistics_fetcher'; +import { HOST_OS_VERSION } from '../../../../../../common/es_fields/apm'; + +function OSVersionTab({ + environment, + kuery, + start, + end, + comparisonEnabled, + offset, +}: TabContentProps) { + const { + mainStatistics, + mainStatisticsStatus, + detailedStatistics, + detailedStatisticsStatus, + } = useMobileStatisticsFetcher({ + field: HOST_OS_VERSION, + environment, + kuery, + start, + end, + comparisonEnabled, + offset, + }); + + return ( + + ); +} + +export const osVersionTab = { + dataTestSubj: 'apmOsVersionTab', + key: 'os_version_tab', + label: i18n.translate( + 'xpack.apm.mobile.transactions.overview.tabs.os.version', + { + defaultMessage: 'OS version', + } + ), + component: OSVersionTab, +}; diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/stats_list/get_columns.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/stats_list/get_columns.tsx new file mode 100644 index 0000000000000..18ac252011357 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/stats_list/get_columns.tsx @@ -0,0 +1,148 @@ +/* + * 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 { RIGHT_ALIGNMENT, EuiText } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { ValuesType } from 'utility-types'; +import { APIReturnType } from '../../../../../../services/rest/create_call_apm_api'; +import { + ChartType, + getTimeSeriesColor, +} from '../../../../../shared/charts/helper/get_timeseries_color'; +import { SparkPlot } from '../../../../../shared/charts/spark_plot'; +import { isTimeComparison } from '../../../../../shared/time_comparison/get_comparison_options'; +import { + asMillisecondDuration, + asPercent, + asTransactionRate, +} from '../../../../../../../common/utils/formatters'; +import { ITableColumn } from '../../../../../shared/managed_table'; + +type MobileMainStatisticsByField = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/main_statistics'>; + +type MobileMainStatisticsByFieldItem = ValuesType< + MobileMainStatisticsByField['mainStatistics'] +>; + +type MobileDetailedStatisticsByField = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics'>; + +export function getColumns({ + agentName, + detailedStatisticsLoading, + detailedStatistics, + comparisonEnabled, + offset, +}: { + agentName?: string; + detailedStatisticsLoading: boolean; + detailedStatistics: MobileDetailedStatisticsByField; + comparisonEnabled?: boolean; + offset?: string; +}): Array> { + return [ + // version/device + { + field: 'name', + name: i18n.translate( + 'xpack.apm.mobile.transactions.overview.table.nameColumnLabel', + { + defaultMessage: 'Name', + } + ), + }, + // latency + { + field: 'latency', + name: i18n.translate( + 'xpack.apm.mobile.transactions.overview.table.latencyColumnAvgLabel', + { + defaultMessage: 'Latency (avg.)', + } + ), + align: RIGHT_ALIGNMENT, + render: (_, { latency, name }) => { + const currentPeriodTimeseries = + detailedStatistics?.currentPeriod?.[name]?.latency; + const previousPeriodTimeseries = + detailedStatistics?.previousPeriod?.[name]?.latency; + + const { currentPeriodColor, previousPeriodColor } = getTimeSeriesColor( + ChartType.LATENCY_AVG + ); + + return ( + + ); + }, + }, + // throughput + { + field: 'throughput', + name: i18n.translate( + 'xpack.apm.mobile.transactions.overview.table.throughputColumnAvgLabel', + { defaultMessage: 'Throughput' } + ), + align: RIGHT_ALIGNMENT, + render: (_, { throughput, name }) => { + const currentPeriodTimeseries = + detailedStatistics?.currentPeriod?.[name]?.throughput; + const previousPeriodTimeseries = + detailedStatistics?.previousPeriod?.[name]?.throughput; + + const { currentPeriodColor, previousPeriodColor } = getTimeSeriesColor( + ChartType.THROUGHPUT + ); + + return ( + + ); + }, + }, + // crash rate + { + field: 'crashRate', + name: i18n.translate( + 'xpack.apm.mobile.transactions.overview.table.crashRateColumnLabel', + { + defaultMessage: 'Crash rate', + } + ), + align: RIGHT_ALIGNMENT, + render: (_, { crashRate }) => { + return ( + + {asPercent(crashRate, 1)} + + ); + }, + }, + ]; +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/stats_list/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/stats_list/index.tsx new file mode 100644 index 0000000000000..ab71f49421ddd --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/stats_list/index.tsx @@ -0,0 +1,67 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { ManagedTable } from '../../../../../shared/managed_table'; +import { APIReturnType } from '../../../../../../services/rest/create_call_apm_api'; +import { getColumns } from './get_columns'; + +type MobileMainStatisticsByField = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/main_statistics'>['mainStatistics']; + +type MobileDetailedStatisticsByField = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics'>; + +interface Props { + isLoading: boolean; + mainStatistics: MobileMainStatisticsByField; + detailedStatisticsLoading: boolean; + detailedStatistics: MobileDetailedStatisticsByField; + comparisonEnabled?: boolean; + offset?: string; +} +export function StatsList({ + isLoading, + mainStatistics, + detailedStatisticsLoading, + detailedStatistics, + comparisonEnabled, + offset, +}: Props) { + const columns = useMemo(() => { + return getColumns({ + detailedStatisticsLoading, + detailedStatistics, + comparisonEnabled, + offset, + }); + }, [ + detailedStatisticsLoading, + detailedStatistics, + comparisonEnabled, + offset, + ]); + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/transactions_tab.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/transactions_tab.tsx new file mode 100644 index 0000000000000..4fef8262e6305 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/transactions_tab.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 { i18n } from '@kbn/i18n'; +import { TabContentProps } from '.'; +import { TransactionsTable } from '../../../../shared/transactions_table'; + +function TransactionsTab({ environment, kuery, start, end }: TabContentProps) { + return ( + + ); +} + +export const transactionsTab = { + dataTestSubj: 'apmTransactionsTab', + key: 'transactions', + label: i18n.translate( + 'xpack.apm.mobile.transactions.overview.tabs.transactions', + { + defaultMessage: 'Transactions', + } + ), + component: TransactionsTab, +}; diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/use_mobile_statistics_fetcher.ts b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/use_mobile_statistics_fetcher.ts new file mode 100644 index 0000000000000..4c3bd48e5e089 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_overview_tabs/use_mobile_statistics_fetcher.ts @@ -0,0 +1,120 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import { useApmServiceContext } from '../../../../../context/apm_service/use_apm_service_context'; +import { useFetcher } from '../../../../../hooks/use_fetcher'; +import { isTimeComparison } from '../../../../shared/time_comparison/get_comparison_options'; + +const INITIAL_STATE_MAIN_STATISTICS = { + mainStatistics: [], + requestId: undefined, + totalItems: 0, +}; + +const INITIAL_STATE_DETAILED_STATISTICS = { + currentPeriod: {}, + previousPeriod: {}, +}; + +interface Props { + field: string; + environment: string; + start: string; + end: string; + kuery: string; + comparisonEnabled: boolean; + offset?: string; +} + +export function useMobileStatisticsFetcher({ + field, + environment, + start, + end, + kuery, + comparisonEnabled, + offset, +}: Props) { + const { serviceName } = useApmServiceContext(); + + const { data = INITIAL_STATE_MAIN_STATISTICS, status: mainStatisticsStatus } = + useFetcher( + (callApmApi) => { + if (start && end) { + return callApmApi( + 'GET /internal/apm/mobile-services/{serviceName}/main_statistics', + { + params: { + path: { serviceName }, + query: { + environment, + kuery, + start, + end, + field, + }, + }, + } + ).then((response) => { + return { + // Everytime the main statistics is refetched, updates the requestId making the comparison API to be refetched. + requestId: uuidv4(), + mainStatistics: response.mainStatistics, + totalItems: response.mainStatistics.length, + }; + }); + } + }, + [environment, start, end, kuery, serviceName, field] + ); + + const { mainStatistics, requestId, totalItems } = data; + + const { + data: detailedStatistics = INITIAL_STATE_DETAILED_STATISTICS, + status: detailedStatisticsStatus, + } = useFetcher( + (callApmApi) => { + if (totalItems && start && end) { + return callApmApi( + 'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics', + { + params: { + path: { serviceName }, + query: { + environment, + kuery, + start, + end, + field, + fieldValues: JSON.stringify( + data?.mainStatistics.map(({ name }) => name).sort() + ), + offset: + comparisonEnabled && isTimeComparison(offset) + ? offset + : undefined, + }, + }, + } + ); + } + }, + // only fetches agg results when requestId changes + // eslint-disable-next-line react-hooks/exhaustive-deps + [requestId], + { preservePreviousData: false } + ); + + return { + mainStatistics, + mainStatisticsStatus, + detailedStatistics, + detailedStatisticsStatus, + }; +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index 2b3d8c8eaf15c..f9b848b816d4c 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -40,7 +40,7 @@ import { DisabledPrompt } from './disabled_prompt'; function PromptContainer({ children }: { children: ReactNode }) { return ( <> - + - + diff --git a/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx b/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx index be52301aaaa43..8e30f70c8de61 100644 --- a/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/settings/schema/schema_overview.tsx @@ -248,7 +248,7 @@ function getDisabledReason({ ) ); } - if (!hasCloudAgentPolicy) { + if (hasRequiredRole && !hasCloudAgentPolicy) { reasons.push( i18n.translate( 'xpack.apm.settings.schema.disabledReason.hasCloudAgentPolicy', diff --git a/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx index 0d7a6ade36140..7b8e4a120c298 100644 --- a/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/mobile_service_detail/index.tsx @@ -33,7 +33,7 @@ export function page({ tabKey: React.ComponentProps['selectedTabKey']; element: React.ReactElement; searchBarOptions?: { - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTransactionTypeSelector?: boolean; showTimeComparison?: boolean; showMobileFilters?: boolean; @@ -146,6 +146,7 @@ export const mobileServiceDetail = { osVersion: t.string, appVersion: t.string, netConnectionType: t.string, + mobileSelectedTab: t.string, }), }), children: { diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx index 6158cf85d0a84..ed2b3f477ced0 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -48,7 +48,7 @@ function page({ tab: React.ComponentProps['selectedTab']; element: React.ReactElement; searchBarOptions?: { - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTransactionTypeSelector?: boolean; showTimeComparison?: boolean; hidden?: boolean; @@ -320,7 +320,7 @@ export const serviceDetail = { }), element: , searchBarOptions: { - showKueryBar: false, + showUnifiedSearchBar: false, }, }), '/services/{serviceName}/infrastructure': { @@ -331,7 +331,7 @@ export const serviceDetail = { }), element: , searchBarOptions: { - showKueryBar: false, + showUnifiedSearchBar: false, }, }), params: t.partial({ diff --git a/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx index 7b57dbade2216..ecc4435f092ed 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx @@ -8,10 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { - getKueryBarBoolFilter, - kueryBarPlaceholder, -} from '../../../../common/dependencies'; +import { unifiedSearchBarPlaceholder } from '../../../../common/dependencies'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useApmRouter } from '../../../hooks/use_apm_router'; import { useApmRoutePath } from '../../../hooks/use_apm_route_path'; @@ -29,7 +26,7 @@ interface Props { export function DependencyDetailTemplate({ children }: Props) { const { query, - query: { dependencyName, rangeFrom, rangeTo, environment }, + query: { dependencyName, rangeFrom, rangeTo }, } = useApmParams('/dependencies'); const router = useApmRouter(); @@ -38,11 +35,6 @@ export function DependencyDetailTemplate({ children }: Props) { const path = useApmRoutePath(); - const kueryBarBoolFilter = getKueryBarBoolFilter({ - environment, - dependencyName, - }); - const dependencyMetadataFetch = useFetcher( (callApmApi) => { if (!start || !end) { @@ -113,8 +105,7 @@ export function DependencyDetailTemplate({ children }: Props) { > {children} diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts b/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts index 9a775df78c4e3..04bc276dcfa65 100644 --- a/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts @@ -25,7 +25,7 @@ import darkIosIcon from './icons/ios_dark.svg'; import javaIcon from './icons/java.svg'; import nodeJsIcon from './icons/nodejs.svg'; import ocamlIcon from './icons/ocaml.svg'; -import openTelemetryIcon from './icons/opentelemetry.svg'; +import openTelemetryIcon from './icons/otel_default.svg'; import phpIcon from './icons/php.svg'; import pythonIcon from './icons/python.svg'; import rubyIcon from './icons/ruby.svg'; diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/icons/otel_default.svg b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/otel_default.svg new file mode 100644 index 0000000000000..ef13d7190c4f9 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/otel_default.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts b/x-pack/plugins/apm/public/components/shared/get_bool_filter.ts similarity index 85% rename from x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts rename to x-pack/plugins/apm/public/components/shared/get_bool_filter.ts index 92a8a9172ff9a..42575126aaab5 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/get_bool_filter.ts +++ b/x-pack/plugins/apm/public/components/shared/get_bool_filter.ts @@ -12,11 +12,11 @@ import { SERVICE_NAME, TRANSACTION_NAME, TRANSACTION_TYPE, -} from '../../../../common/es_fields/apm'; -import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; -import { UIProcessorEvent } from '../../../../common/processor_event'; -import { environmentQuery } from '../../../../common/utils/environment_query'; -import { ApmUrlParams } from '../../../context/url_params_context/types'; +} from '../../../common/es_fields/apm'; +import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; +import { UIProcessorEvent } from '../../../common/processor_event'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { ApmUrlParams } from '../../context/url_params_context/types'; export function getBoolFilter({ groupId, diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx index 825fa8e3ef7f4..d166c7d283eda 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx @@ -18,9 +18,9 @@ import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_ import { useApmParams } from '../../../hooks/use_apm_params'; import { useApmDataView } from '../../../hooks/use_apm_data_view'; import { fromQuery, toQuery } from '../links/url_helpers'; -import { getBoolFilter } from './get_bool_filter'; +import { getBoolFilter } from '../get_bool_filter'; import { Typeahead } from './typeahead'; -import { useProcessorEvent } from './use_processor_event'; +import { useProcessorEvent } from '../../../hooks/use_processor_event'; interface State { suggestions: QuerySuggestion[]; diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx index 27eecaf573bf6..d9efbe0403b41 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.test.tsx @@ -39,6 +39,20 @@ function setup({ const KibanaReactContext = createKibanaReactContext({ usageCollection: { reportUiCounter: () => {} }, dataViews: { get: async () => {} }, + data: { + query: { + queryString: { + setQuery: () => {}, + getQuery: () => {}, + clearQuery: () => {}, + }, + timefilter: { + timefilter: { + setTime: () => {}, + }, + }, + }, + }, } as Partial); // mock transaction types diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx index edff90281b9ec..d2f25880ade33 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx @@ -4,8 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexGroupProps, @@ -13,30 +11,30 @@ import { EuiSpacer, } from '@elastic/eui'; import React from 'react'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isMobileAgentName } from '../../../../common/agent_name'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; -import { ApmDatePicker } from '../date_picker/apm_date_picker'; -import { KueryBar } from '../kuery_bar'; import { TimeComparison } from '../time_comparison'; import { TransactionTypeSelect } from '../transaction_type_select'; +import { UnifiedSearchBar } from '../unified_search_bar'; interface Props { hidden?: boolean; - showKueryBar?: boolean; + showUnifiedSearchBar?: boolean; showTimeComparison?: boolean; showTransactionTypeSelector?: boolean; - kueryBarPlaceholder?: string; - kueryBarBoolFilter?: QueryDslQueryContainer[]; + searchBarPlaceholder?: string; + searchBarBoolFilter?: QueryDslQueryContainer[]; } export function SearchBar({ hidden = false, - showKueryBar = true, + showUnifiedSearchBar = true, showTimeComparison = false, showTransactionTypeSelector = false, - kueryBarBoolFilter, - kueryBarPlaceholder, + searchBarPlaceholder, + searchBarBoolFilter, }: Props) { const { agentName } = useApmServiceContext(); const isMobileAgent = isMobileAgentName(agentName); @@ -69,11 +67,11 @@ export function SearchBar({ )} - {showKueryBar && ( + {showUnifiedSearchBar && ( - )} @@ -91,9 +89,6 @@ export function SearchBar({ )} - - - diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx index ca9142668a65a..f5a647d3ca488 100644 --- a/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx @@ -16,9 +16,12 @@ import { getServerlessIcon } from '../agent_icon/get_serverless_icon'; import { CloudDetails } from './cloud_details'; import { ServerlessDetails } from './serverless_details'; import { ContainerDetails } from './container_details'; +import { OTelDetails } from './otel_details'; import { IconPopover } from './icon_popover'; import { ServiceDetails } from './service_details'; import { ServerlessType } from '../../../../common/serverless'; +import { isOpenTelemetryAgentName } from '../../../../common/agent_name'; +import openTelemetryIcon from '../agent_icon/icons/opentelemetry.svg'; interface Props { serviceName: string; @@ -70,7 +73,13 @@ export function getContainerIcon(container?: ContainerType) { } } -type Icons = 'service' | 'container' | 'serverless' | 'cloud' | 'alerts'; +type Icons = + | 'service' + | 'opentelemetry' + | 'container' + | 'serverless' + | 'cloud' + | 'alerts'; export interface PopoverItem { key: Icons; @@ -142,6 +151,23 @@ export function ServiceIcons({ start, end, serviceName }: Props) { }), component: , }, + { + key: 'opentelemetry', + icon: { + type: openTelemetryIcon, + }, + isVisible: + !!icons?.agentName && isOpenTelemetryAgentName(icons.agentName), + title: i18n.translate('xpack.apm.serviceIcons.opentelemetry', { + defaultMessage: 'OpenTelemetry', + }), + component: ( + + ), + }, { key: 'container', icon: { diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/otel_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/otel_details.tsx new file mode 100644 index 0000000000000..a9d2e8a963cac --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/service_icons/otel_details.tsx @@ -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 { EuiDescriptionList, EuiDescriptionListProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { APIReturnType } from '../../../services/rest/create_call_apm_api'; + +type ServiceDetailsReturnType = + APIReturnType<'GET /internal/apm/services/{serviceName}/metadata/details'>; + +interface Props { + opentelemetry: ServiceDetailsReturnType['opentelemetry']; + agentName?: string; +} + +export function OTelDetails({ opentelemetry }: Props) { + if (!opentelemetry) { + return null; + } + + const listItems: EuiDescriptionListProps['listItems'] = []; + listItems.push({ + title: i18n.translate( + 'xpack.apm.serviceIcons.otelDetails.opentelemetry.language', + { + defaultMessage: 'Language', + } + ), + description: ( + <>{!!opentelemetry.language ? opentelemetry.language : 'unknown'} + ), + }); + + if (!!opentelemetry.sdkVersion) { + listItems.push({ + title: i18n.translate( + 'xpack.apm.serviceIcons.otelDetails.opentelemetry.sdkVersion', + { + defaultMessage: 'OTel SDK version', + } + ), + description: <>{opentelemetry.sdkVersion}, + }); + } + + if (!!opentelemetry.autoVersion) { + listItems.push({ + title: i18n.translate( + 'xpack.apm.serviceIcons.otelDetails.opentelemetry.autoVersion', + { + defaultMessage: 'Auto instrumentation agent version', + } + ), + description: <>{opentelemetry.autoVersion}, + }); + } + + return ; +} diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx index 42d03902bc313..c4df8a4f7e9a9 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -65,6 +65,7 @@ const DEFAULT_SORT = { }; interface Props { + hideTitle?: boolean; hideViewTransactionsLink?: boolean; isSingleColumn?: boolean; numberOfTransactionsPerPage?: number; @@ -81,6 +82,7 @@ interface Props { export function TransactionsTable({ fixedHeight = false, hideViewTransactionsLink = false, + hideTitle = false, isSingleColumn = true, numberOfTransactionsPerPage = 5, showPerPageOptions = true, @@ -294,32 +296,35 @@ export function TransactionsTable({ gutterSize="s" data-test-subj="transactionsGroupTable" > - - - - -

    - {i18n.translate('xpack.apm.transactionsTable.title', { - defaultMessage: 'Transactions', - })} -

    -
    -
    - {!hideViewTransactionsLink && ( + {!hideTitle && ( + + - - {i18n.translate('xpack.apm.transactionsTable.linkText', { - defaultMessage: 'View transactions', - })} - + +

    + {i18n.translate('xpack.apm.transactionsTable.title', { + defaultMessage: 'Transactions', + })} +

    +
    - )} -
    -
    + {!hideViewTransactionsLink && ( + + + {i18n.translate('xpack.apm.transactionsTable.linkText', { + defaultMessage: 'View transactions', + })} + + + )} +
    +
    + )} + {showMaxTransactionGroupsExceededWarning && maxTransactionGroupsExceeded && ( 300000', + error: 'http.response.status_code >= 400', + metric: 'process.pid = "1234"', + defaults: + 'transaction.duration.us > 300000 AND http.response.status_code >= 400', + }; + const example = examples[processorEvent || 'defaults']; + + return ( + searchbarPlaceholder ?? + i18n.translate('xpack.apm.unifiedSearchBar.placeholder', { + defaultMessage: `Search {event, select, + transaction {transactions} + metric {metrics} + error {errors} + other {transactions, errors and metrics} + } (E.g. {queryExample})`, + values: { + queryExample: example, + event: processorEvent, + }, + }) + ); +} + +function convertKueryToEsQuery(kuery: string, dataView: DataView) { + const ast = fromKueryExpression(kuery); + return toElasticsearchQuery(ast, dataView); +} + +export function UnifiedSearchBar({ + placeholder, + value, + showDatePicker = true, + showSubmitButton = true, + isClearable = true, + boolFilter, +}: { + placeholder?: string; + value?: string; + showDatePicker?: boolean; + showSubmitButton?: boolean; + isClearable?: boolean; + boolFilter?: QueryDslQueryContainer[]; +}) { + const { + unifiedSearch: { + ui: { SearchBar }, + }, + core, + } = useApmPluginContext(); + const { services } = useKibana(); + const { + data: { + query: { queryString: queryStringService, timefilter: timeFilterService }, + }, + } = services; + + const { kuery, serviceName, environment, groupId } = + useSearchBarParams(value); + const timePickerTimeDefaults = core.uiSettings.get( + UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS + ); + const urlTimeRange = useUrlTimeRange(timePickerTimeDefaults); + const [displaySearchBar, setDisplaySearchBar] = useState(false); + + const syncSearchBarWithUrl = useCallback(() => { + // Sync Kuery params with Search Bar + if (kuery && !deepEqual(queryStringService.getQuery(), kuery)) { + queryStringService.setQuery(kuery); + } + // On page navigation the search bar persists the state where as the url is cleared, hence we need to clear the search bar + if (!kuery) { + queryStringService.clearQuery(); + } + // Sync Time Range with Search Bar + timeFilterService.timefilter.setTime(urlTimeRange as TimeRange); + }, [kuery, queryStringService, timeFilterService, urlTimeRange]); + + useEffect(() => { + syncSearchBarWithUrl(); + }, [syncSearchBarWithUrl]); + + const location = useLocation(); + const history = useHistory(); + const { dataView } = useApmDataView(); + const { urlParams } = useLegacyUrlParams(); + const processorEvent = useProcessorEvent(); + const searchbarPlaceholder = getSearchBarPlaceholder( + placeholder, + processorEvent + ); + + useEffect(() => { + if (dataView) setDisplaySearchBar(true); + }, [dataView]); + + const customFilters = + boolFilter ?? + getBoolFilter({ + groupId, + processorEvent, + serviceName, + environment, + urlParams, + }); + + const filtersForSearchBarSuggestions = customFilters.map((filter) => { + return { + query: filter, + } as Filter; + }); + const handleSubmit = (payload: { dateRange: TimeRange; query?: Query }) => { + if (dataView == null) { + return; + } + + const { dateRange, query } = payload; + const { from: rangeFrom, to: rangeTo } = dateRange; + + try { + const res = convertKueryToEsQuery( + query?.query as string, + dataView as DataView + ); + if (!res) { + return; + } + + const existingQueryParams = toQuery(location.search); + const updatedQueryWithTime = { + ...existingQueryParams, + rangeFrom, + rangeTo, + }; + history.push({ + ...location, + search: fromQuery({ + ...updatedQueryWithTime, + kuery: query?.query, + }), + }); + } catch (e) { + console.log('Invalid kuery syntax'); // eslint-disable-line no-console + } + }; + + return ( + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx b/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx new file mode 100644 index 0000000000000..25d3ecbc41b0f --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/unified_search_bar/unified_search_bar.test.tsx @@ -0,0 +1,138 @@ +/* + * 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 { createMemoryHistory, MemoryHistory } from 'history'; +import React from 'react'; +import { Router, useLocation } from 'react-router-dom'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { MockApmPluginContextWrapper } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import * as useFetcherHook from '../../../hooks/use_fetcher'; +import * as useApmDataViewHook from '../../../hooks/use_apm_data_view'; +import * as useApmParamsHook from '../../../hooks/use_apm_params'; +import * as useProcessorEventHook from '../../../hooks/use_processor_event'; +import { fromQuery } from '../links/url_helpers'; +import { CoreStart } from '@kbn/core/public'; +import { UnifiedSearchBar } from '.'; +import { UrlParams } from '../../../context/url_params_context/types'; +import { mount } from 'enzyme'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn(), +})); + +function setup({ + urlParams, + history, +}: { + urlParams: UrlParams; + history: MemoryHistory; +}) { + history.replace({ + pathname: '/services', + search: fromQuery(urlParams), + }); + + const setQuerySpy = jest.fn(); + const getQuerySpy = jest.fn(); + const clearQuerySpy = jest.fn(); + const setTimeSpy = jest.fn(); + + const KibanaReactContext = createKibanaReactContext({ + usageCollection: { + reportUiCounter: () => {}, + }, + dataViews: { + get: async () => {}, + }, + data: { + query: { + queryString: { + setQuery: setQuerySpy, + getQuery: getQuerySpy, + clearQuery: clearQuerySpy, + }, + timefilter: { + timefilter: { + setTime: setTimeSpy, + }, + }, + }, + }, + } as Partial); + + // mock transaction types + jest + .spyOn(useApmDataViewHook, 'useApmDataView') + .mockReturnValue({ dataView: undefined }); + + jest.spyOn(useFetcherHook, 'useFetcher').mockReturnValue({} as any); + + const wrapper = mount( + + + + + + + + ); + + return { wrapper, setQuerySpy, getQuerySpy, clearQuerySpy, setTimeSpy }; +} + +describe('when kuery is already present in the url, the search bar must reflect the same', () => { + let history: MemoryHistory; + beforeEach(() => { + history = createMemoryHistory(); + jest.spyOn(history, 'push'); + jest.spyOn(history, 'replace'); + }); + jest + .spyOn(useProcessorEventHook, 'useProcessorEvent') + .mockReturnValue(undefined); + + const search = '?method=json'; + const pathname = '/services'; + (useLocation as jest.Mock).mockImplementationOnce(() => ({ + search, + pathname, + })); + + it('sets the searchbar value based on URL', () => { + const expectedQuery = { + query: 'service.name:"opbeans-android"', + language: 'kuery', + }; + + const expectedTimeRange = { + from: 'now-15m', + to: 'now', + }; + + const urlParams = { + kuery: expectedQuery.query, + rangeFrom: expectedTimeRange.from, + rangeTo: expectedTimeRange.to, + environment: 'ENVIRONMENT_ALL', + comparisonEnabled: true, + serviceGroup: '', + offset: '1d', + }; + jest + .spyOn(useApmParamsHook, 'useApmParams') + .mockReturnValue({ query: urlParams, path: {} }); + + const { setQuerySpy, setTimeSpy } = setup({ + history, + urlParams, + }); + + expect(setQuerySpy).toBeCalledWith(expectedQuery); + expect(setTimeSpy).toBeCalledWith(expectedTimeRange); + }); +}); diff --git a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx index 6924277f229c0..9ba943b79b12c 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx @@ -98,6 +98,12 @@ const mockCorePlugins = { data: {}, }; +const mockUnifiedSearch = { + ui: { + SearchBar: () =>
    , + }, +}; + export const mockApmPluginContextValue = { appMountParameters: coreMock.createAppMountParameters('/basepath'), config: mockConfig, @@ -106,6 +112,7 @@ export const mockApmPluginContextValue = { observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), corePlugins: mockCorePlugins, deps: {}, + unifiedSearch: mockUnifiedSearch, }; export function MockApmPluginContextWrapper({ diff --git a/x-pack/plugins/apm/public/hooks/use_preferred_data_source_and_bucket_size.ts b/x-pack/plugins/apm/public/hooks/use_preferred_data_source_and_bucket_size.ts index 89ca576190d18..bd757ea870f2b 100644 --- a/x-pack/plugins/apm/public/hooks/use_preferred_data_source_and_bucket_size.ts +++ b/x-pack/plugins/apm/public/hooks/use_preferred_data_source_and_bucket_size.ts @@ -35,7 +35,7 @@ export function usePreferredDataSourceAndBucketSize< ? | ApmDocumentType.ServiceTransactionMetric | ApmDocumentType.TransactionMetric - | ApmDocumentType.TransactionMetric + | ApmDocumentType.TransactionEvent : ApmDocumentType.TransactionMetric | ApmDocumentType.TransactionEvent >; } | null { diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/use_processor_event.ts b/x-pack/plugins/apm/public/hooks/use_processor_event.ts similarity index 94% rename from x-pack/plugins/apm/public/components/shared/kuery_bar/use_processor_event.ts rename to x-pack/plugins/apm/public/hooks/use_processor_event.ts index d8abb49e42277..deffd7eae1517 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/use_processor_event.ts +++ b/x-pack/plugins/apm/public/hooks/use_processor_event.ts @@ -7,7 +7,7 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { useLocation } from 'react-router-dom'; -import { UIProcessorEvent } from '../../../../common/processor_event'; +import { UIProcessorEvent } from '../../common/processor_event'; /** * Infer the processor.event to used based on the route path diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts index 2ebee7e23b8e7..97d3578b63900 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts @@ -88,6 +88,10 @@ const documentTypeConfigMap: Record< }, }), }, + [ApmDocumentType.ErrorEvent]: { + processorEvent: ProcessorEvent.error, + rollupIntervals: [RollupInterval.None], + }, }; type DocumentTypeConfigOf = diff --git a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts index 0df3d7f9d7b71..1bb9c41546cf8 100644 --- a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts +++ b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts @@ -66,6 +66,13 @@ export const apmActionVariables = { ), name: 'transactionType' as const, }, + transactionName: { + description: i18n.translate( + 'xpack.apm.alerts.action_variables.transactionName', + { defaultMessage: 'The transaction name the alert is created for' } + ), + name: 'transactionName' as const, + }, triggerValue: { description: i18n.translate( 'xpack.apm.alerts.action_variables.triggerValue', @@ -87,4 +94,13 @@ export const apmActionVariables = { name: 'viewInAppUrl' as const, usesPublicBaseUrl: true, }, + errorGroupingKey: { + description: i18n.translate( + 'xpack.apm.alerts.action_variables.errorGroupingKey', + { + defaultMessage: 'The error grouping key the alert is created for', + } + ), + name: 'errorGroupingKey' as const, + }, }; diff --git a/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts b/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts index 951d733836528..e34957083d3e4 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_apm_rule_types.ts @@ -17,10 +17,12 @@ import { MlPluginSetup } from '@kbn/ml-plugin/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { AGENT_NAME, + ERROR_GROUP_ID, PROCESSOR_EVENT, SERVICE_ENVIRONMENT, SERVICE_LANGUAGE_NAME, SERVICE_NAME, + TRANSACTION_NAME, TRANSACTION_TYPE, } from '../../../common/es_fields/apm'; import { registerTransactionDurationRuleType } from './rule_types/transaction_duration/register_transaction_duration_rule_type'; @@ -45,6 +47,14 @@ export const apmRuleTypeAlertFieldMap = { type: 'keyword', required: false, }, + [TRANSACTION_NAME]: { + type: 'keyword', + required: false, + }, + [ERROR_GROUP_ID]: { + type: 'keyword', + required: false, + }, [PROCESSOR_EVENT]: { type: 'keyword', required: false, diff --git a/x-pack/plugins/apm/server/routes/alerts/route.ts b/x-pack/plugins/apm/server/routes/alerts/route.ts index b03fe65560ec8..13b790155520f 100644 --- a/x-pack/plugins/apm/server/routes/alerts/route.ts +++ b/x-pack/plugins/apm/server/routes/alerts/route.ts @@ -28,7 +28,9 @@ const alertParamsRt = t.intersection([ t.literal(AggregationType.P99), ]), serviceName: t.string, + errorGroupingKey: t.string, transactionType: t.string, + transactionName: t.string, }), environmentRt, rangeRt, diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts index 6175b2578a236..59ae52eab6a5a 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/get_error_count_chart_preview.ts @@ -7,7 +7,10 @@ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { SERVICE_NAME } from '../../../../../common/es_fields/apm'; +import { + ERROR_GROUP_ID, + SERVICE_NAME, +} from '../../../../../common/es_fields/apm'; import { AlertParams } from '../../route'; import { environmentQuery } from '../../../../../common/utils/environment_query'; import { APMEventClient } from '../../../../lib/helpers/create_es_client/create_apm_event_client'; @@ -24,12 +27,14 @@ export async function getTransactionErrorCountChartPreview({ apmEventClient: APMEventClient; alertParams: AlertParams; }): Promise { - const { serviceName, environment, interval, start, end } = alertParams; + const { serviceName, environment, errorGroupingKey, interval, start, end } = + alertParams; const query = { bool: { filter: [ ...termQuery(SERVICE_NAME, serviceName), + ...termQuery(ERROR_GROUP_ID, errorGroupingKey), ...rangeQuery(start, end), ...environmentQuery(environment), ], diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.test.ts index 4513db455cf6d..781094ca55257 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.test.ts @@ -44,7 +44,11 @@ describe('Error count alert', () => { registerErrorCountRuleType(dependencies); - const params = { threshold: 2, windowSize: 5, windowUnit: 'm' }; + const params = { + threshold: 2, + windowSize: 5, + windowUnit: 'm', + }; services.scopedClusterClient.asCurrentUser.search.mockResponse({ hits: { @@ -126,11 +130,208 @@ describe('Error count alert', () => { }, }); + await executor({ params }); + ['foo_env-foo', 'foo_env-foo-2', 'bar_env-bar'].forEach((instanceName) => + expect(services.alertFactory.create).toHaveBeenCalledWith(instanceName) + ); + + expect(scheduleActions).toHaveBeenCalledTimes(3); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'env-foo', + threshold: 2, + triggerValue: 5, + reason: + 'Error count is 5 in the last 5 mins for service: foo, env: env-foo. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'env-foo-2', + threshold: 2, + triggerValue: 4, + reason: + 'Error count is 4 in the last 5 mins for service: foo, env: env-foo-2. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo-2', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'bar', + environment: 'env-bar', + reason: + 'Error count is 3 in the last 5 mins for service: bar, env: env-bar. Alert when > 2.', + threshold: 2, + triggerValue: 3, + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/bar/errors?environment=env-bar', + }); + }); + + it('sends alert when rule is configured with group by on transaction.name', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerErrorCountRuleType(dependencies); + + const params = { + threshold: 2, + windowSize: 5, + windowUnit: 'm', + groupBy: ['service.name', 'service.environment', 'transaction.name'], + }; + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + error_counts: { + buckets: [ + { + key: ['foo', 'env-foo', 'tx-name-foo'], + doc_count: 5, + }, + { + key: ['foo', 'env-foo-2', 'tx-name-foo-2'], + doc_count: 4, + }, + { + key: ['bar', 'env-bar', 'tx-name-bar'], + doc_count: 3, + }, + { + key: ['bar', 'env-bar-2', 'tx-name-bar-2'], + doc_count: 1, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + await executor({ params }); + [ + 'foo_env-foo_tx-name-foo', + 'foo_env-foo-2_tx-name-foo-2', + 'bar_env-bar_tx-name-bar', + ].forEach((instanceName) => + expect(services.alertFactory.create).toHaveBeenCalledWith(instanceName) + ); + + expect(scheduleActions).toHaveBeenCalledTimes(3); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'env-foo', + threshold: 2, + triggerValue: 5, + reason: + 'Error count is 5 in the last 5 mins for service: foo, env: env-foo, name: tx-name-foo. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo', + transactionName: 'tx-name-foo', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'env-foo-2', + threshold: 2, + triggerValue: 4, + reason: + 'Error count is 4 in the last 5 mins for service: foo, env: env-foo-2, name: tx-name-foo-2. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo-2', + transactionName: 'tx-name-foo-2', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'bar', + environment: 'env-bar', + reason: + 'Error count is 3 in the last 5 mins for service: bar, env: env-bar, name: tx-name-bar. Alert when > 2.', + threshold: 2, + triggerValue: 3, + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/bar/errors?environment=env-bar', + transactionName: 'tx-name-bar', + }); + }); + + it('sends alert when rule is configured with group by on error.grouping_key', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerErrorCountRuleType(dependencies); + + const params = { + threshold: 2, + windowSize: 5, + windowUnit: 'm', + groupBy: ['service.name', 'service.environment', 'error.grouping_key'], + }; + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + error_counts: { + buckets: [ + { + key: ['foo', 'env-foo', 'error-key-foo'], + doc_count: 5, + }, + { + key: ['foo', 'env-foo-2', 'error-key-foo-2'], + doc_count: 4, + }, + { + key: ['bar', 'env-bar', 'error-key-bar'], + doc_count: 3, + }, + { + key: ['bar', 'env-bar-2', 'error-key-bar-2'], + doc_count: 1, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + await executor({ params }); [ - 'apm.error_rate_foo_env-foo', - 'apm.error_rate_foo_env-foo-2', - 'apm.error_rate_bar_env-bar', + 'foo_env-foo_error-key-foo', + 'foo_env-foo-2_error-key-foo-2', + 'bar_env-bar_error-key-bar', ].forEach((instanceName) => expect(services.alertFactory.create).toHaveBeenCalledWith(instanceName) ); @@ -142,25 +343,225 @@ describe('Error count alert', () => { environment: 'env-foo', threshold: 2, triggerValue: 5, - reason: 'Error count is 5 in the last 5 mins for foo. Alert when > 2.', + reason: + 'Error count is 5 in the last 5 mins for service: foo, env: env-foo, error key: error-key-foo. Alert when > 2.', interval: '5 mins', viewInAppUrl: 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo', + errorGroupingKey: 'error-key-foo', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'foo', environment: 'env-foo-2', threshold: 2, triggerValue: 4, - reason: 'Error count is 4 in the last 5 mins for foo. Alert when > 2.', + reason: + 'Error count is 4 in the last 5 mins for service: foo, env: env-foo-2, error key: error-key-foo-2. Alert when > 2.', interval: '5 mins', viewInAppUrl: 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo-2', + errorGroupingKey: 'error-key-foo-2', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'bar', + environment: 'env-bar', + reason: + 'Error count is 3 in the last 5 mins for service: bar, env: env-bar, error key: error-key-bar. Alert when > 2.', + threshold: 2, + triggerValue: 3, + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/bar/errors?environment=env-bar', + errorGroupingKey: 'error-key-bar', + }); + }); + + it('sends alert when rule is configured with preselected group by', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerErrorCountRuleType(dependencies); + + const params = { + threshold: 2, + windowSize: 5, + windowUnit: 'm', + groupBy: ['service.name', 'service.environment'], + }; + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + error_counts: { + buckets: [ + { + key: ['foo', 'env-foo'], + doc_count: 5, + }, + { + key: ['foo', 'env-foo-2'], + doc_count: 4, + }, + { + key: ['bar', 'env-bar'], + doc_count: 3, + }, + { + key: ['bar', 'env-bar-2'], + doc_count: 1, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + await executor({ params }); + ['foo_env-foo', 'foo_env-foo-2', 'bar_env-bar'].forEach((instanceName) => + expect(services.alertFactory.create).toHaveBeenCalledWith(instanceName) + ); + + expect(scheduleActions).toHaveBeenCalledTimes(3); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'env-foo', + threshold: 2, + triggerValue: 5, + reason: + 'Error count is 5 in the last 5 mins for service: foo, env: env-foo. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'env-foo-2', + threshold: 2, + triggerValue: 4, + reason: + 'Error count is 4 in the last 5 mins for service: foo, env: env-foo-2. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=env-foo-2', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'bar', + environment: 'env-bar', + reason: + 'Error count is 3 in the last 5 mins for service: bar, env: env-bar. Alert when > 2.', + threshold: 2, + triggerValue: 3, + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/bar/errors?environment=env-bar', + }); + }); + + it('sends alert when service.environment field does not exist in the source', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerErrorCountRuleType(dependencies); + + const params = { + threshold: 2, + windowSize: 5, + windowUnit: 'm', + groupBy: ['service.name', 'service.environment'], + }; + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + error_counts: { + buckets: [ + { + key: ['foo', 'ENVIRONMENT_NOT_DEFINED'], + doc_count: 5, + }, + { + key: ['foo', 'ENVIRONMENT_NOT_DEFINED'], + doc_count: 4, + }, + { + key: ['bar', 'env-bar'], + doc_count: 3, + }, + { + key: ['bar', 'env-bar-2'], + doc_count: 1, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + await executor({ params }); + [ + 'foo_ENVIRONMENT_NOT_DEFINED', + 'foo_ENVIRONMENT_NOT_DEFINED', + 'bar_env-bar', + ].forEach((instanceName) => + expect(services.alertFactory.create).toHaveBeenCalledWith(instanceName) + ); + + expect(scheduleActions).toHaveBeenCalledTimes(3); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'Not defined', + threshold: 2, + triggerValue: 5, + reason: + 'Error count is 5 in the last 5 mins for service: foo, env: Not defined. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=ENVIRONMENT_ALL', + }); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + environment: 'Not defined', + threshold: 2, + triggerValue: 4, + reason: + 'Error count is 4 in the last 5 mins for service: foo, env: Not defined. Alert when > 2.', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo/errors?environment=ENVIRONMENT_ALL', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', environment: 'env-bar', - reason: 'Error count is 3 in the last 5 mins for bar. Alert when > 2.', + reason: + 'Error count is 3 in the last 5 mins for service: bar, env: env-bar. Alert when > 2.', threshold: 2, triggerValue: 3, interval: '5 mins', diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts index d97d3f3356a6c..6ac9a87789136 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/error_count/register_error_count_rule_type.ts @@ -19,12 +19,9 @@ import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server import { termQuery } from '@kbn/observability-plugin/server'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/common'; import { firstValueFrom } from 'rxjs'; +import { getEnvironmentEsField } from '../../../../../common/environment_filter_values'; import { - ENVIRONMENT_NOT_DEFINED, - getEnvironmentEsField, - getEnvironmentLabel, -} from '../../../../../common/environment_filter_values'; -import { + ERROR_GROUP_ID, PROCESSOR_EVENT, SERVICE_ENVIRONMENT, SERVICE_NAME, @@ -49,6 +46,8 @@ import { getServiceGroupFields, getServiceGroupFieldsAgg, } from '../get_service_group_fields'; +import { getGroupByTerms } from '../utils/get_groupby_terms'; +import { getGroupByActionVariables } from '../utils/get_groupby_action_variables'; const ruleTypeConfig = RULE_TYPES_CONFIG[ApmRuleType.ErrorCount]; @@ -77,6 +76,8 @@ export function registerErrorCountRuleType({ apmActionVariables.interval, apmActionVariables.reason, apmActionVariables.serviceName, + apmActionVariables.transactionName, + apmActionVariables.errorGroupingKey, apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.viewInAppUrl, @@ -86,6 +87,12 @@ export function registerErrorCountRuleType({ minimumLicenseRequired: 'basic', isExportable: true, executor: async ({ params: ruleParams, services, spaceId }) => { + const predefinedGroupby = [SERVICE_NAME, SERVICE_ENVIRONMENT]; + + const allGroupbyFields = Array.from( + new Set([...predefinedGroupby, ...(ruleParams.groupBy ?? [])]) + ); + const config = await firstValueFrom(config$); const { savedObjectsClient, scopedClusterClient } = services; @@ -114,6 +121,9 @@ export function registerErrorCountRuleType({ ...termQuery(SERVICE_NAME, ruleParams.serviceName, { queryEmptyString: false, }), + ...termQuery(ERROR_GROUP_ID, ruleParams.errorGroupingKey, { + queryEmptyString: false, + }), ...environmentQuery(ruleParams.environment), ], }, @@ -121,13 +131,7 @@ export function registerErrorCountRuleType({ aggs: { error_counts: { multi_terms: { - terms: [ - { field: SERVICE_NAME }, - { - field: SERVICE_ENVIRONMENT, - missing: ENVIRONMENT_NOT_DEFINED.value, - }, - ], + terms: getGroupByTerms(allGroupbyFields), size: 1000, order: { _count: 'desc' as const }, }, @@ -144,35 +148,42 @@ export function registerErrorCountRuleType({ const errorCountResults = response.aggregations?.error_counts.buckets.map((bucket) => { - const [serviceName, environment] = bucket.key; + const groupByFields = bucket.key.reduce( + (obj, bucketKey, bucketIndex) => { + obj[allGroupbyFields[bucketIndex]] = bucketKey; + return obj; + }, + {} as Record + ); + + const bucketKey = bucket.key; + return { - serviceName, - environment, errorCount: bucket.doc_count, sourceFields: getServiceGroupFields(bucket), + groupByFields, + bucketKey, }; }) ?? []; errorCountResults .filter((result) => result.errorCount >= ruleParams.threshold) .forEach((result) => { - const { serviceName, environment, errorCount, sourceFields } = + const { errorCount, sourceFields, groupByFields, bucketKey } = result; const alertReason = formatErrorCountReason({ - serviceName, threshold: ruleParams.threshold, measured: errorCount, windowSize: ruleParams.windowSize, windowUnit: ruleParams.windowUnit, + groupByFields, }); - const id = [ApmRuleType.ErrorCount, serviceName, environment] - .filter((name) => name) - .join('_'); - const relativeViewInAppUrl = getAlertUrlErrorCount( - serviceName, - getEnvironmentEsField(environment)?.[SERVICE_ENVIRONMENT] + groupByFields[SERVICE_NAME], + getEnvironmentEsField(groupByFields[SERVICE_ENVIRONMENT])?.[ + SERVICE_ENVIRONMENT + ] ); const viewInAppUrl = addSpaceIdToPath( @@ -181,30 +192,33 @@ export function registerErrorCountRuleType({ relativeViewInAppUrl ); + const groupByActionVariables = + getGroupByActionVariables(groupByFields); + services .alertWithLifecycle({ - id, + id: bucketKey.join('_'), fields: { - [SERVICE_NAME]: serviceName, - ...getEnvironmentEsField(environment), [PROCESSOR_EVENT]: ProcessorEvent.error, [ALERT_EVALUATION_VALUE]: errorCount, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, + [ERROR_GROUP_ID]: ruleParams.errorGroupingKey, [ALERT_REASON]: alertReason, ...sourceFields, + ...groupByFields, }, }) .scheduleActions(ruleTypeConfig.defaultActionGroupId, { - environment: getEnvironmentLabel(environment), interval: formatDurationFromTimeUnitChar( ruleParams.windowSize, ruleParams.windowUnit as TimeUnitChar ), reason: alertReason, - serviceName, threshold: ruleParams.threshold, + errorGroupingKey: ruleParams.errorGroupingKey, // When group by doesn't include error.grouping_key, the context.error.grouping_key action variable will contain value of the Error Grouping Key filter triggerValue: errorCount, viewInAppUrl, + ...groupByActionVariables, }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/get_transaction_duration_chart_preview.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/get_transaction_duration_chart_preview.ts index e18ad1fc187a5..f7b226216aef0 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/get_transaction_duration_chart_preview.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/get_transaction_duration_chart_preview.ts @@ -12,6 +12,7 @@ import { SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, + TRANSACTION_NAME, } from '../../../../../common/es_fields/apm'; import { environmentQuery } from '../../../../../common/utils/environment_query'; import { AlertParams } from '../../route'; @@ -48,6 +49,7 @@ export async function getTransactionDurationChartPreview({ environment, serviceName, transactionType, + transactionName, interval, start, end, @@ -63,6 +65,7 @@ export async function getTransactionDurationChartPreview({ filter: [ ...termQuery(SERVICE_NAME, serviceName), ...termQuery(TRANSACTION_TYPE, transactionType), + ...termQuery(TRANSACTION_NAME, transactionName), ...rangeQuery(start, end), ...environmentQuery(environment), ...getDocumentTypeFilterForTransactions(searchAggregatedTransactions), diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.test.ts index dcd8994860ae7..f3993dfed7009 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.test.ts @@ -27,7 +27,7 @@ describe('registerTransactionDurationRuleType', () => { series: { buckets: [ { - key: ['opbeans-java', 'ENVIRONMENT_NOT_DEFINED', 'request'], + key: ['opbeans-java', 'development', 'request'], avgLatency: { value: 5500000, }, @@ -52,6 +52,217 @@ describe('registerTransactionDurationRuleType', () => { transactionType: 'request', serviceName: 'opbeans-java', aggregationType: 'avg', + transactionName: 'GET /orders', + }; + await executor({ params }); + expect(scheduleActions).toHaveBeenCalledTimes(1); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + alertDetailsUrl: expect.stringContaining( + 'http://localhost:5601/eyr/app/observability/alerts/' + ), + transactionName: 'GET /orders', + environment: 'development', + interval: `5 mins`, + reason: + 'Avg. latency is 5,500 ms in the last 5 mins for service: opbeans-java, env: development, type: request. Alert when > 3,000 ms.', + transactionType: 'request', + serviceName: 'opbeans-java', + threshold: 3000, + triggerValue: '5,500 ms', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/opbeans-java?transactionType=request&environment=development', + }); + }); + + it('sends alert when rule is configured with group by on transaction.name', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerTransactionDurationRuleType(dependencies); + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + series: { + buckets: [ + { + key: ['opbeans-java', 'development', 'request', 'GET /products'], + avgLatency: { + value: 5500000, + }, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + const params = { + threshold: 3000, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + serviceName: 'opbeans-java', + aggregationType: 'avg', + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.type', + 'transaction.name', + ], + }; + await executor({ params }); + expect(scheduleActions).toHaveBeenCalledTimes(1); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + alertDetailsUrl: expect.stringContaining( + 'http://localhost:5601/eyr/app/observability/alerts/' + ), + environment: 'development', + interval: `5 mins`, + reason: + 'Avg. latency is 5,500 ms in the last 5 mins for service: opbeans-java, env: development, type: request, name: GET /products. Alert when > 3,000 ms.', + transactionType: 'request', + serviceName: 'opbeans-java', + threshold: 3000, + triggerValue: '5,500 ms', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/opbeans-java?transactionType=request&environment=development', + transactionName: 'GET /products', + }); + }); + + it('sends alert when rule is configured with preselected group by', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerTransactionDurationRuleType(dependencies); + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + series: { + buckets: [ + { + key: ['opbeans-java', 'development', 'request'], + avgLatency: { + value: 5500000, + }, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + const params = { + threshold: 3000, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + serviceName: 'opbeans-java', + aggregationType: 'avg', + groupBy: ['service.name', 'service.environment', 'transaction.type'], + }; + + await executor({ params }); + expect(scheduleActions).toHaveBeenCalledTimes(1); + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + alertDetailsUrl: expect.stringContaining( + 'http://localhost:5601/eyr/app/observability/alerts/' + ), + environment: 'development', + interval: `5 mins`, + reason: + 'Avg. latency is 5,500 ms in the last 5 mins for service: opbeans-java, env: development, type: request. Alert when > 3,000 ms.', + transactionType: 'request', + serviceName: 'opbeans-java', + threshold: 3000, + triggerValue: '5,500 ms', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/opbeans-java?transactionType=request&environment=development', + }); + }); + + it('sends alert when service.environment field does not exist in the source', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerTransactionDurationRuleType(dependencies); + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + series: { + buckets: [ + { + key: [ + 'opbeans-java', + 'ENVIRONMENT_NOT_DEFINED', + 'request', + 'tx-java', + ], + avgLatency: { + value: 5500000, + }, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + const params = { + threshold: 3000, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + serviceName: 'opbeans-java', + aggregationType: 'avg', + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.type', + 'transaction.name', + ], }; await executor({ params }); expect(scheduleActions).toHaveBeenCalledTimes(1); @@ -62,13 +273,14 @@ describe('registerTransactionDurationRuleType', () => { environment: 'Not defined', interval: `5 mins`, reason: - 'Avg. latency is 5,500 ms in the last 5 mins for opbeans-java. Alert when > 3,000 ms.', + 'Avg. latency is 5,500 ms in the last 5 mins for service: opbeans-java, env: Not defined, type: request, name: tx-java. Alert when > 3,000 ms.', transactionType: 'request', serviceName: 'opbeans-java', threshold: 3000, triggerValue: '5,500 ms', viewInAppUrl: 'http://localhost:5601/eyr/app/apm/services/opbeans-java?transactionType=request&environment=ENVIRONMENT_ALL', + transactionName: 'tx-java', }); }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts index d7d763bfb2ca6..0ac465261c954 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type.ts @@ -22,16 +22,14 @@ import { import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/common'; import { firstValueFrom } from 'rxjs'; +import { getGroupByTerms } from '../utils/get_groupby_terms'; import { SearchAggregatedTransactionSetting } from '../../../../../common/aggregated_transactions'; -import { - ENVIRONMENT_NOT_DEFINED, - getEnvironmentEsField, - getEnvironmentLabel, -} from '../../../../../common/environment_filter_values'; +import { getEnvironmentEsField } from '../../../../../common/environment_filter_values'; import { PROCESSOR_EVENT, SERVICE_ENVIRONMENT, SERVICE_NAME, + TRANSACTION_NAME, TRANSACTION_TYPE, } from '../../../../../common/es_fields/apm'; import { @@ -65,6 +63,7 @@ import { averageOrPercentileAgg, getMultiTermsSortOrder, } from './average_or_percentile_agg'; +import { getGroupByActionVariables } from '../utils/get_groupby_action_variables'; const ruleTypeConfig = RULE_TYPES_CONFIG[ApmRuleType.TransactionDuration]; @@ -94,6 +93,7 @@ export function registerTransactionDurationRuleType({ apmActionVariables.reason, apmActionVariables.serviceName, apmActionVariables.transactionType, + apmActionVariables.transactionName, apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.viewInAppUrl, @@ -103,6 +103,16 @@ export function registerTransactionDurationRuleType({ minimumLicenseRequired: 'basic', isExportable: true, executor: async ({ params: ruleParams, services, spaceId }) => { + const predefinedGroupby = [ + SERVICE_NAME, + SERVICE_ENVIRONMENT, + TRANSACTION_TYPE, + ]; + + const allGroupbyFields = Array.from( + new Set([...predefinedGroupby, ...(ruleParams.groupBy ?? [])]) + ); + const config = await firstValueFrom(config$); const { getAlertUuid, savedObjectsClient, scopedClusterClient } = @@ -152,6 +162,9 @@ export function registerTransactionDurationRuleType({ ...termQuery(TRANSACTION_TYPE, ruleParams.transactionType, { queryEmptyString: false, }), + ...termQuery(TRANSACTION_NAME, ruleParams.transactionName, { + queryEmptyString: false, + }), ...environmentQuery(ruleParams.environment), ] as QueryDslQueryContainer[], }, @@ -159,14 +172,7 @@ export function registerTransactionDurationRuleType({ aggs: { series: { multi_terms: { - terms: [ - { field: SERVICE_NAME }, - { - field: SERVICE_ENVIRONMENT, - missing: ENVIRONMENT_NOT_DEFINED.value, - }, - { field: TRANSACTION_TYPE }, - ], + terms: [...getGroupByTerms(allGroupbyFields)], size: 1000, ...getMultiTermsSortOrder(ruleParams.aggregationType), }, @@ -197,7 +203,15 @@ export function registerTransactionDurationRuleType({ const triggeredBuckets = []; for (const bucket of response.aggregations.series.buckets) { - const [serviceName, environment, transactionType] = bucket.key; + const groupByFields = bucket.key.reduce( + (obj, bucketKey, bucketIndex) => { + obj[allGroupbyFields[bucketIndex]] = bucketKey; + return obj; + }, + {} as Record + ); + + const bucketKey = bucket.key; const transactionDuration = 'avgLatency' in bucket // only true if ruleParams.aggregationType === 'avg' @@ -209,24 +223,20 @@ export function registerTransactionDurationRuleType({ transactionDuration > thresholdMicroseconds ) { triggeredBuckets.push({ - environment, - serviceName, sourceFields: getServiceGroupFields(bucket), - transactionType, transactionDuration, + groupByFields, + bucketKey, }); } } for (const { - serviceName, - environment, - transactionType, transactionDuration, sourceFields, + groupByFields, + bucketKey, } of triggeredBuckets) { - const environmentLabel = getEnvironmentLabel(environment); - const durationFormatter = getDurationFormatter(transactionDuration); const transactionDurationFormatted = durationFormatter(transactionDuration).formatted; @@ -235,15 +245,13 @@ export function registerTransactionDurationRuleType({ aggregationType: String(ruleParams.aggregationType), asDuration, measured: transactionDuration, - serviceName, threshold: thresholdMicroseconds, windowSize: ruleParams.windowSize, windowUnit: ruleParams.windowUnit, + groupByFields, }); - const id = `${ApmRuleType.TransactionDuration}_${environmentLabel}`; - - const alertUuid = getAlertUuid(id); + const alertUuid = getAlertUuid(bucketKey.join('_')); const alertDetailsUrl = getAlertDetailsUrl( basePath, @@ -255,39 +263,41 @@ export function registerTransactionDurationRuleType({ basePath.publicBaseUrl, spaceId, getAlertUrlTransaction( - serviceName, - getEnvironmentEsField(environment)?.[SERVICE_ENVIRONMENT], - transactionType + groupByFields[SERVICE_NAME], + getEnvironmentEsField(groupByFields[SERVICE_ENVIRONMENT])?.[ + SERVICE_ENVIRONMENT + ], + groupByFields[TRANSACTION_TYPE] ) ); + const groupByActionVariables = getGroupByActionVariables(groupByFields); + services .alertWithLifecycle({ - id, + id: bucketKey.join('_'), fields: { - [SERVICE_NAME]: serviceName, - ...getEnvironmentEsField(environment), - [TRANSACTION_TYPE]: transactionType, + [TRANSACTION_NAME]: ruleParams.transactionName, [PROCESSOR_EVENT]: ProcessorEvent.transaction, [ALERT_EVALUATION_VALUE]: transactionDuration, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, [ALERT_REASON]: reason, ...sourceFields, + ...groupByFields, }, }) .scheduleActions(ruleTypeConfig.defaultActionGroupId, { alertDetailsUrl, - environment: environmentLabel, interval: formatDurationFromTimeUnitChar( ruleParams.windowSize, ruleParams.windowUnit as TimeUnitChar ), reason, - serviceName, + transactionName: ruleParams.transactionName, // When group by doesn't include transaction.name, the context.transaction.name action variable will contain value of the Transaction Name filter threshold: ruleParams.threshold, - transactionType, triggerValue: transactionDurationFormatted, viewInAppUrl, + ...groupByActionVariables, }); } diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/get_transaction_error_rate_chart_preview.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/get_transaction_error_rate_chart_preview.ts index ad27b5680b15c..3996ad5994710 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/get_transaction_error_rate_chart_preview.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/get_transaction_error_rate_chart_preview.ts @@ -9,6 +9,7 @@ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; import { SERVICE_NAME, TRANSACTION_TYPE, + TRANSACTION_NAME, } from '../../../../../common/es_fields/apm'; import { environmentQuery } from '../../../../../common/utils/environment_query'; import { AlertParams } from '../../route'; @@ -39,8 +40,15 @@ export async function getTransactionErrorRateChartPreview({ apmEventClient: APMEventClient; alertParams: AlertParams; }): Promise { - const { serviceName, environment, transactionType, interval, start, end } = - alertParams; + const { + serviceName, + environment, + transactionType, + interval, + start, + end, + transactionName, + } = alertParams; const searchAggregatedTransactions = await getSearchTransactionsEvents({ config, @@ -62,6 +70,7 @@ export async function getTransactionErrorRateChartPreview({ filter: [ ...termQuery(SERVICE_NAME, serviceName), ...termQuery(TRANSACTION_TYPE, transactionType), + ...termQuery(TRANSACTION_NAME, transactionName), ...rangeQuery(start, end), ...environmentQuery(environment), ...getDocumentTypeFilterForTransactions( diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.test.ts index a1c28f5dd77e4..708d5c533ba6b 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.test.ts @@ -107,17 +107,120 @@ describe('Transaction error rate alert', () => { }, }); - const params = { threshold: 10, windowSize: 5, windowUnit: 'm' }; + const params = { + threshold: 10, + windowSize: 5, + windowUnit: 'm', + }; + + await executor({ params }); + + expect(services.alertFactory.create).toHaveBeenCalledTimes(1); + + expect(services.alertFactory.create).toHaveBeenCalledWith( + 'foo_env-foo_type-foo' + ); + expect(services.alertFactory.create).not.toHaveBeenCalledWith( + 'bar_env-bar_type-bar' + ); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + transactionType: 'type-foo', + environment: 'env-foo', + reason: + 'Failed transactions is 10% in the last 5 mins for service: foo, env: env-foo, type: type-foo. Alert when > 10%.', + threshold: 10, + triggerValue: '10', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo?transactionType=type-foo&environment=env-foo', + }); + }); + + it('sends alert when rule is configured with group by on transaction.name', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerTransactionErrorRateRuleType({ + ...dependencies, + }); + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + series: { + buckets: [ + { + key: ['foo', 'env-foo', 'type-foo', 'tx-name-foo'], + outcomes: { + buckets: [ + { + key: 'success', + doc_count: 90, + }, + { + key: 'failure', + doc_count: 10, + }, + ], + }, + }, + { + key: ['bar', 'env-bar', 'type-bar', 'tx-name-bar'], + outcomes: { + buckets: [ + { + key: 'success', + doc_count: 90, + }, + { + key: 'failure', + doc_count: 1, + }, + ], + }, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + const params = { + threshold: 10, + windowSize: 5, + windowUnit: 'm', + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.type', + 'transaction.name', + ], + }; await executor({ params }); expect(services.alertFactory.create).toHaveBeenCalledTimes(1); expect(services.alertFactory.create).toHaveBeenCalledWith( - 'apm.transaction_error_rate_foo_type-foo_env-foo' + 'foo_env-foo_type-foo_tx-name-foo' ); expect(services.alertFactory.create).not.toHaveBeenCalledWith( - 'apm.transaction_error_rate_bar_type-bar_env-bar' + 'bar_env-bar_type-bar_tx-name-bar' ); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { @@ -125,12 +228,201 @@ describe('Transaction error rate alert', () => { transactionType: 'type-foo', environment: 'env-foo', reason: - 'Failed transactions is 10% in the last 5 mins for foo. Alert when > 10%.', + 'Failed transactions is 10% in the last 5 mins for service: foo, env: env-foo, type: type-foo, name: tx-name-foo. Alert when > 10%.', threshold: 10, triggerValue: '10', interval: '5 mins', viewInAppUrl: 'http://localhost:5601/eyr/app/apm/services/foo?transactionType=type-foo&environment=env-foo', + transactionName: 'tx-name-foo', + }); + }); + + it('sends alert when rule is configured with preselected group by', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerTransactionErrorRateRuleType({ + ...dependencies, + }); + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + series: { + buckets: [ + { + key: ['foo', 'env-foo', 'type-foo'], + outcomes: { + buckets: [ + { + key: 'success', + doc_count: 90, + }, + { + key: 'failure', + doc_count: 10, + }, + ], + }, + }, + { + key: ['bar', 'env-bar', 'type-bar'], + outcomes: { + buckets: [ + { + key: 'success', + doc_count: 90, + }, + { + key: 'failure', + doc_count: 1, + }, + ], + }, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + const params = { + threshold: 10, + windowSize: 5, + windowUnit: 'm', + groupBy: ['service.name', 'service.environment', 'transaction.type'], + }; + + await executor({ params }); + + expect(services.alertFactory.create).toHaveBeenCalledTimes(1); + + expect(services.alertFactory.create).toHaveBeenCalledWith( + 'foo_env-foo_type-foo' + ); + expect(services.alertFactory.create).not.toHaveBeenCalledWith( + 'bar_env-bar_type-bar' + ); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + transactionType: 'type-foo', + environment: 'env-foo', + reason: + 'Failed transactions is 10% in the last 5 mins for service: foo, env: env-foo, type: type-foo. Alert when > 10%.', + threshold: 10, + triggerValue: '10', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo?transactionType=type-foo&environment=env-foo', + }); + }); + + it('sends alert when service.environment field does not exist in the source', async () => { + const { services, dependencies, executor, scheduleActions } = + createRuleTypeMocks(); + + registerTransactionErrorRateRuleType({ + ...dependencies, + }); + + services.scopedClusterClient.asCurrentUser.search.mockResponse({ + hits: { + hits: [], + total: { + relation: 'eq', + value: 2, + }, + }, + aggregations: { + series: { + buckets: [ + { + key: ['foo', 'ENVIRONMENT_NOT_DEFINED', 'type-foo'], + outcomes: { + buckets: [ + { + key: 'success', + doc_count: 90, + }, + { + key: 'failure', + doc_count: 10, + }, + ], + }, + }, + { + key: ['bar', 'ENVIRONMENT_NOT_DEFINED', 'type-bar'], + outcomes: { + buckets: [ + { + key: 'success', + doc_count: 90, + }, + { + key: 'failure', + doc_count: 1, + }, + ], + }, + }, + ], + }, + }, + took: 0, + timed_out: false, + _shards: { + failed: 0, + skipped: 0, + successful: 1, + total: 1, + }, + }); + + const params = { + threshold: 10, + windowSize: 5, + windowUnit: 'm', + groupBy: ['service.name', 'service.environment', 'transaction.type'], + }; + + await executor({ params }); + + expect(services.alertFactory.create).toHaveBeenCalledTimes(1); + + expect(services.alertFactory.create).toHaveBeenCalledWith( + 'foo_ENVIRONMENT_NOT_DEFINED_type-foo' + ); + expect(services.alertFactory.create).not.toHaveBeenCalledWith( + 'bar_ENVIRONMENT_NOT_DEFINED_type-bar' + ); + + expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { + serviceName: 'foo', + transactionType: 'type-foo', + environment: 'Not defined', + reason: + 'Failed transactions is 10% in the last 5 mins for service: foo, env: Not defined, type: type-foo. Alert when > 10%.', + threshold: 10, + triggerValue: '10', + interval: '5 mins', + viewInAppUrl: + 'http://localhost:5601/eyr/app/apm/services/foo?transactionType=type-foo&environment=ENVIRONMENT_ALL', }); }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts index eac21063bc40b..6fa9319b71753 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type.ts @@ -21,17 +21,14 @@ import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server import { addSpaceIdToPath } from '@kbn/spaces-plugin/common'; import { firstValueFrom } from 'rxjs'; import { SearchAggregatedTransactionSetting } from '../../../../../common/aggregated_transactions'; -import { - ENVIRONMENT_NOT_DEFINED, - getEnvironmentEsField, - getEnvironmentLabel, -} from '../../../../../common/environment_filter_values'; +import { getEnvironmentEsField } from '../../../../../common/environment_filter_values'; import { EVENT_OUTCOME, PROCESSOR_EVENT, SERVICE_ENVIRONMENT, SERVICE_NAME, TRANSACTION_TYPE, + TRANSACTION_NAME, } from '../../../../../common/es_fields/apm'; import { EventOutcome } from '../../../../../common/event_outcome'; import { @@ -58,6 +55,8 @@ import { getServiceGroupFields, getServiceGroupFieldsAgg, } from '../get_service_group_fields'; +import { getGroupByTerms } from '../utils/get_groupby_terms'; +import { getGroupByActionVariables } from '../utils/get_groupby_action_variables'; const ruleTypeConfig = RULE_TYPES_CONFIG[ApmRuleType.TransactionErrorRate]; @@ -86,8 +85,10 @@ export function registerTransactionErrorRateRuleType({ apmActionVariables.interval, apmActionVariables.reason, apmActionVariables.serviceName, + apmActionVariables.transactionName, apmActionVariables.threshold, apmActionVariables.transactionType, + apmActionVariables.transactionName, apmActionVariables.triggerValue, apmActionVariables.viewInAppUrl, ], @@ -96,6 +97,16 @@ export function registerTransactionErrorRateRuleType({ minimumLicenseRequired: 'basic', isExportable: true, executor: async ({ services, spaceId, params: ruleParams }) => { + const predefinedGroupby = [ + SERVICE_NAME, + SERVICE_ENVIRONMENT, + TRANSACTION_TYPE, + ]; + + const allGroupbyFields = Array.from( + new Set([...predefinedGroupby, ...(ruleParams.groupBy ?? [])]) + ); + const config = await firstValueFrom(config$); const { savedObjectsClient, scopedClusterClient } = services; @@ -148,6 +159,9 @@ export function registerTransactionErrorRateRuleType({ ...termQuery(TRANSACTION_TYPE, ruleParams.transactionType, { queryEmptyString: false, }), + ...termQuery(TRANSACTION_NAME, ruleParams.transactionName, { + queryEmptyString: false, + }), ...environmentQuery(ruleParams.environment), ], }, @@ -155,14 +169,7 @@ export function registerTransactionErrorRateRuleType({ aggs: { series: { multi_terms: { - terms: [ - { field: SERVICE_NAME }, - { - field: SERVICE_ENVIRONMENT, - missing: ENVIRONMENT_NOT_DEFINED.value, - }, - { field: TRANSACTION_TYPE }, - ], + terms: [...getGroupByTerms(allGroupbyFields)], size: 1000, order: { _count: 'desc' as const }, }, @@ -189,8 +196,17 @@ export function registerTransactionErrorRateRuleType({ } const results = []; + for (const bucket of response.aggregations.series.buckets) { - const [serviceName, environment, transactionType] = bucket.key; + const groupByFields = bucket.key.reduce( + (obj, bucketKey, bucketIndex) => { + obj[allGroupbyFields[bucketIndex]] = bucketKey; + return obj; + }, + {} as Record + ); + + const bucketKey = bucket.key; const failedOutcomeBucket = bucket.outcomes.buckets.find( (outcomeBucket) => outcomeBucket.key === EventOutcome.failure @@ -204,46 +220,32 @@ export function registerTransactionErrorRateRuleType({ if (errorRate >= ruleParams.threshold) { results.push({ - serviceName, - environment, - transactionType, errorRate, sourceFields: getServiceGroupFields(failedOutcomeBucket), + groupByFields, + bucketKey, }); } } results.forEach((result) => { - const { - serviceName, - environment, - transactionType, - errorRate, - sourceFields, - } = result; + const { errorRate, sourceFields, groupByFields, bucketKey } = result; const reasonMessage = formatTransactionErrorRateReason({ threshold: ruleParams.threshold, measured: errorRate, asPercent, - serviceName, windowSize: ruleParams.windowSize, windowUnit: ruleParams.windowUnit, + groupByFields, }); - const id = [ - ApmRuleType.TransactionErrorRate, - serviceName, - transactionType, - environment, - ] - .filter((name) => name) - .join('_'); - const relativeViewInAppUrl = getAlertUrlTransaction( - serviceName, - getEnvironmentEsField(environment)?.[SERVICE_ENVIRONMENT], - transactionType + groupByFields[SERVICE_NAME], + getEnvironmentEsField(groupByFields[SERVICE_ENVIRONMENT])?.[ + SERVICE_ENVIRONMENT + ], + groupByFields[TRANSACTION_TYPE] ); const viewInAppUrl = addSpaceIdToPath( @@ -252,32 +254,33 @@ export function registerTransactionErrorRateRuleType({ relativeViewInAppUrl ); + const groupByActionVariables = + getGroupByActionVariables(groupByFields); + services .alertWithLifecycle({ - id, + id: bucketKey.join('_'), fields: { - [SERVICE_NAME]: serviceName, - ...getEnvironmentEsField(environment), - [TRANSACTION_TYPE]: transactionType, + [TRANSACTION_NAME]: ruleParams.transactionName, [PROCESSOR_EVENT]: ProcessorEvent.transaction, [ALERT_EVALUATION_VALUE]: errorRate, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, [ALERT_REASON]: reasonMessage, ...sourceFields, + ...groupByFields, }, }) .scheduleActions(ruleTypeConfig.defaultActionGroupId, { - environment: getEnvironmentLabel(environment), interval: formatDurationFromTimeUnitChar( ruleParams.windowSize, ruleParams.windowUnit as TimeUnitChar ), reason: reasonMessage, - serviceName, threshold: ruleParams.threshold, - transactionType, + transactionName: ruleParams.transactionName, triggerValue: asDecimalOrInteger(errorRate), viewInAppUrl, + ...groupByActionVariables, }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_action_variables.test.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_action_variables.test.ts new file mode 100644 index 0000000000000..96e0cbd7f09c1 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_action_variables.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getGroupByActionVariables } from './get_groupby_action_variables'; + +describe('getGroupByActionVariables', () => { + it('should rename action variables', () => { + const result = getGroupByActionVariables({ + 'service.name': 'opbeans-java', + 'service.environment': 'development', + 'transaction.type': 'request', + 'transaction.name': 'tx-java', + 'error.grouping_key': 'error-key-0', + }); + expect(result).toMatchInlineSnapshot(` + Object { + "environment": "development", + "errorGroupingKey": "error-key-0", + "serviceName": "opbeans-java", + "transactionName": "tx-java", + "transactionType": "request", + } + `); + }); + + it('environment action variable should have value "Not defined"', () => { + const result = getGroupByActionVariables({ + 'service.name': 'opbeans-java', + 'service.environment': 'ENVIRONMENT_NOT_DEFINED', + 'transaction.type': 'request', + 'transaction.name': 'tx-java', + }); + expect(result).toMatchInlineSnapshot(` + Object { + "environment": "Not defined", + "serviceName": "opbeans-java", + "transactionName": "tx-java", + "transactionType": "request", + } + `); + }); +}); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_action_variables.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_action_variables.ts new file mode 100644 index 0000000000000..fd245bb3a1d0c --- /dev/null +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_action_variables.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 { getFieldValueLabel } from '../../../../../common/rules/apm_rule_types'; +import { + ERROR_GROUP_ID, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_NAME, + TRANSACTION_TYPE, +} from '../../../../../common/es_fields/apm'; + +const renameActionVariable = (field: string): string => { + switch (field) { + case SERVICE_NAME: + return 'serviceName'; + case SERVICE_ENVIRONMENT: + return 'environment'; + case TRANSACTION_TYPE: + return 'transactionType'; + case TRANSACTION_NAME: + return 'transactionName'; + case ERROR_GROUP_ID: + return 'errorGroupingKey'; + default: + return field; + } +}; + +export const getGroupByActionVariables = ( + groupByFields: Record +): Record => { + return Object.keys(groupByFields).reduce>( + (acc, cur) => { + acc[renameActionVariable(cur)] = getFieldValueLabel( + cur, + groupByFields[cur] + ); + return acc; + }, + {} + ); +}; diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_terms.test.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_terms.test.ts new file mode 100644 index 0000000000000..01d96f787bd9b --- /dev/null +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_terms.test.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 { getGroupByTerms } from './get_groupby_terms'; + +describe('get terms fields for multi-terms aggregation', () => { + it('returns terms array based on the group-by fields', () => { + const ruleParams = { + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.type', + 'transaction.name', + ], + }; + const terms = getGroupByTerms(ruleParams.groupBy); + expect(terms).toEqual([ + { field: 'service.name' }, + { field: 'service.environment', missing: 'ENVIRONMENT_NOT_DEFINED' }, + { field: 'transaction.type' }, + { field: 'transaction.name' }, + ]); + }); + + it('returns an empty terms array when group-by is undefined', () => { + const ruleParams = { groupBy: undefined }; + const terms = getGroupByTerms(ruleParams.groupBy); + expect(terms).toEqual([]); + }); +}); diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_terms.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_terms.ts new file mode 100644 index 0000000000000..22b52fa6a3116 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/utils/get_groupby_terms.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 { ENVIRONMENT_NOT_DEFINED } from '../../../../../common/environment_filter_values'; +import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; + +export const getGroupByTerms = (groupByFields: string[] | undefined = []) => { + return groupByFields.map((groupByField) => { + return { + field: groupByField, + missing: + groupByField === SERVICE_ENVIRONMENT + ? ENVIRONMENT_NOT_DEFINED.value + : undefined, + }; + }); +}; diff --git a/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts b/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts index 671b4d56dad94..43528c037222e 100644 --- a/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts +++ b/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts @@ -17,12 +17,19 @@ export async function getLatestApmPackage({ request: KibanaRequest; }) { const packageClient = fleetPluginStart.packageService.asScoped(request); - const { name, version } = await packageClient.fetchFindLatestPackage( + const latestPackage = await packageClient.fetchFindLatestPackage( APM_PACKAGE_NAME ); - const registryPackage = await packageClient.getPackage(name, version); - const { title, policy_templates: policyTemplates } = - registryPackage.packageInfo; + const packageInfo = + 'buffer' in latestPackage + ? (await packageClient.readBundledPackage(latestPackage)).packageInfo + : latestPackage; + const { + name, + version, + title, + policy_templates: policyTemplates, + } = packageInfo; const firstTemplate = policyTemplates?.[0]; const policyTemplateInputVars = firstTemplate && 'inputs' in firstTemplate diff --git a/x-pack/plugins/apm/server/routes/fleet/run_migration_check.ts b/x-pack/plugins/apm/server/routes/fleet/run_migration_check.ts index 3d1a2d740df6e..072d280200850 100644 --- a/x-pack/plugins/apm/server/routes/fleet/run_migration_check.ts +++ b/x-pack/plugins/apm/server/routes/fleet/run_migration_check.ts @@ -49,6 +49,17 @@ export async function runMigrationCheck({ ]); const hasRequiredRole = isSuperuser({ securityPluginStart, request }); + if (!hasRequiredRole) { + return { + has_cloud_agent_policy: false, + has_cloud_apm_package_policy: false, + cloud_apm_migration_enabled: cloudApmMigrationEnabled, + has_required_role: false, + cloud_apm_package_policy: undefined, + has_apm_integrations: false, + latest_apm_package_version: '', + }; + } const cloudAgentPolicy = hasRequiredRole ? await getCloudAgentPolicy({ savedObjectsClient, @@ -57,14 +68,15 @@ export async function runMigrationCheck({ : undefined; const apmPackagePolicy = getApmPackagePolicy(cloudAgentPolicy); const coreStart = await core.start(); - const packagePolicies = await getApmPackagePolicies({ - coreStart, - fleetPluginStart, - }); const latestApmPackage = await getLatestApmPackage({ fleetPluginStart, request, }); + + const packagePolicies = await getApmPackagePolicies({ + coreStart, + fleetPluginStart, + }); return { has_cloud_agent_policy: !!cloudAgentPolicy, has_cloud_apm_package_policy: !!apmPackagePolicy, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_detailed_statistics_by_field.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_detailed_statistics_by_field.ts new file mode 100644 index 0000000000000..d511b22b13274 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_detailed_statistics_by_field.ts @@ -0,0 +1,214 @@ +/* + * 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 { + kqlQuery, + rangeQuery, + termQuery, +} from '@kbn/observability-plugin/server'; +import { keyBy } from 'lodash'; +import { getBucketSize } from '../../../common/utils/get_bucket_size'; +import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; +import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { + SERVICE_NAME, + TRANSACTION_DURATION, +} from '../../../common/es_fields/apm'; +import { getLatencyValue } from '../../lib/helpers/latency_aggregation_type'; +import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; +import { offsetPreviousPeriodCoordinates } from '../../../common/utils/offset_previous_period_coordinate'; +import { Coordinate } from '../../../typings/timeseries'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; + +interface MobileDetailedStatistics { + fieldName: string; + latency: Coordinate[]; + throughput: Coordinate[]; +} + +export interface MobileDetailedStatisticsResponse { + currentPeriod: Record; + previousPeriod: Record; +} + +interface Props { + kuery: string; + apmEventClient: APMEventClient; + serviceName: string; + environment: string; + start: number; + end: number; + field: string; + fieldValues: string[]; + offset?: string; +} + +async function getMobileDetailedStatisticsByField({ + environment, + kuery, + serviceName, + field, + fieldValues, + apmEventClient, + start, + end, + offset, +}: Props) { + const { startWithOffset, endWithOffset } = getOffsetInMs({ + start, + end, + offset, + }); + + const { intervalString } = getBucketSize({ + start: startWithOffset, + end: endWithOffset, + minBucketSize: 60, + }); + + const response = await apmEventClient.search( + `get_mobile_detailed_statistics_by_field`, + { + apm: { + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(startWithOffset, endWithOffset), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + detailed_statistics: { + terms: { + field, + include: fieldValues, + size: fieldValues.length, + }, + aggs: { + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0, + extended_bounds: { + min: startWithOffset, + max: endWithOffset, + }, + }, + aggs: { + latency: { + avg: { + field: TRANSACTION_DURATION, + }, + }, + }, + }, + }, + }, + }, + }, + } + ); + + const buckets = response.aggregations?.detailed_statistics.buckets ?? []; + + return buckets.map((bucket) => { + const fieldName = bucket.key as string; + const latency = bucket.timeseries.buckets.map((timeseriesBucket) => ({ + x: timeseriesBucket.key, + y: getLatencyValue({ + latencyAggregationType: LatencyAggregationType.avg, + aggregation: timeseriesBucket.latency, + }), + })); + const throughput = bucket.timeseries.buckets.map((timeseriesBucket) => ({ + x: timeseriesBucket.key, + y: timeseriesBucket.doc_count, + })); + + return { + fieldName, + latency, + throughput, + }; + }); +} + +export async function getMobileDetailedStatisticsByFieldPeriods({ + environment, + kuery, + serviceName, + field, + fieldValues, + apmEventClient, + start, + end, + offset, +}: Props): Promise { + const commonProps = { + environment, + kuery, + serviceName, + field, + fieldValues, + apmEventClient, + start, + end, + }; + + const currentPeriodPromise = getMobileDetailedStatisticsByField({ + ...commonProps, + }); + + const previousPeriodPromise = offset + ? getMobileDetailedStatisticsByField({ + ...commonProps, + offset, + }) + : []; + + const [currentPeriod, previousPeriod] = await Promise.all([ + currentPeriodPromise, + previousPeriodPromise, + ]); + + const firstCurrentPeriod = currentPeriod?.[0]; + return { + currentPeriod: keyBy(currentPeriod, 'fieldName'), + previousPeriod: keyBy( + previousPeriod.map((data) => { + return { + ...data, + latency: offsetPreviousPeriodCoordinates({ + currentPeriodTimeseries: firstCurrentPeriod?.latency, + previousPeriodTimeseries: data.latency, + }), + throughput: offsetPreviousPeriodCoordinates({ + currentPeriodTimeseries: firstCurrentPeriod?.throughput, + previousPeriodTimeseries: data.throughput, + }), + }; + }), + 'fieldName' + ), + }; +} diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_main_statistics_by_field.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_main_statistics_by_field.ts new file mode 100644 index 0000000000000..a5783997e391b --- /dev/null +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_main_statistics_by_field.ts @@ -0,0 +1,186 @@ +/* + * 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 { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { merge } from 'lodash'; +import { + SERVICE_NAME, + SESSION_ID, + TRANSACTION_DURATION, + ERROR_TYPE, +} from '../../../common/es_fields/apm'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; +import { getLatencyValue } from '../../lib/helpers/latency_aggregation_type'; +import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; +import { calculateThroughputWithRange } from '../../lib/helpers/calculate_throughput'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; + +interface Props { + kuery: string; + apmEventClient: APMEventClient; + serviceName: string; + environment: string; + start: number; + end: number; + field: string; +} + +export interface MobileMainStatisticsResponse { + mainStatistics: Array<{ + name: string | number; + latency: number | null; + throughput: number; + crashRate?: number; + }>; +} + +export async function getMobileMainStatisticsByField({ + kuery, + apmEventClient, + serviceName, + environment, + start, + end, + field, +}: Props) { + async function getMobileTransactionEventStatistics() { + const response = await apmEventClient.search( + `get_mobile_main_statistics_by_field`, + { + apm: { + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + main_statistics: { + terms: { + field, + size: 1000, + }, + aggs: { + latency: { + avg: { + field: TRANSACTION_DURATION, + }, + }, + }, + }, + }, + }, + } + ); + + return ( + response.aggregations?.main_statistics.buckets.map((bucket) => { + return { + name: bucket.key, + latency: getLatencyValue({ + latencyAggregationType: LatencyAggregationType.avg, + aggregation: bucket.latency, + }), + throughput: calculateThroughputWithRange({ + start, + end, + value: bucket.doc_count, + }), + }; + }) ?? [] + ); + } + + async function getMobileErrorEventStatistics() { + const response = await apmEventClient.search( + `get_mobile_transaction_events_main_statistics_by_field`, + { + apm: { + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + main_statistics: { + terms: { + field, + size: 1000, + }, + aggs: { + sessions: { + cardinality: { + field: SESSION_ID, + }, + }, + crashes: { + filter: { + term: { + [ERROR_TYPE]: 'crash', + }, + }, + }, + }, + }, + }, + }, + } + ); + return ( + response.aggregations?.main_statistics.buckets.map((bucket) => { + return { + name: bucket.key, + crashRate: bucket.crashes.doc_count / bucket.sessions.value ?? 0, + }; + }) ?? [] + ); + } + + const [transactioEventStatistics, errorEventStatistics] = await Promise.all([ + getMobileTransactionEventStatistics(), + getMobileErrorEventStatistics(), + ]); + + const mainStatistics = merge(transactioEventStatistics, errorEventStatistics); + + return { mainStatistics }; +} diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/get_device_os_app_charts.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/get_device_os_app_charts.ts new file mode 100644 index 0000000000000..3c5e8e558770d --- /dev/null +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/get_device_os_app_charts.ts @@ -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 { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { + DEVICE_MODEL_IDENTIFIER, + HOST_OS_VERSION, + SERVICE_NAME, + SERVICE_VERSION, + TRANSACTION_TYPE, +} from '../../../../common/es_fields/apm'; +import { environmentQuery } from '../../../../common/utils/environment_query'; +import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { mergeCountWithOther } from './merge_other_count'; +import { + MobilePropertyType, + MobilePropertyDeviceOsAppVersionType, +} from '../../../../common/mobile_types'; + +export type MobileMostUsedChartResponse = Array<{ + key: MobilePropertyDeviceOsAppVersionType; + options: Array<{ + key: string | number; + docCount: number; + }>; +}>; +export async function getMobileMostUsedCharts({ + kuery, + apmEventClient, + serviceName, + transactionType, + environment, + start, + end, +}: { + kuery: string; + apmEventClient: APMEventClient; + serviceName: string; + transactionType?: string; + environment: string; + start: number; + end: number; +}): Promise { + const MAX_ITEMS_PER_CHART = 5; + const response = await apmEventClient.search('get_mobile_most_used_charts', { + apm: { + events: [ProcessorEvent.transaction], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...termQuery(TRANSACTION_TYPE, transactionType), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + devices: { + terms: { + field: DEVICE_MODEL_IDENTIFIER, + size: MAX_ITEMS_PER_CHART, + }, + }, + osVersions: { + terms: { + field: HOST_OS_VERSION, + size: MAX_ITEMS_PER_CHART, + }, + }, + appVersions: { + terms: { + field: SERVICE_VERSION, + size: MAX_ITEMS_PER_CHART, + }, + }, + }, + }, + }); + + return [ + { + key: MobilePropertyType.Device, + options: + mergeCountWithOther( + response.aggregations?.devices?.buckets, + response.aggregations?.devices?.sum_other_doc_count + ) || [], + }, + { + key: MobilePropertyType.OsVersion, + options: + mergeCountWithOther( + response.aggregations?.osVersions?.buckets, + response.aggregations?.osVersions?.sum_other_doc_count + ) || [], + }, + { + key: MobilePropertyType.AppVersion, + options: + mergeCountWithOther( + response.aggregations?.appVersions?.buckets, + response.aggregations?.appVersions?.sum_other_doc_count + ) || [], + }, + ]; +} diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/get_nct_chart.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/get_nct_chart.ts new file mode 100644 index 0000000000000..987f5cf9cb1a2 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/get_nct_chart.ts @@ -0,0 +1,90 @@ +/* + * 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 { + termQuery, + kqlQuery, + rangeQuery, +} from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { + NETWORK_CONNECTION_TYPE, + SERVICE_NAME, +} from '../../../../common/es_fields/apm'; +import { environmentQuery } from '../../../../common/utils/environment_query'; +import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { mergeCountWithOther } from './merge_other_count'; +import { + MobilePropertyType, + MobilePropertyNctType, +} from '../../../../common/mobile_types'; + +export interface MobileMostUsedNCTChartResponse { + key: MobilePropertyNctType; + options: Array<{ + key: string | number; + docCount: number; + }>; +} + +export async function getMobileMostUsedNCTCharts({ + kuery, + apmEventClient, + serviceName, + environment, + start, + end, +}: { + kuery: string; + apmEventClient: APMEventClient; + serviceName: string; + transactionType?: string; + environment: string; + start: number; + end: number; +}): Promise { + const MAX_ITEMS_PER_CHART = 5; + const response = await apmEventClient.search( + 'get_mobile_most_used_nct_charts', + { + apm: { + events: [ProcessorEvent.span], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [ + ...termQuery(SERVICE_NAME, serviceName), + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + netConnectionTypes: { + terms: { + field: NETWORK_CONNECTION_TYPE, + size: MAX_ITEMS_PER_CHART, + }, + }, + }, + }, + } + ); + + return { + key: MobilePropertyType.NetworkConnectionType, + options: + mergeCountWithOther( + response.aggregations?.netConnectionTypes?.buckets, + response.aggregations?.netConnectionTypes?.sum_other_doc_count + ) || [], + }; +} diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/merge_other_count.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/merge_other_count.ts new file mode 100644 index 0000000000000..69a4849793247 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_most_used_charts/merge_other_count.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function mergeCountWithOther( + buckets: Array<{ key: string | number; doc_count: number }> = [], + otherCount: number = 0 +) { + const options = buckets.map(({ key, doc_count: docCount }) => ({ + key, + docCount, + })); + if (otherCount > 0) { + options.push({ + key: 'other', + docCount: otherCount, + }); + } + + return options; +} diff --git a/x-pack/plugins/apm/server/routes/mobile/route.ts b/x-pack/plugins/apm/server/routes/mobile/route.ts index b86223e4f1bca..3323172a5e6d5 100644 --- a/x-pack/plugins/apm/server/routes/mobile/route.ts +++ b/x-pack/plugins/apm/server/routes/mobile/route.ts @@ -6,7 +6,7 @@ */ import * as t from 'io-ts'; -import { toNumberRt } from '@kbn/io-ts-utils'; +import { jsonRt, toNumberRt } from '@kbn/io-ts-utils'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { environmentRt, kueryRt, rangeRt } from '../default_api_types'; @@ -26,6 +26,23 @@ import { getMobileTermsByField, MobileTermsByFieldResponse, } from './get_mobile_terms_by_field'; +import { + getMobileMainStatisticsByField, + MobileMainStatisticsResponse, +} from './get_mobile_main_statistics_by_field'; +import { + getMobileDetailedStatisticsByFieldPeriods, + MobileDetailedStatisticsResponse, +} from './get_mobile_detailed_statistics_by_field'; +import { + getMobileMostUsedCharts, + MobileMostUsedChartResponse, +} from './get_mobile_most_used_charts/get_device_os_app_charts'; +import { + getMobileMostUsedNCTCharts, + MobileMostUsedNCTChartResponse, +} from './get_mobile_most_used_charts/get_nct_chart'; +import { MobilePropertyType } from '../../../common/mobile_types'; const mobileFiltersRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/services/{serviceName}/mobile/filters', @@ -66,6 +83,60 @@ const mobileFiltersRoute = createApmServerRoute({ }, }); +const mobileChartsRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/most_used_charts', + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.intersection([ + kueryRt, + rangeRt, + environmentRt, + t.partial({ + transactionType: t.string, + }), + ]), + }), + options: { tags: ['access:apm'] }, + handler: async ( + resources + ): Promise<{ + mostUsedCharts: Array<{ + key: MobilePropertyType; + options: MobileMostUsedChartResponse[number]['options'] & + MobileMostUsedNCTChartResponse['options']; + }>; + }> => { + const apmEventClient = await getApmEventClient(resources); + const { params } = resources; + const { serviceName } = params.path; + const { kuery, environment, start, end, transactionType } = params.query; + + const [deviceOsAndAppVersionChart, nctChart] = await Promise.all([ + getMobileMostUsedCharts({ + kuery, + environment, + transactionType, + start, + end, + serviceName, + apmEventClient, + }), + getMobileMostUsedNCTCharts({ + kuery, + environment, + start, + end, + serviceName, + apmEventClient, + }), + ]); + + return { mostUsedCharts: [...deviceOsAndAppVersionChart, nctChart] }; + }, +}); + const mobileStatsRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/mobile-services/{serviceName}/stats', params: t.type({ @@ -266,11 +337,92 @@ const mobileTermsByFieldRoute = createApmServerRoute({ }, }); +const mobileMainStatisticsByField = createApmServerRoute({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/main_statistics', + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.intersection([ + kueryRt, + rangeRt, + environmentRt, + t.type({ + field: t.string, + }), + ]), + }), + options: { + tags: ['access:apm'], + }, + handler: async (resources): Promise => { + const apmEventClient = await getApmEventClient(resources); + const { params } = resources; + const { serviceName } = params.path; + const { kuery, environment, start, end, field } = params.query; + + return await getMobileMainStatisticsByField({ + kuery, + environment, + start, + end, + serviceName, + apmEventClient, + field, + }); + }, +}); + +const mobileDetailedStatisticsByField = createApmServerRoute({ + endpoint: + 'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics', + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.intersection([ + kueryRt, + rangeRt, + offsetRt, + environmentRt, + t.type({ + field: t.string, + fieldValues: jsonRt.pipe(t.array(t.string)), + }), + ]), + }), + options: { + tags: ['access:apm'], + }, + handler: async (resources): Promise => { + const apmEventClient = await getApmEventClient(resources); + const { params } = resources; + const { serviceName } = params.path; + const { kuery, environment, start, end, field, offset, fieldValues } = + params.query; + + return await getMobileDetailedStatisticsByFieldPeriods({ + kuery, + environment, + start, + end, + serviceName, + apmEventClient, + field, + fieldValues, + offset, + }); + }, +}); + export const mobileRouteRepository = { ...mobileFiltersRoute, + ...mobileChartsRoute, ...sessionsChartRoute, ...httpRequestsChartRoute, ...mobileStatsRoute, ...mobileLocationStatsRoute, ...mobileTermsByFieldRoute, + ...mobileMainStatisticsByField, + ...mobileDetailedStatisticsByField, }; diff --git a/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.test.ts b/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.test.ts index 2a61baf923e83..7cf1336f5cfec 100644 --- a/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.test.ts +++ b/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.test.ts @@ -32,6 +32,12 @@ const nodejsExternal = { [SPAN_SUBTYPE]: 'aa', }; +const kafkaExternal = { + [SPAN_DESTINATION_SERVICE_RESOURCE]: 'kafka/some-queue', + [SPAN_TYPE]: 'messaging', + [SPAN_SUBTYPE]: 'kafka', +}; + const javaService = { [SERVICE_NAME]: 'opbeans-java', [SERVICE_ENVIRONMENT]: 'production', @@ -88,6 +94,67 @@ describe('transformServiceMapResponses', () => { ).toBeUndefined(); }); + it('adds connection for messaging-based external destinations', () => { + const response: ServiceMapResponse = { + services: [nodejsService, javaService], + discoveredServices: [ + { + from: kafkaExternal, + to: nodejsService, + }, + ], + connections: [ + { + source: javaService, + destination: kafkaExternal, + }, + ], + anomalies, + }; + + const { elements } = transformServiceMapResponses({ response }); + + expect(elements.length).toBe(5); + + const connections = elements.filter( + (element) => 'source' in element.data && 'target' in element.data + ); + expect(connections.length).toBe(2); + + const sendMessageConnection = connections.find( + (element) => + 'source' in element.data && element.data.source === 'opbeans-java' + ); + + expect(sendMessageConnection).toHaveProperty('data'); + expect(sendMessageConnection?.data).toHaveProperty('target'); + + if (sendMessageConnection?.data && 'target' in sendMessageConnection.data) { + expect(sendMessageConnection.data.target).toBe('>kafka/some-queue'); + expect(sendMessageConnection.data.id).toBe( + 'opbeans-java~>kafka/some-queue' + ); + } + + const receiveMessageConnection = connections.find( + (element) => + 'target' in element.data && element.data.target === 'opbeans-node' + ); + + expect(receiveMessageConnection).toHaveProperty('data'); + expect(receiveMessageConnection?.data).toHaveProperty('target'); + + if ( + receiveMessageConnection?.data && + 'source' in receiveMessageConnection.data + ) { + expect(receiveMessageConnection.data.source).toBe('>kafka/some-queue'); + expect(receiveMessageConnection.data.id).toBe( + '>kafka/some-queue~opbeans-node' + ); + } + }); + it('collapses external destinations based on span.destination.resource.name', () => { const response: ServiceMapResponse = { services: [nodejsService, javaService], diff --git a/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.ts b/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.ts index 7ed09d9eb4f65..97a13c368b162 100644 --- a/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.ts +++ b/x-pack/plugins/apm/server/routes/service_map/transform_service_map_responses.ts @@ -41,6 +41,40 @@ function getConnectionId(connection: Connection) { )}`; } +function addMessagingConnections( + connections: Connection[], + discoveredServices: Array<{ + from: ExternalConnectionNode; + to: ServiceConnectionNode; + }> +): Connection[] { + const messagingDestinations = connections + .map((connection) => connection.destination) + .filter( + (dest) => + dest['span.type'] === 'messaging' && + SPAN_DESTINATION_SERVICE_RESOURCE in dest + ); + const newConnections = messagingDestinations + .map((node) => { + const matchedService = discoveredServices.find( + ({ from }) => + node[SPAN_DESTINATION_SERVICE_RESOURCE] === + from[SPAN_DESTINATION_SERVICE_RESOURCE] + )?.to; + if (matchedService) { + return { + source: node, + destination: matchedService, + }; + } + return undefined; + }) + .filter((c) => c !== undefined) as Connection[]; + + return [...connections, ...newConnections]; +} + export function getAllNodes( services: ServiceMapResponse['services'], connections: ServiceMapResponse['connections'] @@ -82,8 +116,11 @@ export function transformServiceMapResponses({ response: ServiceMapResponse; }): TransformServiceMapResponse { const { discoveredServices, services, connections, anomalies } = response; - - const allNodes = getAllNodes(services, connections); + const allConnections = addMessagingConnections( + connections, + discoveredServices + ); + const allNodes = getAllNodes(services, allConnections); const serviceNodes = getServiceNodes(allNodes); // List of nodes that are externals @@ -98,9 +135,17 @@ export function transformServiceMapResponses({ if (!node.id || map[node.id]) { return map; } - + const outboundConnectionExists = allConnections.some( + (con) => + SPAN_DESTINATION_SERVICE_RESOURCE in con.source && + con.source[SPAN_DESTINATION_SERVICE_RESOURCE] === + node[SPAN_DESTINATION_SERVICE_RESOURCE] + ); const matchedService = discoveredServices.find(({ from }) => { - if ('span.destination.service.resource' in node) { + if ( + !outboundConnectionExists && + SPAN_DESTINATION_SERVICE_RESOURCE in node + ) { return ( node[SPAN_DESTINATION_SERVICE_RESOURCE] === from[SPAN_DESTINATION_SERVICE_RESOURCE] @@ -162,7 +207,7 @@ export function transformServiceMapResponses({ } // Build connections with mapped nodes - const mappedConnections = connections + const mappedConnections = allConnections .map((connection) => { const sourceData = getConnectionNode(connection.source); const targetData = getConnectionNode(connection.destination); diff --git a/x-pack/plugins/apm/server/routes/services/get_service_metadata_details.ts b/x-pack/plugins/apm/server/routes/services/get_service_metadata_details.ts index 4563bd851f8a8..2231caa7df71e 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_metadata_details.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_metadata_details.ts @@ -24,17 +24,18 @@ import { SERVICE_VERSION, FAAS_ID, FAAS_TRIGGER_TYPE, + LABEL_TELEMETRY_AUTO_VERSION, } from '../../../common/es_fields/apm'; import { ContainerType } from '../../../common/service_metadata'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; -import { getProcessorEventForTransactions } from '../../lib/helpers/transactions'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { should } from './get_service_metadata_icons'; +import { isOpenTelemetryAgentName } from '../../../common/agent_name'; type ServiceMetadataDetailsRaw = Pick< TransactionRaw, - 'service' | 'agent' | 'host' | 'container' | 'kubernetes' | 'cloud' + 'service' | 'agent' | 'host' | 'container' | 'kubernetes' | 'cloud' | 'labels' >; export interface ServiceMetadataDetails { @@ -50,6 +51,11 @@ export interface ServiceMetadataDetails { version: string; }; }; + opentelemetry?: { + language?: string; + sdkVersion?: string; + autoVersion?: string; + }; container?: { ids?: string[]; image?: string; @@ -81,13 +87,11 @@ export interface ServiceMetadataDetails { export async function getServiceMetadataDetails({ serviceName, apmEventClient, - searchAggregatedTransactions, start, end, }: { serviceName: string; apmEventClient: APMEventClient; - searchAggregatedTransactions: boolean; start: number; end: number; }): Promise { @@ -99,16 +103,27 @@ export async function getServiceMetadataDetails({ const params = { apm: { events: [ - getProcessorEventForTransactions(searchAggregatedTransactions), + ProcessorEvent.transaction, ProcessorEvent.error, ProcessorEvent.metric, ], }, - sort: [{ '@timestamp': { order: 'desc' as const } }], + sort: [ + { _score: { order: 'desc' as const } }, + { '@timestamp': { order: 'desc' as const } }, + ], body: { track_total_hits: 1, size: 1, - _source: [SERVICE, AGENT, HOST, CONTAINER, KUBERNETES, CLOUD], + _source: [ + SERVICE, + AGENT, + HOST, + CONTAINER, + KUBERNETES, + CLOUD, + LABEL_TELEMETRY_AUTO_VERSION, + ], query: { bool: { filter, should } }, aggs: { serviceVersions: { @@ -178,8 +193,8 @@ export async function getServiceMetadataDetails({ }; } - const { service, agent, host, kubernetes, container, cloud } = response.hits - .hits[0]._source as ServiceMetadataDetailsRaw; + const { service, agent, host, kubernetes, container, cloud, labels } = + response.hits.hits[0]._source as ServiceMetadataDetailsRaw; const serviceMetadataDetails = { versions: response.aggregations?.serviceVersions.buckets.map( @@ -190,6 +205,17 @@ export async function getServiceMetadataDetails({ agent, }; + const otelDetails = + !!agent?.name && isOpenTelemetryAgentName(agent.name) + ? { + language: agent.name.startsWith('opentelemetry') + ? agent.name.replace(/^opentelemetry\//, '') + : undefined, + sdkVersion: agent?.version, + autoVersion: labels?.telemetry_auto_version as string, + } + : undefined; + const totalNumberInstances = response.aggregations?.totalNumberInstances.value; @@ -238,6 +264,7 @@ export async function getServiceMetadataDetails({ return { service: serviceMetadataDetails, + opentelemetry: otelDetails, container: containerDetails, serverless: serverlessDetails, cloud: cloudDetails, diff --git a/x-pack/plugins/apm/server/routes/services/get_service_metadata_icons.ts b/x-pack/plugins/apm/server/routes/services/get_service_metadata_icons.ts index 4890648a27ad3..37214b362e125 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_metadata_icons.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_metadata_icons.ts @@ -16,6 +16,9 @@ import { SERVICE_NAME, KUBERNETES_POD_NAME, HOST_OS_PLATFORM, + LABEL_TELEMETRY_AUTO_VERSION, + AGENT_VERSION, + SERVICE_FRAMEWORK_NAME, } from '../../../common/es_fields/apm'; import { ContainerType } from '../../../common/service_metadata'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; @@ -44,6 +47,9 @@ export const should = [ { exists: { field: CLOUD_PROVIDER } }, { exists: { field: HOST_OS_PLATFORM } }, { exists: { field: AGENT_NAME } }, + { exists: { field: AGENT_VERSION } }, + { exists: { field: SERVICE_FRAMEWORK_NAME } }, + { exists: { field: LABEL_TELEMETRY_AUTO_VERSION } }, ]; export async function getServiceMetadataIcons({ diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index 2b31be70ad6ba..9cddb4d3577f6 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -240,22 +240,13 @@ const serviceMetadataDetailsRoute = createApmServerRoute({ handler: async (resources): Promise => { const apmEventClient = await getApmEventClient(resources); const infraMetricsClient = createInfraMetricsClient(resources); - const { params, config } = resources; + const { params } = resources; const { serviceName } = params.path; const { start, end } = params.query; - const searchAggregatedTransactions = await getSearchTransactionsEvents({ - apmEventClient, - config, - start, - end, - kuery: '', - }); - const serviceMetadataDetails = await getServiceMetadataDetails({ serviceName, apmEventClient, - searchAggregatedTransactions, start, end, }); diff --git a/x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts b/x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts index f4ea0629f848b..fedc98151ce40 100644 --- a/x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts +++ b/x-pack/plugins/apm/server/test_helpers/create_apm_users/authentication.ts @@ -16,6 +16,7 @@ export enum ApmUsername { apmManageOwnAgentKeys = 'apm_manage_own_agent_keys', apmManageOwnAndCreateAgentKeys = 'apm_manage_own_and_create_agent_keys', apmMonitorClusterAndIndices = 'apm_monitor_cluster_and_indices', + apmManageServiceAccount = 'apm_manage_service_account', } export enum ApmCustomRolename { @@ -24,6 +25,7 @@ export enum ApmCustomRolename { apmManageOwnAgentKeys = 'apm_manage_own_agent_keys', apmManageOwnAndCreateAgentKeys = 'apm_manage_own_and_create_agent_keys', apmMonitorClusterAndIndices = 'apm_monitor_cluster_and_indices', + apmManageServiceAccount = 'apm_manage_service_account', } export const customRoles = { @@ -88,6 +90,11 @@ export const customRoles = { cluster: ['monitor'], }, }, + [ApmCustomRolename.apmManageServiceAccount]: { + elasticsearch: { + cluster: ['manage_service_account'], + }, + }, }; export const users: Record< @@ -123,6 +130,10 @@ export const users: Record< builtInRoleNames: ['viewer'], customRoleNames: [ApmCustomRolename.apmMonitorClusterAndIndices], }, + [ApmUsername.apmManageServiceAccount]: { + builtInRoleNames: ['editor'], + customRoleNames: [ApmCustomRolename.apmManageServiceAccount], + }, }; export const APM_TEST_PASSWORD = 'changeme'; diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index e049bed128afa..cfa3bc4ac660e 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -83,6 +83,7 @@ "@kbn/alerts-as-data-utils", "@kbn/exploratory-view-plugin", "@kbn/logging-mocks", + "@kbn/chart-icons", "@kbn/observability-shared-plugin", ], "exclude": [ diff --git a/x-pack/plugins/canvas/server/collectors/collector.ts b/x-pack/plugins/canvas/server/collectors/collector.ts index f98bd319a8ac0..31919bc663c1e 100644 --- a/x-pack/plugins/canvas/server/collectors/collector.ts +++ b/x-pack/plugins/canvas/server/collectors/collector.ts @@ -29,7 +29,7 @@ const collectors: TelemetryCollector[] = [workpadCollector, customElementCollect */ export function registerCanvasUsageCollector( usageCollection: UsageCollectionSetup | undefined, - kibanaIndex: string + getIndexForType: (type: string) => Promise ) { if (!usageCollection) { return; @@ -40,7 +40,7 @@ export function registerCanvasUsageCollector( isReady: () => true, fetch: async ({ esClient }: CollectorFetchContext) => { const collectorResults = await Promise.all( - collectors.map((collector) => collector(kibanaIndex, esClient)) + collectors.map((collector) => collector(getIndexForType, esClient)) ); return collectorResults.reduce((reduction, usage) => { diff --git a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts index 66238abfe89df..51aa616f4e5a7 100644 --- a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts @@ -141,12 +141,13 @@ export function summarizeCustomElements( } const customElementCollector: TelemetryCollector = async function customElementCollector( - kibanaIndex, + getIndexForType, esClient ) { + const index = await getIndexForType(CUSTOM_ELEMENT_TYPE); const customElementParams = { size: 10000, - index: kibanaIndex, + index, ignore_unavailable: true, filter_path: [`hits.hits._source.${CUSTOM_ELEMENT_TYPE}.content`], body: { query: { bool: { filter: { term: { type: CUSTOM_ELEMENT_TYPE } } } } }, diff --git a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts index 6ab5f62f8f5d2..252d05bfc4f7e 100644 --- a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts @@ -377,10 +377,11 @@ export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetr }; } -const workpadCollector: TelemetryCollector = async function (kibanaIndex, esClient) { +const workpadCollector: TelemetryCollector = async function (getIndexForType, esClient) { + const index = await getIndexForType(CANVAS_TYPE); const searchParams = { size: 10000, // elasticsearch index.max_result_window default value - index: kibanaIndex, + index, ignore_unavailable: true, filter_path: ['hits.hits._source.canvas-workpad', '-hits.hits._source.canvas-workpad.assets'], body: { query: { bool: { filter: { term: { type: CANVAS_TYPE } } } } }, diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index ec5cb4969be19..8c9748cc47887 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -90,9 +90,11 @@ export class CanvasPlugin implements Plugin { plugins.home.sampleData.addAppLinksToSampleDataset ); - // we need the kibana index for the Canvas usage collector - const kibanaIndex = coreSetup.savedObjects.getKibanaIndex(); - registerCanvasUsageCollector(plugins.usageCollection, kibanaIndex); + const getIndexForType = (type: string) => + coreSetup + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); + registerCanvasUsageCollector(plugins.usageCollection, getIndexForType); } public start(coreStart: CoreStart) { diff --git a/x-pack/plugins/canvas/server/saved_objects/custom_element.ts b/x-pack/plugins/canvas/server/saved_objects/custom_element.ts index c9f9ea8453e5f..c4a25f1d2e385 100644 --- a/x-pack/plugins/canvas/server/saved_objects/custom_element.ts +++ b/x-pack/plugins/canvas/server/saved_objects/custom_element.ts @@ -5,12 +5,14 @@ * 2.0. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { CUSTOM_ELEMENT_TYPE } from '../../common/lib/constants'; import { customElementMigrationsFactory, CanvasSavedObjectTypeMigrationsDeps } from './migrations'; export const customElementType = (deps: CanvasSavedObjectTypeMigrationsDeps): SavedObjectsType => ({ name: CUSTOM_ELEMENT_TYPE, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/canvas/server/saved_objects/workpad.ts b/x-pack/plugins/canvas/server/saved_objects/workpad.ts index 6c392aaaa62b1..dd79ebec576bc 100644 --- a/x-pack/plugins/canvas/server/saved_objects/workpad.ts +++ b/x-pack/plugins/canvas/server/saved_objects/workpad.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsType } from '@kbn/core/server'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CANVAS_TYPE } from '../../common/lib/constants'; import { workpadMigrationsFactory } from './migrations'; import type { CanvasSavedObjectTypeMigrationsDeps } from './migrations'; @@ -14,6 +15,7 @@ export const workpadTypeFactory = ( deps: CanvasSavedObjectTypeMigrationsDeps ): SavedObjectsType => ({ name: CANVAS_TYPE, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts b/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts index 224af9bed6759..2bf84d3d983c4 100644 --- a/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts +++ b/x-pack/plugins/canvas/server/saved_objects/workpad_template.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { TEMPLATE_TYPE } from '../../common/lib/constants'; import { @@ -16,6 +17,7 @@ export const workpadTemplateType = ( deps: CanvasSavedObjectTypeMigrationsDeps ): SavedObjectsType => ({ name: TEMPLATE_TYPE, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', mappings: { diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index 228ae010ddf43..6bd18423c138c 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -80,6 +80,7 @@ "@kbn/shared-ux-button-toolbar", "@kbn/saved-objects-finder-plugin", "@kbn/saved-objects-management-plugin", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/canvas/types/telemetry.ts b/x-pack/plugins/canvas/types/telemetry.ts index 2f9f6d48ecda7..24c8126d29de6 100644 --- a/x-pack/plugins/canvas/types/telemetry.ts +++ b/x-pack/plugins/canvas/types/telemetry.ts @@ -8,11 +8,10 @@ import type { ElasticsearchClient } from '@kbn/core/server'; /** - Function for collecting information about canvas usage + Function for collecting information about canvas usage */ export type TelemetryCollector = ( - /** The server instance */ - kibanaIndex: string, + getIndexForType: (type: string) => Promise, /** Function for calling elasticsearch */ esClient: ElasticsearchClient ) => Record; diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index f247945c7c700..d33cf6efd4fbf 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -189,7 +189,7 @@ describe('CaseViewPage', () => { expect(result.getAllByText(data.createdBy.fullName!)[0]).toBeInTheDocument(); expect( - within(result.getByTestId('description-action')).getByTestId('user-action-markdown') + within(result.getByTestId('description')).getByTestId('scrollable-markdown') ).toHaveTextContent(data.description); expect(result.getByTestId('case-view-status-action-button')).toHaveTextContent( @@ -604,15 +604,15 @@ describe('CaseViewPage', () => { }); describe('description', () => { - it('renders the descriptions user correctly', async () => { + it('renders the description correctly', async () => { appMockRenderer.render(); - const description = within(screen.getByTestId('description-action')); + const description = within(screen.getByTestId('description')); - expect(await description.findByText('Leslie Knope')).toBeInTheDocument(); + expect(await description.findByText(caseData.description)).toBeInTheDocument(); }); - it('should display description isLoading', async () => { + it('should display description when case is loading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -622,8 +622,7 @@ describe('CaseViewPage', () => { appMockRenderer.render(); await waitFor(() => { - expect(screen.getByTestId('description-loading')).toBeInTheDocument(); - expect(screen.queryByTestId('description-action')).not.toBeInTheDocument(); + expect(screen.getByTestId('description')).toBeInTheDocument(); }); }); @@ -636,11 +635,11 @@ describe('CaseViewPage', () => { userEvent.type(await screen.findByTestId('euiMarkdownEditorTextArea'), newComment); - userEvent.click(await screen.findByTestId('editable-description-edit-icon')); + userEvent.click(await screen.findByTestId('description-edit-icon')); userEvent.type(screen.getAllByTestId('euiMarkdownEditorTextArea')[0], 'Edited!'); - userEvent.click(screen.getByTestId('user-action-save-markdown')); + userEvent.click(screen.getByTestId('editable-save-markdown')); expect(await screen.findByTestId('euiMarkdownEditorTextArea')).toHaveTextContent( newComment diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx index aa28340c55308..7386d8bcd7ea1 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -493,17 +493,38 @@ describe.skip('Case View Page activity tab', () => { }); describe('User actions', () => { - it('renders the descriptions user correctly', async () => { + it('renders the description correctly', async () => { appMockRender = createAppMockRenderer(); const result = appMockRender.render(); - const description = within(result.getByTestId('description-action')); + const description = within(result.getByTestId('description')); await waitFor(() => { - expect(description.getByText('Leslie Knope')).toBeInTheDocument(); + expect(description.getByText(caseData.description)).toBeInTheDocument(); }); }); + it('renders edit description user action correctly', async () => { + useFindCaseUserActionsMock.mockReturnValue({ + ...defaultUseFindCaseUserActions, + data: { + userActions: [ + getUserAction('description', 'create'), + getUserAction('description', 'update'), + ], + }, + }); + + appMockRender = createAppMockRenderer(); + const result = appMockRender.render(); + + const userActions = within(result.getAllByTestId('user-actions-list')[1]); + + expect( + userActions.getByTestId('description-update-action-description-update') + ).toBeInTheDocument(); + }); + it('renders the unassigned users correctly', async () => { useFindCaseUserActionsMock.mockReturnValue({ ...defaultUseFindCaseUserActions, diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx index b178fe37bb450..c4f387c617417 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx @@ -37,7 +37,7 @@ import { convertToCaseUserWithProfileInfo } from '../../user_profiles/user_conve import type { UserActivityParams } from '../../user_actions_activity_bar/types'; import { CASE_VIEW_PAGE_TABS } from '../../../../common/types'; import { CaseViewTabs } from '../case_view_tabs'; -import { DescriptionWrapper } from '../../description/description_wrapper'; +import { Description } from '../../description'; const buildUserProfilesMap = (users?: CaseUsers): Map => { const userProfiles = new Map(); @@ -210,10 +210,9 @@ export const CaseViewActivity = ({ <> - diff --git a/x-pack/plugins/cases/public/components/case_view/translations.ts b/x-pack/plugins/cases/public/components/case_view/translations.ts index 8fc80c1a0aba3..d0be88f729a69 100644 --- a/x-pack/plugins/cases/public/components/case_view/translations.ts +++ b/x-pack/plugins/cases/public/components/case_view/translations.ts @@ -76,6 +76,14 @@ export const EDIT_DESCRIPTION = i18n.translate('xpack.cases.caseView.edit.descri defaultMessage: 'Edit description', }); +export const COLLAPSE_DESCRIPTION = i18n.translate('xpack.cases.caseView.description.collapse', { + defaultMessage: 'Collapse description', +}); + +export const EXPAND_DESCRIPTION = i18n.translate('xpack.cases.caseView.description.expand', { + defaultMessage: 'Expand description', +}); + export const QUOTE = i18n.translate('xpack.cases.caseView.edit.quote', { defaultMessage: 'Quote', }); diff --git a/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx b/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx deleted file mode 100644 index 40fcf91ffe1fa..0000000000000 --- a/x-pack/plugins/cases/public/components/description/description_wrapper.test.tsx +++ /dev/null @@ -1,88 +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 { waitFor, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -// eslint-disable-next-line @kbn/eslint/module_migration -import routeData from 'react-router'; - -import { useUpdateComment } from '../../containers/use_update_comment'; -import { basicCase } from '../../containers/mock'; -import type { AppMockRenderer } from '../../common/mock'; -import { createAppMockRenderer } from '../../common/mock'; -import { DescriptionWrapper } from './description_wrapper'; -import { waitForComponentToUpdate } from '../../common/test_utils'; - -const onUpdateField = jest.fn(); - -const defaultProps = { - data: basicCase, - onUpdateField, - isLoadingDescription: false, - userProfiles: new Map(), -}; - -jest.mock('../../containers/use_update_comment'); -jest.mock('../../common/lib/kibana'); - -const useUpdateCommentMock = useUpdateComment as jest.Mock; -const patchComment = jest.fn(); - -// FLAKY: -describe.skip(`DescriptionWrapper`, () => { - const sampleData = { - content: 'what a great comment update', - }; - let appMockRender: AppMockRenderer; - - beforeEach(() => { - jest.clearAllMocks(); - useUpdateCommentMock.mockReturnValue({ - isLoadingIds: [], - patchComment, - }); - - jest.spyOn(routeData, 'useParams').mockReturnValue({ detailName: 'case-id' }); - appMockRender = createAppMockRenderer(); - }); - - it('renders correctly', () => { - appMockRender.render(); - - expect(screen.getByTestId('description-action')).toBeInTheDocument(); - }); - - it('renders loading state', () => { - appMockRender.render(); - - expect(screen.getByTestId('description-loading')).toBeInTheDocument(); - }); - - it('calls update description when description markdown is saved', async () => { - const newData = { - content: 'what a great comment update', - }; - - appMockRender.render(); - - userEvent.click(screen.getByTestId('editable-description-edit-icon')); - - userEvent.clear(screen.getAllByTestId('euiMarkdownEditorTextArea')[0]); - - userEvent.type(screen.getAllByTestId('euiMarkdownEditorTextArea')[0], newData.content); - - userEvent.click(screen.getByTestId('user-action-save-markdown')); - - await waitForComponentToUpdate(); - - await waitFor(() => { - expect(screen.queryByTestId('user-action-markdown-form')).not.toBeInTheDocument(); - expect(onUpdateField).toBeCalledWith({ key: 'description', value: sampleData.content }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/description/description_wrapper.tsx b/x-pack/plugins/cases/public/components/description/description_wrapper.tsx deleted file mode 100644 index 77b29abfa0c0d..0000000000000 --- a/x-pack/plugins/cases/public/components/description/description_wrapper.tsx +++ /dev/null @@ -1,106 +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 { EuiCommentProps } from '@elastic/eui'; -import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; -import { EuiCommentList, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; - -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import type { Case } from '../../containers/types'; -import type { OnUpdateFields } from '../case_view/types'; -import { getDescriptionUserAction } from '../user_actions/description'; -import { useUserActionsHandler } from '../user_actions/use_user_actions_handler'; -import { useCasesContext } from '../cases_context/use_cases_context'; - -interface DescriptionWrapperProps { - data: Case; - isLoadingDescription: boolean; - userProfiles: Map; - onUpdateField: ({ key, value, onSuccess, onError }: OnUpdateFields) => void; -} - -const MyEuiCommentList = styled(EuiCommentList)` - & .euiComment > [class*='euiTimelineItemIcon-top'] { - display: none; - } - - & .draftFooter { - & .euiCommentEvent__body { - padding: 0; - } - } - - & .euiComment.isEdit { - & .euiCommentEvent { - border: none; - box-shadow: none; - } - - & .euiCommentEvent__body { - padding: 0; - } - - & .euiCommentEvent__header { - display: none; - } - } -`; - -export const DescriptionWrapper = React.memo( - ({ - data: caseData, - isLoadingDescription, - userProfiles, - onUpdateField, - }: DescriptionWrapperProps) => { - const { appId } = useCasesContext(); - - const { commentRefs, manageMarkdownEditIds, handleManageMarkdownEditId } = - useUserActionsHandler(); - - const descriptionCommentListObj: EuiCommentProps = useMemo( - () => - getDescriptionUserAction({ - appId, - caseData, - commentRefs, - userProfiles, - manageMarkdownEditIds, - isLoadingDescription, - onUpdateField, - handleManageMarkdownEditId, - }), - [ - appId, - caseData, - commentRefs, - manageMarkdownEditIds, - isLoadingDescription, - userProfiles, - onUpdateField, - handleManageMarkdownEditId, - ] - ); - - return isLoadingDescription ? ( - - - - - - ) : ( - - ); - } -); - -DescriptionWrapper.displayName = 'DescriptionWrapper'; diff --git a/x-pack/plugins/cases/public/components/description/index.test.tsx b/x-pack/plugins/cases/public/components/description/index.test.tsx new file mode 100644 index 0000000000000..bf92733b4526a --- /dev/null +++ b/x-pack/plugins/cases/public/components/description/index.test.tsx @@ -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 React from 'react'; +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { basicCase } from '../../containers/mock'; + +import { Description } from '.'; +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer, noUpdateCasesPermissions, TestProviders } from '../../common/mock'; + +jest.mock('../../common/lib/kibana'); +jest.mock('../../common/navigation/hooks'); + +const defaultProps = { + appId: 'testAppId', + caseData: { + ...basicCase, + }, + isLoadingDescription: false, +}; + +describe('Description', () => { + const onUpdateField = jest.fn(); + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); + }); + + it('renders description correctly', async () => { + appMockRender.render(); + + expect(screen.getByTestId('description')).toBeInTheDocument(); + expect(screen.getByText('Security banana Issue')).toBeInTheDocument(); + }); + + it('hides and shows the description correctly when collapse button clicked', async () => { + const res = appMockRender.render( + + ); + + userEvent.click(res.getByTestId('description-collapse-icon')); + + await waitFor(() => { + expect(screen.queryByText('Security banana Issue')).not.toBeInTheDocument(); + }); + + userEvent.click(res.getByTestId('description-collapse-icon')); + + await waitFor(() => { + expect(screen.getByText('Security banana Issue')).toBeInTheDocument(); + }); + }); + + it('shows textarea on edit click', async () => { + const res = appMockRender.render( + + ); + + userEvent.click(res.getByTestId('description-edit-icon')); + + await waitFor(() => { + expect(screen.getByTestId('euiMarkdownEditorTextArea')).toBeInTheDocument(); + }); + }); + + it('edits the description correctly when saved', async () => { + const editedDescription = 'New updated description'; + const res = appMockRender.render( + + ); + + userEvent.click(res.getByTestId('description-edit-icon')); + + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), editedDescription); + + userEvent.click(screen.getByTestId('editable-save-markdown')); + + await waitFor(() => { + expect(onUpdateField).toHaveBeenCalledWith({ key: 'description', value: editedDescription }); + }); + }); + + it('keeps the old description correctly when canceled', async () => { + const editedDescription = 'New updated description'; + const res = appMockRender.render( + + ); + + userEvent.click(res.getByTestId('description-edit-icon')); + + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), editedDescription); + + userEvent.click(screen.getByTestId('editable-cancel-markdown')); + + await waitFor(() => { + expect(onUpdateField).not.toHaveBeenCalled(); + expect(screen.getByText('Security banana Issue')).toBeInTheDocument(); + }); + }); + + it('should hide the edit button when the user does not have update permissions', () => { + appMockRender.render( + + + + ); + + expect(screen.getByText('Security banana Issue')).toBeInTheDocument(); + expect(screen.queryByTestId('description-edit-icon')).not.toBeInTheDocument(); + }); + + describe('draft message', () => { + const draftStorageKey = `cases.testAppId.basic-case-id.description.markdownEditor`; + + beforeEach(() => { + sessionStorage.setItem(draftStorageKey, 'value set in storage'); + }); + + it('should show unsaved draft message correctly', async () => { + appMockRender.render(); + + expect(screen.getByTestId('description-unsaved-draft')).toBeInTheDocument(); + }); + + it('should not show unsaved draft message when loading', async () => { + appMockRender.render( + + ); + + expect(screen.queryByTestId('description-unsaved-draft')).not.toBeInTheDocument(); + }); + + it('should not show unsaved draft message when description and storage value are same', async () => { + const props = { + ...defaultProps, + caseData: { ...defaultProps.caseData, description: 'value set in storage' }, + }; + + appMockRender.render(); + + expect(screen.queryByTestId('description-unsaved-draft')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/description/index.tsx b/x-pack/plugins/cases/public/components/description/index.tsx new file mode 100644 index 0000000000000..6ebbf8edd6f45 --- /dev/null +++ b/x-pack/plugins/cases/public/components/description/index.tsx @@ -0,0 +1,203 @@ +/* + * 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, useRef, useState } from 'react'; +import styled from 'styled-components'; +import { css } from '@emotion/react'; +import { + EuiButtonIcon, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiText, + useEuiTheme, +} from '@elastic/eui'; + +import { getMarkdownEditorStorageKey } from '../markdown_editor/utils'; +import * as i18n from '../user_actions/translations'; +import { useCasesContext } from '../cases_context/use_cases_context'; +import { useLensDraftComment } from '../markdown_editor/plugins/lens/use_lens_draft_comment'; +import { EditableMarkdown, ScrollableMarkdown } from '../markdown_editor'; +import type { Case } from '../../containers/types'; +import type { OnUpdateFields } from '../case_view/types'; +import { schema } from './schema'; + +const DESCRIPTION_ID = 'description'; +export interface DescriptionProps { + caseData: Case; + isLoadingDescription: boolean; + onUpdateField: ({ key, value, onSuccess, onError }: OnUpdateFields) => void; +} + +const DescriptionFooter = styled(EuiFlexItem)` + ${({ theme }) => ` + border-top: ${theme.eui.euiBorderThin}; + padding: ${theme.eui.euiSizeS}; + `} +`; + +const Panel = styled(EuiPanel)` + padding: 0; +`; + +const Header = styled(EuiFlexGroup)` + ${({ theme }) => ` + display: flex; + padding: ${theme.eui.euiSizeS}; + align-items: center; + `} +`; + +const Body = styled(EuiFlexItem)` + ${({ theme }) => ` + padding: ${theme.eui.euiSize}; + padding-top: 0; + + > div { + padding: 0; + } + `} +`; + +const getDraftDescription = ( + applicationId = '', + caseId: string, + commentId: string +): string | null => { + const draftStorageKey = getMarkdownEditorStorageKey(applicationId, caseId, commentId); + + return sessionStorage.getItem(draftStorageKey); +}; + +export const Description = ({ + caseData, + onUpdateField, + isLoadingDescription, +}: DescriptionProps) => { + const [isCollapsed, setIsCollapsed] = useState(false); + const [isEditable, setIsEditable] = useState(false); + + const descriptionRef = useRef(null); + const { euiTheme } = useEuiTheme(); + const { appId, permissions } = useCasesContext(); + + const { + clearDraftComment: clearLensDraftComment, + draftComment: lensDraftComment, + hasIncomingLensState, + } = useLensDraftComment(); + + const handleOnChangeEditable = useCallback(() => { + clearLensDraftComment(); + setIsEditable(false); + }, [setIsEditable, clearLensDraftComment]); + + const handleOnSave = useCallback( + (content: string) => { + onUpdateField({ key: DESCRIPTION_ID, value: content }); + setIsEditable(false); + }, + [onUpdateField, setIsEditable] + ); + + const toggleCollapse = () => setIsCollapsed((oldValue: boolean) => !oldValue); + + const draftDescription = getDraftDescription(appId, caseData.id, DESCRIPTION_ID); + + if ( + hasIncomingLensState && + lensDraftComment !== null && + lensDraftComment?.commentId === DESCRIPTION_ID && + !isEditable + ) { + setIsEditable(true); + } + + const hasUnsavedChanges = + draftDescription && draftDescription !== caseData.description && !isLoadingDescription; + + return isEditable ? ( + + ) : ( + + + +
    + + + {i18n.DESCRIPTION} + + + + + {permissions.update ? ( + setIsEditable(true)} + data-test-subj="description-edit-icon" + /> + ) : null} + + + + + +
    +
    + {!isCollapsed ? ( + + + + ) : null} + {hasUnsavedChanges ? ( + + + {i18n.UNSAVED_DRAFT_DESCRIPTION} + + + ) : null} +
    +
    + ); +}; + +Description.displayName = 'Description'; diff --git a/x-pack/plugins/cases/public/components/description/schema.ts b/x-pack/plugins/cases/public/components/description/schema.ts new file mode 100644 index 0000000000000..8c47b700adeb5 --- /dev/null +++ b/x-pack/plugins/cases/public/components/description/schema.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 type { FormSchema } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { FIELD_TYPES } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import * as i18n from '../../common/translations'; + +const { emptyField } = fieldValidators; +export interface Content { + content: string; +} +export const schema: FormSchema = { + content: { + type: FIELD_TYPES.TEXTAREA, + validations: [ + { + validator: emptyField(i18n.REQUIRED_FIELD), + }, + ], + }, +}; diff --git a/x-pack/plugins/cases/public/components/files/file_preview.test.tsx b/x-pack/plugins/cases/public/components/files/file_preview.test.tsx index b02df3a82228f..c1d7fe20bee48 100644 --- a/x-pack/plugins/cases/public/components/files/file_preview.test.tsx +++ b/x-pack/plugins/cases/public/components/files/file_preview.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import type { AppMockRenderer } from '../../common/mock'; @@ -35,4 +36,23 @@ describe('FilePreview', () => { expect(await screen.findByTestId('cases-files-image-preview')).toBeInTheDocument(); }); + + it('pressing escape calls closePreview', async () => { + const closePreview = jest.fn(); + + appMockRender.render(); + + await waitFor(() => + expect(appMockRender.getFilesClient().getDownloadHref).toHaveBeenCalledWith({ + id: basicFileMock.id, + fileKind: constructFileKindIdByOwner(mockedTestProvidersOwner[0]), + }) + ); + + expect(await screen.findByTestId('cases-files-image-preview')).toBeInTheDocument(); + + userEvent.keyboard('{esc}'); + + await waitFor(() => expect(closePreview).toHaveBeenCalled()); + }); }); diff --git a/x-pack/plugins/cases/public/components/files/file_preview.tsx b/x-pack/plugins/cases/public/components/files/file_preview.tsx index 1bb91c5b53ff7..09cee1320ec2a 100644 --- a/x-pack/plugins/cases/public/components/files/file_preview.tsx +++ b/x-pack/plugins/cases/public/components/files/file_preview.tsx @@ -4,12 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import styled from 'styled-components'; import type { FileJSON } from '@kbn/shared-ux-file-types'; -import { EuiOverlayMask, EuiFocusTrap, EuiImage } from '@elastic/eui'; +import { EuiOverlayMask, EuiFocusTrap, EuiImage, keys } from '@elastic/eui'; import { useFilesContext } from '@kbn/shared-ux-file-context'; import type { Owner } from '../../../common/constants/types'; @@ -36,6 +36,20 @@ export const FilePreview = ({ closePreview, selectedFile }: FilePreviewProps) => const { client: filesClient } = useFilesContext(); const { owner } = useCasesContext(); + useEffect(() => { + const keyboardListener = (event: KeyboardEvent) => { + if (event.key === keys.ESCAPE || event.code === 'Escape') { + closePreview(); + } + }; + + window.addEventListener('keyup', keyboardListener); + + return () => { + window.removeEventListener('keyup', keyboardListener); + }; + }, [closePreview]); + return ( diff --git a/x-pack/plugins/cases/public/components/user_actions/markdown_form_footer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx similarity index 76% rename from x-pack/plugins/cases/public/components/user_actions/markdown_form_footer.tsx rename to x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx index d2c5f692d396b..86772136c18d0 100644 --- a/x-pack/plugins/cases/public/components/user_actions/markdown_form_footer.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx @@ -12,12 +12,12 @@ import { useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib import * as i18n from '../case_view/translations'; -interface UserActionMarkdownFooterProps { +interface EditableMarkdownFooterProps { handleSaveAction: () => Promise; handleCancelAction: () => void; } -const UserActionMarkdownFooterComponent: React.FC = ({ +const EditableMarkdownFooterComponent: React.FC = ({ handleSaveAction, handleCancelAction, }) => { @@ -27,7 +27,7 @@ const UserActionMarkdownFooterComponent: React.FC ); }; -UserActionMarkdownFooterComponent.displayName = 'UserActionMarkdownFooterComponent'; +EditableMarkdownFooterComponent.displayName = 'EditableMarkdownFooterComponent'; -export const UserActionMarkdownFooter = React.memo(UserActionMarkdownFooterComponent); +export const EditableMarkdownFooter = React.memo(EditableMarkdownFooterComponent); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx new file mode 100644 index 0000000000000..59d424fdd82c8 --- /dev/null +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -0,0 +1,220 @@ +/* + * 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 { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { waitFor, fireEvent, screen, render, act } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { EditableMarkdown } from '.'; +import { TestProviders } from '../../common/mock'; +import type { Content } from '../user_actions/schema'; +import { schema } from '../user_actions/schema'; + +jest.mock('../../common/lib/kibana'); + +const onChangeEditable = jest.fn(); +const onSaveContent = jest.fn(); + +const newValue = 'Hello from Tehas'; +const emptyValue = ''; +const hyperlink = `[hyperlink](http://elastic.co)`; +const draftStorageKey = `cases.testAppId.caseId.markdown-id.markdownEditor`; +const content = `A link to a timeline ${hyperlink}`; + +const editorRef: React.MutableRefObject = { current: null }; +const defaultProps = { + content, + id: 'markdown-id', + caseId: 'caseId', + isEditable: true, + draftStorageKey, + onChangeEditable, + onSaveContent, + fieldName: 'content', + formSchema: schema, + editorRef, +}; + +describe('EditableMarkdown', () => { + const MockHookWrapperComponent: React.FC<{ testProviderProps?: unknown }> = ({ + children, + testProviderProps = {}, + }) => { + const { form } = useForm({ + defaultValue: { content }, + options: { stripEmptyFields: false }, + schema, + }); + + return ( + +
    {children}
    +
    + ); + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + sessionStorage.removeItem(draftStorageKey); + }); + + it('Save button click calls onSaveContent and onChangeEditable when text area value changed', async () => { + render( + + + + ); + + fireEvent.change(screen.getByTestId('euiMarkdownEditorTextArea'), { + target: { value: newValue }, + }); + + userEvent.click(screen.getByTestId('editable-save-markdown')); + + await waitFor(() => { + expect(onSaveContent).toHaveBeenCalledWith(newValue); + expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); + }); + }); + + it('Does not call onSaveContent if no change from current text', async () => { + render( + + + + ); + + userEvent.click(screen.getByTestId('editable-save-markdown')); + + await waitFor(() => { + expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); + }); + expect(onSaveContent).not.toHaveBeenCalled(); + }); + + it('Save button disabled if current text is empty', async () => { + render( + + + + ); + + fireEvent.change(screen.getByTestId('euiMarkdownEditorTextArea'), { value: emptyValue }); + + await waitFor(() => { + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); + }); + + it('Cancel button click calls only onChangeEditable', async () => { + render( + + + + ); + + userEvent.click(screen.getByTestId('editable-cancel-markdown')); + + await waitFor(() => { + expect(onSaveContent).not.toHaveBeenCalled(); + expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); + }); + }); + + describe('draft comment ', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + sessionStorage.removeItem(draftStorageKey); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('Save button click clears session storage', async () => { + const result = render( + + + + ); + + fireEvent.change(result.getByTestId('euiMarkdownEditorTextArea'), { + target: { value: newValue }, + }); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(sessionStorage.getItem(draftStorageKey)).toBe(newValue); + + fireEvent.click(result.getByTestId(`editable-save-markdown`)); + + await waitFor(() => { + expect(onSaveContent).toHaveBeenCalledWith(newValue); + expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); + expect(sessionStorage.getItem(draftStorageKey)).toBe(null); + }); + }); + + it('Cancel button click clears session storage', async () => { + const result = render( + + + + ); + + expect(sessionStorage.getItem(draftStorageKey)).toBe(''); + + fireEvent.change(result.getByTestId('euiMarkdownEditorTextArea'), { + target: { value: newValue }, + }); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + await waitFor(() => { + expect(sessionStorage.getItem(draftStorageKey)).toBe(newValue); + }); + + fireEvent.click(result.getByTestId('editable-cancel-markdown')); + + await waitFor(() => { + expect(sessionStorage.getItem(draftStorageKey)).toBe(null); + }); + }); + + describe('existing storage key', () => { + beforeEach(() => { + sessionStorage.setItem(draftStorageKey, 'value set in storage'); + }); + + it('should have session storage value same as draft comment', async () => { + const result = render( + + + + ); + + expect(result.getByText('value set in storage')).toBeInTheDocument(); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx new file mode 100644 index 0000000000000..1706e30fcc8f4 --- /dev/null +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx @@ -0,0 +1,105 @@ +/* + * 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, { forwardRef, useCallback, useImperativeHandle } from 'react'; + +import type { FormSchema } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { Form, UseField, useForm } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { MarkdownEditorForm } from '.'; +import { removeItemFromSessionStorage } from '../utils'; +import { useCasesContext } from '../cases_context/use_cases_context'; +import { getMarkdownEditorStorageKey } from './utils'; +import { EditableMarkdownFooter } from './editable_markdown_footer'; + +export interface EditableMarkdownRefObject { + setComment: (newComment: string) => void; +} +interface EditableMarkdownRendererProps { + content: string; + id: string; + caseId: string; + fieldName: string; + onChangeEditable: (id: string) => void; + onSaveContent: (content: string) => void; + editorRef: React.MutableRefObject; + formSchema: FormSchema<{ content: string }> | undefined; +} + +const EditableMarkDownRenderer = forwardRef< + EditableMarkdownRefObject, + EditableMarkdownRendererProps +>( + ( + { id, content, caseId, fieldName, onChangeEditable, onSaveContent, editorRef, formSchema }, + ref + ) => { + const { appId } = useCasesContext(); + const draftStorageKey = getMarkdownEditorStorageKey(appId, caseId, id); + const initialState = { content }; + + const { form } = useForm({ + defaultValue: initialState, + options: { stripEmptyFields: false }, + schema: formSchema, + }); + const { submit, setFieldValue } = form; + + const setComment = useCallback( + (newComment) => { + setFieldValue(fieldName, newComment); + }, + [setFieldValue, fieldName] + ); + + useImperativeHandle(ref, () => ({ + setComment, + editor: editorRef.current, + })); + + const handleCancelAction = useCallback(() => { + onChangeEditable(id); + removeItemFromSessionStorage(draftStorageKey); + }, [id, onChangeEditable, draftStorageKey]); + + const handleSaveAction = useCallback(async () => { + const { isValid, data } = await submit(); + + if (isValid && data.content !== content) { + onSaveContent(data.content); + } + onChangeEditable(id); + removeItemFromSessionStorage(draftStorageKey); + }, [content, id, onChangeEditable, onSaveContent, submit, draftStorageKey]); + + return ( +
    + + ), + initialValue: content, + }} + /> + + ); + } +); + +EditableMarkDownRenderer.displayName = 'EditableMarkDownRenderer'; + +export const EditableMarkdown = React.memo(EditableMarkDownRenderer); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/index.tsx b/x-pack/plugins/cases/public/components/markdown_editor/index.tsx index e77a36d48f7d9..214779263f773 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/index.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/index.tsx @@ -9,3 +9,5 @@ export * from './types'; export * from './renderer'; export * from './editor'; export * from './eui_form'; +export * from './scrollable_markdown_renderer'; +export * from './editable_markdown_renderer'; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/scrollable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/scrollable_markdown_renderer.test.tsx new file mode 100644 index 0000000000000..05ea034e776a3 --- /dev/null +++ b/x-pack/plugins/cases/public/components/markdown_editor/scrollable_markdown_renderer.test.tsx @@ -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 React from 'react'; +import { screen } from '@testing-library/react'; +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; + +import { ScrollableMarkdown } from '.'; + +const content = 'This is sample content'; + +describe('ScrollableMarkdown', () => { + let appMockRenderer: AppMockRenderer; + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + it('renders correctly', () => { + appMockRenderer.render(); + expect(screen.getByTestId('scrollable-markdown')).toBeInTheDocument(); + expect(screen.getByText(content)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/scrollable_markdown_renderer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/scrollable_markdown_renderer.tsx new file mode 100644 index 0000000000000..dd5ab2ec8241a --- /dev/null +++ b/x-pack/plugins/cases/public/components/markdown_editor/scrollable_markdown_renderer.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 React from 'react'; +import styled from 'styled-components'; + +import { MarkdownRenderer } from './renderer'; + +export const ContentWrapper = styled.div` + padding: ${({ theme }) => `${theme.eui.euiSizeM} ${theme.eui.euiSizeL}`}; + text-overflow: ellipsis; + word-break: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; +`; + +const ScrollableMarkdownRenderer = ({ content }: { content: string }) => { + return ( + + {content} + + ); +}; + +ScrollableMarkdownRenderer.displayName = 'ScrollableMarkdownRenderer'; + +export const ScrollableMarkdown = React.memo(ScrollableMarkdownRenderer); diff --git a/x-pack/plugins/cases/public/components/user_actions/comment/actions.tsx b/x-pack/plugins/cases/public/components/user_actions/comment/actions.tsx index 5b17b05a45f68..d4086805d7cd9 100644 --- a/x-pack/plugins/cases/public/components/user_actions/comment/actions.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/comment/actions.tsx @@ -13,8 +13,7 @@ import type { UserActionBuilder, UserActionBuilderArgs } from '../types'; import { UserActionTimestamp } from '../timestamp'; import type { SnakeToCamelCase } from '../../../../common/types'; import { UserActionCopyLink } from '../copy_link'; -import { MarkdownRenderer } from '../../markdown_editor'; -import { ContentWrapper } from '../markdown_form'; +import { ScrollableMarkdown } from '../../markdown_editor'; import { HostIsolationCommentEvent } from './host_isolation_event'; import { HoverableUserWithAvatarResolver } from '../../user_profiles/hoverable_user_with_avatar_resolver'; @@ -34,6 +33,7 @@ export const createActionAttachmentUserActionBuilder = ({ // TODO: Fix this manually. Issue #123375 // eslint-disable-next-line react/display-name build: () => { + const actionIconName = comment.actions.type === 'isolate' ? 'lock' : 'lockOpen'; return [ { username: ( @@ -52,12 +52,11 @@ export const createActionAttachmentUserActionBuilder = ({ ), 'data-test-subj': 'endpoint-action', timestamp: , - timelineAvatar: comment.actions.type === 'isolate' ? 'lock' : 'lockOpen', + timelineAvatar: actionIconName, + timelineAvatarAriaLabel: actionIconName, actions: , children: comment.comment.trim().length > 0 && ( - - {comment.comment} - + ), }, ]; diff --git a/x-pack/plugins/cases/public/components/user_actions/description.test.tsx b/x-pack/plugins/cases/public/components/user_actions/description.test.tsx index 0f0d284130519..d7f393f987b92 100644 --- a/x-pack/plugins/cases/public/components/user_actions/description.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/description.test.tsx @@ -7,65 +7,27 @@ import React from 'react'; import { EuiCommentList } from '@elastic/eui'; -import { render, screen, waitFor } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { Actions } from '../../../common/api'; import { getUserAction } from '../../containers/mock'; import { TestProviders } from '../../common/mock'; -import { createDescriptionUserActionBuilder, getDescriptionUserAction } from './description'; +import { createDescriptionUserActionBuilder } from './description'; import { getMockBuilderArgs } from './mock'; -import userEvent from '@testing-library/user-event'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/navigation/hooks'); describe('createDescriptionUserActionBuilder ', () => { - const onUpdateField = jest.fn(); const builderArgs = getMockBuilderArgs(); beforeEach(() => { jest.clearAllMocks(); }); - it('renders correctly description', async () => { - const descriptionUserAction = getDescriptionUserAction({ - ...builderArgs, - onUpdateField, - isLoadingDescription: false, - }); - - render( - - - - ); - - expect(screen.getByText('added description')).toBeInTheDocument(); - expect(screen.getByText('Security banana Issue')).toBeInTheDocument(); - }); - - it('edits the description correctly', async () => { - const descriptionUserAction = getDescriptionUserAction({ - ...builderArgs, - onUpdateField, - isLoadingDescription: false, - }); - - const res = render( - - - - ); - - userEvent.click(res.getByTestId('editable-description-edit-icon')); - - await waitFor(() => { - expect(builderArgs.handleManageMarkdownEditId).toHaveBeenCalledWith('description'); - }); - }); - - it('renders correctly when editing a description', async () => { + it('renders correctly', async () => { const userAction = getUserAction('description', Actions.update); + // @ts-ignore no need to pass all the arguments const builder = createDescriptionUserActionBuilder({ ...builderArgs, userAction, diff --git a/x-pack/plugins/cases/public/components/user_actions/description.tsx b/x-pack/plugins/cases/public/components/user_actions/description.tsx index 236252927ad6e..42deabb9f1346 100644 --- a/x-pack/plugins/cases/public/components/user_actions/description.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/description.tsx @@ -5,108 +5,12 @@ * 2.0. */ -import React from 'react'; -import classNames from 'classnames'; -import type { EuiCommentProps } from '@elastic/eui'; -import styled from 'styled-components'; -import { EuiText, EuiButtonIcon } from '@elastic/eui'; - -import type { UserActionBuilder, UserActionBuilderArgs, UserActionTreeProps } from './types'; -import { createCommonUpdateUserActionBuilder } from './common'; -import { UserActionTimestamp } from './timestamp'; -import { UserActionMarkdown } from './markdown_form'; -import { getMarkdownEditorStorageKey } from '../markdown_editor/utils'; +import type { UserActionBuilder } from './types'; import * as i18n from './translations'; -import { HoverableUsernameResolver } from '../user_profiles/hoverable_username_resolver'; - -const DESCRIPTION_ID = 'description'; +import { createCommonUpdateUserActionBuilder } from './common'; const getLabelTitle = () => `${i18n.EDITED_FIELD} ${i18n.DESCRIPTION.toLowerCase()}`; -type GetDescriptionUserActionArgs = Pick< - UserActionBuilderArgs, - | 'caseData' - | 'commentRefs' - | 'userProfiles' - | 'manageMarkdownEditIds' - | 'handleManageMarkdownEditId' - | 'appId' -> & - Pick & { isLoadingDescription: boolean }; - -const MyEuiCommentFooter = styled(EuiText)` - ${({ theme }) => ` - border-top: ${theme.eui.euiBorderThin}; - padding: ${theme.eui.euiSizeS}; - `} -`; - -const hasDraftComment = (appId = '', caseId: string, commentId: string): boolean => { - const draftStorageKey = getMarkdownEditorStorageKey(appId, caseId, commentId); - - return Boolean(sessionStorage.getItem(draftStorageKey)); -}; - -export const getDescriptionUserAction = ({ - appId, - caseData, - commentRefs, - manageMarkdownEditIds, - isLoadingDescription, - userProfiles, - onUpdateField, - handleManageMarkdownEditId, -}: GetDescriptionUserActionArgs): EuiCommentProps => { - const isEditable = manageMarkdownEditIds.includes(DESCRIPTION_ID); - return { - username: , - event: i18n.ADDED_DESCRIPTION, - 'data-test-subj': 'description-action', - timestamp: , - children: ( - <> - (commentRefs.current[DESCRIPTION_ID] = element)} - caseId={caseData.id} - id={DESCRIPTION_ID} - content={caseData.description} - isEditable={isEditable} - onSaveContent={(content: string) => { - onUpdateField({ key: DESCRIPTION_ID, value: content }); - }} - onChangeEditable={handleManageMarkdownEditId} - /> - {!isEditable && - !isLoadingDescription && - hasDraftComment(appId, caseData.id, DESCRIPTION_ID) ? ( - - - {i18n.UNSAVED_DRAFT_DESCRIPTION} - - - ) : ( - '' - )} - - ), - timelineAvatar: null, - className: classNames({ - isEdit: manageMarkdownEditIds.includes(DESCRIPTION_ID), - draftFooter: - !isEditable && !isLoadingDescription && hasDraftComment(appId, caseData.id, DESCRIPTION_ID), - }), - actions: ( - handleManageMarkdownEditId(DESCRIPTION_ID)} - data-test-subj="editable-description-edit-icon" - /> - ), - }; -}; - export const createDescriptionUserActionBuilder: UserActionBuilder = ({ userAction, userProfiles, diff --git a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx index 5008fc99c0f01..47f0bd6ab4939 100644 --- a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx @@ -171,14 +171,14 @@ describe(`UserActions`, () => { userEvent.click( within( screen.getAllByTestId(`comment-create-action-${defaultProps.data.comments[0].id}`)[1] - ).getByTestId('user-action-cancel-markdown') + ).getByTestId('editable-cancel-markdown') ); await waitFor(() => { expect( within( screen.getAllByTestId(`comment-create-action-${defaultProps.data.comments[0].id}`)[1] - ).queryByTestId('user-action-markdown-form') + ).queryByTestId('editable-markdown-form') ).not.toBeInTheDocument(); }); }); @@ -212,14 +212,14 @@ describe(`UserActions`, () => { userEvent.click( within( screen.getAllByTestId(`comment-create-action-${defaultProps.data.comments[0].id}`)[1] - ).getByTestId('user-action-save-markdown') + ).getByTestId('editable-save-markdown') ); await waitFor(() => { expect( within( screen.getAllByTestId(`comment-create-action-${defaultProps.data.comments[0].id}`)[1] - ).queryByTestId('user-action-markdown-form') + ).queryByTestId('editable-markdown-form') ).not.toBeInTheDocument(); expect(patchComment).toBeCalledWith({ @@ -306,14 +306,14 @@ describe(`UserActions`, () => { userEvent.click( within( screen.getAllByTestId(`comment-create-action-${defaultProps.data.comments[0].id}`)[1] - ).getByTestId('user-action-save-markdown') + ).getByTestId('editable-save-markdown') ); await waitFor(() => { expect( within( screen.getAllByTestId(`comment-create-action-${defaultProps.data.comments[0].id}`)[1] - ).queryByTestId('user-action-markdown-form') + ).queryByTestId('editable-markdown-form') ).not.toBeInTheDocument(); }); diff --git a/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx b/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx index 972a520d2085c..a52d1bf221119 100644 --- a/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx @@ -6,20 +6,19 @@ */ import React from 'react'; -import { mount } from 'enzyme'; -import { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import type { Content } from './schema'; -import { schema } from './schema'; -import { UserActionMarkdown } from './markdown_form'; -import type { AppMockRenderer } from '../../common/mock'; -import { createAppMockRenderer, TestProviders } from '../../common/mock'; -import { waitFor, fireEvent, render, act } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import { UserActionMarkdown } from './markdown_form'; + +jest.mock('../../common/lib/kibana'); +jest.mock('../../common/navigation/hooks'); + const onChangeEditable = jest.fn(); const onSaveContent = jest.fn(); -const newValue = 'Hello from Tehas'; -const emptyValue = ''; const hyperlink = `[hyperlink](http://elastic.co)`; const draftStorageKey = `cases.testAppId.caseId.markdown-id.markdownEditor`; const defaultProps = { @@ -33,8 +32,10 @@ const defaultProps = { }; describe('UserActionMarkdown ', () => { + let appMockRenderer: AppMockRenderer; beforeEach(() => { jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); }); afterEach(() => { @@ -42,147 +43,26 @@ describe('UserActionMarkdown ', () => { }); it('Renders markdown correctly when not in edit mode', async () => { - const wrapper = mount( - - - - ); + appMockRenderer.render(); - expect(wrapper.find(`[data-test-subj="markdown-link"]`).first().text()).toContain('hyperlink'); + expect(screen.getByTestId('scrollable-markdown')).toBeInTheDocument(); + expect(screen.getByTestId('markdown-link')).toBeInTheDocument(); + expect(screen.queryByTestId('editable-save-markdown')).not.toBeInTheDocument(); + expect(screen.queryByTestId('editable-cancel-markdown')).not.toBeInTheDocument(); }); - it('Save button click calls onSaveContent and onChangeEditable when text area value changed', async () => { - const wrapper = mount( - - - - ); + it('Renders markdown correctly when in edit mode', async () => { + appMockRenderer.render(); - wrapper - .find(`.euiMarkdownEditorTextArea`) - .first() - .simulate('change', { - target: { value: newValue }, - }); - - wrapper.find(`button[data-test-subj="user-action-save-markdown"]`).first().simulate('click'); - - await waitFor(() => { - expect(onSaveContent).toHaveBeenCalledWith(newValue); - expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); - }); - }); - - it('Does not call onSaveContent if no change from current text', async () => { - const wrapper = mount( - - - - ); - - wrapper.find(`button[data-test-subj="user-action-save-markdown"]`).first().simulate('click'); - - await waitFor(() => { - expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); - }); - expect(onSaveContent).not.toHaveBeenCalled(); - }); - - it('Save button disabled if current text is empty', async () => { - const wrapper = mount( - - - - ); - - wrapper - .find(`.euiMarkdownEditorTextArea`) - .first() - .simulate('change', { - target: { value: emptyValue }, - }); - - await waitFor(() => { - expect( - wrapper.find(`button[data-test-subj="user-action-save-markdown"]`).first().prop('disabled') - ).toBeTruthy(); - }); - }); - - it('Cancel button click calls only onChangeEditable', async () => { - const wrapper = mount( - - - - ); - wrapper.find(`[data-test-subj="user-action-cancel-markdown"]`).first().simulate('click'); - - await waitFor(() => { - expect(onSaveContent).not.toHaveBeenCalled(); - expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); - }); + expect(screen.getByTestId('editable-save-markdown')).toBeInTheDocument(); + expect(screen.getByTestId('editable-cancel-markdown')).toBeInTheDocument(); }); describe('useForm stale state bug', () => { - let appMockRenderer: AppMockRenderer; const oldContent = defaultProps.content; const appendContent = ' appended content'; const newContent = defaultProps.content + appendContent; - beforeEach(() => { - appMockRenderer = createAppMockRenderer(); - }); - - it('creates a stale state if a key is not passed to the component', async () => { - const TestComponent = () => { - const [isEditable, setIsEditable] = React.useState(true); - const [saveContent, setSaveContent] = React.useState(defaultProps.content); - return ( -
    - -
    - ); - }; - - const result = appMockRenderer.render(); - - expect(result.getByTestId('user-action-markdown-form')).toBeTruthy(); - - // append some content and save - userEvent.type(result.container.querySelector('textarea')!, appendContent); - userEvent.click(result.getByTestId('user-action-save-markdown')); - - // wait for the state to update - await waitFor(() => { - expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); - }); - - // toggle to non-edit state - userEvent.click(result.getByTestId('test-button')); - expect(result.getByTestId('user-action-markdown')).toBeTruthy(); - - // toggle to edit state again - userEvent.click(result.getByTestId('test-button')); - - // the text area holds a stale value - // this is the wrong behaviour. The textarea holds the old content - expect(result.container.querySelector('textarea')!.value).toEqual(oldContent); - expect(result.container.querySelector('textarea')!.value).not.toEqual(newContent); - }); - it("doesn't create a stale state if a key is passed to the component", async () => { const TestComponent = () => { const [isEditable, setIsEditable] = React.useState(true); @@ -208,11 +88,11 @@ describe('UserActionMarkdown ', () => { ); }; const result = appMockRenderer.render(); - expect(result.getByTestId('user-action-markdown-form')).toBeTruthy(); + expect(result.getByTestId('editable-markdown-form')).toBeTruthy(); // append content and save userEvent.type(result.container.querySelector('textarea')!, appendContent); - userEvent.click(result.getByTestId('user-action-save-markdown')); + userEvent.click(result.getByTestId('editable-save-markdown')); // wait for the state to update await waitFor(() => { @@ -221,7 +101,7 @@ describe('UserActionMarkdown ', () => { // toggle to non-edit state userEvent.click(result.getByTestId('test-button')); - expect(result.getByTestId('user-action-markdown')).toBeTruthy(); + expect(result.getByTestId('scrollable-markdown')).toBeTruthy(); // toggle to edit state again userEvent.click(result.getByTestId('test-button')); @@ -231,112 +111,4 @@ describe('UserActionMarkdown ', () => { expect(result.container.querySelector('textarea')!.value).not.toEqual(oldContent); }); }); - - describe('draft comment ', () => { - const content = 'test content'; - const initialState = { content }; - const MockHookWrapperComponent: React.FC<{ testProviderProps?: unknown }> = ({ - children, - testProviderProps = {}, - }) => { - const { form } = useForm({ - defaultValue: initialState, - options: { stripEmptyFields: false }, - schema, - }); - - return ( - -
    {children}
    -
    - ); - }; - - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.clearAllTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - sessionStorage.removeItem(draftStorageKey); - }); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('Save button click clears session storage', async () => { - const result = render( - - - - ); - - fireEvent.change(result.getByTestId('euiMarkdownEditorTextArea'), { - target: { value: newValue }, - }); - - act(() => { - jest.advanceTimersByTime(1000); - }); - - expect(sessionStorage.getItem(draftStorageKey)).toBe(newValue); - - fireEvent.click(result.getByTestId(`user-action-save-markdown`)); - - await waitFor(() => { - expect(onSaveContent).toHaveBeenCalledWith(newValue); - expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); - expect(sessionStorage.getItem(draftStorageKey)).toBe(null); - }); - }); - - it('Cancel button click clears session storage', async () => { - const result = render( - - - - ); - - expect(sessionStorage.getItem(draftStorageKey)).toBe(''); - - fireEvent.change(result.getByTestId('euiMarkdownEditorTextArea'), { - target: { value: newValue }, - }); - - act(() => { - jest.advanceTimersByTime(1000); - }); - - await waitFor(() => { - expect(sessionStorage.getItem(draftStorageKey)).toBe(newValue); - }); - - fireEvent.click(result.getByTestId('user-action-cancel-markdown')); - - await waitFor(() => { - expect(sessionStorage.getItem(draftStorageKey)).toBe(null); - }); - }); - - describe('existing storage key', () => { - beforeEach(() => { - sessionStorage.setItem(draftStorageKey, 'value set in storage'); - }); - - it('should have session storage value same as draft comment', async () => { - const result = render( - - - - ); - - expect(result.getByText('value set in storage')).toBeInTheDocument(); - }); - }); - }); }); diff --git a/x-pack/plugins/cases/public/components/user_actions/markdown_form.tsx b/x-pack/plugins/cases/public/components/user_actions/markdown_form.tsx index 3ddb62eb4fb7a..3866fe774ec14 100644 --- a/x-pack/plugins/cases/public/components/user_actions/markdown_form.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/markdown_form.tsx @@ -5,25 +5,10 @@ * 2.0. */ -import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; -import styled from 'styled-components'; +import React, { forwardRef, useRef } from 'react'; -import { Form, useForm, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import type { Content } from './schema'; import { schema } from './schema'; -import { MarkdownRenderer, MarkdownEditorForm } from '../markdown_editor'; -import { removeItemFromSessionStorage } from '../utils'; -import { useCasesContext } from '../cases_context/use_cases_context'; -import { getMarkdownEditorStorageKey } from '../markdown_editor/utils'; -import { UserActionMarkdownFooter } from './markdown_form_footer'; - -export const ContentWrapper = styled.div` - padding: ${({ theme }) => `${theme.eui.euiSizeM} ${theme.eui.euiSizeL}`}; - text-overflow: ellipsis; - word-break: break-word; - display: -webkit-box; - -webkit-box-orient: vertical; -`; +import { ScrollableMarkdown, EditableMarkdown } from '../markdown_editor'; interface UserActionMarkdownProps { content: string; @@ -43,70 +28,22 @@ const UserActionMarkdownComponent = forwardRef< UserActionMarkdownProps >(({ id, content, caseId, isEditable, onChangeEditable, onSaveContent }, ref) => { const editorRef = useRef(); - const initialState = { content }; - const { form } = useForm({ - defaultValue: initialState, - options: { stripEmptyFields: false }, - schema, - }); - const fieldName = 'content'; - const { appId } = useCasesContext(); - const draftStorageKey = getMarkdownEditorStorageKey(appId, caseId, id); - const { setFieldValue, submit } = form; - - const handleCancelAction = useCallback(() => { - onChangeEditable(id); - removeItemFromSessionStorage(draftStorageKey); - }, [id, onChangeEditable, draftStorageKey]); - - const handleSaveAction = useCallback(async () => { - const { isValid, data } = await submit(); - - if (isValid && data.content !== content) { - onSaveContent(data.content); - } - onChangeEditable(id); - removeItemFromSessionStorage(draftStorageKey); - }, [content, id, onChangeEditable, onSaveContent, submit, draftStorageKey]); - - const setComment = useCallback( - (newComment) => { - setFieldValue(fieldName, newComment); - }, - [setFieldValue] - ); - - useImperativeHandle(ref, () => ({ - setComment, - editor: editorRef.current, - })); return isEditable ? ( -
    - - ), - initialValue: content, - }} - /> - + ) : ( - - {content} - + ); }); diff --git a/x-pack/plugins/cases/public/files/index.ts b/x-pack/plugins/cases/public/files/index.ts index 1804e11b0ea17..7240093c01c96 100644 --- a/x-pack/plugins/cases/public/files/index.ts +++ b/x-pack/plugins/cases/public/files/index.ts @@ -7,16 +7,42 @@ import type { FilesSetup } from '@kbn/files-plugin/public'; import type { FileKindBrowser } from '@kbn/shared-ux-file-types'; -import { MAX_FILE_SIZE, OWNERS } from '../../common/constants'; +import { + GENERAL_CASES_OWNER, + MAX_FILE_SIZE, + OBSERVABILITY_OWNER, + OWNERS, + SECURITY_SOLUTION_OWNER, +} from '../../common/constants'; import type { Owner } from '../../common/constants/types'; import { constructFileKindIdByOwner } from '../../common/files'; import type { CaseFileKinds, FilesConfig } from './types'; +import * as i18n from './translations'; + +const getOwnerUIName = (owner: Owner) => { + switch (owner) { + case SECURITY_SOLUTION_OWNER: + return 'Security'; + case OBSERVABILITY_OWNER: + return 'Observability'; + case GENERAL_CASES_OWNER: + return 'Stack Management'; + default: + return owner; + } +}; const buildFileKind = (config: FilesConfig, owner: Owner): FileKindBrowser => { return { id: constructFileKindIdByOwner(owner), allowedMimeTypes: config.allowedMimeTypes, maxSizeBytes: config.maxSize ?? MAX_FILE_SIZE, + managementUiActions: { + delete: { + enabled: false, + reason: i18n.FILE_DELETE_REASON(getOwnerUIName(owner)), + }, + }, }; }; diff --git a/x-pack/plugins/cases/public/files/translations.ts b/x-pack/plugins/cases/public/files/translations.ts new file mode 100644 index 0000000000000..9a5c0af7ff95e --- /dev/null +++ b/x-pack/plugins/cases/public/files/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 FILE_DELETE_REASON = (owner: string) => + i18n.translate('xpack.cases.files.deleteReason', { + values: { owner }, + defaultMessage: + 'This file is managed by Cases. Navigate to the Cases page under {owner} to delete it.', + }); diff --git a/x-pack/plugins/cases/server/client/attachments/bulk_delete.test.ts b/x-pack/plugins/cases/server/client/attachments/bulk_delete.test.ts new file mode 100644 index 0000000000000..49a12c904eb63 --- /dev/null +++ b/x-pack/plugins/cases/server/client/attachments/bulk_delete.test.ts @@ -0,0 +1,99 @@ +/* + * 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 { loggerMock } from '@kbn/logging-mocks'; +import pReflect from 'p-reflect'; +import type { File } from '@kbn/files-plugin/common'; +import { FileNotFoundError } from '@kbn/files-plugin/server/file_service/errors'; +import { retrieveFilesIgnoringNotFound } from './bulk_delete'; + +describe('bulk_delete', () => { + describe('retrieveFilesIgnoringNotFound', () => { + const mockLogger = loggerMock.create(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns an empty array when the results is an empty array', () => { + expect(retrieveFilesIgnoringNotFound([], [], mockLogger)).toEqual([]); + }); + + it('returns a fulfilled file', async () => { + expect(retrieveFilesIgnoringNotFound([await createFakeFile()], ['abc'], mockLogger)).toEqual([ + {}, + ]); + }); + + it('logs a warning when encountering a file not found error', async () => { + const fileNotFound = await pReflect(Promise.reject(new FileNotFoundError('not found'))); + + expect(retrieveFilesIgnoringNotFound([fileNotFound], ['abc'], mockLogger)).toEqual([]); + expect(mockLogger.warn).toBeCalledTimes(1); + expect(mockLogger.warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Failed to find file id: abc: Error: not found", + ] + `); + }); + + it('logs a warning without the fileId when the results length is different from the file ids', async () => { + const fileNotFound = await pReflect(Promise.reject(new FileNotFoundError('not found'))); + + expect(retrieveFilesIgnoringNotFound([fileNotFound], ['abc', '123'], mockLogger)).toEqual([]); + expect(mockLogger.warn).toBeCalledTimes(1); + expect(mockLogger.warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Failed to find file: Error: not found", + ] + `); + }); + + it('throws when encountering an error that is not a file not found', async () => { + const otherError = new Error('other error'); + const otherErrorResult = await pReflect(Promise.reject(new Error('other error'))); + + expect.assertions(2); + + expect(() => + retrieveFilesIgnoringNotFound([otherErrorResult], ['abc'], mockLogger) + ).toThrowError(otherError); + expect(mockLogger.warn).not.toBeCalled(); + }); + + it('throws when encountering an error that is not a file not found after a valid file', async () => { + const otherError = new Error('other error'); + const otherErrorResult = await pReflect(Promise.reject(otherError)); + const fileResult = await createFakeFile(); + + expect.assertions(2); + + expect(() => + retrieveFilesIgnoringNotFound([fileResult, otherErrorResult], ['1', '2'], mockLogger) + ).toThrowError(otherError); + expect(mockLogger.warn).not.toBeCalled(); + }); + + it('throws a new error when encountering an error that is a string', async () => { + // this produces an error because .reject() must be passed an error but I want to test a string just in case + // eslint-disable-next-line prefer-promise-reject-errors + const otherErrorResult = await pReflect(Promise.reject('string error')); + const fileResult = await createFakeFile(); + + expect.assertions(2); + + expect(() => + retrieveFilesIgnoringNotFound([fileResult, otherErrorResult], ['1', '2'], mockLogger) + ).toThrowErrorMatchingInlineSnapshot(`"Failed to retrieve file id: 2: string error"`); + expect(mockLogger.warn).not.toBeCalled(); + }); + }); +}); + +const createFakeFile = () => { + return pReflect(Promise.resolve({} as File)); +}; diff --git a/x-pack/plugins/cases/server/client/attachments/bulk_delete.ts b/x-pack/plugins/cases/server/client/attachments/bulk_delete.ts index 75cdf6f42e05f..9f9aaaf59c736 100644 --- a/x-pack/plugins/cases/server/client/attachments/bulk_delete.ts +++ b/x-pack/plugins/cases/server/client/attachments/bulk_delete.ts @@ -10,8 +10,10 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; -import pMap from 'p-map'; +import type { PromiseResult, PromiseRejectedResult } from 'p-settle'; +import pSettle from 'p-settle'; import { partition } from 'lodash'; +import type { Logger } from '@kbn/core/server'; import type { File, FileJSON } from '@kbn/files-plugin/common'; import type { FileServiceStart } from '@kbn/files-plugin/server'; import { FileNotFoundError } from '@kbn/files-plugin/server/file_service/errors'; @@ -46,7 +48,12 @@ export const bulkDeleteFileAttachments = async ( await casesClient.cases.resolve({ id: caseId, includeComments: false }); - const fileEntities = await getFileEntities(caseId, request.ids, fileService); + const fileEntities = await getFileEntities({ + caseId, + fileIds: request.ids, + fileService, + logger, + }); // It's possible for this to return an empty array if there was an error creating file attachments in which case the // file would be present but the case attachment would not @@ -67,7 +74,10 @@ export const bulkDeleteFileAttachments = async ( }); await Promise.all([ - deleteFiles(request.ids, fileService), + deleteFiles( + fileEntities.map((entity) => entity.id), + fileService + ), attachmentService.bulkDelete({ attachmentIds: fileAttachments.map((so) => so.id), refresh: false, @@ -84,43 +94,53 @@ export const bulkDeleteFileAttachments = async ( user, }); } catch (error) { - let errorToTrack = error; - - // if it's an error from the file service let's put it in a boom so we don't loose the status code of a 404 - if (error instanceof FileNotFoundError) { - errorToTrack = Boom.notFound(error.message); - } - throw createCaseError({ message: `Failed to delete file attachments for case: ${caseId}: ${error}`, - error: errorToTrack, + error, logger, }); } }; -const getFileEntities = async ( - caseId: BulkDeleteFileArgs['caseId'], - fileIds: BulkDeleteFileArgs['fileIds'], - fileService: FileServiceStart -) => { - const files = await getFiles(caseId, fileIds, fileService); +const getFileEntities = async ({ + caseId, + fileIds, + fileService, + logger, +}: { + caseId: BulkDeleteFileArgs['caseId']; + fileIds: BulkDeleteFileArgs['fileIds']; + fileService: FileServiceStart; + logger: Logger; +}) => { + const files = await getFiles({ caseId, fileIds, fileService, logger }); const fileEntities = createFileEntities(files); return fileEntities; }; -const getFiles = async ( - caseId: BulkDeleteFileArgs['caseId'], - fileIds: BulkDeleteFileArgs['fileIds'], - fileService: FileServiceStart -): Promise => { +const getFiles = async ({ + caseId, + fileIds, + fileService, + logger, +}: { + caseId: BulkDeleteFileArgs['caseId']; + fileIds: BulkDeleteFileArgs['fileIds']; + fileService: FileServiceStart; + logger: Logger; +}): Promise => { // it's possible that we're trying to delete a file when an attachment wasn't created (for example if the create // attachment request failed) - const files = await pMap(fileIds, async (fileId: string) => fileService.getById({ id: fileId }), { - concurrency: MAX_CONCURRENT_SEARCHES, - }); + const fileSettleResults = await pSettle( + fileIds.map(async (fileId) => fileService.getById({ id: fileId })), + { + concurrency: MAX_CONCURRENT_SEARCHES, + } + ); + + const files = retrieveFilesIgnoringNotFound(fileSettleResults, fileIds, logger); const [validFiles, invalidFiles] = partition(files, (file) => { return ( @@ -137,9 +157,52 @@ const getFiles = async ( throw Boom.badRequest(`Failed to delete files because filed ids were invalid: ${invalidIds}`); } - if (validFiles.length <= 0) { - throw Boom.badRequest('Failed to find files to delete'); + return validFiles.map((fileInfo) => fileInfo.data); +}; + +export const retrieveFilesIgnoringNotFound = ( + results: Array>>, + fileIds: BulkDeleteFileArgs['fileIds'], + logger: Logger +) => { + const files: File[] = []; + + results.forEach((result, index) => { + if (result.isFulfilled) { + files.push(result.value); + } else if (result.reason instanceof FileNotFoundError) { + const warningMessage = getFileNotFoundErrorMessage({ + resultsLength: results.length, + fileIds, + index, + result, + }); + + logger.warn(warningMessage); + } else if (result.reason instanceof Error) { + throw result.reason; + } else { + throw new Error(`Failed to retrieve file id: ${fileIds[index]}: ${result.reason}`); + } + }); + + return files; +}; + +const getFileNotFoundErrorMessage = ({ + resultsLength, + fileIds, + index, + result, +}: { + resultsLength: number; + fileIds: BulkDeleteFileArgs['fileIds']; + index: number; + result: PromiseRejectedResult; +}) => { + if (resultsLength === fileIds.length) { + return `Failed to find file id: ${fileIds[index]}: ${result.reason}`; } - return validFiles.map((fileInfo) => fileInfo.data); + return `Failed to find file: ${result.reason}`; }; diff --git a/x-pack/plugins/cases/server/client/files/index.test.ts b/x-pack/plugins/cases/server/client/files/index.test.ts index e1727f5885d4a..853f7debabad9 100644 --- a/x-pack/plugins/cases/server/client/files/index.test.ts +++ b/x-pack/plugins/cases/server/client/files/index.test.ts @@ -51,6 +51,15 @@ describe('server files', () => { }); describe('deleteFiles', () => { + it('does not call delete when the file ids is empty', async () => { + const fileServiceMock = createFileServiceMock(); + + expect.assertions(1); + await deleteFiles([], fileServiceMock); + + expect(fileServiceMock.delete).not.toBeCalled(); + }); + it('calls delete twice with the ids passed in', async () => { const fileServiceMock = createFileServiceMock(); diff --git a/x-pack/plugins/cases/server/client/files/index.ts b/x-pack/plugins/cases/server/client/files/index.ts index 31e0bcf8fb2dd..2a400b8037d6d 100644 --- a/x-pack/plugins/cases/server/client/files/index.ts +++ b/x-pack/plugins/cases/server/client/files/index.ts @@ -33,7 +33,12 @@ export const createFileEntities = (files: FileEntityInfo[]): OwnerEntity[] => { return fileEntities; }; -export const deleteFiles = async (fileIds: string[], fileService: FileServiceStart) => - pMap(fileIds, async (fileId: string) => fileService.delete({ id: fileId }), { +export const deleteFiles = async (fileIds: string[], fileService: FileServiceStart) => { + if (fileIds.length <= 0) { + return; + } + + return pMap(fileIds, async (fileId: string) => fileService.delete({ id: fileId }), { concurrency: MAX_CONCURRENT_SEARCHES, }); +}; diff --git a/x-pack/plugins/cases/server/saved_object_types/cases.ts b/x-pack/plugins/cases/server/saved_object_types/cases.ts index 1d70808f14db2..2a9492b6c755f 100644 --- a/x-pack/plugins/cases/server/saved_object_types/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/cases.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { CoreSetup, Logger, @@ -22,6 +23,7 @@ export const createCaseSavedObjectType = ( logger: Logger ): SavedObjectsType => ({ name: CASE_SAVED_OBJECT, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/cases/server/saved_object_types/comments.ts b/x-pack/plugins/cases/server/saved_object_types/comments.ts index 071f26b1e2bad..af15343741b3a 100644 --- a/x-pack/plugins/cases/server/saved_object_types/comments.ts +++ b/x-pack/plugins/cases/server/saved_object_types/comments.ts @@ -6,6 +6,7 @@ */ import type { SavedObjectsType } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CASE_COMMENT_SAVED_OBJECT } from '../../common/constants'; import type { CreateCommentsMigrationsDeps } from './migrations'; import { createCommentsMigrations } from './migrations'; @@ -21,6 +22,7 @@ export const createCaseCommentSavedObjectType = ({ migrationDeps: CreateCommentsMigrationsDeps; }): SavedObjectsType => ({ name: CASE_COMMENT_SAVED_OBJECT, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/cases/server/saved_object_types/configure.ts b/x-pack/plugins/cases/server/saved_object_types/configure.ts index cd8b98daa3c94..105198825c68e 100644 --- a/x-pack/plugins/cases/server/saved_object_types/configure.ts +++ b/x-pack/plugins/cases/server/saved_object_types/configure.ts @@ -6,6 +6,7 @@ */ import type { SavedObjectsType } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CASE_CONFIGURE_SAVED_OBJECT } from '../../common/constants'; import { configureMigrations } from './migrations'; @@ -16,6 +17,7 @@ import { configureMigrations } from './migrations'; export const caseConfigureSavedObjectType: SavedObjectsType = { name: CASE_CONFIGURE_SAVED_OBJECT, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts b/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts index 81f9e1ca6f2f1..f25c14e3e8ec9 100644 --- a/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts +++ b/x-pack/plugins/cases/server/saved_object_types/connector_mappings.ts @@ -6,6 +6,7 @@ */ import type { SavedObjectsType } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT } from '../../common/constants'; import { connectorMappingsMigrations } from './migrations'; @@ -16,6 +17,7 @@ import { connectorMappingsMigrations } from './migrations'; export const caseConnectorMappingsSavedObjectType: SavedObjectsType = { name: CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/cases/server/saved_object_types/telemetry.ts b/x-pack/plugins/cases/server/saved_object_types/telemetry.ts index 515d1e63c7858..86beb594f466a 100644 --- a/x-pack/plugins/cases/server/saved_object_types/telemetry.ts +++ b/x-pack/plugins/cases/server/saved_object_types/telemetry.ts @@ -6,10 +6,12 @@ */ import type { SavedObjectsType } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CASE_TELEMETRY_SAVED_OBJECT } from '../../common/constants'; export const casesTelemetrySavedObjectType: SavedObjectsType = { name: CASE_TELEMETRY_SAVED_OBJECT, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', mappings: { diff --git a/x-pack/plugins/cases/server/saved_object_types/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/user_actions.ts index 60180595999aa..0195992105562 100644 --- a/x-pack/plugins/cases/server/saved_object_types/user_actions.ts +++ b/x-pack/plugins/cases/server/saved_object_types/user_actions.ts @@ -6,6 +6,7 @@ */ import type { SavedObjectsType } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CASE_USER_ACTION_SAVED_OBJECT } from '../../common/constants'; import type { UserActionsMigrationsDeps } from './migrations/user_actions'; import { createUserActionsMigrations } from './migrations/user_actions'; @@ -19,6 +20,7 @@ export const createCaseUserActionSavedObjectType = ( migrationDeps: UserActionsMigrationsDeps ): SavedObjectsType => ({ name: CASE_USER_ACTION_SAVED_OBJECT, + indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/cases/server/telemetry/queries/utils.test.ts b/x-pack/plugins/cases/server/telemetry/queries/utils.test.ts index 359aa621798f0..50b5b142fd319 100644 --- a/x-pack/plugins/cases/server/telemetry/queries/utils.test.ts +++ b/x-pack/plugins/cases/server/telemetry/queries/utils.test.ts @@ -565,6 +565,88 @@ describe('utils', () => { }); describe('files', () => { + it('rounds the average file size when it is a decimal', () => { + const attachmentFramework: AttachmentFrameworkAggsResult = { + externalReferenceTypes: { + buckets: [ + { + doc_count: 5, + key: '.files', + references: { + cases: { + max: { + value: 10, + }, + }, + }, + }, + ], + }, + persistableReferenceTypes: { + buckets: [], + }, + }; + + expect( + getAttachmentsFrameworkStats({ + attachmentAggregations: attachmentFramework, + totalCasesForOwner: 5, + filesAggregations: { + averageSize: { value: 1.1 }, + topMimeTypes: { + buckets: [], + }, + }, + }).attachmentFramework.files + ).toMatchInlineSnapshot(` + Object { + "average": 1, + "averageSize": 1, + "maxOnACase": 10, + "topMimeTypes": Array [], + "total": 5, + } + `); + }); + + it('sets the average file size to 0 when the aggregation does not exist', () => { + const attachmentFramework: AttachmentFrameworkAggsResult = { + externalReferenceTypes: { + buckets: [ + { + doc_count: 5, + key: '.files', + references: { + cases: { + max: { + value: 10, + }, + }, + }, + }, + ], + }, + persistableReferenceTypes: { + buckets: [], + }, + }; + + expect( + getAttachmentsFrameworkStats({ + attachmentAggregations: attachmentFramework, + totalCasesForOwner: 5, + }).attachmentFramework.files + ).toMatchInlineSnapshot(` + Object { + "average": 1, + "averageSize": 0, + "maxOnACase": 10, + "topMimeTypes": Array [], + "total": 5, + } + `); + }); + it('sets the files stats to empty when the file aggregation results is the empty version', () => { const attachmentFramework: AttachmentFrameworkAggsResult = { externalReferenceTypes: { diff --git a/x-pack/plugins/cases/server/telemetry/queries/utils.ts b/x-pack/plugins/cases/server/telemetry/queries/utils.ts index 7896c2bdac760..e47e9d1613bce 100644 --- a/x-pack/plugins/cases/server/telemetry/queries/utils.ts +++ b/x-pack/plugins/cases/server/telemetry/queries/utils.ts @@ -230,7 +230,7 @@ export const getAttachmentsFrameworkStats = ({ return emptyAttachmentFramework(); } - const averageFileSize = filesAggregations?.averageSize?.value; + const averageFileSize = getAverageFileSize(filesAggregations); const topMimeTypes = filesAggregations?.topMimeTypes; return { @@ -253,6 +253,14 @@ export const getAttachmentsFrameworkStats = ({ }; }; +const getAverageFileSize = (filesAggregations?: FileAttachmentAggsResult) => { + if (filesAggregations?.averageSize?.value == null) { + return 0; + } + + return Math.round(filesAggregations.averageSize.value); +}; + const getAttachmentRegistryStats = ( registryResults: BucketsWithMaxOnCase, totalCasesForOwner: number diff --git a/x-pack/plugins/cloud/server/utils.test.ts b/x-pack/plugins/cloud/common/parse_deployment_id_from_deployment_url.test.ts similarity index 87% rename from x-pack/plugins/cloud/server/utils.test.ts rename to x-pack/plugins/cloud/common/parse_deployment_id_from_deployment_url.test.ts index 00e7de7336c7a..de54561783285 100644 --- a/x-pack/plugins/cloud/server/utils.test.ts +++ b/x-pack/plugins/cloud/common/parse_deployment_id_from_deployment_url.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { parseDeploymentIdFromDeploymentUrl } from './utils'; +import { parseDeploymentIdFromDeploymentUrl } from './parse_deployment_id_from_deployment_url'; describe('parseDeploymentIdFromDeploymentUrl', () => { it('should return undefined if there is no deploymentUrl configured', () => { diff --git a/x-pack/plugins/cloud/server/utils.ts b/x-pack/plugins/cloud/common/parse_deployment_id_from_deployment_url.ts similarity index 100% rename from x-pack/plugins/cloud/server/utils.ts rename to x-pack/plugins/cloud/common/parse_deployment_id_from_deployment_url.ts diff --git a/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts index 4793bb1ac6af9..26f4acb9ae576 100644 --- a/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts +++ b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.test.ts @@ -29,4 +29,17 @@ describe('registerCloudDeploymentIdAnalyticsContext', () => { cloudId: 'cloud_id', }); }); + + test('it registers the context provider and emits the cloudId and deploymentId', async () => { + registerCloudDeploymentMetadataAnalyticsContext(analytics, { + id: 'cloud_id', + deployment_url: 'deployments/uuid-of-my-deployment', + }); + expect(analytics.registerContextProvider).toHaveBeenCalledTimes(1); + const [{ context$ }] = analytics.registerContextProvider.mock.calls[0]; + await expect(firstValueFrom(context$)).resolves.toEqual({ + cloudId: 'cloud_id', + deploymentId: 'uuid-of-my-deployment', + }); + }); }); diff --git a/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts index 68130cdcda799..d3c8d0df8e553 100644 --- a/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts +++ b/x-pack/plugins/cloud/common/register_cloud_deployment_id_analytics_context.ts @@ -7,11 +7,13 @@ import type { AnalyticsClient } from '@kbn/analytics-client'; import { of } from 'rxjs'; +import { parseDeploymentIdFromDeploymentUrl } from './parse_deployment_id_from_deployment_url'; export interface CloudDeploymentMetadata { id?: string; trial_end_date?: string; is_elastic_staff_owned?: boolean; + deployment_url?: string; } export function registerCloudDeploymentMetadataAnalyticsContext( @@ -29,11 +31,20 @@ export function registerCloudDeploymentMetadataAnalyticsContext( analytics.registerContextProvider({ name: 'Cloud Deployment Metadata', - context$: of({ cloudId, cloudTrialEndDate, cloudIsElasticStaffOwned }), + context$: of({ + cloudId, + deploymentId: parseDeploymentIdFromDeploymentUrl(cloudMetadata.deployment_url), + cloudTrialEndDate, + cloudIsElasticStaffOwned, + }), schema: { cloudId: { type: 'keyword', - _meta: { description: 'The Cloud Deployment ID' }, + _meta: { description: 'The Cloud ID' }, + }, + deploymentId: { + type: 'keyword', + _meta: { description: 'The Deployment ID', optional: true }, }, cloudTrialEndDate: { type: 'date', diff --git a/x-pack/plugins/cloud/server/plugin.ts b/x-pack/plugins/cloud/server/plugin.ts index c0f52645521fe..7d33ac99629b8 100644 --- a/x-pack/plugins/cloud/server/plugin.ts +++ b/x-pack/plugins/cloud/server/plugin.ts @@ -11,7 +11,7 @@ import { registerCloudDeploymentMetadataAnalyticsContext } from '../common/regis import type { CloudConfigType } from './config'; import { registerCloudUsageCollector } from './collectors'; import { getIsCloudEnabled } from '../common/is_cloud_enabled'; -import { parseDeploymentIdFromDeploymentUrl } from './utils'; +import { parseDeploymentIdFromDeploymentUrl } from '../common/parse_deployment_id_from_deployment_url'; import { readInstanceSizeMb } from './env'; interface PluginsSetup { diff --git a/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts b/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts index 15bcfdc53512f..85822bae819eb 100644 --- a/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts +++ b/x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @@ -22,6 +22,8 @@ const configSchema = schema.object({ 'Loaded Kibana', // Sent once per page refresh (potentially, once per session) 'Hosts View Query Submitted', // Worst-case scenario 1 every 2 seconds 'Host Entry Clicked', // Worst-case scenario once per second - AT RISK, + 'Host Flyout Filter Removed', // Worst-case scenario once per second - AT RISK, + 'Host Flyout Filter Added', // Worst-case scenario once per second - AT RISK, ], }), }); diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 69b99bc9d58cb..0194fbf969ae3 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -75,6 +75,7 @@ export const CLOUDBEAT_VULN_MGMT_AZURE = 'cloudbeat/vuln_mgmt_azure'; export const KSPM_POLICY_TEMPLATE = 'kspm'; export const CSPM_POLICY_TEMPLATE = 'cspm'; export const VULN_MGMT_POLICY_TEMPLATE = 'vuln_mgmt'; +export const CNVM_POLICY_TEMPLATE = 'cnvm'; export const SUPPORTED_POLICY_TEMPLATES = [ KSPM_POLICY_TEMPLATE, CSPM_POLICY_TEMPLATE, diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx index 7fb386ae16160..3e84cc663d0c5 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx @@ -92,7 +92,7 @@ export const CspNoDataPage = ({ ); }; -const defaultLoadingRenderer = () => ( +export const defaultLoadingRenderer = () => ( ( ); -const defaultNoDataRenderer = () => ( +export const defaultNoDataRenderer = () => ( ({ @@ -22,12 +24,19 @@ jest.mock('react-router-dom', () => ({ integration: undefined, }), })); +jest.mock('../../common/api/use_setup_status_api'); describe('', () => { beforeEach(() => { (useParams as jest.Mock).mockReturnValue({ integration: undefined, }); + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { status: 'indexed', installedPackageVersion: '1.2.13' }, + }) + ); }); const onChange = jest.fn(); @@ -200,10 +209,18 @@ describe('', () => { })); policy.name = 'cloud_security_posture-1'; + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + kspm: { status: 'not-deployed', healthyAgents: 0, installedPackagePolicies: 1 }, + }, + }) + ); + (useParams as jest.Mock).mockReturnValue({ integration: 'kspm', }); - render(); // 1st call happens on mount and selects the default policy template enabled input @@ -220,7 +237,7 @@ describe('', () => { isValid: true, updatedPolicy: { ...policy, - name: 'kspm-1', + name: 'kspm-2', }, }); }); @@ -237,6 +254,56 @@ describe('', () => { (useParams as jest.Mock).mockReturnValue({ integration: 'cspm', }); + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + cspm: { status: 'not-deployed', healthyAgents: 0, installedPackagePolicies: 1 }, + }, + }) + ); + + render(); + + // 1st call happens on mount and selects the default policy template enabled input + expect(onChange).toHaveBeenNthCalledWith(1, { + isValid: true, + updatedPolicy: { + ...getMockPolicyAWS(), + name: 'cloud_security_posture-1', + }, + }); + + // 2nd call happens to set integration name + expect(onChange).toHaveBeenNthCalledWith(2, { + isValid: true, + updatedPolicy: { + ...policy, + name: 'cspm-2', + }, + }); + }); + + it('selects default VULN_MGMT input selector', () => { + const policy = getMockPolicyAWS(); + // enable all inputs of a policy template, same as fleet does + policy.inputs = policy.inputs.map((input) => ({ + ...input, + enabled: input.policy_template === 'cspm', + })); + policy.name = 'cloud_security_posture-1'; + + (useParams as jest.Mock).mockReturnValue({ + integration: 'vuln_mgmt', + }); + (useCspSetupStatusApi as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: { + vuln_mgmt: { status: 'not-deployed', healthyAgents: 0, installedPackagePolicies: 1 }, + }, + }) + ); render(); @@ -254,7 +321,7 @@ describe('', () => { isValid: true, updatedPolicy: { ...policy, - name: 'cspm-1', + name: 'vuln_mgmt-2', }, }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 0c5e237bfccb1..133f2c42997c3 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -43,6 +43,7 @@ import { PolicyTemplateVarsForm, } from './policy_template_selectors'; import { assert } from '../../../common/utils/helpers'; +import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; const DEFAULT_INPUT_TYPE = { kspm: CLOUDBEAT_VANILLA, @@ -96,7 +97,6 @@ export const CspPolicyTemplateForm = memo onChange({ isValid: true, updatedPolicy }), [onChange] ); - /** * - Updates policy inputs by user selection * - Updates hidden policy vars @@ -140,7 +140,13 @@ export const CspPolicyTemplateForm = memo +
    {isEditPage && } - {/* Defines the enabled policy template */} {!integration && ( <> @@ -195,11 +200,9 @@ export const CspPolicyTemplateForm = memo )} - {/* Shows info on the active policy template */} - {/* Defines the single enabled input of the active policy template */} - {/* Defines the name/description */} void; }) => { + const getSetupStatus = useCspSetupStatusApi(); + const installedPackagePolicyCount = Object.entries(getSetupStatus?.data ?? {})?.find( + ([key, _value]) => key === integration + )?.[1]?.installedPackagePolicies; + + const currentPackagePolicyCount = + typeof installedPackagePolicyCount === 'number' ? installedPackagePolicyCount + 1 : undefined; + useEffect(() => { if (!integration) return; if (isEditPage) return; if (isLoading) return; - const sequenceNumber = newPolicy.name.replace(/\D/g, ''); - const sequenceSuffix = sequenceNumber ? `-${sequenceNumber}` : ''; + + const sequenceSuffix = currentPackagePolicyCount ? `-${currentPackagePolicyCount}` : ''; const currentIntegrationName = `${integration}${sequenceSuffix}`; if (newPolicy.name === currentIntegrationName) { return; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx index 1a3ac98536943..814e5251238a2 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -5,10 +5,15 @@ * 2.0. */ import React from 'react'; -import { EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/common'; -import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../../common/constants'; +import { + CSPM_POLICY_TEMPLATE, + KSPM_POLICY_TEMPLATE, + VULN_MGMT_POLICY_TEMPLATE, + CNVM_POLICY_TEMPLATE, +} from '../../../common/constants'; import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils'; import { RadioGroup } from './csp_boxed_radio_group'; @@ -21,17 +26,26 @@ interface PolicyTemplateSelectorProps { disabled: boolean; } +const getPolicyTemplateLabel = (policyTemplate: CloudSecurityPolicyTemplate) => { + if (policyTemplate === VULN_MGMT_POLICY_TEMPLATE) { + return CNVM_POLICY_TEMPLATE.toUpperCase(); + } + return policyTemplate.toUpperCase(); +}; + export const PolicyTemplateSelector = ({ policy, selectedTemplate, setPolicyTemplate, disabled, }: PolicyTemplateSelectorProps) => { - const policyTemplates = new Set(policy.inputs.map((input) => input.policy_template!)); + const policyTemplates = new Set( + policy.inputs.map((input) => input.policy_template as CloudSecurityPolicyTemplate) + ); return (
    - + ({ id: v, label: v.toUpperCase() }))} + options={Array.from(policyTemplates, (v) => ({ id: v, label: getPolicyTemplateLabel(v) }))} idSelected={selectedTemplate} - onChange={(id) => setPolicyTemplate(id as CloudSecurityPolicyTemplate)} + onChange={(id: CloudSecurityPolicyTemplate) => setPolicyTemplate(id)} disabled={disabled} />
    @@ -69,7 +83,7 @@ interface PolicyTemplateInfoProps { } export const PolicyTemplateInfo = ({ postureType }: PolicyTemplateInfoProps) => ( - + {postureType === KSPM_POLICY_TEMPLATE && ( defaultMessage="Select the cloud service provider (CSP) you want to monitor and then fill in the name and description to help identify this integration" /> )} + {postureType === VULN_MGMT_POLICY_TEMPLATE && ( + <> + + } + > + +

    + +

    +
    +
    + + + + )}
    ); diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx index 3c6a286ba0e33..897de7df552b9 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx @@ -117,7 +117,7 @@ const IndexTimeout = () => (

    } @@ -125,13 +125,13 @@ const IndexTimeout = () => (

    ), diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx index 61c9fc8c407dd..705a0231fce10 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx @@ -13,6 +13,7 @@ import { EuiMarkdownFormat, EuiLink, EuiButton, + EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiImage, @@ -43,7 +44,7 @@ const ScanningVulnerabilitiesEmptyPrompt = () => (

    } @@ -51,7 +52,7 @@ const ScanningVulnerabilitiesEmptyPrompt = () => (

    } @@ -72,7 +73,7 @@ const VulnerabilitiesFindingsInstalledEmptyPrompt = ({ } @@ -82,17 +83,7 @@ const VulnerabilitiesFindingsInstalledEmptyPrompt = ({

    - - - ), - }} + defaultMessage="Add the Cloud Native Vulnerability Management integration to begin" />

    } @@ -102,10 +93,18 @@ const VulnerabilitiesFindingsInstalledEmptyPrompt = ({ + + + + + } /> @@ -129,13 +128,13 @@ const IndexTimeout = () => (

    ), @@ -206,10 +205,10 @@ export const NoVulnerabilitiesStates = () => { .sort((a, b) => a.localeCompare(b)); const render = () => { - if (status === 'not-deployed' || status === 'indexing' || status === 'waiting_for_results') + if (status === 'indexing' || status === 'waiting_for_results') return ; // integration installed, but no agents added if (status === 'index-timeout') return ; // agent added, index timeout has passed - if (status === 'not-installed') + if (status === 'not-deployed' || status === 'not-installed') return ( { @@ -32,11 +32,13 @@ export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => { .euiBadge__text { display: flex; } + display: flex; + width: 62px; `} > {versionDisplay && ( <> - {score} + {score < 10 ? score.toFixed(1) : score}


    { }; export const SeverityStatusBadge = ({ score, status }: SeverityStatusBadgeProps) => { - const color = getCvsScoreColor(score); + const color = score ? getCvsScoreColor(score) : undefined; return (
    - + {status}
    ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx similarity index 91% rename from x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx index c1b86258a46c9..c15e0ce87570f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx @@ -29,13 +29,14 @@ import { import { FormattedDate, FormattedTime } from '@kbn/i18n-react'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; +import { DASHBOARD_COMPLIANCE_SCORE_CHART } from '../test_subjects'; import { statusColors } from '../../../common/constants'; import { RULE_FAILED, RULE_PASSED } from '../../../../common/constants'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; import type { Evaluation, PostureTrend, Stats } from '../../../../common/types'; import { useKibana } from '../../../common/hooks/use_kibana'; -interface CloudPostureScoreChartProps { +interface ComplianceScoreChartProps { compact?: boolean; trend: PostureTrend[]; data: Stats; @@ -48,7 +49,7 @@ const getPostureScorePercentage = (postureScore: number): string => `${Math.roun const PercentageInfo = ({ compact, postureScore, -}: CloudPostureScoreChartProps['data'] & { compact?: CloudPostureScoreChartProps['compact'] }) => { +}: ComplianceScoreChartProps['data'] & { compact?: ComplianceScoreChartProps['compact'] }) => { const { euiTheme } = useEuiTheme(); const percentage = getPostureScorePercentage(postureScore); @@ -59,6 +60,7 @@ const PercentageInfo = ({ paddingLeft: compact ? euiTheme.size.s : euiTheme.size.xs, marginBottom: compact ? euiTheme.size.s : 'none', }} + data-test-subj={DASHBOARD_COMPLIANCE_SCORE_CHART.COMPLIANCE_SCORE} >

    {percentage}

    @@ -140,12 +142,12 @@ const CounterLink = ({ ); }; -export const CloudPostureScoreChart = ({ +export const ComplianceScoreChart = ({ data, trend, onEvalCounterClick, compact, -}: CloudPostureScoreChartProps) => { +}: ComplianceScoreChartProps) => { const { euiTheme } = useEuiTheme(); return ( @@ -173,7 +175,7 @@ export const CloudPostureScoreChart = ({ color={statusColors.passed} onClick={() => onEvalCounterClick(RULE_PASSED)} tooltipContent={i18n.translate( - 'xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip', + 'xpack.csp.complianceScoreChart.counterLink.passedFindingsTooltip', { defaultMessage: 'Passed findings' } )} /> @@ -184,7 +186,7 @@ export const CloudPostureScoreChart = ({ color={statusColors.failed} onClick={() => onEvalCounterClick(RULE_FAILED)} tooltipContent={i18n.translate( - 'xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip', + 'xpack.csp.complianceScoreChart.counterLink.failedFindingsTooltip', { defaultMessage: 'Failed findings' } )} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx index 4f95992472ab6..ac7048533ead5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx @@ -90,7 +90,9 @@ export const RisksTable = ({ compact ? css` thead { - display: none; + .euiTableCellContent { + padding: 0; + } } .euiTable .euiTableRow .euiTableRowCell { border-top: none; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx index 6c75891fc62af..fe4f62dbbc8f5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx @@ -31,6 +31,7 @@ import { KUBERNETES_DASHBOARD_CONTAINER, KUBERNETES_DASHBOARD_TAB, CLOUD_DASHBOARD_TAB, + CLOUD_POSTURE_DASHBOARD_PAGE_HEADER, } from './test_subjects'; import { useCspmStatsApi, useKspmStatsApi } from '../../common/api/use_stats_api'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; @@ -332,6 +333,7 @@ export const ComplianceDashboard = () => { return ( - - - navToFailedFindingsByClusterAndSection(cluster, resourceTypeName) - } - viewAllButtonTitle={i18n.translate( - 'xpack.csp.dashboard.risksTable.clusterCardViewAllButtonTitle', - { - defaultMessage: 'View all failed findings for this {postureAsset}', - values: { - postureAsset: - dashboardType === CSPM_POLICY_TEMPLATE ? 'cloud account' : 'cluster', - }, +
    + + navToFailedFindingsByClusterAndSection(cluster, resourceTypeName) } - )} - onViewAllClick={() => navToFailedFindingsByCluster(cluster)} - /> + viewAllButtonTitle={i18n.translate( + 'xpack.csp.dashboard.risksTable.clusterCardViewAllButtonTitle', + { + defaultMessage: 'View all failed findings for this {postureAsset}', + values: { + postureAsset: + dashboardType === CSPM_POLICY_TEMPLATE ? 'cloud account' : 'cluster', + }, + } + )} + onViewAllClick={() => navToFailedFindingsByCluster(cluster)} + /> +
    ))} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx index 647b022036676..bd6e46f018063 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx @@ -10,11 +10,11 @@ import { EuiFlexGroup, EuiFlexItem, EuiFlexItemProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { statusColors } from '../../../common/constants'; -import { DASHBOARD_COUNTER_CARDS } from '../test_subjects'; +import { DASHBOARD_COUNTER_CARDS, DASHBOARD_SUMMARY_CONTAINER } from '../test_subjects'; import { CspCounterCard, CspCounterCardProps } from '../../../components/csp_counter_card'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; import { ChartPanel } from '../../../components/chart_panel'; -import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart'; +import { ComplianceScoreChart } from '../compliance_charts/compliance_score_chart'; import type { ComplianceDashboardData, Evaluation, @@ -139,6 +139,7 @@ export const SummarySection = ({ // height for compliance by cis section with max rows height: 310px; `} + data-test-subj={DASHBOARD_SUMMARY_CONTAINER} > @@ -151,7 +152,7 @@ export const SummarySection = ({ - > = /> ), render: (complianceScore: FindingsByResourcePage['compliance_score'], data) => ( -
    - -
    + ), dataType: 'number', }, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_search_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_search_bar.tsx index df8c0df546957..22c2c8ff4d7a3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_search_bar.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_search_bar.tsx @@ -21,12 +21,16 @@ type SearchBarQueryProps = Pick; interface FindingsSearchBarProps { setQuery(v: Partial): void; loading: boolean; + placeholder?: string; } export const FindingsSearchBar = ({ dataView, loading, setQuery, + placeholder = i18n.translate('xpack.csp.findings.searchBar.searchPlaceholder', { + defaultMessage: 'Search findings (eg. rule.section : "API Server" )', + }), }: FindingsSearchBarProps & { dataView: DataView }) => { const { euiTheme } = useEuiTheme(); const { @@ -49,9 +53,7 @@ export const FindingsSearchBar = ({ indexPatterns={[dataView]} onQuerySubmit={setQuery} onFiltersUpdated={(value: Filter[]) => setQuery({ filters: value })} - placeholder={i18n.translate('xpack.csp.findings.searchBar.searchPlaceholder', { - defaultMessage: 'Search findings (eg. rule.section : "API Server" )', - })} + placeholder={placeholder} />
    ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/_mocks_/vulnerability.mock.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/_mocks_/vulnerability.mock.ts new file mode 100644 index 0000000000000..7a80bd66488cf --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/_mocks_/vulnerability.mock.ts @@ -0,0 +1,89 @@ +/* + * 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 { VulnerabilityRecord } from '../types'; + +export const mockVulnerabilityHit: VulnerabilityRecord = { + '@timestamp': '2023-03-30T10:27:35.013Z', + resource: { name: '634yfsdg2.dkr.ecr.eu-central-1.amazon.stage', id: 'ami_12328' }, + agent: { + name: 'ip-172-31-33-74', + type: 'cloudbeat', + version: '8.8.0', + ephemeral_id: '49f19e6a-94e9-4f2b-81e3-2f3794a74068', + id: 'd0313a94-c168-4d95-b1f0-97a388dac29a', + }, + cloud: { + availability_zone: 'eu-west-1c', + service: { name: 'EC2' }, + account: { id: '704479110758' }, + image: { id: 'ami-02dc8dbcc971f2c74' }, + provider: 'aws', + instance: { id: 'i-0fb7759c6e5d400cf' }, + machine: { type: 'c6g.medium' }, + region: 'eu-west-1', + }, + vulnerability: { + published_date: '2022-08-10T00:00:00.000Z', + data_source: { + ID: 'go-vulndb', + Name: 'The Go Vulnerability Database', + URL: 'https://github.com/golang/vulndb', + }, + package: { fixed_version: '0.4.0', version: 'v0.2.0', name: 'golang.org/x/net' }, + enumeration: 'CVE', + description: + 'An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests. HTTP/2 server connections contain a cache of HTTP header keys sent by the client. While the total number of entries in this cache is capped, an attacker sending very large keys can cause the server to allocate approximately 64 MiB per open connection.', + title: + 'golang: net/http: An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests', + reference: 'https://avd.aquasec.com/nvd/cve-2022-41717', + severity: 'MEDIUM', + cvss: { + nvd: { V3Vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', V3Score: 5.3 }, + redhat: { V3Vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', V3Score: 5.3 }, + ghsa: { V3Vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', V3Score: 5.3 }, + }, + scanner: { vendor: 'Trivy' }, + score: { base: 5.3, version: '3.0' }, + cwe: ['CWE-770'], + id: 'CVE-2022-41717', + classification: 'CVSS', + }, + cloudbeat: { + commit_sha: 'b5c4b728f0a9268e7f2d195c00dad0320c8a74e6', + commit_time: '2023-03-30T07:47:06Z', + version: '8.8.0', + }, + event: { + category: ['vulnerability'], + created: '2023-03-30T10:27:35.013537768Z', + id: '5cfbcbe5-7f90-47b8-b1d4-7f79313b2a6d', + kind: 'state', + sequence: 1680172055, + outcome: 'success', + type: ['info'], + }, + ecs: { version: '8.0.0' }, + host: { + os: { + kernel: '5.15.0-1028-aws', + codename: 'jammy', + type: 'linux', + platform: 'ubuntu', + version: '22.04.1 LTS (Jammy Jellyfish)', + family: 'debian', + name: 'Ubuntu', + }, + id: 'ec2644f440799ed0cf8aa595a9a105cc', + containerized: false, + name: 'ip-172-31-33-74', + ip: ['172.31.33.74', 'fe80::85d:f0ff:fe91:c01b'], + mac: ['0A-5D-F0-91-C0-1B'], + hostname: 'ip-172-31-33-74', + architecture: 'aarch64', + }, +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx index 798f66fa0dfca..7c73eccff12eb 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities.tsx @@ -13,6 +13,7 @@ import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constan import { useKibana } from '../../../common/hooks/use_kibana'; import { showErrorToast } from '../../../common/utils/show_error_toast'; import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants'; +import { FindingsBaseEsQuery } from '../../../common/types'; type LatestFindingsRequest = IKibanaSearchRequest; type LatestFindingsResponse = IKibanaSearchResponse>; @@ -20,13 +21,35 @@ interface FindingsAggs { count: estypes.AggregationsMultiBucketAggregateBase; } -export const getFindingsQuery = ({ query }: any) => ({ +interface VulnerabilitiesQuery extends FindingsBaseEsQuery { + sort: estypes.Sort; + enabled: boolean; +} + +export const getFindingsQuery = ({ query, sort }: VulnerabilitiesQuery) => ({ index: LATEST_VULNERABILITIES_INDEX_PATTERN, - query, + query: { + ...query, + bool: { + ...query?.bool, + filter: [ + ...(query?.bool?.filter || []), + { exists: { field: 'vulnerability.score.base' } }, + { exists: { field: 'vulnerability.score.version' } }, + { exists: { field: 'resource.name' } }, + { match_phrase: { 'vulnerability.enumeration': 'CVE' } }, + ], + must_not: [ + ...(query?.bool?.must_not || []), + { match_phrase: { 'vulnerability.severity': 'UNKNOWN' } }, + ], + }, + }, size: MAX_FINDINGS_TO_LOAD, + sort, }); -export const useLatestVulnerabilities = (options: any) => { +export const useLatestVulnerabilities = (options: VulnerabilitiesQuery) => { const { data, notifications: { toasts }, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.ts new file mode 100644 index 0000000000000..6a2e2a22d506d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const FINDINGS_VULNERABILITY_FLYOUT = 'vulnerability_findings_flyout'; +export const JSON_TAB_VULNERABILITY_FLYOUT = 'vulnerability_json_tab_flyout'; +export const OVERVIEW_TAB_VULNERABILITY_FLYOUT = 'vulnerability_overview_tab_flyout'; +export const SEVERITY_STATUS_VULNERABILITY_FLYOUT = 'vulnerability_severity_status_flyout'; +export const TAB_ID_VULNERABILITY_FLYOUT = (tabId: string) => + `vulnerability-finding-flyout-tab-${tabId}`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts new file mode 100644 index 0000000000000..b2c0ca0ca8366 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const FILTER_IN = i18n.translate('xpack.csp.vulnerabilities.table.filterIn', { + defaultMessage: 'Filter in', +}); +export const FILTER_OUT = i18n.translate('xpack.csp.vulnerabilities.table.filterOut', { + defaultMessage: 'Filter out', +}); +export const SEARCH_BAR_PLACEHOLDER = i18n.translate( + 'xpack.csp.vulnerabilities.searchBar.placeholder', + { + defaultMessage: 'Search vulnerabilities (eg. vulnerability.severity : "CRITICAL" )', + } +); +export const VULNERABILITIES = i18n.translate('xpack.csp.vulnerabilities', { + defaultMessage: 'Vulnerabilities', +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts index 8f01271677321..0d27161079f8e 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts @@ -77,10 +77,9 @@ export interface VulnerabilityRecord { } export interface Vulnerability { - published_at: string; + published_date: string; score: { version: string; - impact: number; base: number; }; cwe: string[]; @@ -107,7 +106,7 @@ export interface Vulnerability { package: { version: string; name: string; - fixed_version: string; + fixed_version?: string; }; } diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils.ts deleted file mode 100644 index 4e9c346e3969a..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils.ts +++ /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 { EuiDataGridColumn } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { VectorScoreBase, Vector } from './types'; - -export const vulnerabilitiesColumns = { - actions: 'actions', - vulnerability: 'vulnerability', - cvss: 'cvss', - resource: 'resource', - severity: 'severity', - package_version: 'package_version', - fix_version: 'fix_version', -}; - -const defaultColumnProps = () => ({ - isExpandable: false, - actions: { - showHide: false, - showMoveLeft: false, - showMoveRight: false, - }, -}); - -export const getVulnerabilitiesColumnsGrid = (): EuiDataGridColumn[] => { - return [ - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.actions, - initialWidth: 40, - display: [], - actions: false, - isSortable: false, - isResizable: false, - }, - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.vulnerability, - displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.vulnerability', { - defaultMessage: 'Vulnerability', - }), - initialWidth: 150, - isResizable: false, - }, - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.cvss, - displayAsText: 'CVSS', - initialWidth: 84, - isResizable: false, - }, - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.resource, - displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.resource', { - defaultMessage: 'Resource', - }), - }, - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.severity, - displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.severity', { - defaultMessage: 'Severity', - }), - initialWidth: 100, - }, - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.package_version, - displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.packageAndVersion', { - defaultMessage: 'Package and Version', - }), - }, - { - ...defaultColumnProps(), - id: vulnerabilitiesColumns.fix_version, - displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.fixVersion', { - defaultMessage: 'Fix Version', - }), - }, - ]; -}; - -export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => { - const result: Vector[] = []; - const v2Vector = vectorBaseScore?.V2Vector; - const v2Score = vectorBaseScore?.V2Score; - const v3Vector = vectorBaseScore?.V3Vector; - const v3Score = vectorBaseScore?.V3Score; - - if (v2Vector) { - result.push({ - version: '2.0', - vector: v2Vector, - score: v2Score, - }); - } - - if (v3Vector) { - result.push({ - version: '2.0', - vector: v3Vector, - score: v3Score, - }); - } - - return result; -}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/get_filters.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/get_filters.ts new file mode 100644 index 0000000000000..7f7d9ff544c62 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/get_filters.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 { + type Filter, + buildFilter, + FILTERS, + FilterStateStore, + compareFilters, + FilterCompareOptions, +} from '@kbn/es-query'; +import type { Serializable } from '@kbn/utility-types'; +import type { FindingsBaseProps } from '../../../common/types'; + +const compareOptions: FilterCompareOptions = { + negate: false, +}; + +/** + * adds a new filter to a new filters array + * removes existing filter if negated filter is added + * + * @returns {Filter[]} a new array of filters to be added back to filterManager + */ +export const getFilters = ({ + filters: existingFilters, + dataView, + field, + value, + negate, +}: { + filters: Filter[]; + dataView: FindingsBaseProps['dataView']; + field: string; + value: Serializable; + negate: boolean; +}): Filter[] => { + const dataViewField = dataView.fields.find((f) => f.spec.name === field); + if (!dataViewField) return existingFilters; + + const phraseFilter = buildFilter( + dataView, + dataViewField, + FILTERS.PHRASE, + negate, + false, + value, + null, + FilterStateStore.APP_STATE + ); + + const nextFilters = [ + ...existingFilters.filter( + // Exclude existing filters that match the newly added 'phraseFilter' + (filter) => !compareFilters(filter, phraseFilter, compareOptions) + ), + phraseFilter, + ]; + + return nextFilters; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/index.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/index.ts new file mode 100644 index 0000000000000..0f7a3b6f1477b --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/index.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 { VectorScoreBase, Vector } from '../types'; + +export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => { + const result: Vector[] = []; + const v2Vector = vectorBaseScore?.V2Vector; + const v2Score = vectorBaseScore?.V2Score; + const v3Vector = vectorBaseScore?.V3Vector; + const v3Score = vectorBaseScore?.V3Score; + + if (v2Vector) { + result.push({ + version: '2.0', + vector: v2Vector, + score: v2Score, + }); + } + + if (v3Vector) { + result.push({ + version: '3.0', + vector: v3Vector, + score: v3Score, + }); + } + + return result; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx index 19f0cb88aa219..38b9dfcacf9d0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities.tsx @@ -9,19 +9,20 @@ import { EuiButtonIcon, EuiDataGrid, EuiDataGridCellValueElementProps, - EuiLoadingSpinner, + EuiDataGridColumnCellAction, + EuiProgress, EuiSpacer, + EuiToolTip, useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { DataView } from '@kbn/data-views-plugin/common'; -import React, { useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY } from '../../common/constants'; import { useCloudPostureTable } from '../../common/hooks/use_cloud_posture_table'; import { useLatestVulnerabilities } from './hooks/use_latest_vulnerabilities'; import { VulnerabilityRecord } from './types'; -import { getVulnerabilitiesColumnsGrid, vulnerabilitiesColumns } from './utils'; import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../common/constants'; import { ErrorCallout } from '../configurations/layout/error_callout'; import { FindingsSearchBar } from '../configurations/layout/findings_search_bar'; @@ -31,6 +32,15 @@ import { EmptyState } from '../../components/empty_state'; import { VulnerabilityFindingFlyout } from './vulnerabilities_finding_flyout/vulnerability_finding_flyout'; import { NoVulnerabilitiesStates } from '../../components/no_vulnerabilities_states'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; +import { useLimitProperties } from '../../common/utils/get_limit_properties'; +import { LimitedResultsBar } from '../configurations/layout/findings_layout'; +import { + getVulnerabilitiesColumnsGrid, + vulnerabilitiesColumns, +} from './vulnerabilities_table_columns'; +import { defaultLoadingRenderer, defaultNoDataRenderer } from '../../components/cloud_posture_page'; +import { getFilters } from './utils/get_filters'; +import { FILTER_IN, FILTER_OUT, SEARCH_BAR_PLACEHOLDER, VULNERABILITIES } from './translations'; const getDefaultQuery = ({ query, filters }: any): any => ({ query, @@ -48,8 +58,12 @@ export const Vulnerabilities = () => { if (error) { return ; } - if (isLoading || !data) { - return ; + if (isLoading) { + return defaultLoadingRenderer(); + } + + if (!data) { + return defaultNoDataRenderer(); } return ; @@ -65,6 +79,7 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { onChangeItemsPerPage, onChangePage, onSort, + urlQuery, setUrlQuery, onResetFilters, } = useCloudPostureTable({ @@ -74,26 +89,124 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { }); const { euiTheme } = useEuiTheme(); - const { data, isLoading } = useLatestVulnerabilities({ + const multiFieldsSort = useMemo(() => { + return sort.map(({ id, direction }: { id: string; direction: string }) => ({ + [id]: direction, + })); + }, [sort]); + + const { data, isLoading, isFetching } = useLatestVulnerabilities({ query, - sort, + sort: multiFieldsSort, enabled: !queryError, }); - const [isVulnerabilityDetailFlyoutVisible, setIsVulnerabilityDetailFlyoutVisible] = - useState(false); - - const [vulnerability, setVulnerability] = useState(); + const invalidIndex = -1; + const selectedVulnerability = data?.page[urlQuery.vulnerabilityIndex]; - const showFlyout = (vulnerabilityRecord: VulnerabilityRecord) => { - setIsVulnerabilityDetailFlyoutVisible(true); - setVulnerability(vulnerabilityRecord); + const onCloseFlyout = () => { + setUrlQuery({ + vulnerabilityIndex: invalidIndex, + }); }; - const hideFlyout = () => { - setIsVulnerabilityDetailFlyoutVisible(false); - setVulnerability(undefined); - }; + const onOpenFlyout = useCallback( + (rowIndex: number) => { + setUrlQuery({ + vulnerabilityIndex: rowIndex, + }); + }, + [setUrlQuery] + ); + + const { isLastLimitedPage, limitedTotalItemCount } = useLimitProperties({ + total: data?.total, + pageIndex, + pageSize, + }); + + const columns = useMemo(() => { + const getColumnIdValue = (rowIndex: number, columnId: string) => { + const vulnerabilityRow = data?.page[rowIndex] as VulnerabilityRecord; + if (columnId === vulnerabilitiesColumns.vulnerability) { + return vulnerabilityRow.vulnerability.id; + } + if (columnId === vulnerabilitiesColumns.cvss) { + return vulnerabilityRow.vulnerability.score.base; + } + if (columnId === vulnerabilitiesColumns.resource) { + return vulnerabilityRow.resource?.name; + } + if (columnId === vulnerabilitiesColumns.severity) { + return vulnerabilityRow.vulnerability.severity; + } + if (columnId === vulnerabilitiesColumns.package_version) { + return vulnerabilityRow.vulnerability?.package?.name; + } + if (columnId === vulnerabilitiesColumns.fix_version) { + return vulnerabilityRow.vulnerability.package?.fixed_version; + } + }; + + const cellActions: EuiDataGridColumnCellAction[] = [ + ({ Component, rowIndex, columnId }) => { + const value = getColumnIdValue(rowIndex, columnId); + + if (!value) return null; + return ( + + { + setUrlQuery({ + pageIndex: 0, + filters: getFilters({ + filters: urlQuery.filters, + dataView, + field: columnId, + value, + negate: false, + }), + }); + }} + > + {FILTER_IN} + + + ); + }, + ({ Component, rowIndex, columnId }) => { + const value = getColumnIdValue(rowIndex, columnId); + + if (!value) return null; + return ( + + { + setUrlQuery({ + pageIndex: 0, + filters: getFilters({ + filters: urlQuery.filters, + dataView, + field: columnId, + value: getColumnIdValue(rowIndex, columnId), + negate: true, + }), + }); + }} + > + {FILTER_OUT} + + + ); + }, + ]; + + return getVulnerabilitiesColumnsGrid(cellActions); + }, [data?.page, dataView, setUrlQuery, urlQuery.filters]); const renderCellValue = useMemo(() => { return ({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => { @@ -108,13 +221,13 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { iconType="expand" aria-label="View" onClick={() => { - showFlyout(vulnerabilityRow); + onOpenFlyout(rowIndex); }} /> ); } if (columnId === vulnerabilitiesColumns.vulnerability) { - return vulnerabilityRow.vulnerability.id || null; + return vulnerabilityRow.vulnerability.id || ''; } if (columnId === vulnerabilitiesColumns.cvss) { if (!vulnerabilityRow.vulnerability.score?.base) { @@ -131,15 +244,12 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { return vulnerabilityRow.resource?.name || null; } if (columnId === vulnerabilitiesColumns.severity) { - if ( - !vulnerabilityRow.vulnerability.score?.base || - !vulnerabilityRow.vulnerability.severity - ) { + if (!vulnerabilityRow.vulnerability.severity) { return null; } return ( ); @@ -153,26 +263,51 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { ); } if (columnId === vulnerabilitiesColumns.fix_version) { + if (!vulnerabilityRow.vulnerability.package?.fixed_version) { + return null; + } return ( <> {vulnerabilityRow.vulnerability.package?.name}{' '} - {vulnerabilityRow.vulnerability.package?.fixed_version} + {vulnerabilityRow.vulnerability.package.fixed_version} ); } }; - }, [data?.page]); + }, [data?.page, onOpenFlyout]); + + const onPaginateFlyout = useCallback( + (nextVulnerabilityIndex: number) => { + // the index of the vulnerability in the current page + const newVulnerabilityIndex = nextVulnerabilityIndex % pageSize; + + // if the vulnerability is not in the current page, we need to change the page + const flyoutPageIndex = Math.floor(nextVulnerabilityIndex / pageSize); + + setUrlQuery({ + pageIndex: flyoutPageIndex, + vulnerabilityIndex: newVulnerabilityIndex, + }); + }, + [pageSize, setUrlQuery] + ); + + const flyoutVulnerabilityIndex = urlQuery?.vulnerabilityIndex; const error = queryError || null; if (error) { return ; } - if (isLoading || !data?.page) { - return ; + if (isLoading) { + return defaultLoadingRenderer(); + } + + if (!data?.page) { + return defaultNoDataRenderer(); } - const columns = getVulnerabilitiesColumnsGrid(); + const showVulnerabilityFlyout = flyoutVulnerabilityIndex > invalidIndex; return ( <> @@ -182,12 +317,22 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { setUrlQuery({ ...newQuery, pageIndex: 0 }); }} loading={isLoading} + placeholder={SEARCH_BAR_PLACEHOLDER} /> {!isLoading && data.page.length === 0 ? ( ) : ( <> + {isFetching ? ( + + ) : ( + + )} { & .euiDataGridRowCell { font-size: ${euiTheme.size.m}; } + & + .euiDataGridRowCell__expandActions + > [data-test-subj='euiDataGridCellExpandButton'] { + display: none; + } + & .euiDataGridRowCell__expandFlex { + align-items: center; + } `} - aria-label="Data grid styling demo" + aria-label={VULNERABILITIES} columns={columns} columnVisibility={{ visibleColumns: columns.map(({ id }) => id), setVisibleColumns: () => {}, }} - rowCount={data?.total} + rowCount={limitedTotalItemCount} toolbarVisibility={{ showColumnSelector: false, showDisplaySelector: false, @@ -236,7 +389,7 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { header: 'underline', }} renderCellValue={renderCellValue} - inMemory={{ level: 'sorting' }} + inMemory={{ level: 'pagination' }} sorting={{ columns: sort, onSort }} pagination={{ pageIndex, @@ -246,11 +399,14 @@ const VulnerabilitiesContent = ({ dataView }: { dataView: DataView }) => { onChangePage, }} /> - {/* Todo: Add Pagination */} - {isVulnerabilityDetailFlyoutVisible && !!vulnerability && ( + {isLastLimitedPage && } + {showVulnerabilityFlyout && ( )} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.test.tsx new file mode 100644 index 0000000000000..7cb370ced7197 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.test.tsx @@ -0,0 +1,107 @@ +/* + * 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 userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; +import { TestProvider } from '../../../test/test_provider'; +import { VulnerabilityFindingFlyout } from './vulnerability_finding_flyout'; +import { mockVulnerabilityHit } from '../_mocks_/vulnerability.mock'; +import { VulnerabilityOverviewTab } from './vulnerability_overview_tab'; +import moment from 'moment'; + +const onPaginate = jest.fn(); + +const TestComponent = ({ ...overrideProps }) => ( + + + +); + +describe('', () => { + describe('Header Info', () => { + it('displays text details flyout header info', () => { + const { getAllByText, getByText } = render(); + + getAllByText(mockVulnerabilityHit.vulnerability.id); + getByText(mockVulnerabilityHit.vulnerability.description); + getByText( + `${mockVulnerabilityHit.resource?.name} | ${mockVulnerabilityHit.vulnerability.package.name} ${mockVulnerabilityHit.vulnerability.package.version}` + ); + getByText(mockVulnerabilityHit.vulnerability.severity); + }); + }); + + describe('JSON Tab', () => { + it('show display Vulnerability JSON Tab', () => { + const { getAllByText } = render(); + + userEvent.click(screen.getByTestId(`vulnerability-finding-flyout-tab-json`)); + + getAllByText('JSON'); + }); + }); + + describe('Overview Summary Details Tab', () => { + it('show display Vulnerability details in a Overview Tab', () => { + const { getByText, getAllByText } = render( + + + + ); + getByText(mockVulnerabilityHit.vulnerability.data_source.ID); + getByText(moment(mockVulnerabilityHit.vulnerability.published_date).format('LL').toString()); + getByText(mockVulnerabilityHit.vulnerability.description); + getAllByText(mockVulnerabilityHit.vulnerability?.cvss?.nvd?.V3Vector as string); + getAllByText(mockVulnerabilityHit.vulnerability?.cvss?.nvd?.V3Score?.toString() as string); + getAllByText(mockVulnerabilityHit.vulnerability?.cvss?.redhat?.V3Vector as string); + getAllByText(mockVulnerabilityHit.vulnerability?.cvss?.redhat?.V3Score?.toString() as string); + getAllByText(mockVulnerabilityHit.vulnerability?.cvss?.ghsa?.V3Vector?.toString() as string); + getAllByText(mockVulnerabilityHit.vulnerability?.cvss?.ghsa?.V3Score?.toString() as string); + getByText( + `${mockVulnerabilityHit.vulnerability.package.name} ${mockVulnerabilityHit.vulnerability.package.fixed_version}` + ); + }); + + it('show empty state for no fixes', () => { + const { getByText } = render( + + + + ); + + getByText(`No available fixes yet.`); + }); + }); + + it('should allow pagination with next', async () => { + const { getByTestId } = render(); + + userEvent.click(getByTestId('pagination-button-next')); + + expect(onPaginate).toHaveBeenCalledWith(1); + }); + + it('should allow pagination with previous', async () => { + const { getByTestId } = render(); + + userEvent.click(getByTestId('pagination-button-previous')); + + expect(onPaginate).toHaveBeenCalledWith(0); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx index b29239f2dc275..5ea65846950bf 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_finding_flyout.tsx @@ -11,8 +11,10 @@ import { EuiFlexItem, EuiFlyout, EuiFlyoutBody, + EuiFlyoutFooter, EuiFlyoutHeader, EuiLink, + EuiPagination, EuiTab, EuiTabs, EuiTitle, @@ -25,15 +27,25 @@ import { VulnerabilityOverviewTab } from './vulnerability_overview_tab'; import { VulnerabilityJsonTab } from './vulnerability_json_tab'; import { SeverityStatusBadge } from '../../../components/vulnerability_badges'; import { VulnerabilityRecord } from '../types'; +import { + SEVERITY_STATUS_VULNERABILITY_FLYOUT, + TAB_ID_VULNERABILITY_FLYOUT, +} from '../test_subjects'; const overviewTabId = 'overview'; const jsonTabId = 'json'; export const VulnerabilityFindingFlyout = ({ closeFlyout, + onPaginate, + totalVulnerabilitiesCount, + flyoutIndex, vulnerabilityRecord, }: { closeFlyout: () => void; + onPaginate: (pageIndex: number) => void; + totalVulnerabilitiesCount: number; + flyoutIndex?: number; vulnerabilityRecord: VulnerabilityRecord; }) => { const [selectedTabId, setSelectedTabId] = useState(overviewTabId); @@ -71,6 +83,7 @@ export const VulnerabilityFindingFlyout = ({ const renderTabs = () => tabs.map((tab, index) => ( onSelectedTabChanged(tab.id)} isSelected={tab.id === selectedTabId} key={index} @@ -86,9 +99,7 @@ export const VulnerabilityFindingFlyout = ({ const nvdDomain = 'https://nvd'; const nvdWebsite = `${nvdDomain}.nist.gov/vuln/detail/${vulnerabilityRecord?.vulnerability?.id}`; - const vulnerabilityReference = vulnerability?.reference.startsWith(nvdDomain) - ? vulnerability?.reference - : nvdWebsite; + const vulnerabilityReference = vulnerability?.cvss?.nvd ? nvdWebsite : vulnerability?.reference; return ( @@ -99,9 +110,9 @@ export const VulnerabilityFindingFlyout = ({ gap: ${euiThemeVars.euiSizeS}; `} > - + @@ -143,7 +154,18 @@ export const VulnerabilityFindingFlyout = ({
    {selectedTabContent} - {/* Todo: Add Pagination */} + + + + + + + ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_json_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_json_tab.tsx index 99f0da1eb4454..7aa78a5b8b726 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_json_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_json_tab.tsx @@ -9,13 +9,17 @@ import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import React from 'react'; import { XJsonLang } from '@kbn/monaco'; import { VulnerabilityRecord } from '../types'; +import { JSON_TAB_VULNERABILITY_FLYOUT } from '../test_subjects'; interface VulnerabilityJsonTabProps { vulnerabilityRecord: VulnerabilityRecord; } export const VulnerabilityJsonTab = ({ vulnerabilityRecord }: VulnerabilityJsonTabProps) => { const offsetHeight = 188; return ( -
    +
    = { nvd: 'NVD', @@ -114,24 +115,24 @@ const VulnerabilityOverviewTiles = ({ vulnerability }: VulnerabilityTabProps) => margin-bottom: 6px; `; - const date = moment(vulnerability?.published_at).format('LL').toString(); + const date = moment(vulnerability?.published_date).format('LL').toString(); return ( - - - - - -
    - -
    -
    + + {vulnerability?.score?.version && vulnerability?.score?.base && ( + + + + + +
    + +
    +
    + )} (vulnerability?.cvss).map( - ([vendor, vectorScoreBase]: [string, VectorScoreBase]) => { - return ( - - - - ); - } - ); + const cvssScores: JSX.Element[] = vulnerability?.cvss + ? Object.entries(vulnerability.cvss).map( + ([vendor, vectorScoreBase]: [string, VectorScoreBase]) => { + return ( + + + + ); + } + ) + : []; const horizontalStyle = css` margin-block: 12px; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts new file mode 100644 index 0000000000000..bfa23ac440cd6 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_table_columns.ts @@ -0,0 +1,93 @@ +/* + * 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 { EuiDataGridColumn, EuiDataGridColumnCellAction } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export const vulnerabilitiesColumns = { + actions: 'actions', + vulnerability: 'vulnerability.id', + cvss: 'vulnerability.score.base', + resource: 'resource.name', + severity: 'vulnerability.severity', + package_version: 'vulnerability.package.name', + fix_version: 'vulnerability.package.fixed_version', +}; + +const defaultColumnProps = () => ({ + isExpandable: false, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + }, +}); + +export const getVulnerabilitiesColumnsGrid = ( + cellActions: EuiDataGridColumnCellAction[] +): EuiDataGridColumn[] => [ + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.actions, + initialWidth: 40, + display: [], + actions: false, + isSortable: false, + isResizable: false, + cellActions: [], + }, + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.vulnerability, + displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.vulnerability', { + defaultMessage: 'Vulnerability', + }), + initialWidth: 130, + cellActions, + }, + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.cvss, + displayAsText: 'CVSS', + initialWidth: 80, + isResizable: false, + cellActions, + }, + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.resource, + displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.resource', { + defaultMessage: 'Resource', + }), + cellActions, + }, + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.severity, + displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.severity', { + defaultMessage: 'Severity', + }), + initialWidth: 100, + cellActions, + }, + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.package_version, + displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.packageAndVersion', { + defaultMessage: 'Package and Version', + }), + cellActions, + }, + { + ...defaultColumnProps(), + id: vulnerabilitiesColumns.fix_version, + displayAsText: i18n.translate('xpack.csp.vulnerabilityTable.column.fixVersion', { + defaultMessage: 'Fix Version', + }), + cellActions, + }, +]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilties.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilties.test.tsx index f083eaf8796aa..1b2a88446b8f9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilties.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilties.test.tsx @@ -84,9 +84,10 @@ describe('', () => { renderVulnerabilitiesPage(); expectIdsInDoc({ - be: [NO_VULNERABILITIES_STATUS_TEST_SUBJ.SCANNING_VULNERABILITIES], + be: [VULN_MGMT_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT], notToBe: [ VULNERABILITIES_CONTAINER_TEST_SUBJ, + NO_VULNERABILITIES_STATUS_TEST_SUBJ.SCANNING_VULNERABILITIES, NO_VULNERABILITIES_STATUS_TEST_SUBJ.INDEX_TIMEOUT, NO_VULNERABILITIES_STATUS_TEST_SUBJ.UNPRIVILEGED, ], diff --git a/x-pack/plugins/cloud_security_posture/server/create_indices/create_indices.ts b/x-pack/plugins/cloud_security_posture/server/create_indices/create_indices.ts index f040980dc59ad..c269e201f4243 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_indices/create_indices.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_indices/create_indices.ts @@ -228,6 +228,13 @@ const updateIndexSafe = async ( index: string, mappings: MappingTypeMapping ) => { + // for now, remove from object so as not to update stream or data stream properties of the index until type and name + // are added in https://github.com/elastic/kibana/issues/66551. namespace value we will continue + // to skip updating and assume the value in the index mapping is correct + if (mappings && mappings.properties) { + delete mappings.properties.stream; + delete mappings.properties.data_stream; + } try { await esClient.indices.putMapping({ index, diff --git a/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_vulnerabilities_transforms.ts b/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_vulnerabilities_transforms.ts index 9af2d91a2b730..fb30631cab240 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_vulnerabilities_transforms.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_transforms/latest_vulnerabilities_transforms.ts @@ -37,7 +37,12 @@ export const latestVulnerabilitiesTransform: TransformPutTransformRequest = { }, latest: { sort: '@timestamp', - unique_key: ['vulnerability.id', 'resource.id', 'package.version'], + unique_key: [ + 'vulnerability.id', + 'resource.id', + 'vulnerability.package.name', + 'vulnerability.package.version', + ], }, _meta: { package: { diff --git a/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts b/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts index 47ab871dc9d21..273ead42f4443 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts @@ -12,22 +12,19 @@ export const checkIndexStatus = async ( esClient: ElasticsearchClient, index: string, logger: Logger, - postureType: PostureTypes = 'all' + postureType?: PostureTypes ): Promise => { - const query = - postureType === 'all' - ? { - match_all: {}, - } - : { - bool: { - filter: { - term: { - 'rule.benchmark.posture_type': postureType, - }, + const query = !postureType + ? undefined + : { + bool: { + filter: { + term: { + 'rule.benchmark.posture_type': postureType, }, }, - }; + }, + }; try { const queryResult = await esClient.search({ diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/indices_stats_collector.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/indices_stats_collector.ts index fabd8cd6581bc..7797bdd521cc3 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/indices_stats_collector.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/indices_stats_collector.ts @@ -14,6 +14,8 @@ import { BENCHMARK_SCORE_INDEX_DEFAULT_NS, FINDINGS_INDEX_DEFAULT_NS, LATEST_FINDINGS_INDEX_DEFAULT_NS, + LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, + VULNERABILITIES_INDEX_DEFAULT_NS, } from '../../../../common/constants'; const getIndexDocCount = (esClient: ElasticsearchClient, index: string) => @@ -76,9 +78,11 @@ export const getIndicesStats = async ( coreServices: Promise<[CoreStart, CspServerPluginStartDeps, CspServerPluginStart]>, logger: Logger ): Promise => { - const [findings, latestFindings, score] = await Promise.all([ + const [findings, latestFindings, vulMng, vulMngLatest, score] = await Promise.all([ getIndexStats(esClient, FINDINGS_INDEX_DEFAULT_NS, logger), getIndexStats(esClient, LATEST_FINDINGS_INDEX_DEFAULT_NS, logger), + getIndexStats(esClient, VULNERABILITIES_INDEX_DEFAULT_NS, logger), + getIndexStats(esClient, LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, logger), getIndexStats(esClient, BENCHMARK_SCORE_INDEX_DEFAULT_NS, logger), ]); @@ -100,6 +104,8 @@ export const getIndicesStats = async ( return { findings, latest_findings: latestFindings, + vulnerabilities: vulMng, + latest_vulnerabilities: vulMngLatest, score, latestPackageVersion: status.latestPackageVersion, diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/schema.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/schema.ts index 77f24d98f24a6..4fb7bfee22692 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/schema.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/schema.ts @@ -38,6 +38,34 @@ export const cspmUsageSchema: MakeSchemaFrom = { type: 'date', }, }, + vulnerabilities: { + doc_count: { + type: 'long', + }, + deleted: { + type: 'long', + }, + size_in_bytes: { + type: 'long', + }, + last_doc_timestamp: { + type: 'date', + }, + }, + latest_vulnerabilities: { + doc_count: { + type: 'long', + }, + deleted: { + type: 'long', + }, + size_in_bytes: { + type: 'long', + }, + last_doc_timestamp: { + type: 'date', + }, + }, score: { doc_count: { type: 'long', diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts index ebfc02b7c3e3a..fe24101dfa8d4 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { BaseCspSetupBothPolicy } from '../../../../common/types'; +import { CspStatusCode } from '../../../../common/types'; export interface CspmUsage { indices: CspmIndicesStats; @@ -14,14 +14,22 @@ export interface CspmUsage { rules_stats: CspmRulesStats[]; } +export interface PackageSetupStatus { + status: CspStatusCode; + installedPackagePolicies: number; + healthyAgents: number; +} + export interface CspmIndicesStats { findings: IndexStats | {}; latest_findings: IndexStats | {}; + vulnerabilities: IndexStats | {}; + latest_vulnerabilities: IndexStats | {}; score: IndexStats | {}; latestPackageVersion: string; - cspm: BaseCspSetupBothPolicy; - kspm: BaseCspSetupBothPolicy; - vuln_mgmt: BaseCspSetupBothPolicy; + cspm: PackageSetupStatus; + kspm: PackageSetupStatus; + vuln_mgmt: PackageSetupStatus; } export interface IndexStats { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts b/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts index aa98b0e5e5bfd..69ae5eae2a414 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts @@ -167,12 +167,7 @@ export const getCspStatus = async ({ checkIndexStatus(esClient, FINDINGS_INDEX_PATTERN, logger, 'kspm'), checkIndexStatus(esClient, BENCHMARK_SCORE_INDEX_DEFAULT_NS, logger, 'kspm'), - checkIndexStatus( - esClient, - LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, - logger, - VULN_MGMT_POLICY_TEMPLATE - ), + checkIndexStatus(esClient, LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, logger), checkIndexStatus(esClient, VULNERABILITIES_INDEX_PATTERN, logger, VULN_MGMT_POLICY_TEMPLATE), packageService.asInternalUser.getInstallation(CLOUD_SECURITY_POSTURE_PACKAGE_NAME), diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.ts index 92b709db1825c..e218808caa420 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsServiceSetup } from '@kbn/core/server'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { cspRuleTemplateSavedObjectMapping } from './mappings'; import { cspRuleTemplateMigrations } from './migrations'; import { @@ -20,6 +21,7 @@ import { CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE } from '../../common/constants'; export function setupSavedObjects(savedObjects: SavedObjectsServiceSetup) { savedObjects.registerType({ name: CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index a0fe377baf69c..468ef332df26f 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -46,6 +46,7 @@ "@kbn/ecs", "@kbn/core-saved-objects-api-server", "@kbn/shared-ux-router", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx index 54cbeb28cf6f3..0f3dc78f97487 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx @@ -8,14 +8,14 @@ import { EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { DVErrorObject } from '../../../../../index_data_visualizer/utils/error_utils'; +import { MLErrorObject } from '@kbn/ml-error-utils'; export const ErrorMessageContent = ({ fieldName, error, }: { fieldName: string; - error: DVErrorObject; + error: MLErrorObject; }) => { return ( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts index ced7bf1058762..3419b1674fddc 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts @@ -16,6 +16,7 @@ import type { IKibanaSearchResponse, ISearchOptions, } from '@kbn/data-plugin/common'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { useDataVisualizerKibana } from '../../kibana_context'; import { AggregatableFieldOverallStats, @@ -29,7 +30,6 @@ import { } from '../search_strategy/requests/overall_stats'; import type { OverallStats } from '../types/overall_stats'; import { getDefaultPageState } from '../components/index_data_visualizer_view/index_data_visualizer_view'; -import { extractErrorProperties } from '../utils/error_utils'; import { DataStatsFetchProgress, isRandomSamplingOption, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts index bf941e0952657..e394f6456d8f5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts @@ -15,6 +15,7 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; @@ -25,7 +26,6 @@ import type { FieldStatsCommonRequestParams, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getBooleanFieldsStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts index 863cd6885fe88..4bd914f3637e5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts @@ -16,11 +16,11 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import type { FieldStatsCommonRequestParams } from '../../../../../common/types/field_stats'; import type { Field, DateFieldStats, Aggs } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getDateFieldsStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts index df7afb16479f0..3943979d290d9 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts @@ -17,6 +17,7 @@ import type { import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { buildBaseFilterCriteria } from '@kbn/ml-query-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { getUniqGeoOrStrExamples } from '../../../common/util/example_utils'; import type { Field, @@ -24,7 +25,6 @@ import type { FieldStatsCommonRequestParams, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; import { MAX_EXAMPLES_DEFAULT } from './constants'; export const getFieldExamplesRequest = (params: FieldStatsCommonRequestParams, field: Field) => { diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts index b9dd55351781f..f7d1b39f15d3f 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts @@ -18,6 +18,7 @@ import type { import type { ISearchStart } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { isDefined } from '@kbn/ml-is-defined'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import { MAX_PERCENT, PERCENTILE_SPACING, SAMPLER_TOP_TERMS_THRESHOLD } from './constants'; @@ -32,7 +33,6 @@ import type { FieldStatsError, } from '../../../../../common/types/field_stats'; import { processDistributionData } from '../../utils/process_distribution_data'; -import { extractErrorProperties } from '../../utils/error_utils'; import { isIKibanaSearchResponse, isNormalSamplingOption, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts index a035842fa8767..159be48b338e4 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts @@ -16,6 +16,7 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import { SAMPLER_TOP_TERMS_THRESHOLD } from './constants'; @@ -26,7 +27,6 @@ import type { StringFieldStats, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getStringFieldStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts deleted file mode 100644 index 06a9b0b4002a6..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts +++ /dev/null @@ -1,184 +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 { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; - -export interface WrappedError { - body: { - attributes: { - body: EsErrorBody; - }; - message: Boom.Boom; - }; - statusCode: number; -} - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface DVResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface DVErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface DVHttpFetchError extends IHttpFetchError { - body: T; -} - -export type ErrorType = - | WrappedError - | DVHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isDVResponseError(error: any): error is DVResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} - -export function isWrappedError(error: any): error is WrappedError { - return error && isBoomError(error.body?.message) === true; -} - -export const extractErrorProperties = (error: ErrorType): DVErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own DV messages - - // some responses contain raw es errors as part of a bulk response - // e.g. if some jobs fail the action in a bulk request - - if (isEsErrorBody(error)) { - return { - message: error.error.reason, - statusCode: error.status, - fullError: error, - }; - } - - if (isErrorString(error)) { - return { - message: error, - }; - } - if (isWrappedError(error)) { - return error.body.message?.output?.payload; - } - - if (isBoomError(error)) { - return { - message: error.output.payload.message, - statusCode: error.output.payload.statusCode, - }; - } - - if (error?.body === undefined && !error?.message) { - return { - message: '', - }; - } - - if (typeof error.body === 'string') { - return { - message: error.body, - }; - } - - if (isDVResponseError(error)) { - if ( - typeof error.body.attributes === 'object' && - typeof error.body.attributes.body?.error?.reason === 'string' - ) { - const errObj: DVErrorObject = { - message: error.body.attributes.body.error.reason, - statusCode: error.body.statusCode, - fullError: error.body.attributes.body, - }; - if ( - typeof error.body.attributes.body.error.caused_by === 'object' && - (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || - typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') - ) { - errObj.causedBy = - error.body.attributes.body.error.caused_by?.caused_by?.reason || - error.body.attributes.body.error.caused_by?.reason; - } - if ( - Array.isArray(error.body.attributes.body.error.root_cause) && - typeof error.body.attributes.body.error.root_cause[0] === 'object' && - isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) - ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; - errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; - } - return errObj; - } else { - return { - message: error.body.message, - statusCode: error.body.statusCode, - }; - } - } - - if (isErrorMessage(error)) { - return { - message: error.message, - }; - } - - // If all else fail return an empty message instead of JSON.stringify - return { - message: '', - }; -}; diff --git a/x-pack/plugins/data_visualizer/tsconfig.json b/x-pack/plugins/data_visualizer/tsconfig.json index 7f38e7b9b77cb..4609d1e9497d2 100644 --- a/x-pack/plugins/data_visualizer/tsconfig.json +++ b/x-pack/plugins/data_visualizer/tsconfig.json @@ -17,7 +17,6 @@ "@kbn/cloud-chat-plugin", "@kbn/cloud-plugin", "@kbn/core-execution-context-common", - "@kbn/core-http-browser", "@kbn/core", "@kbn/custom-integrations-plugin", "@kbn/data-plugin", @@ -60,6 +59,7 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/enterprise_search/README.md b/x-pack/plugins/enterprise_search/README.md index 5694258001dd4..af0cdd43d97b8 100644 --- a/x-pack/plugins/enterprise_search/README.md +++ b/x-pack/plugins/enterprise_search/README.md @@ -89,29 +89,13 @@ Cypress tests can be run directly from the `x-pack/plugins/enterprise_search` fo ```bash # Basic syntax -sh cypress.sh {run|open} {suite} +sh cypress.sh {run|open|dev} # Examples -sh cypress.sh run overview # run Enterprise Search overview tests -sh cypress.sh open overview # open Enterprise Search overview tests +sh cypress.sh run # run Enterprise Search tests +sh cypress.sh open # open Enterprise Search tests +sh cypress.sh dev # run "cypress only" with Enterprise Search config -sh cypress.sh run as # run App Search tests -sh cypress.sh open as # open App Search tests - -sh cypress.sh run ws # run Workplace Search tests -sh cypress.sh open ws # open Workplace Search tests - -# Overriding env variables -sh cypress.sh open as --env username=enterprise_search password=123 - -# Overriding config settings, e.g. changing the base URL to a dev path, or enabling video recording -sh cypress.sh open as --config baseUrl=http://localhost:5601/xyz video=true - -# Only run a single specific test file -sh cypress.sh run ws --spec '**/example.spec.ts' - -# Opt to run Chrome headlessly -sh cypress.sh run ws --headless ``` There are 3 ways you can spin up the required environments to run our Cypress tests: @@ -126,13 +110,8 @@ There are 3 ways you can spin up the required environments to run our Cypress te - Enterprise Search: - Nothing extra is required to run Cypress tests, only what is already needed to run Kibana/Enterprise Search locally. 2. Running Cypress against Kibana's functional test server: - - :information_source: While we won't use the runner, we can still make use of Kibana's functional test server to help us spin up Elasticsearch and Kibana instances. - - NOTE: We recommend stopping any other local dev processes, to reduce issues with memory/performance - - From the `x-pack/` project folder, run `node scripts/functional_tests_server --config test/functional_enterprise_search/cypress.config.ts` - - Kibana: - - You will need to pass `--config baseUrl=http://localhost:5620` into your Cypress command. - - Enterprise Search: - - :warning: TODO: We _currently_ do not have a way of spinning up Enterprise Search from Kibana's FTR - for now, you can use local Enterprise Search (pointed at the FTR's `http://localhost:9220` Elasticsearch host instance) + - Make sure docker is up and running in you system + - From the `x-pack/` project folder, run `sh cypress.sh` which will spin up Kibana, Elasticsearch through functional test runners and Enterprise Search instance in Docker. 3. Running Cypress against Enterprise Search dockerized stack scripts - :warning: This is for Enterprise Search devs only, as this requires access to our closed source Enterprise Search repo - `stack_scripts/start-with-es-native-auth.sh --with-kibana` @@ -158,4 +137,4 @@ To track what Cypress is doing while running tests, you can pass in `--config vi See [our functional test runner README](../../test/functional_enterprise_search). -Our automated accessibility tests can be found in [x-pack/test/accessibility/apps](../../test/accessibility/apps/enterprise_search.ts). +Our automated accessibility tests can be found in [x-pack/test/accessibility/apps](../../test/accessibility/apps/enterprise_search.ts). \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts new file mode 100644 index 0000000000000..953c49b493b37 --- /dev/null +++ b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts @@ -0,0 +1,120 @@ +/* + * 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 interface ConnectorServerSideDefinition { + iconPath: string; + isBeta: boolean; + isNative: boolean; + keywords: string[]; + name: string; + serviceType: string; +} + +export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [ + { + iconPath: 'mongodb.svg', + isBeta: false, + isNative: true, + keywords: ['mongo', 'mongodb', 'database', 'nosql', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.mongodb.name', { + defaultMessage: 'MongoDB', + }), + serviceType: 'mongodb', + }, + { + iconPath: 'mysql.svg', + isBeta: false, + isNative: true, + keywords: ['mysql', 'sql', 'database', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.mysql.name', { + defaultMessage: 'MySQL', + }), + serviceType: 'mysql', + }, + { + iconPath: 'azure_blob_storage.svg', + isBeta: true, + isNative: false, + keywords: ['cloud', 'azure', 'blob', 's3', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.azureBlob.name', { + defaultMessage: 'Azure Blob Storage', + }), + serviceType: 'azure_blob_storage', + }, + { + iconPath: 'google_cloud_storage.svg', + isBeta: true, + isNative: false, + keywords: ['google', 'cloud', 'blob', 's3', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.googleCloud.name', { + defaultMessage: 'Google Cloud Storage', + }), + serviceType: 'google_cloud_storage', + }, + { + iconPath: 'mssql.svg', + isBeta: true, + isNative: false, + keywords: ['mssql', 'microsoft', 'sql', 'database', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.microsoftSQL.name', { + defaultMessage: 'Microsoft SQL', + }), + serviceType: 'mssql', + }, + { + iconPath: 'network_drive.svg', + isBeta: true, + isNative: false, + keywords: ['network', 'drive', 'file', 'directory', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.networkDrive.name', { + defaultMessage: 'Network drive', + }), + serviceType: 'network_drive', + }, + { + iconPath: 'oracle.svg', + isBeta: true, + isNative: false, + keywords: ['oracle', 'sql', 'database', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.oracle.name', { + defaultMessage: 'Oracle', + }), + serviceType: 'oracle', + }, + { + iconPath: 'postgresql.svg', + isBeta: true, + isNative: false, + keywords: ['postgresql', 'sql', 'database', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.postgresql.name', { + defaultMessage: 'Postgresql', + }), + serviceType: 'postgresql', + }, + { + iconPath: 's3.svg', + isBeta: true, + isNative: false, + keywords: ['s3', 'cloud', 'amazon', 'connector'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.s3.name', { + defaultMessage: 'S3', + }), + serviceType: 's3', + }, + { + iconPath: 'custom.svg', + isBeta: true, + isNative: false, + keywords: ['custom', 'connector', 'code'], + name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.customConnector.name', { + defaultMessage: 'Customized connector', + }), + serviceType: '', + }, +]; diff --git a/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts index 8d962ee206f9e..bccfe15835a63 100644 --- a/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts +++ b/x-pack/plugins/enterprise_search/common/connectors/native_connectors.ts @@ -19,7 +19,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record /dev/null 2>&1; then + echo "This script needs docker to run. Start docker and try again." + echo "If you are testing against your own setup use ./cypress.sh dev" + exit 1 + fi -# Pass all remaining arguments (e.g., ...rest) from the 3rd arg onwards -# as an open-ended string. Appends onto to the end the Cypress command -# @see https://docs.cypress.io/guides/guides/command-line.html#Options -ARGS="${*:3}" + if [ "$MODE" == "open" ]; then + node ../../../scripts/functional_tests --config ../../test/functional_enterprise_search/visual_config.ts + else + node ../../../scripts/functional_tests --config ../../test/functional_enterprise_search/cli_config.ts + fi +fi -../../../node_modules/.bin/cypress "$MODE" --project "public/applications/$PRODUCT" --browser chrome $ARGS diff --git a/x-pack/plugins/enterprise_search/cypress/e2e/content/basic_crawler.cy.ts b/x-pack/plugins/enterprise_search/cypress/e2e/content/basic_crawler.cy.ts new file mode 100644 index 0000000000000..484aa6976072d --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/e2e/content/basic_crawler.cy.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 { login } from '../../tasks/login'; + +describe('Enterprise Search Crawler', () => { + it('test', () => { + login(); + cy.visit('/app/enterprise_search/content/search_indices/new_index'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/cypress/support/e2e.ts b/x-pack/plugins/enterprise_search/cypress/support/e2e.ts new file mode 100644 index 0000000000000..1e2b130d59973 --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/support/e2e.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// / + +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Enforce building this file. +export {}; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + interface Chainable { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getBySel(value: string, ...args: any[]): Chainable; + getKibanaVersion(): 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.on('uncaught:exception', () => { + return false; +}); diff --git a/x-pack/plugins/enterprise_search/cypress/tasks/login.ts b/x-pack/plugins/enterprise_search/cypress/tasks/login.ts new file mode 100644 index 0000000000000..bf4ad9b836def --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/tasks/login.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +const ELASTICSEARCH_USERNAME = 'elastic'; +const ELASTICSEARCH_PASSWORD = 'changeme'; +const LOGIN_API_ENDPOINT = '/internal/security/login'; + +export const constructUrlWithUser = (route: string) => { + const url = Cypress.config().baseUrl; + const kibana = new URL(String(url)); + const hostname = kibana.hostname; + const username = ELASTICSEARCH_USERNAME; + const password = ELASTICSEARCH_PASSWORD; + const protocol = kibana.protocol.replace(':', ''); + const port = kibana.port; + + const path = `${route.startsWith('/') ? '' : '/'}${route}`; + const strUrl = `${protocol}://${username}:${password}@${hostname}:${port}${path}`; + const builtUrl = new URL(strUrl); + + cy.log(`origin: ${builtUrl.href}`); + return builtUrl.href; +}; + +export const login = () => { + cy.session('user', () => { + cy.request({ + body: { + currentURL: '/', + params: { + password: ELASTICSEARCH_PASSWORD, + username: ELASTICSEARCH_USERNAME, + }, + providerName: 'basic', + providerType: 'basic', + }, + headers: { 'kbn-xsrf': 'cypress-creds-via-config' }, + method: 'POST', + url: constructUrlWithUser(LOGIN_API_ENDPOINT), + }); + }); +}; diff --git a/x-pack/plugins/enterprise_search/cypress/tsconfig.json b/x-pack/plugins/enterprise_search/cypress/tsconfig.json new file mode 100644 index 0000000000000..06640dca03363 --- /dev/null +++ b/x-pack/plugins/enterprise_search/cypress/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../tsconfig.base.json", + "include": ["**/*", "../cypress.config.ts"], + "exclude": ["target/**/*"], + "compilerOptions": { + "outDir": "target/types", + "types": ["cypress", "node", "cypress-react-selector"], + "resolveJsonModule": true + }, + "kbn_references": [ + "@kbn/cypress-config", + // cypress projects that are nested inside of other ts project use code + // from the parent ts project in ways that can't be automatically deteceted + // at this time so we have to force the inclusion of this reference + { + "path": "../tsconfig.json", + "force": true + } + ] +} diff --git a/x-pack/plugins/enterprise_search/kibana.jsonc b/x-pack/plugins/enterprise_search/kibana.jsonc index 5886cf02bfd47..5593addaeca35 100644 --- a/x-pack/plugins/enterprise_search/kibana.jsonc +++ b/x-pack/plugins/enterprise_search/kibana.jsonc @@ -26,6 +26,7 @@ ], "optionalPlugins": [ "customIntegrations", + "globalSearch", "home", "ml", "spaces", diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.test.ts index 08478bf32b362..15d73d21ff890 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.test.ts @@ -21,9 +21,11 @@ describe('FetchAnalyticsCollectionsApiLogic', () => { it('calls the analytics collections list api', async () => { const promise = Promise.resolve([{ name: 'result' }]); http.get.mockReturnValue(promise); - const result = fetchAnalyticsCollections(); + const result = fetchAnalyticsCollections({}); await nextTick(); - expect(http.get).toHaveBeenCalledWith('/internal/enterprise_search/analytics/collections'); + expect(http.get).toHaveBeenCalledWith('/internal/enterprise_search/analytics/collections', { + query: { query: '' }, + }); await expect(result).resolves.toEqual([{ name: 'result' }]); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.ts index 399038a776c10..45567d9202639 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/api/index/fetch_analytics_collections_api_logic.ts @@ -12,10 +12,20 @@ import { HttpLogic } from '../../../shared/http'; export type FetchAnalyticsCollectionsApiLogicResponse = AnalyticsCollection[]; -export const fetchAnalyticsCollections = async () => { +interface FetchAnalyticsCollectionsApiLogicArgs { + query?: string; +} + +export const fetchAnalyticsCollections = async ({ + query = '', +}: FetchAnalyticsCollectionsApiLogicArgs) => { const { http } = HttpLogic.values; const route = '/internal/enterprise_search/analytics/collections'; - const response = await http.get(route); + const response = await http.get(route, { + query: { + query, + }, + }); return response; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_logic.test.ts new file mode 100644 index 0000000000000..4c57bc4325f3e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_logic.test.ts @@ -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 { LogicMounter } from '../../../__mocks__/kea_logic'; + +import { DataView } from '@kbn/data-views-plugin/common'; + +import { AnalyticsCollection } from '../../../../../common/types/analytics'; + +import { findOrCreateDataView } from '../../utils/find_or_create_data_view'; + +import { AnalyticsCollectionDataViewLogic } from './analytics_collection_data_view_logic'; +import { FetchAnalyticsCollectionLogic } from './fetch_analytics_collection_logic'; + +jest.mock('../../utils/find_or_create_data_view', () => { + return { + findOrCreateDataView: jest.fn(), + }; +}); + +describe('AnalyticsCollectionDataViewLogic', () => { + const { mount } = new LogicMounter(AnalyticsCollectionDataViewLogic); + + beforeEach(() => { + jest.clearAllMocks(); + + mount(); + }); + + const defaultProps = { + dataView: null, + }; + + it('initializes with default values', () => { + expect(AnalyticsCollectionDataViewLogic.values).toEqual(defaultProps); + }); + + describe('reducers', () => { + it('should handle set dataView', () => { + const dataView = { id: 'test' } as DataView; + AnalyticsCollectionDataViewLogic.actions.setDataView(dataView); + expect(AnalyticsCollectionDataViewLogic.values.dataView).toBe(dataView); + }); + }); + + describe('listeners', () => { + it('should find and set dataView when analytics collection fetched', async () => { + const dataView = { id: 'test' } as DataView; + (findOrCreateDataView as jest.Mock).mockResolvedValue(dataView); + + await FetchAnalyticsCollectionLogic.actions.apiSuccess({ + events_datastream: 'events1', + name: 'collection1', + } as AnalyticsCollection); + + expect(AnalyticsCollectionDataViewLogic.values.dataView).toEqual(dataView); + }); + + it('should create, save and set dataView when analytics collection fetched but dataView is not found', async () => { + const dataView = { id: 'test' } as DataView; + (findOrCreateDataView as jest.Mock).mockResolvedValue(dataView); + + await FetchAnalyticsCollectionLogic.actions.apiSuccess({ + events_datastream: 'events1', + name: 'collection1', + } as AnalyticsCollection); + + expect(AnalyticsCollectionDataViewLogic.values.dataView).toEqual(dataView); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_logic.ts new file mode 100644 index 0000000000000..f00ef0fecc1ed --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_logic.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { DataView } from '@kbn/data-views-plugin/common'; + +import { findOrCreateDataView } from '../../utils/find_or_create_data_view'; + +import { + FetchAnalyticsCollectionActions, + FetchAnalyticsCollectionLogic, +} from './fetch_analytics_collection_logic'; + +export interface AnalyticsCollectionDataViewLogicValues { + dataView: DataView | null; +} + +interface AnalyticsCollectionDataViewLogicActions { + fetchedAnalyticsCollection: FetchAnalyticsCollectionActions['apiSuccess']; + setDataView(dataView: DataView): { dataView: DataView }; +} + +export const AnalyticsCollectionDataViewLogic = kea< + MakeLogicType +>({ + actions: { + setDataView: (dataView) => ({ dataView }), + }, + connect: { + actions: [FetchAnalyticsCollectionLogic, ['apiSuccess as fetchedAnalyticsCollection']], + }, + listeners: ({ actions }) => ({ + fetchedAnalyticsCollection: async (collection) => { + actions.setDataView(await findOrCreateDataView(collection)); + }, + }), + path: ['enterprise_search', 'analytics', 'collection', 'dataView'], + reducers: () => ({ + dataView: [null, { setDataView: (_, { dataView }) => dataView }], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_logic.test.ts deleted file mode 100644 index 4220c30985a95..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_logic.test.ts +++ /dev/null @@ -1,149 +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 { LogicMounter } from '../../../../__mocks__/kea_logic'; - -import { DataView } from '@kbn/data-views-plugin/common'; - -import { AnalyticsCollection } from '../../../../../../common/types/analytics'; - -import { KibanaLogic } from '../../../../shared/kibana/kibana_logic'; - -import { AnalyticsCollectionToolbarLogic } from '../analytics_collection_toolbar/analytics_collection_toolbar_logic'; - -import { - AnalyticsCollectionExploreTableLogic, - Sorting, -} from './analytics_collection_explore_table_logic'; -import { ExploreTableColumns, ExploreTables } from './analytics_collection_explore_table_types'; - -jest.mock('../../../../shared/kibana/kibana_logic', () => ({ - KibanaLogic: { - values: { - data: { - dataViews: { - find: jest.fn(() => Promise.resolve([{ id: 'some-data-view-id' }])), - }, - search: { - search: jest.fn().mockReturnValue({ subscribe: jest.fn() }), - }, - }, - }, - }, -})); - -describe('AnalyticsCollectionExplorerTablesLogic', () => { - const { mount } = new LogicMounter(AnalyticsCollectionExploreTableLogic); - - beforeEach(() => { - jest.clearAllMocks(); - - mount(); - }); - - const defaultProps = { - dataView: null, - isLoading: false, - items: [], - selectedTable: null, - sorting: null, - }; - - it('initializes with default values', () => { - expect(AnalyticsCollectionExploreTableLogic.values).toEqual(defaultProps); - }); - - describe('reducers', () => { - it('should handle set dataView', () => { - const dataView = { id: 'test' } as DataView; - AnalyticsCollectionExploreTableLogic.actions.setDataView(dataView); - expect(AnalyticsCollectionExploreTableLogic.values.dataView).toBe(dataView); - }); - - it('should handle set items', () => { - const items = [ - { count: 1, query: 'test' }, - { count: 2, query: 'test2' }, - ]; - AnalyticsCollectionExploreTableLogic.actions.setItems(items); - expect(AnalyticsCollectionExploreTableLogic.values.items).toEqual(items); - }); - - it('should handle set selectedTable', () => { - const id = ExploreTables.WorsePerformers; - const sorting = { direction: 'desc', field: ExploreTableColumns.count } as Sorting; - AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(id, sorting); - expect(AnalyticsCollectionExploreTableLogic.values.selectedTable).toEqual(id); - expect(AnalyticsCollectionExploreTableLogic.values.sorting).toEqual(sorting); - }); - - it('should handle set sorting', () => { - const sorting = { direction: 'asc', field: ExploreTableColumns.sessions } as Sorting; - AnalyticsCollectionExploreTableLogic.actions.setSorting(sorting); - expect(AnalyticsCollectionExploreTableLogic.values.sorting).toEqual(sorting); - }); - - it('should handle isLoading', () => { - expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(false); - - AnalyticsCollectionExploreTableLogic.actions.setItems([]); - expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(false); - - AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); - expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); - - AnalyticsCollectionToolbarLogic.actions.setTimeRange({ from: 'now-7d', to: 'now' }); - expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); - - AnalyticsCollectionToolbarLogic.actions.setSearchSessionId('12345'); - expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); - }); - }); - - describe('listeners', () => { - it('should fetch items when selectedTable changes', () => { - AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.TopReferrers); - expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { - indexPattern: undefined, - sessionId: undefined, - }); - }); - - it('should fetch items when timeRange changes', () => { - AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); - (KibanaLogic.values.data.search.search as jest.Mock).mockClear(); - - AnalyticsCollectionToolbarLogic.actions.setTimeRange({ from: 'now-7d', to: 'now' }); - expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { - indexPattern: undefined, - sessionId: undefined, - }); - }); - - it('should fetch items when searchSessionId changes', () => { - AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); - (KibanaLogic.values.data.search.search as jest.Mock).mockClear(); - - AnalyticsCollectionToolbarLogic.actions.setSearchSessionId('1234'); - expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { - indexPattern: undefined, - sessionId: '1234', - }); - }); - - it('should find and set dataView when findDataView is called', async () => { - const dataView = { id: 'test' } as DataView; - jest.spyOn(KibanaLogic.values.data.dataViews, 'find').mockResolvedValue([dataView]); - await AnalyticsCollectionExploreTableLogic.actions.findDataView({ - events_datastream: 'events1', - name: 'collection1', - } as AnalyticsCollection); - - expect(AnalyticsCollectionExploreTableLogic.values.dataView).toEqual(dataView); - }); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_logic.ts deleted file mode 100644 index 46df5692aa963..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_logic.ts +++ /dev/null @@ -1,333 +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 { kea, MakeLogicType } from 'kea'; - -import { - IKibanaSearchRequest, - IKibanaSearchResponse, - isCompleteResponse, - TimeRange, -} from '@kbn/data-plugin/common'; -import { DataView } from '@kbn/data-views-plugin/common'; - -import { AnalyticsCollection } from '../../../../../../common/types/analytics'; -import { KibanaLogic } from '../../../../shared/kibana/kibana_logic'; -import { AnalyticsCollectionToolbarLogic } from '../analytics_collection_toolbar/analytics_collection_toolbar_logic'; - -import { - ExploreTableColumns, - ExploreTableItem, - ExploreTables, - SearchTermsTable, - TopClickedTable, - TopReferrersTable, - WorsePerformersTable, -} from './analytics_collection_explore_table_types'; - -const BASE_PAGE_SIZE = 10; - -export interface Sorting { - direction: 'asc' | 'desc'; - field: keyof T; -} - -interface TableParams { - parseResponseToItems(response: IKibanaSearchResponse): T[]; - requestParams(timeRange: TimeRange, sorting: Sorting | null): IKibanaSearchRequest; -} - -const tablesParams: { - [ExploreTables.SearchTerms]: TableParams; - [ExploreTables.TopClicked]: TableParams; - [ExploreTables.TopReferrers]: TableParams; - [ExploreTables.WorsePerformers]: TableParams; -} = { - [ExploreTables.SearchTerms]: { - parseResponseToItems: ( - response: IKibanaSearchResponse<{ - aggregations: { searches: { buckets: Array<{ doc_count: number; key: string }> } }; - }> - ) => - response.rawResponse.aggregations.searches.buckets.map((bucket) => ({ - [ExploreTableColumns.count]: bucket.doc_count, - [ExploreTableColumns.searchTerms]: bucket.key, - })), - requestParams: (timeRange, sorting) => ({ - params: { - aggs: { - searches: { - terms: { - field: 'search.query', - order: sorting - ? { - [sorting.field === ExploreTableColumns.count ? '_count' : '_key']: - sorting.direction, - } - : undefined, - size: BASE_PAGE_SIZE, - }, - }, - }, - query: { - range: { - '@timestamp': { - gte: timeRange.from, - lt: timeRange.to, - }, - }, - }, - size: 0, - track_total_hits: false, - }, - }), - }, - [ExploreTables.WorsePerformers]: { - parseResponseToItems: ( - response: IKibanaSearchResponse<{ - aggregations: { - formula: { searches: { buckets: Array<{ doc_count: number; key: string }> } }; - }; - }> - ) => - response.rawResponse.aggregations.formula.searches.buckets.map((bucket) => ({ - [ExploreTableColumns.count]: bucket.doc_count, - [ExploreTableColumns.query]: bucket.key, - })), - requestParams: (timeRange, sorting) => ({ - params: { - aggs: { - formula: { - aggs: { - searches: { - terms: { - field: 'search.query', - order: sorting - ? { - [sorting?.field === ExploreTableColumns.count ? '_count' : '_key']: - sorting?.direction, - } - : undefined, - size: BASE_PAGE_SIZE, - }, - }, - }, - filter: { term: { 'search.results.total_results': '0' } }, - }, - }, - query: { - range: { - '@timestamp': { - gte: timeRange.from, - lt: timeRange.to, - }, - }, - }, - size: 0, - track_total_hits: false, - }, - }), - }, - [ExploreTables.TopClicked]: { - parseResponseToItems: ( - response: IKibanaSearchResponse<{ - aggregations: { - formula: { searches: { buckets: Array<{ doc_count: number; key: string }> } }; - }; - }> - ) => - response.rawResponse.aggregations.formula.searches.buckets.map((bucket) => ({ - [ExploreTableColumns.count]: bucket.doc_count, - [ExploreTableColumns.page]: bucket.key, - })), - requestParams: (timeRange, sorting) => ({ - params: { - aggs: { - formula: { - aggs: { - searches: { - terms: { - field: 'search.results.items.page.url', - order: sorting - ? { - [sorting.field === ExploreTableColumns.count ? '_count' : '_key']: - sorting.direction, - } - : undefined, - size: BASE_PAGE_SIZE, - }, - }, - }, - filter: { term: { 'event.action': 'search_click' } }, - }, - }, - query: { - range: { - '@timestamp': { - gte: timeRange.from, - lt: timeRange.to, - }, - }, - }, - size: 0, - track_total_hits: false, - }, - }), - }, - [ExploreTables.TopReferrers]: { - parseResponseToItems: ( - response: IKibanaSearchResponse<{ - aggregations: { - formula: { searches: { buckets: Array<{ doc_count: number; key: string }> } }; - }; - }> - ) => - response.rawResponse.aggregations.formula.searches.buckets.map((bucket) => ({ - [ExploreTableColumns.sessions]: bucket.doc_count, - [ExploreTableColumns.page]: bucket.key, - })), - requestParams: (timeRange, sorting) => ({ - params: { - aggs: { - formula: { - aggs: { - searches: { - terms: { - field: 'page.referrer', - order: sorting - ? { - [sorting?.field === ExploreTableColumns.sessions ? '_count' : '_key']: - sorting?.direction, - } - : undefined, - size: BASE_PAGE_SIZE, - }, - }, - }, - filter: { term: { 'event.action': 'page_view' } }, - }, - }, - query: { - range: { - '@timestamp': { - gte: timeRange.from, - lt: timeRange.to, - }, - }, - }, - size: 0, - track_total_hits: false, - }, - }), - }, -}; - -export interface AnalyticsCollectionExploreTableLogicValues { - dataView: DataView | null; - isLoading: boolean; - items: ExploreTableItem[]; - selectedTable: ExploreTables | null; - sorting: Sorting | null; -} - -export interface AnalyticsCollectionExploreTableLogicActions { - findDataView(collection: AnalyticsCollection): { collection: AnalyticsCollection }; - setDataView(dataView: DataView): { dataView: DataView }; - setItems(items: ExploreTableItem[]): { items: ExploreTableItem[] }; - setSelectedTable( - id: ExploreTables | null, - sorting?: Sorting - ): { id: ExploreTables | null; sorting?: Sorting }; - setSorting(sorting?: Sorting): { sorting?: Sorting }; -} - -export const AnalyticsCollectionExploreTableLogic = kea< - MakeLogicType< - AnalyticsCollectionExploreTableLogicValues, - AnalyticsCollectionExploreTableLogicActions - > ->({ - actions: { - findDataView: (collection) => ({ collection }), - setDataView: (dataView) => ({ dataView }), - setItems: (items) => ({ items }), - setSelectedTable: (id, sorting) => ({ id, sorting }), - setSorting: (sorting) => ({ sorting }), - }, - listeners: ({ actions, values }) => { - const fetchItems = () => { - if (values.selectedTable === null || !(values.selectedTable in tablesParams)) { - return; - } - - const { requestParams, parseResponseToItems } = tablesParams[ - values.selectedTable - ] as TableParams; - const timeRange = AnalyticsCollectionToolbarLogic.values.timeRange; - - const search$ = KibanaLogic.values.data.search - .search(requestParams(timeRange, values.sorting), { - indexPattern: values.dataView || undefined, - sessionId: AnalyticsCollectionToolbarLogic.values.searchSessionId, - }) - .subscribe({ - error: (e) => { - KibanaLogic.values.data.search.showError(e); - }, - next: (response) => { - if (isCompleteResponse(response)) { - actions.setItems(parseResponseToItems(response)); - search$.unsubscribe(); - } - }, - }); - }; - - return { - findDataView: async ({ collection }) => { - const dataView = ( - await KibanaLogic.values.data.dataViews.find(collection.events_datastream, 1) - )?.[0]; - - if (dataView) { - actions.setDataView(dataView); - } - }, - setSelectedTable: () => { - fetchItems(); - }, - [AnalyticsCollectionToolbarLogic.actionTypes.setTimeRange]: () => { - fetchItems(); - }, - [AnalyticsCollectionToolbarLogic.actionTypes.setSearchSessionId]: () => { - fetchItems(); - }, - }; - }, - path: ['enterprise_search', 'analytics', 'collections', 'explore', 'table'], - reducers: () => ({ - dataView: [null, { setDataView: (_, { dataView }) => dataView }], - isLoading: [ - false, - { - setItems: () => false, - setSelectedTable: () => true, - [AnalyticsCollectionToolbarLogic.actionTypes.setTimeRange]: () => true, - [AnalyticsCollectionToolbarLogic.actionTypes.setSearchSessionId]: () => true, - }, - ], - items: [[], { setItems: (_, { items }) => items }], - selectedTable: [null, { setSelectedTable: (_, { id }) => id }], - sorting: [ - null, - { - setSelectedTable: (_, { sorting = null }) => sorting, - setSorting: (_, { sorting = null }) => sorting, - }, - ], - }), -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_formulas.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_formulas.ts new file mode 100644 index 0000000000000..87e9812b94ac7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_formulas.ts @@ -0,0 +1,76 @@ +/* + * 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 { IKibanaSearchRequest, TimeRange } from '@kbn/data-plugin/common'; + +const getSearchQueryRequestParams = (field: string, search: string): { regexp: {} } => { + const createRegexQuery = (queryString: string) => { + const query = queryString.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + + return `.*${query}.*`; + }; + + return { + regexp: { + [field]: { + value: createRegexQuery(search), + }, + }, + }; +}; +export const getTotalCountRequestParams = (field: string) => ({ + totalCount: { + cardinality: { + field, + }, + }, +}); +export const getPaginationRequestSizeParams = (pageIndex: number, pageSize: number) => ({ + size: (pageIndex + 1) * pageSize, +}); +export const getPaginationRequestParams = (pageIndex: number, pageSize: number) => ({ + aggs: { + sort: { + bucket_sort: { + from: pageIndex * pageSize, + size: pageSize, + }, + }, + }, +}); + +export const getBaseSearchTemplate = ( + aggregationFieldName: string, + { + search, + timeRange, + eventType, + }: { search: string; timeRange: TimeRange; eventType?: 'search' | 'search_click' | 'page_view' }, + aggs: IKibanaSearchRequest['params']['aggs'] +): IKibanaSearchRequest => ({ + params: { + aggs, + query: { + bool: { + must: [ + { + range: { + '@timestamp': { + gte: timeRange.from, + lt: timeRange.to, + }, + }, + }, + ...(eventType ? [{ term: { 'event.action': eventType } }] : []), + ...(search ? [getSearchQueryRequestParams(aggregationFieldName, search)] : []), + ], + }, + }, + size: 0, + track_total_hits: false, + }, +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.test.ts new file mode 100644 index 0000000000000..226c521c44894 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.test.ts @@ -0,0 +1,270 @@ +/* + * 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 { nextTick } from '@kbn/test-jest-helpers'; + +import { KibanaLogic } from '../../../shared/kibana/kibana_logic'; + +import { + AnalyticsCollectionExploreTableLogic, + Sorting, +} from './analytics_collection_explore_table_logic'; +import { ExploreTableColumns, ExploreTables } from './analytics_collection_explore_table_types'; +import { AnalyticsCollectionToolbarLogic } from './analytics_collection_toolbar/analytics_collection_toolbar_logic'; + +jest.mock('../../../shared/kibana/kibana_logic', () => ({ + KibanaLogic: { + values: { + data: { + search: { + search: jest.fn().mockReturnValue({ subscribe: jest.fn() }), + }, + }, + }, + }, +})); + +describe('AnalyticsCollectionExplorerTablesLogic', () => { + const { mount } = new LogicMounter(AnalyticsCollectionExploreTableLogic); + + beforeEach(() => { + jest.clearAllMocks(); + + mount(); + }); + + const defaultProps = { + dataView: null, + isLoading: false, + items: [], + pageIndex: 0, + pageSize: 10, + search: '', + selectedTable: null, + sorting: null, + timeRange: { + from: 'now-7d', + to: 'now', + }, + totalItemsCount: 0, + }; + + it('initializes with default values', () => { + expect(AnalyticsCollectionExploreTableLogic.values).toEqual(defaultProps); + }); + + describe('reducers', () => { + it('should handle set items', () => { + const items = [ + { count: 1, query: 'test' }, + { count: 2, query: 'test2' }, + ]; + AnalyticsCollectionExploreTableLogic.actions.setItems(items); + expect(AnalyticsCollectionExploreTableLogic.values.items).toEqual(items); + }); + + it('should handle set selectedTable', () => { + const id = ExploreTables.WorsePerformers; + const sorting = { direction: 'desc', field: ExploreTableColumns.count } as Sorting; + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(id, sorting); + expect(AnalyticsCollectionExploreTableLogic.values.selectedTable).toEqual(id); + expect(AnalyticsCollectionExploreTableLogic.values.sorting).toEqual(sorting); + }); + + it('should handle set sorting', () => { + const sorting = { direction: 'asc', field: ExploreTableColumns.sessions } as Sorting; + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + sort: sorting, + }); + expect(AnalyticsCollectionExploreTableLogic.values.sorting).toEqual(sorting); + }); + + describe('isLoading', () => { + beforeEach(() => { + mount({ selectedTable: ExploreTables.TopReferrers }); + }); + + it('should handle onTableChange', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + sort: { + direction: 'asc', + field: ExploreTableColumns.sessions, + } as Sorting, + }); + expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); + }); + + it('should handle setSearch', () => { + AnalyticsCollectionExploreTableLogic.actions.setSearch('test'); + expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); + }); + + it('should handle setItems', () => { + AnalyticsCollectionExploreTableLogic.actions.setItems([]); + expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(false); + }); + + it('should handle setSelectedTable', () => { + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.TopReferrers); + expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); + }); + + it('should handle setTimeRange', () => { + AnalyticsCollectionToolbarLogic.actions.setTimeRange({ from: 'now-7d', to: 'now' }); + expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); + }); + + it('should handle setSearchSessionId', () => { + AnalyticsCollectionToolbarLogic.actions.setSearchSessionId('12345'); + expect(AnalyticsCollectionExploreTableLogic.values.isLoading).toEqual(true); + }); + }); + + describe('pageIndex', () => { + it('should handle setPageIndex', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + expect(AnalyticsCollectionExploreTableLogic.values.pageIndex).toEqual(2); + }); + + it('should handle setSelectedTable', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.TopReferrers); + expect(AnalyticsCollectionExploreTableLogic.values.pageIndex).toEqual(0); + }); + + it('should handle reset', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + AnalyticsCollectionExploreTableLogic.actions.reset(); + expect(AnalyticsCollectionExploreTableLogic.values.pageIndex).toEqual(0); + }); + + it('should handle setSearch', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + AnalyticsCollectionExploreTableLogic.actions.setSearch(''); + expect(AnalyticsCollectionExploreTableLogic.values.pageIndex).toEqual(0); + }); + }); + + describe('pageSize', () => { + it('should handle setPageSize', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + expect(AnalyticsCollectionExploreTableLogic.values.pageSize).toEqual(10); + }); + + it('should handle setSelectedTable', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.TopReferrers); + expect(AnalyticsCollectionExploreTableLogic.values.pageSize).toEqual(10); + }); + + it('should handle reset', () => { + AnalyticsCollectionExploreTableLogic.actions.onTableChange({ + page: { index: 2, size: 10 }, + }); + AnalyticsCollectionExploreTableLogic.actions.reset(); + expect(AnalyticsCollectionExploreTableLogic.values.pageSize).toEqual(10); + }); + }); + + describe('search', () => { + it('should handle setSearch', () => { + AnalyticsCollectionExploreTableLogic.actions.setSearch('test'); + expect(AnalyticsCollectionExploreTableLogic.values.search).toEqual('test'); + }); + + it('should handle setSelectedTable', () => { + AnalyticsCollectionExploreTableLogic.actions.setSearch('test'); + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.TopReferrers); + expect(AnalyticsCollectionExploreTableLogic.values.search).toEqual(''); + }); + + it('should handle reset', () => { + AnalyticsCollectionExploreTableLogic.actions.setSearch('test'); + AnalyticsCollectionExploreTableLogic.actions.reset(); + expect(AnalyticsCollectionExploreTableLogic.values.search).toEqual(''); + }); + }); + + it('should handle totalItemsCount', () => { + AnalyticsCollectionExploreTableLogic.actions.setTotalItemsCount(100); + expect(AnalyticsCollectionExploreTableLogic.values.totalItemsCount).toEqual(100); + }); + }); + + describe('listeners', () => { + it('should fetch items when selectedTable changes', () => { + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.TopReferrers); + expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { + indexPattern: undefined, + sessionId: undefined, + }); + }); + + it('should fetch items when timeRange changes', () => { + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); + (KibanaLogic.values.data.search.search as jest.Mock).mockClear(); + + AnalyticsCollectionToolbarLogic.actions.setTimeRange({ from: 'now-7d', to: 'now' }); + expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { + indexPattern: undefined, + sessionId: undefined, + }); + }); + + it('should fetch items when searchSessionId changes', () => { + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); + (KibanaLogic.values.data.search.search as jest.Mock).mockClear(); + + AnalyticsCollectionToolbarLogic.actions.setSearchSessionId('1234'); + expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { + indexPattern: undefined, + sessionId: '1234', + }); + }); + + it('should fetch items when onTableChange called', () => { + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); + (KibanaLogic.values.data.search.search as jest.Mock).mockClear(); + + AnalyticsCollectionExploreTableLogic.actions.onTableChange({}); + expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { + indexPattern: undefined, + sessionId: undefined, + }); + }); + + it('should fetch items when search changes', async () => { + jest.useFakeTimers({ legacyFakeTimers: true }); + AnalyticsCollectionExploreTableLogic.actions.setSelectedTable(ExploreTables.WorsePerformers); + (KibanaLogic.values.data.search.search as jest.Mock).mockClear(); + + AnalyticsCollectionExploreTableLogic.actions.setSearch('test'); + jest.advanceTimersByTime(200); + await nextTick(); + + expect(KibanaLogic.values.data.search.search).toHaveBeenCalledWith(expect.any(Object), { + indexPattern: undefined, + sessionId: undefined, + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts new file mode 100644 index 0000000000000..e5e181ccfa266 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts @@ -0,0 +1,402 @@ +/* + * 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, MakeLogicType } from 'kea'; + +import { + IKibanaSearchRequest, + IKibanaSearchResponse, + isCompleteResponse, + TimeRange, +} from '@kbn/data-plugin/common'; + +import { KibanaLogic } from '../../../shared/kibana/kibana_logic'; + +import { + AnalyticsCollectionDataViewLogic, + AnalyticsCollectionDataViewLogicValues, +} from './analytics_collection_data_view_logic'; + +import { + getBaseSearchTemplate, + getPaginationRequestParams, + getPaginationRequestSizeParams, + getTotalCountRequestParams, +} from './analytics_collection_explore_table_formulas'; +import { + ExploreTableColumns, + ExploreTableItem, + ExploreTables, + SearchTermsTable, + TopClickedTable, + TopReferrersTable, + WorsePerformersTable, +} from './analytics_collection_explore_table_types'; +import { + AnalyticsCollectionToolbarLogic, + AnalyticsCollectionToolbarLogicValues, +} from './analytics_collection_toolbar/analytics_collection_toolbar_logic'; + +const BASE_PAGE_SIZE = 10; +const SEARCH_COOLDOWN = 200; + +export interface Sorting { + direction: 'asc' | 'desc'; + field: keyof T; +} + +interface TableParams { + parseResponse(response: IKibanaSearchResponse): { items: T[]; totalCount: number }; + requestParams(props: { + pageIndex: number; + pageSize: number; + search: string; + sorting: Sorting | null; + timeRange: TimeRange; + }): IKibanaSearchRequest; +} + +const tablesParams: { + [ExploreTables.SearchTerms]: TableParams; + [ExploreTables.TopClicked]: TableParams; + [ExploreTables.TopReferrers]: TableParams; + [ExploreTables.WorsePerformers]: TableParams; +} = { + [ExploreTables.SearchTerms]: { + parseResponse: ( + response: IKibanaSearchResponse<{ + aggregations: { + searches: { buckets: Array<{ doc_count: number; key: string }> }; + totalCount: { value: number }; + }; + }> + ) => ({ + items: response.rawResponse.aggregations.searches.buckets.map((bucket) => ({ + [ExploreTableColumns.count]: bucket.doc_count, + [ExploreTableColumns.searchTerms]: bucket.key, + })), + totalCount: response.rawResponse.aggregations.totalCount.value, + }), + requestParams: ( + { timeRange, sorting, pageIndex, pageSize, search }, + aggregationFieldName = 'search.query' + ) => + getBaseSearchTemplate( + aggregationFieldName, + { search, timeRange, eventType: 'search' }, + { + searches: { + terms: { + ...getPaginationRequestSizeParams(pageIndex, pageSize), + field: aggregationFieldName, + order: sorting + ? { + [sorting.field === ExploreTableColumns.count ? '_count' : '_key']: + sorting.direction, + } + : undefined, + }, + ...getPaginationRequestParams(pageIndex, pageSize), + }, + ...getTotalCountRequestParams(aggregationFieldName), + } + ), + }, + [ExploreTables.WorsePerformers]: { + parseResponse: ( + response: IKibanaSearchResponse<{ + aggregations: { + formula: { + searches: { buckets: Array<{ doc_count: number; key: string }> }; + totalCount: { value: number }; + }; + }; + }> + ) => ({ + items: response.rawResponse.aggregations.formula.searches.buckets.map((bucket) => ({ + [ExploreTableColumns.count]: bucket.doc_count, + [ExploreTableColumns.query]: bucket.key, + })), + totalCount: response.rawResponse.aggregations.formula.totalCount.value, + }), + requestParams: ( + { timeRange, sorting, pageIndex, pageSize, search }, + aggregationFieldName = 'search.query' + ) => + getBaseSearchTemplate( + aggregationFieldName, + { search, timeRange, eventType: 'search' }, + { + formula: { + aggs: { + ...getTotalCountRequestParams(aggregationFieldName), + searches: { + terms: { + ...getPaginationRequestSizeParams(pageIndex, pageSize), + field: aggregationFieldName, + order: sorting + ? { + [sorting?.field === ExploreTableColumns.count ? '_count' : '_key']: + sorting?.direction, + } + : undefined, + }, + ...getPaginationRequestParams(pageIndex, pageSize), + }, + }, + filter: { term: { 'search.results.total_results': '0' } }, + }, + } + ), + }, + [ExploreTables.TopClicked]: { + parseResponse: ( + response: IKibanaSearchResponse<{ + aggregations: { + formula: { + searches: { buckets: Array<{ doc_count: number; key: string }> }; + totalCount: { value: number }; + }; + }; + }> + ) => ({ + items: response.rawResponse.aggregations.formula.searches.buckets.map((bucket) => ({ + [ExploreTableColumns.count]: bucket.doc_count, + [ExploreTableColumns.page]: bucket.key, + })), + totalCount: response.rawResponse.aggregations.formula.totalCount.value, + }), + requestParams: ( + { timeRange, sorting, pageIndex, pageSize, search }, + aggregationFieldName = 'search.results.items.page.url' + ) => + getBaseSearchTemplate( + aggregationFieldName, + { search, timeRange, eventType: 'search_click' }, + { + formula: { + aggs: { + ...getTotalCountRequestParams(aggregationFieldName), + searches: { + terms: { + ...getPaginationRequestSizeParams(pageIndex, pageSize), + field: aggregationFieldName, + order: sorting + ? { + [sorting.field === ExploreTableColumns.count ? '_count' : '_key']: + sorting.direction, + } + : undefined, + }, + ...getPaginationRequestParams(pageIndex, pageSize), + }, + }, + filter: { term: { 'event.action': 'search_click' } }, + }, + } + ), + }, + [ExploreTables.TopReferrers]: { + parseResponse: ( + response: IKibanaSearchResponse<{ + aggregations: { + formula: { + searches: { buckets: Array<{ doc_count: number; key: string }> }; + totalCount: { value: number }; + }; + }; + }> + ) => ({ + items: response.rawResponse.aggregations.formula.searches.buckets.map((bucket) => ({ + [ExploreTableColumns.sessions]: bucket.doc_count, + [ExploreTableColumns.page]: bucket.key, + })), + totalCount: response.rawResponse.aggregations.formula.totalCount.value, + }), + requestParams: ( + { timeRange, sorting, pageIndex, pageSize, search }, + aggregationFieldName = 'page.referrer' + ) => + getBaseSearchTemplate( + aggregationFieldName, + { search, timeRange, eventType: 'page_view' }, + { + formula: { + aggs: { + ...getTotalCountRequestParams(aggregationFieldName), + searches: { + terms: { + ...getPaginationRequestSizeParams(pageIndex, pageSize), + field: aggregationFieldName, + order: sorting + ? { + [sorting?.field === ExploreTableColumns.sessions ? '_count' : '_key']: + sorting?.direction, + } + : undefined, + }, + ...getPaginationRequestParams(pageIndex, pageSize), + }, + }, + filter: { term: { 'event.action': 'page_view' } }, + }, + } + ), + }, +}; + +export interface AnalyticsCollectionExploreTableLogicValues { + dataView: AnalyticsCollectionDataViewLogicValues['dataView']; + isLoading: boolean; + items: ExploreTableItem[]; + pageIndex: number; + pageSize: number; + search: string; + searchSessionId: AnalyticsCollectionToolbarLogicValues['searchSessionId']; + selectedTable: ExploreTables | null; + sorting: Sorting | null; + timeRange: AnalyticsCollectionToolbarLogicValues['timeRange']; + totalItemsCount: number; +} + +export interface AnalyticsCollectionExploreTableLogicActions { + onTableChange(state: { page?: { index: number; size: number }; sort?: Sorting }): { + page?: { index: number; size: number }; + sort?: Sorting; + }; + reset(): void; + setItems(items: ExploreTableItem[]): { items: ExploreTableItem[] }; + setSearch(search: string): { search: string }; + setSelectedTable( + id: ExploreTables | null, + sorting?: Sorting + ): { id: ExploreTables | null; sorting?: Sorting }; + setTotalItemsCount(count: number): { count: number }; +} + +export const AnalyticsCollectionExploreTableLogic = kea< + MakeLogicType< + AnalyticsCollectionExploreTableLogicValues, + AnalyticsCollectionExploreTableLogicActions + > +>({ + actions: { + onTableChange: ({ page, sort }) => ({ page, sort }), + reset: true, + setItems: (items) => ({ items }), + setSearch: (search) => ({ search }), + setSelectedTable: (id, sorting) => ({ id, sorting }), + setTotalItemsCount: (count) => ({ count }), + }, + connect: { + actions: [AnalyticsCollectionToolbarLogic, ['setTimeRange', 'setSearchSessionId']], + values: [ + AnalyticsCollectionDataViewLogic, + ['dataView'], + AnalyticsCollectionToolbarLogic, + ['timeRange', 'searchSessionId'], + ], + }, + listeners: ({ actions, values }) => { + const fetchItems = () => { + if (values.selectedTable === null || !(values.selectedTable in tablesParams)) { + actions.setItems([]); + actions.setTotalItemsCount(0); + + return; + } + + const { requestParams, parseResponse } = tablesParams[values.selectedTable] as TableParams; + const timeRange = values.timeRange; + + const search$ = KibanaLogic.values.data.search + .search( + requestParams({ + pageIndex: values.pageIndex, + pageSize: values.pageSize, + search: values.search, + sorting: values.sorting, + timeRange, + }), + { + indexPattern: values.dataView || undefined, + sessionId: values.searchSessionId, + } + ) + .subscribe({ + error: (e) => { + KibanaLogic.values.data.search.showError(e); + }, + next: (response) => { + if (isCompleteResponse(response)) { + const { items, totalCount } = parseResponse(response); + + actions.setItems(items); + actions.setTotalItemsCount(totalCount); + search$.unsubscribe(); + } + }, + }); + }; + + return { + onTableChange: fetchItems, + setSearch: async (_, breakpoint) => { + await breakpoint(SEARCH_COOLDOWN); + fetchItems(); + }, + setSearchSessionId: fetchItems, + setSelectedTable: fetchItems, + setTimeRange: fetchItems, + }; + }, + path: ['enterprise_search', 'analytics', 'collection', 'explore', 'table'], + reducers: () => ({ + isLoading: [ + false, + { + onTableChange: () => true, + setItems: () => false, + setSearch: () => true, + setSearchSessionId: () => true, + setSelectedTable: () => true, + setTableState: () => true, + setTimeRange: () => true, + }, + ], + items: [[], { setItems: (_, { items }) => items }], + pageIndex: [ + 0, + { + onTableChange: (_, { page }) => page?.index || 0, + reset: () => 0, + setSearch: () => 0, + setSelectedTable: () => 0, + }, + ], + pageSize: [ + BASE_PAGE_SIZE, + { + onTableChange: (_, { page }) => page?.size || BASE_PAGE_SIZE, + reset: () => BASE_PAGE_SIZE, + }, + ], + search: [ + '', + { reset: () => '', setSearch: (_, { search }) => search, setSelectedTable: () => '' }, + ], + selectedTable: [null, { setSelectedTable: (_, { id }) => id }], + sorting: [ + null, + { + onTableChange: (_, { sort = null }) => sort, + setSelectedTable: (_, { sorting = null }) => sorting, + }, + ], + totalItemsCount: [0, { setTotalItemsCount: (_, { count }) => count }], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_types.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_types.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table_types.ts rename to x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_types.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer.test.tsx new file mode 100644 index 0000000000000..bc9d1206ff41a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EnterpriseSearchAnalyticsPageTemplate } from '../../layout/page_template'; + +import { AnalyticsCollectionToolbar } from '../analytics_collection_toolbar/analytics_collection_toolbar'; + +import { AnalyticsCollectionExplorer } from './analytics_collection_explorer'; +import { AnalyticsCollectionExplorerTable } from './analytics_collection_explorer_table'; + +describe('AnalyticsCollectionExplorer', () => { + const mockValues = { + analyticsCollection: { event_data_stream: 'test_data_stream', name: 'Mock Collection' }, + refreshInterval: { pause: false, value: 1000 }, + timeRange: { from: 'now-15m', to: 'now' }, + }; + const mockActions = { reset: jest.fn() }; + + beforeAll(() => { + jest.clearAllMocks(); + + setMockValues(mockValues); + setMockActions(mockActions); + }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + it('renders the AnalyticsCollectionExplorerTable', () => { + const wrapper = shallow(); + expect(wrapper.find(AnalyticsCollectionExplorerTable)).toHaveLength(1); + }); + + it('renders the EnterpriseSearchAnalyticsPageTemplate', () => { + const wrapper = shallow(); + expect(wrapper.find(EnterpriseSearchAnalyticsPageTemplate)).toHaveLength(1); + }); + + it('passes the expected props to EnterpriseSearchAnalyticsPageTemplate', () => { + const wrapper = shallow().find( + EnterpriseSearchAnalyticsPageTemplate + ); + + expect(wrapper.prop('pageChrome')).toEqual([mockValues.analyticsCollection.name]); + expect(wrapper.prop('analyticsName')).toEqual(mockValues.analyticsCollection.name); + expect(wrapper.prop('pageHeader')).toEqual({ + bottomBorder: false, + pageTitle: 'Explorer', + rightSideItems: [], + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer.tsx new file mode 100644 index 0000000000000..80a4b87f27d92 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer.tsx @@ -0,0 +1,51 @@ +/* + * 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 } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; + +import { EnterpriseSearchAnalyticsPageTemplate } from '../../layout/page_template'; +import { AnalyticsCollectionExploreTableLogic } from '../analytics_collection_explore_table_logic'; +import { AnalyticsCollectionToolbar } from '../analytics_collection_toolbar/analytics_collection_toolbar'; +import { FetchAnalyticsCollectionLogic } from '../fetch_analytics_collection_logic'; + +import { AnalyticsCollectionExplorerTable } from './analytics_collection_explorer_table'; + +export const AnalyticsCollectionExplorer: React.FC = ({}) => { + const { analyticsCollection } = useValues(FetchAnalyticsCollectionLogic); + const { reset } = useActions(AnalyticsCollectionExploreTableLogic); + + useEffect(() => { + return () => { + reset(); + }; + }, []); + + return ( + ], + }} + > + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_callout.tsx new file mode 100644 index 0000000000000..da24d06f81be8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_callout.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 { useValues } from 'kea'; + +import { EuiButton, EuiCallOut } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; + +import { KibanaLogic } from '../../../../shared/kibana'; +import { useDiscoverLink } from '../use_discover_link'; + +export const AnalyticsCollectionExplorerCallout: React.FC = () => { + const { application } = useValues(KibanaLogic); + const discoverLink = useDiscoverLink(); + + return discoverLink ? ( + +

    + +

    + + + + + + +
    + ) : null; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.test.tsx new file mode 100644 index 0000000000000..fc702a0493369 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.test.tsx @@ -0,0 +1,70 @@ +/* + * 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 { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { mount, shallow } from 'enzyme'; + +import { ExploreTables } from '../analytics_collection_explore_table_types'; + +import { AnalyticsCollectionExplorerTable } from './analytics_collection_explorer_table'; + +describe('AnalyticsCollectionExplorerTable', () => { + const mockActions = { + onTableChange: jest.fn(), + setPageIndex: jest.fn(), + setPageSize: jest.fn(), + setSearch: jest.fn(), + setSelectedTable: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + + setMockValues({ items: [], selectedTable: ExploreTables.TopClicked }); + setMockActions(mockActions); + }); + + it('should set default selectedTable', () => { + setMockValues({ items: [], selectedTable: null }); + const wrapper = mount(); + + wrapper.update(); + + expect(mockActions.setSelectedTable).toHaveBeenCalledWith(ExploreTables.SearchTerms, { + direction: 'desc', + field: 'count', + }); + }); + + it('should call setSelectedTable when click on a tab', () => { + const tabs = shallow().find('EuiTab'); + + expect(tabs.length).toBe(4); + + tabs.at(2).simulate('click'); + expect(mockActions.setSelectedTable).toHaveBeenCalledWith(ExploreTables.WorsePerformers, { + direction: 'desc', + field: 'count', + }); + }); + + it('should call onTableChange when table called onChange', () => { + const table = shallow().find('EuiBasicTable'); + + table.simulate('change', { + page: { index: 23, size: 44 }, + sort: { direction: 'asc', field: 'test' }, + }); + expect(mockActions.onTableChange).toHaveBeenCalledWith({ + page: { index: 23, size: 44 }, + sort: { direction: 'asc', field: 'test' }, + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx new file mode 100644 index 0000000000000..cc104fe93b7ba --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx @@ -0,0 +1,328 @@ +/* + * 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 } from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + Criteria, + EuiBasicTable, + EuiBasicTableColumn, + EuiFieldSearch, + EuiFlexGroup, + EuiHorizontalRule, + EuiSpacer, + EuiTab, + EuiTabs, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import { + EuiTableFieldDataColumnType, + EuiTableSortingType, +} from '@elastic/eui/src/components/basic_table/table_types'; +import { UseEuiTheme } from '@elastic/eui/src/services/theme/hooks'; + +import { i18n } from '@kbn/i18n'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { AnalyticsCollectionExploreTableLogic } from '../analytics_collection_explore_table_logic'; +import { + ExploreTableColumns, + ExploreTableItem, + ExploreTables, + SearchTermsTable, + TopClickedTable, + TopReferrersTable, + WorsePerformersTable, +} from '../analytics_collection_explore_table_types'; + +import { AnalyticsCollectionExplorerCallout } from './analytics_collection_explorer_callout'; + +interface TableSetting { + columns: Array< + EuiBasicTableColumn & { + render?: (euiTheme: UseEuiTheme['euiTheme']) => EuiTableFieldDataColumnType['render']; + } + >; + sorting: EuiTableSortingType; +} + +const tabs: Array<{ id: ExploreTables; name: string }> = [ + { + id: ExploreTables.SearchTerms, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.explorer.searchTermsTab', + { defaultMessage: 'Search terms' } + ), + }, + { + id: ExploreTables.TopClicked, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.explorer.topClickedTab', + { defaultMessage: 'Top clicked results' } + ), + }, + { + id: ExploreTables.WorsePerformers, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.explorer.noResultsTab', + { defaultMessage: 'No results' } + ), + }, + { + id: ExploreTables.TopReferrers, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.explorer.referrersTab', + { defaultMessage: 'Referrers' } + ), + }, +]; + +const tableSettings: { + [ExploreTables.SearchTerms]: TableSetting; + [ExploreTables.TopClicked]: TableSetting; + [ExploreTables.TopReferrers]: TableSetting; + [ExploreTables.WorsePerformers]: TableSetting; +} = { + [ExploreTables.SearchTerms]: { + columns: [ + { + field: ExploreTableColumns.searchTerms, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.searchTerms', + { defaultMessage: 'Search Terms' } + ), + sortable: true, + truncateText: true, + }, + { + align: 'right', + field: ExploreTableColumns.count, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.count', + { defaultMessage: 'Count' } + ), + sortable: true, + truncateText: true, + }, + ], + sorting: { + sort: { + direction: 'desc', + field: ExploreTableColumns.count, + }, + }, + }, + [ExploreTables.WorsePerformers]: { + columns: [ + { + field: ExploreTableColumns.query, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.query', + { defaultMessage: 'Query' } + ), + sortable: true, + truncateText: true, + }, + { + align: 'right', + field: ExploreTableColumns.count, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.count', + { defaultMessage: 'Count' } + ), + sortable: true, + truncateText: true, + }, + ], + sorting: { + sort: { + direction: 'desc', + field: ExploreTableColumns.count, + }, + }, + }, + [ExploreTables.TopClicked]: { + columns: [ + { + field: ExploreTableColumns.page, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.page', + { defaultMessage: 'Page' } + ), + render: (euiTheme: UseEuiTheme['euiTheme']) => (value: string) => + ( + +

    {value}

    +
    + ), + sortable: true, + truncateText: true, + }, + { + align: 'right', + field: ExploreTableColumns.count, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.count', + { defaultMessage: 'Count' } + ), + sortable: true, + truncateText: true, + }, + ], + sorting: { + sort: { + direction: 'desc', + field: ExploreTableColumns.count, + }, + }, + }, + [ExploreTables.TopReferrers]: { + columns: [ + { + field: ExploreTableColumns.page, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.page', + { defaultMessage: 'Page' } + ), + render: (euiTheme: UseEuiTheme['euiTheme']) => (value: string) => + ( + +

    {value}

    +
    + ), + sortable: true, + truncateText: true, + }, + { + align: 'right', + field: ExploreTableColumns.sessions, + name: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.exploreTable.session', + { defaultMessage: 'Session' } + ), + sortable: true, + truncateText: true, + }, + ], + sorting: { + sort: { + direction: 'desc', + field: ExploreTableColumns.sessions, + }, + }, + }, +}; + +export const AnalyticsCollectionExplorerTable = () => { + const { euiTheme } = useEuiTheme(); + const { onTableChange, setSelectedTable, setSearch } = useActions( + AnalyticsCollectionExploreTableLogic + ); + const { items, isLoading, pageIndex, pageSize, search, selectedTable, sorting, totalItemsCount } = + useValues(AnalyticsCollectionExploreTableLogic); + let table = selectedTable !== null && (tableSettings[selectedTable] as TableSetting); + if (table) { + table = { + ...table, + columns: table.columns.map((column) => ({ + ...column, + render: column.render?.(euiTheme), + })) as TableSetting['columns'], + sorting: { ...table.sorting, sort: sorting || undefined }, + }; + } + const handleTableChange = ({ sort, page }: Criteria) => { + onTableChange({ page, sort }); + }; + + useEffect(() => { + if (!selectedTable) { + const firstTabId = tabs[0].id; + + setSelectedTable(firstTabId, (tableSettings[firstTabId] as TableSetting)?.sorting?.sort); + } + }, []); + + return ( + + + {tabs?.map(({ id, name }) => ( + setSelectedTable(id, (tableSettings[id] as TableSetting)?.sorting?.sort)} + isSelected={id === selectedTable} + > + {name} + + ))} + + + {table && ( + + setSearch(event.target.value)} + isClearable + isLoading={isLoading} + incremental + fullWidth + /> + + + + + + {pageSize * pageIndex + Number(!!items.length)}- + {pageSize * pageIndex + items.length} + + ), + totalItemsCount, + }} + /> + + + + + + + + + )} + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate.test.tsx index f1c66a8cedbb6..df2dbade8c852 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate.test.tsx @@ -36,28 +36,32 @@ describe('AnalyticsCollectionIntegrate', () => { const wrapper = shallow( ); - expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock)).toHaveLength(3); + expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock)).toHaveLength(4); wrapper.find('[data-test-subj="searchuiEmbed"]').at(0).simulate('click'); expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock)).toHaveLength(3); wrapper.find('[data-test-subj="javascriptClientEmbed"]').at(0).simulate('click'); - expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock)).toHaveLength(5); + expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock)).toHaveLength(6); }); it('check value of config & webClientSrc', () => { const wrapper = shallow( ); - expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock).at(0).dive().text()).toContain( - 'https://cdn.jsdelivr.net/npm/@elastic/behavioral-analytics-browser-tracker@2/dist/umd/index.global.js' + expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock).at(1).dive().text()).toContain( + 'https://cdn.jsdelivr.net/npm/@elastic/behavioral-analytics-browser-tracker@2' ); - expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock).at(1).dive().text()) + expect(wrapper.find(EuiSteps).dive().find(EuiCodeBlock).at(2).dive().text()) .toMatchInlineSnapshot(` - "" + apiKey: \\"########\\", + // Optional: sampling rate percentage: 0-1, 0 = no events, 1 = all events + // sampling: 1, + }); + " `); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_javascript_embed.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_javascript_embed.tsx index 632a6626404cd..b44b19042d60c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_javascript_embed.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_javascript_embed.tsx @@ -63,11 +63,15 @@ export const javascriptEmbedSteps = (webClientSrc: string, analyticsConfig: Anal )}

    - {``} + apiKey: "${analyticsConfig.apiKey}", + // Optional: sampling rate percentage: 0-1, 0 = no events, 1 = all events + // sampling: 1, +}); +`}
    diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_searchui.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_searchui.tsx index c52fbdbff01f5..74a08f90b1178 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_searchui.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_searchui.tsx @@ -118,7 +118,7 @@ const searchUIConfig = { ... plugins: [ AnalyticsPlugin({ - client: getTracker(); + client: getTracker() }) ], ... diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_view.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_view.tsx index 550a16e7a78cc..2b69286d43f77 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_integrate/analytics_collection_integrate_view.tsx @@ -19,6 +19,7 @@ import { EuiTabs, EuiLink, EuiText, + EuiCodeBlock, } from '@elastic/eui'; import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; @@ -26,10 +27,10 @@ import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; import { AnalyticsCollection } from '../../../../../../common/types/analytics'; +import { useCloudDetails } from '../../../../shared/cloud_details/cloud_details'; +import { decodeCloudId } from '../../../../shared/decode_cloud_id/decode_cloud_id'; import { docLinks } from '../../../../shared/doc_links'; -import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url'; - import { KibanaLogic } from '../../../../shared/kibana'; import { EnterpriseSearchAnalyticsPageTemplate } from '../../layout/page_template'; @@ -51,6 +52,62 @@ export interface AnalyticsConfig { endpoint: string; } +const CORSStep = (): EuiContainedStepProps => ({ + title: i18n.translate( + 'xpack.enterpriseSearch.analytics.collections.collectionsView.corsStep.title', + { + defaultMessage: 'Configure CORS', + } + ), + children: ( + <> + + <> +

    + {i18n.translate( + 'xpack.enterpriseSearch.analytics.collectionsView.integration.corsStep.description', + { + defaultMessage: + "You must configure CORS to allow requests from your website's domain to the Analytics API endpoint. You can do this by adding the following to your Elasticsearch configuration file:", + } + )} +

    + + + {`http.cors.allow-origin: "*" +http.cors.enabled: true +http.cors.allow-credentials: true +http.cors.allow-methods: OPTIONS, POST +http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length, Authorization, Access-Control-Allow-Headers, Accept`} + +

    + {i18n.translate( + 'xpack.enterpriseSearch.analytics.collectionsView.integration.corsStep.descriptionTwo', + { + defaultMessage: + "Alternatively you can use a proxy server to route analytic requests from your website's domain to the Analytics API endpoint which will allow you to avoid configuring CORS.", + } + )} +

    + + {i18n.translate( + 'xpack.enterpriseSearch.analytics.collectionsView.integration.corsStep.learnMoreLink', + { + defaultMessage: 'Learn more about CORS for Behavioral Analytics.', + } + )} + + +
    + + ), +}); + const apiKeyStep = ( openApiKeyModal: () => void, navigateToUrl: typeof KibanaLogic.values.navigateToUrl @@ -135,13 +192,18 @@ export const AnalyticsCollectionIntegrateView: React.FC(false); const { navigateToUrl } = useValues(KibanaLogic); const { apiKey } = useValues(GenerateApiKeyModalLogic); + const DEFAULT_URL = 'https://localhost:9200'; + const cloudContext = useCloudDetails(); + + const baseUrl = + (cloudContext.cloudId && decodeCloudId(cloudContext.cloudId)?.elasticsearchUrl) || DEFAULT_URL; const analyticsConfig: AnalyticsConfig = { apiKey: apiKey || '########', collectionName: analyticsCollection?.name, - endpoint: getEnterpriseSearchUrl(), + endpoint: baseUrl, }; - const webClientSrc = `https://cdn.jsdelivr.net/npm/@elastic/behavioral-analytics-browser-tracker@2/dist/umd/index.global.js`; + const webClientSrc = `https://cdn.jsdelivr.net/npm/@elastic/behavioral-analytics-browser-tracker@2`; const tabs: Array<{ key: TabKey; @@ -179,8 +241,16 @@ export const AnalyticsCollectionIntegrateView: React.FC setApiKeyModalOpen(true), navigateToUrl); const steps: Record = { - javascriptClientEmbed: [apiKeyStepGuide, ...javascriptClientEmbedSteps(analyticsConfig)], - javascriptEmbed: [apiKeyStepGuide, ...javascriptEmbedSteps(webClientSrc, analyticsConfig)], + javascriptClientEmbed: [ + apiKeyStepGuide, + CORSStep(), + ...javascriptClientEmbedSteps(analyticsConfig), + ], + javascriptEmbed: [ + apiKeyStepGuide, + CORSStep(), + ...javascriptEmbedSteps(webClientSrc, analyticsConfig), + ], searchuiEmbed: searchUIEmbedSteps(setSelectedTab), }; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout.tsx index 7e5ed567bb846..1340a5105dae4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout.tsx @@ -31,13 +31,13 @@ export const AnalyticsCollectionNoEventsCallout: React.FC< > = ({ analyticsCollection }) => { const { navigateToUrl } = useValues(KibanaLogic); const { analyticsEventsExist } = useActions(AnalyticsCollectionNoEventsCalloutLogic); - const { hasEvents } = useValues(AnalyticsCollectionNoEventsCalloutLogic); + const { hasEvents, isLoading } = useValues(AnalyticsCollectionNoEventsCalloutLogic); useEffect(() => { - analyticsEventsExist(analyticsCollection.name); + analyticsEventsExist(analyticsCollection.events_datastream); }, []); - return hasEvents ? null : ( + return hasEvents || isLoading ? null : ( { const DEFAULT_VALUES = { data: undefined, hasEvents: false, + isLoading: false, status: Status.IDLE, }; @@ -33,13 +34,14 @@ describe('analyticsEventsExistLogic', () => { describe('selectors', () => { it('updates when apiSuccess listener triggered', () => { - AnalyticsCollectionNoEventsCalloutLogic.actions.apiSuccess({ exist: indexName }); + AnalyticsCollectionNoEventsCalloutLogic.actions.apiSuccess({ exists: indexName }); expect(AnalyticsCollectionNoEventsCalloutLogic.values).toEqual({ ...DEFAULT_VALUES, + data: { exists: indexName }, hasEvents: true, + isLoading: false, status: Status.SUCCESS, - data: { exist: indexName }, }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout_logic.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout_logic.tsx index c63ddc97cc536..680c4715621a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout_logic.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_no_events_callout/analytics_collection_no_events_callout_logic.tsx @@ -22,6 +22,7 @@ export interface AnalyticsCollectionNoEventsCalloutActions { export interface AnalyticsCollectionNoEventsCalloutValues { hasEvents: boolean; status: Status; + isLoading: boolean; data: typeof AnalyticsEventsExistAPILogic.values.data; } @@ -40,8 +41,15 @@ export const AnalyticsCollectionNoEventsCalloutLogic = kea< actions.makeRequest({ indexName }); }, }), - path: ['enterprise_search', 'analytics', 'events_exist'], + path: ['enterprise_search', 'analytics', 'collection', 'events_exist'], selectors: ({ selectors }) => ({ - hasEvents: [() => [selectors.data], (data) => data?.exist === true], + hasEvents: [ + () => [selectors.data], + (data: AnalyticsCollectionNoEventsCalloutValues['data']) => data?.exists === true, + ], + isLoading: [ + () => [selectors.status], + (status: AnalyticsCollectionNoEventsCalloutValues['status']) => status === Status.LOADING, + ], }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.test.tsx index 31831805a7f4f..ff0b8f4788df7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.test.tsx @@ -35,12 +35,9 @@ describe('AnalyticsCollectionChart', () => { from: moment().subtract(7, 'days').toISOString(), to: moment().toISOString(), }; - const mockedDataViewQuery = 'mockedDataViewQuery'; const defaultProps = { data: {}, - dataViewQuery: mockedDataViewQuery, - id: 'mockedId', isLoading: false, selectedChart: FilterBy.Searches, setSelectedChart: jest.fn(), diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx index 9a86c758bf585..b518bc8326662 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx @@ -42,8 +42,7 @@ const DEFAULT_STROKE_WIDTH = 1; const HOVER_STROKE_WIDTH = 3; const CHART_HEIGHT = 490; -interface AnalyticsCollectionChartProps extends WithLensDataInputProps { - dataViewQuery: string; +interface AnalyticsCollectionChartProps extends Pick { selectedChart: FilterBy | null; setSelectedChart(chart: FilterBy): void; } @@ -303,6 +302,5 @@ export const AnalyticsCollectionChartWithLens = withLensData< visualizationType: 'lnsXY', }; }, - getDataViewQuery: (props) => props.dataViewQuery, initialValues, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_metric.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_metric.tsx index 7660c9e92fa47..9c0e44c02e2f6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_metric.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_metric.tsx @@ -54,7 +54,6 @@ const getMetricStatus = (metric: number): MetricStatus => { }; interface AnalyticsCollectionViewMetricProps { - dataViewQuery: string; getFormula: (shift?: string) => string; isSelected?: boolean; name: string; @@ -230,6 +229,5 @@ export const AnalyticsCollectionViewMetricWithLens = withLensData< visualizationType: 'lnsMetric', }; }, - getDataViewQuery: (props) => props.dataViewQuery, initialValues, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx index b9e54854978fd..fdce4918d6aa5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx @@ -38,6 +38,7 @@ const mockValues = { }; const mockActions = { + analyticsEventsExist: jest.fn(), fetchAnalyticsCollection: jest.fn(), fetchAnalyticsCollectionDataViewId: jest.fn(), analyticsEventsExist: jest.fn(), @@ -92,7 +93,7 @@ describe('AnalyticsOverView', () => { ); expect(wrapper?.find(AnalyticsCollectionChartWithLens)).toHaveLength(1); expect(wrapper?.find(AnalyticsCollectionChartWithLens).props()).toEqual({ - dataViewQuery: 'analytics-events-example', + collection: mockValues.analyticsCollection, id: 'analytics-collection-chart-Analytics-Collection-1', searchSessionId: 'session-id', selectedChart: 'Searches', diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.tsx index 04a8ab8f675c5..5ec96b3103929 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.tsx @@ -18,14 +18,13 @@ import { FilterBy, getFormulaByFilter } from '../../../utils/get_formula_by_filt import { EnterpriseSearchAnalyticsPageTemplate } from '../../layout/page_template'; -import { AnalyticsCollectionExploreTable } from '../analytics_collection_explore_table/analytics_collection_explore_table'; - import { AnalyticsCollectionNoEventsCallout } from '../analytics_collection_no_events_callout/analytics_collection_no_events_callout'; import { AnalyticsCollectionToolbar } from '../analytics_collection_toolbar/analytics_collection_toolbar'; import { AnalyticsCollectionToolbarLogic } from '../analytics_collection_toolbar/analytics_collection_toolbar_logic'; import { AnalyticsCollectionChartWithLens } from './analytics_collection_chart'; import { AnalyticsCollectionViewMetricWithLens } from './analytics_collection_metric'; +import { AnalyticsCollectionOverviewTable } from './analytics_collection_overview_table'; const filters = [ { @@ -107,7 +106,7 @@ export const AnalyticsCollectionOverview: React.FC - +
    ); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview_table.test.tsx similarity index 70% rename from x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview_table.test.tsx index f949639d534a9..e6e553dc51792 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview_table.test.tsx @@ -15,10 +15,11 @@ import { EuiBasicTable, EuiTab } from '@elastic/eui'; import { FilterBy } from '../../../utils/get_formula_by_filter'; -import { AnalyticsCollectionExploreTable } from './analytics_collection_explore_table'; -import { ExploreTables } from './analytics_collection_explore_table_types'; +import { ExploreTables } from '../analytics_collection_explore_table_types'; -describe('AnalyticsCollectionExploreTable', () => { +import { AnalyticsCollectionOverviewTable } from './analytics_collection_overview_table'; + +describe('AnalyticsCollectionOverviewTable', () => { const mockValues = { activeTableId: 'search_terms', analyticsCollection: { @@ -41,7 +42,7 @@ describe('AnalyticsCollectionExploreTable', () => { }); it('should call setSelectedTable with the correct table id when a tab is clicked', () => { - const wrapper = shallow(); + const wrapper = shallow(); const topReferrersTab = wrapper.find(EuiTab).at(0); topReferrersTab.simulate('click'); @@ -53,14 +54,9 @@ describe('AnalyticsCollectionExploreTable', () => { }); }); - it('should call findDataView with the active table ID and search filter when mounted', () => { - mount(); - expect(mockActions.findDataView).toHaveBeenCalledWith(mockValues.analyticsCollection); - }); - it('should render a table with the selectedTable', () => { setMockValues({ ...mockValues, selectedTable: ExploreTables.WorsePerformers }); - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find(EuiBasicTable).prop('itemId')).toBe(ExploreTables.WorsePerformers); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview_table.tsx similarity index 94% rename from x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table.tsx rename to x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview_table.tsx index 7b77cdb05cf61..8538b11f748fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table/analytics_collection_explore_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview_table.tsx @@ -33,9 +33,8 @@ import { generateEncodedPath } from '../../../../shared/encode_path_params'; import { KibanaLogic } from '../../../../shared/kibana'; import { COLLECTION_EXPLORER_PATH } from '../../../routes'; import { FilterBy } from '../../../utils/get_formula_by_filter'; -import { FetchAnalyticsCollectionLogic } from '../fetch_analytics_collection_logic'; -import { AnalyticsCollectionExploreTableLogic } from './analytics_collection_explore_table_logic'; +import { AnalyticsCollectionExploreTableLogic } from '../analytics_collection_explore_table_logic'; import { ExploreTableColumns, ExploreTableItem, @@ -44,7 +43,8 @@ import { TopClickedTable, TopReferrersTable, WorsePerformersTable, -} from './analytics_collection_explore_table_types'; +} from '../analytics_collection_explore_table_types'; +import { FetchAnalyticsCollectionLogic } from '../fetch_analytics_collection_logic'; const tabsByFilter: Record> = { [FilterBy.Searches]: [ @@ -230,28 +230,22 @@ const tableSettings: { }, }; -interface AnalyticsCollectionExploreTableProps { +interface AnalyticsCollectionOverviewTableProps { filterBy: FilterBy; } -export const AnalyticsCollectionExploreTable: React.FC = ({ +export const AnalyticsCollectionOverviewTable: React.FC = ({ filterBy, }) => { const { euiTheme } = useEuiTheme(); const { navigateToUrl } = useValues(KibanaLogic); const { analyticsCollection } = useValues(FetchAnalyticsCollectionLogic); - const { findDataView, setSelectedTable, setSorting } = useActions( - AnalyticsCollectionExploreTableLogic - ); + const { onTableChange, setSelectedTable } = useActions(AnalyticsCollectionExploreTableLogic); const { items, isLoading, selectedTable, sorting } = useValues( AnalyticsCollectionExploreTableLogic ); const tabs = tabsByFilter[filterBy]; - useEffect(() => { - findDataView(analyticsCollection); - }, [analyticsCollection]); - useEffect(() => { const firstTableInTabsId = tabs[0].id; @@ -292,7 +286,7 @@ export const AnalyticsCollectionExploreTable: React.FC { - setSorting(sort); + onTableChange({ sort }); }} /> ) diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.test.tsx index 438d533d126f2..0cdf0fb5d45fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.test.tsx @@ -37,7 +37,7 @@ describe('AnalyticsCollectionToolbar', () => { events_datastream: 'test-events', name: 'test', } as AnalyticsCollection, - dataViewId: 'data-view-test', + dataView: { id: 'data-view-test' }, isLoading: false, refreshInterval: { pause: false, value: 10000 }, timeRange: { from: 'now-90d', to: 'now' }, @@ -93,7 +93,9 @@ describe('AnalyticsCollectionToolbar', () => { expect(exploreInDiscoverItem).toHaveLength(1); - expect(exploreInDiscoverItem.prop('href')).toBe("/app/discover#/?_a=(index:'data-view-test')"); + expect(exploreInDiscoverItem.prop('href')).toBe( + "/app/discover#/?_a=(index:'data-view-test')&_g=(filters:!(),refreshInterval:(pause:!f,value:10000),time:(from:now-90d,to:now))" + ); }); it('should correct link to the manage datastream link', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.tsx index 4162b13569089..66122502afb78 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { useActions, useValues } from 'kea'; @@ -23,10 +23,12 @@ import { import { OnTimeChangeProps } from '@elastic/eui/src/components/date_picker/super_date_picker/super_date_picker'; import { OnRefreshChangeProps } from '@elastic/eui/src/components/date_picker/types'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { RedirectAppLinks } from '@kbn/kibana-react-plugin/public'; + +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { generateEncodedPath } from '../../../../shared/encode_path_params'; @@ -34,6 +36,7 @@ import { KibanaLogic } from '../../../../shared/kibana'; import { COLLECTION_INTEGRATE_PATH } from '../../../routes'; import { DeleteAnalyticsCollectionLogic } from '../delete_analytics_collection_logic'; import { FetchAnalyticsCollectionLogic } from '../fetch_analytics_collection_logic'; +import { useDiscoverLink } from '../use_discover_link'; import { AnalyticsCollectionToolbarLogic } from './analytics_collection_toolbar_logic'; @@ -76,18 +79,16 @@ const defaultQuickRanges: EuiSuperDatePickerCommonRange[] = [ ]; export const AnalyticsCollectionToolbar: React.FC = () => { + const discoverLink = useDiscoverLink(); const [isPopoverOpen, setPopover] = useState(false); const { application, navigateToUrl } = useValues(KibanaLogic); const { analyticsCollection } = useValues(FetchAnalyticsCollectionLogic); - const { setTimeRange, setRefreshInterval, findDataViewId, onTimeRefresh } = useActions( + const { setTimeRange, setRefreshInterval, onTimeRefresh } = useActions( AnalyticsCollectionToolbarLogic ); - const { refreshInterval, timeRange, dataViewId } = useValues(AnalyticsCollectionToolbarLogic); + const { refreshInterval, timeRange } = useValues(AnalyticsCollectionToolbarLogic); const { deleteAnalyticsCollection } = useActions(DeleteAnalyticsCollectionLogic); const { isLoading } = useValues(DeleteAnalyticsCollectionLogic); - const discoverUrl = application.getUrlForApp('discover', { - path: `#/?_a=(index:'${dataViewId}')`, - }); const manageDatastreamUrl = application.getUrlForApp('management', { path: '/data/index_management/data_streams/' + analyticsCollection.events_datastream, }); @@ -104,10 +105,6 @@ export const AnalyticsCollectionToolbar: React.FC = () => { setRefreshInterval({ pause, value }); }; - useEffect(() => { - if (analyticsCollection) findDataViewId(analyticsCollection); - }, [analyticsCollection]); - return ( @@ -126,88 +123,92 @@ export const AnalyticsCollectionToolbar: React.FC = () => { /> - - - - - } - isOpen={isPopoverOpen} - closePopover={closePopover} - anchorPosition="downRight" - panelPaddingSize="none" - > - - - navigateToUrl( - generateEncodedPath(COLLECTION_INTEGRATE_PATH, { - name: analyticsCollection.name, - }) - ) - } - > - - - - + + + + + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + anchorPosition="downRight" + panelPaddingSize="none" + > + + navigateToUrl( + generateEncodedPath(COLLECTION_INTEGRATE_PATH, { + name: analyticsCollection.name, + }) + ) + } > - - - - { - deleteAnalyticsCollection(analyticsCollection.name); - }} - > - - - - - - + + {discoverLink && ( + + + + )} + + + { + deleteAnalyticsCollection(analyticsCollection.name); + }} + > + + + + + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.test.ts index f9c2ac5f4bfb5..ae78ff6198830 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.test.ts @@ -44,7 +44,6 @@ describe('AnalyticsCollectionToolbarLogic', () => { }); const defaultProps: AnalyticsCollectionToolbarLogicValues = { _searchSessionId: null, - dataViewId: null, refreshInterval: { pause: true, value: 10000 }, searchSessionId: undefined, timeRange: { from: 'now-7d', to: 'now' }, @@ -62,11 +61,6 @@ describe('AnalyticsCollectionToolbarLogic', () => { ); }); - it('sets dataViewId', () => { - AnalyticsCollectionToolbarLogic.actions.setDataViewId('sample_data_view_id'); - expect(AnalyticsCollectionToolbarLogic.values.dataViewId).toEqual('sample_data_view_id'); - }); - it('sets refreshInterval', () => { const refreshInterval: RefreshInterval = { pause: false, value: 5000 }; AnalyticsCollectionToolbarLogic.actions.setRefreshInterval(refreshInterval); @@ -81,14 +75,6 @@ describe('AnalyticsCollectionToolbarLogic', () => { }); describe('listeners', () => { - it('should set dataViewId when findDataViewId called', async () => { - await AnalyticsCollectionToolbarLogic.actions.findDataViewId({ - events_datastream: 'some-collection', - name: 'some-collection-name', - }); - expect(AnalyticsCollectionToolbarLogic.values.dataViewId).toBe('some-data-view-id'); - }); - it('should set searchSessionId when onTimeRefresh called', () => { jest.spyOn(AnalyticsCollectionToolbarLogic.actions, 'setSearchSessionId'); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.ts index 23e97a022fed3..cfbe44b64d82c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_toolbar/analytics_collection_toolbar_logic.ts @@ -11,11 +11,9 @@ import { RefreshInterval } from '@kbn/data-plugin/common'; import { TimeRange } from '@kbn/es-query'; -import { AnalyticsCollection } from '../../../../../../common/types/analytics'; import { KibanaLogic } from '../../../../shared/kibana/kibana_logic'; export interface AnalyticsCollectionToolbarLogicActions { - findDataViewId(collection: AnalyticsCollection): { collection: AnalyticsCollection }; onTimeRefresh(): void; setDataViewId(id: string): { id: string }; setRefreshInterval(refreshInterval: RefreshInterval): RefreshInterval; @@ -27,7 +25,6 @@ export interface AnalyticsCollectionToolbarLogicActions { export interface AnalyticsCollectionToolbarLogicValues { // kea forbid to set undefined as a value _searchSessionId: string | null; - dataViewId: string | null; refreshInterval: RefreshInterval; searchSessionId: string | undefined; timeRange: TimeRange; @@ -40,23 +37,12 @@ export const AnalyticsCollectionToolbarLogic = kea< MakeLogicType >({ actions: { - findDataViewId: (collection) => ({ collection }), onTimeRefresh: true, - setDataViewId: (id) => ({ id }), setRefreshInterval: ({ pause, value }) => ({ pause, value }), setSearchSessionId: (searchSessionId) => ({ searchSessionId }), setTimeRange: ({ from, to }) => ({ from, to }), }, listeners: ({ actions }) => ({ - findDataViewId: async ({ collection }) => { - const dataViewId = ( - await KibanaLogic.values.data.dataViews.find(collection.events_datastream, 1) - )?.[0]?.id; - - if (dataViewId) { - actions.setDataViewId(dataViewId); - } - }, onTimeRefresh() { actions.setSearchSessionId(KibanaLogic.values.data.search.session.start()); }, @@ -69,13 +55,12 @@ export const AnalyticsCollectionToolbarLogic = kea< actions.setSearchSessionId(null); }, }), - path: ['enterprise_search', 'analytics', 'collections', 'toolbar'], + path: ['enterprise_search', 'analytics', 'collection', 'toolbar'], reducers: () => ({ _searchSessionId: [ null, { setSearchSessionId: (state, { searchSessionId }) => searchSessionId }, ], - dataViewId: [null, { setDataViewId: (_, { id }) => id }], refreshInterval: [ DEFAULT_REFRESH_INTERVAL, { diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx index 0db1d77ba6b49..b51dfa0c1f5bd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx @@ -25,6 +25,10 @@ import { AddAnalyticsCollection } from '../add_analytics_collections/add_analyti import { EnterpriseSearchAnalyticsPageTemplate } from '../layout/page_template'; +import { AnalyticsCollectionDataViewLogic } from './analytics_collection_data_view_logic'; + +import { AnalyticsCollectionExplorer } from './analytics_collection_explorer/analytics_collection_explorer'; + import { AnalyticsCollectionIntegrateView } from './analytics_collection_integrate/analytics_collection_integrate_view'; import { AnalyticsCollectionOverview } from './analytics_collection_overview/analytics_collection_overview'; import { AnalyticsCollectionToolbarLogic } from './analytics_collection_toolbar/analytics_collection_toolbar_logic'; @@ -33,6 +37,7 @@ import { FetchAnalyticsCollectionLogic } from './fetch_analytics_collection_logi export const AnalyticsCollectionView: React.FC = () => { useMountedLogic(AnalyticsCollectionToolbarLogic); + useMountedLogic(AnalyticsCollectionDataViewLogic); const { fetchAnalyticsCollection } = useActions(FetchAnalyticsCollectionLogic); const { analyticsCollection, isLoading } = useValues(FetchAnalyticsCollectionLogic); const { name } = useParams<{ name: string }>(); @@ -52,7 +57,9 @@ export const AnalyticsCollectionView: React.FC = () => { - + + + ); } diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/delete_analytics_collection_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/delete_analytics_collection_logic.ts index ea0cf4476ac54..6913739863efa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/delete_analytics_collection_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/delete_analytics_collection_logic.ts @@ -48,7 +48,7 @@ export const DeleteAnalyticsCollectionLogic = kea< actions.makeRequest({ name }); }, }), - path: ['enterprise_search', 'analytics', 'collections', 'delete'], + path: ['enterprise_search', 'analytics', 'collection', 'delete'], selectors: ({ selectors }) => ({ isLoading: [ () => [selectors.status], diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/fetch_analytics_collection_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/fetch_analytics_collection_logic.ts index 19d54b5b9dad7..3d96d20f74e76 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/fetch_analytics_collection_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/fetch_analytics_collection_logic.ts @@ -16,6 +16,7 @@ import { } from '../../api/fetch_analytics_collection/fetch_analytics_collection_api_logic'; export interface FetchAnalyticsCollectionActions { + apiSuccess: Actions<{}, FetchAnalyticsCollectionApiLogicResponse>['apiSuccess']; fetchAnalyticsCollection(name: string): AnalyticsCollection; makeRequest: Actions<{}, FetchAnalyticsCollectionApiLogicResponse>['makeRequest']; } @@ -33,7 +34,7 @@ export const FetchAnalyticsCollectionLogic = kea< fetchAnalyticsCollection: (name) => ({ name }), }, connect: { - actions: [FetchAnalyticsCollectionAPILogic, ['makeRequest']], + actions: [FetchAnalyticsCollectionAPILogic, ['makeRequest', 'apiSuccess']], values: [FetchAnalyticsCollectionAPILogic, ['data', 'status']], }, listeners: ({ actions }) => ({ diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/use_discover_link.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/use_discover_link.ts new file mode 100644 index 0000000000000..825cf1d1c3d15 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/use_discover_link.ts @@ -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 { useValues } from 'kea'; + +import { KibanaLogic } from '../../../shared/kibana'; + +import { AnalyticsCollectionDataViewLogic } from './analytics_collection_data_view_logic'; +import { AnalyticsCollectionToolbarLogic } from './analytics_collection_toolbar/analytics_collection_toolbar_logic'; + +export const useDiscoverLink = (): string | null => { + const { application } = useValues(KibanaLogic); + const { dataView } = useValues(AnalyticsCollectionDataViewLogic); + const { refreshInterval, timeRange } = useValues(AnalyticsCollectionToolbarLogic); + + return dataView + ? application.getUrlForApp('discover', { + path: `#/?_a=(index:'${ + dataView.id + }')&_g=(filters:!(),refreshInterval:(pause:!${refreshInterval.pause + .toString() + .charAt(0)},value:${refreshInterval.value}),time:(from:${timeRange.from},to:${ + timeRange.to + }))`, + }) + : null; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx index 031212489213b..c3022d2ea11ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx @@ -93,7 +93,6 @@ const getChartStatus = (metric: number | null): ChartStatus => { if (metric && metric < 0) return ChartStatus.DECREASE; return ChartStatus.CONSTANT; }; - export const AnalyticsCollectionCard: React.FC< AnalyticsCollectionCardProps & AnalyticsCollectionCardLensProps > = ({ collection, isLoading, isCreatedByEngine, subtitle, data, metric, secondaryMetric }) => { @@ -332,6 +331,5 @@ export const AnalyticsCollectionCardWithLens = withLensData< visualizationType: 'lnsMetric', }; }, - getDataViewQuery: ({ collection }) => collection.events_datastream, initialValues, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_not_found.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_not_found.tsx new file mode 100644 index 0000000000000..8f3359e764b5b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_not_found.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiEmptyPrompt, EuiImage } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import noMlModelsGraphicDark from '../../../../assets/images/no_ml_models_dark.svg'; + +const ICON_WIDTH = 294; + +interface AnalyticsCollectionNotFoundProps { + query: string; +} + +export const AnalyticsCollectionNotFound: React.FC = ({ + query, +}) => ( + } + title={ +

    + {i18n.translate('xpack.enterpriseSearch.analytics.collections.notFound.headingTitle', { + defaultMessage: 'No results found for “{query}”', + values: { query }, + })} +

    + } + body={ +

    + {i18n.translate('xpack.enterpriseSearch.analytics.collections.notFound.subHeading', { + defaultMessage: 'Try searching for another term.', + })} +

    + } + /> +); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.test.tsx index 461f24df6a1ca..af6c9a4ebb73c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.test.tsx @@ -16,6 +16,7 @@ import { EuiButtonGroup, EuiSuperDatePicker } from '@elastic/eui'; import { AnalyticsCollection } from '../../../../../common/types/analytics'; import { AnalyticsCollectionCardWithLens } from './analytics_collection_card/analytics_collection_card'; +import { AnalyticsCollectionNotFound } from './analytics_collection_not_found'; import { AnalyticsCollectionTable } from './analytics_collection_table'; @@ -30,13 +31,18 @@ describe('AnalyticsCollectionTable', () => { name: 'example2', }, ]; + const props = { + collections: analyticsCollections, + isSearching: false, + onSearch: jest.fn(), + }; beforeEach(() => { jest.clearAllMocks(); }); it('renders cards', () => { - const wrapper = shallow(); + const wrapper = shallow(); const collectionCards = wrapper.find(AnalyticsCollectionCardWithLens); expect(collectionCards).toHaveLength(analyticsCollections.length); @@ -44,9 +50,7 @@ describe('AnalyticsCollectionTable', () => { }); it('renders filters', () => { - const buttonGroup = shallow( - - ).find(EuiButtonGroup); + const buttonGroup = shallow().find(EuiButtonGroup); expect(buttonGroup).toHaveLength(1); expect(buttonGroup.prop('options')).toHaveLength(4); @@ -54,12 +58,16 @@ describe('AnalyticsCollectionTable', () => { }); it('renders datePick', () => { - const datePicker = shallow( - - ).find(EuiSuperDatePicker); + const datePicker = shallow().find(EuiSuperDatePicker); expect(datePicker).toHaveLength(1); expect(datePicker.prop('start')).toEqual('now-7d'); expect(datePicker.prop('end')).toEqual('now'); }); + + it('renders not found page', () => { + const wrapper = shallow(); + + expect(wrapper.find(AnalyticsCollectionNotFound)).toHaveLength(1); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.tsx index 2d52cf22f6ca2..705ddf29145ad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_table.tsx @@ -13,15 +13,16 @@ import { EuiPanel, EuiSuperDatePicker, EuiSuperDatePickerCommonRange, - EuiSearchBar, EuiFlexGroup, EuiSpacer, EuiButtonGroup, useEuiTheme, EuiButton, + EuiFieldSearch, } from '@elastic/eui'; import { OnTimeChangeProps } from '@elastic/eui/src/components/date_picker/super_date_picker/super_date_picker'; + import { i18n } from '@kbn/i18n'; import { AnalyticsCollection } from '../../../../../common/types/analytics'; @@ -30,6 +31,7 @@ import { AddAnalyticsCollection } from '../add_analytics_collections/add_analyti import { AnalyticsCollectionCardWithLens } from './analytics_collection_card/analytics_collection_card'; +import { AnalyticsCollectionNotFound } from './analytics_collection_not_found'; import { AnalyticsCollectionTableStyles } from './analytics_collection_table.styles'; const defaultQuickRanges: EuiSuperDatePickerCommonRange[] = [ @@ -72,10 +74,14 @@ const defaultQuickRanges: EuiSuperDatePickerCommonRange[] = [ interface AnalyticsCollectionTableProps { collections: AnalyticsCollection[]; + isSearching: boolean; + onSearch: (query: string) => void; } export const AnalyticsCollectionTable: React.FC = ({ collections, + isSearching, + onSearch, }) => { const { euiTheme } = useEuiTheme(); const analyticsCollectionTableStyles = AnalyticsCollectionTableStyles(euiTheme); @@ -113,6 +119,7 @@ export const AnalyticsCollectionTable: React.FC = [analyticsCollectionTableStyles.button] ); const [filterId, setFilterId] = useState(filterOptions[0].id); + const [query, setQuery] = useState(''); const [timeRange, setTimeRange] = useState<{ from: string; to: string }>({ from: defaultQuickRanges[0].start, to: defaultQuickRanges[0].end, @@ -127,12 +134,18 @@ export const AnalyticsCollectionTable: React.FC = - { + setQuery(e.target.value); }} + isLoading={isSearching} + onSearch={onSearch} + incremental + fullWidth /> @@ -162,18 +175,22 @@ export const AnalyticsCollectionTable: React.FC =
    - - {collections.map((collection) => ( - - ))} - + {collections.length ? ( + + {collections.map((collection) => ( + + ))} + + ) : ( + + )} ( { const DEFAULT_VALUES = { analyticsCollections: [], data: undefined, - hasNoAnalyticsCollections: false, - isLoading: true, + hasNoAnalyticsCollections: true, + isFetching: true, + isSearchRequest: false, + isSearching: false, + searchQuery: '', status: Status.IDLE, }; @@ -45,9 +50,9 @@ describe('analyticsCollectionsLogic', () => { expect(AnalyticsCollectionsLogic.values).toEqual({ ...DEFAULT_VALUES, analyticsCollections: [], - hasNoAnalyticsCollections: true, data: [], - isLoading: false, + hasNoAnalyticsCollections: true, + isFetching: false, status: Status.SUCCESS, }); }); @@ -64,12 +69,23 @@ describe('analyticsCollectionsLogic', () => { expect(AnalyticsCollectionsLogic.values).toEqual({ ...DEFAULT_VALUES, analyticsCollections: collections, + hasNoAnalyticsCollections: false, data: collections, - isLoading: false, + isFetching: false, status: Status.SUCCESS, }); }); }); + + it('updates searchQuery when searchAnalyticsCollections is called', () => { + AnalyticsCollectionsLogic.actions.searchAnalyticsCollections('test'); + expect(AnalyticsCollectionsLogic.values.searchQuery).toBe('test'); + }); + + it('updates isSearchRequest when searchAnalyticsCollections is called', () => { + AnalyticsCollectionsLogic.actions.searchAnalyticsCollections('test'); + expect(AnalyticsCollectionsLogic.values.isSearchRequest).toBe(true); + }); }); describe('listeners', () => { @@ -84,11 +100,20 @@ describe('analyticsCollectionsLogic', () => { expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledWith({}); }); - it('calls makeRequest on fetchAnalyticsCollections', async () => { + it('calls makeRequest on fetchAnalyticsCollections', () => { AnalyticsCollectionsLogic.actions.makeRequest = jest.fn(); AnalyticsCollectionsLogic.actions.fetchAnalyticsCollections(); expect(AnalyticsCollectionsLogic.actions.makeRequest).toHaveBeenCalledWith({}); }); + + it('calls makeRequest query on searchAnalyticsCollections', async () => { + jest.useFakeTimers({ legacyFakeTimers: true }); + AnalyticsCollectionsLogic.actions.makeRequest = jest.fn(); + AnalyticsCollectionsLogic.actions.searchAnalyticsCollections('test'); + jest.advanceTimersByTime(200); + await nextTick(); + expect(AnalyticsCollectionsLogic.actions.makeRequest).toHaveBeenCalledWith({ query: 'test' }); + }); }); describe('selectors', () => { @@ -101,10 +126,64 @@ describe('analyticsCollectionsLogic', () => { analyticsCollections: [], data: [], hasNoAnalyticsCollections: true, - isLoading: false, + isFetching: false, status: Status.SUCCESS, }); }); }); + + describe('isFetching', () => { + it('updates on initialState', () => { + expect(AnalyticsCollectionsLogic.values.isFetching).toBe(true); + }); + + it('updates when fetchAnalyticsCollections listener triggered', () => { + AnalyticsCollectionsLogic.actions.fetchAnalyticsCollections(); + expect(AnalyticsCollectionsLogic.values.isFetching).toBe(true); + }); + + it('updates when apiSuccess listener triggered', () => { + FetchAnalyticsCollectionsAPILogic.actions.apiSuccess([]); + expect(AnalyticsCollectionsLogic.values.isFetching).toBe(false); + }); + + it('updates when search request triggered', () => { + AnalyticsCollectionsLogic.actions.searchAnalyticsCollections('test'); + expect(AnalyticsCollectionsLogic.values.isFetching).toBe(false); + }); + }); + + describe('isSearching', () => { + it('updates on initialState', () => { + expect(AnalyticsCollectionsLogic.values.isSearching).toBe(false); + }); + + it('updates when fetchAnalyticsCollections listener triggered', () => { + AnalyticsCollectionsLogic.actions.fetchAnalyticsCollections(); + expect(AnalyticsCollectionsLogic.values.isSearching).toBe(false); + }); + + it('updates when apiSuccess listener triggered', () => { + FetchAnalyticsCollectionsAPILogic.actions.apiSuccess([]); + expect(AnalyticsCollectionsLogic.values.isSearching).toBe(false); + }); + }); + + describe('hasNoAnalyticsCollections', () => { + it('returns false when no items and search query is not empty', () => { + AnalyticsCollectionsLogic.actions.searchAnalyticsCollections('test'); + expect(AnalyticsCollectionsLogic.values.searchQuery).toBe('test'); + expect(AnalyticsCollectionsLogic.values.hasNoAnalyticsCollections).toBe(false); + }); + + it('returns true when no items and search query is empty', () => { + AnalyticsCollectionsLogic.actions.searchAnalyticsCollections(''); + expect(AnalyticsCollectionsLogic.values.hasNoAnalyticsCollections).toBeTruthy(); + }); + + it('returns true when no items and search query is undefined', () => { + expect(AnalyticsCollectionsLogic.values.hasNoAnalyticsCollections).toBeTruthy(); + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collections_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collections_logic.ts index aa12b92b0f177..bfe86f2874a60 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collections_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collections_logic.ts @@ -15,15 +15,21 @@ import { FetchAnalyticsCollectionsApiLogicResponse, } from '../../api/index/fetch_analytics_collections_api_logic'; +const SEARCH_COOLDOWN = 200; + export interface AnalyticsCollectionsActions { fetchAnalyticsCollections(): void; makeRequest: Actions<{}, FetchAnalyticsCollectionsApiLogicResponse>['makeRequest']; + searchAnalyticsCollections(query?: string): { query: string }; } export interface AnalyticsCollectionsValues { analyticsCollections: AnalyticsCollection[]; data: typeof FetchAnalyticsCollectionsAPILogic.values.data; hasNoAnalyticsCollections: boolean; - isLoading: boolean; + isFetching: boolean; + isSearchRequest: boolean; + isSearching: boolean; + searchQuery: string; status: Status; } @@ -31,7 +37,10 @@ export const AnalyticsCollectionsLogic = kea< MakeLogicType >({ actions: { - fetchAnalyticsCollections: () => {}, + fetchAnalyticsCollections: true, + searchAnalyticsCollections: (query) => ({ + query, + }), }, connect: { actions: [FetchAnalyticsCollectionsAPILogic, ['makeRequest']], @@ -41,14 +50,42 @@ export const AnalyticsCollectionsLogic = kea< fetchAnalyticsCollections: () => { actions.makeRequest({}); }, + searchAnalyticsCollections: async ({ query }, breakpoint) => { + if (query) { + await breakpoint(SEARCH_COOLDOWN); + } + actions.makeRequest({ query }); + }, }), path: ['enterprise_search', 'analytics', 'collections'], + reducers: { + isSearchRequest: [ + false, + { + searchAnalyticsCollections: () => true, + }, + ], + searchQuery: [ + '', + { + searchAnalyticsCollections: (_, { query }) => query, + }, + ], + }, selectors: ({ selectors }) => ({ analyticsCollections: [() => [selectors.data], (data) => data || []], - hasNoAnalyticsCollections: [() => [selectors.data], (data) => data?.length === 0], - isLoading: [ - () => [selectors.status], - (status) => [Status.LOADING, Status.IDLE].includes(status), + hasNoAnalyticsCollections: [ + () => [selectors.analyticsCollections, selectors.searchQuery], + (analyticsCollections, searchQuery) => analyticsCollections.length === 0 && !searchQuery, + ], + isFetching: [ + () => [selectors.status, selectors.isSearchRequest], + (status, isSearchRequest) => + [Status.LOADING, Status.IDLE].includes(status) && !isSearchRequest, + ], + isSearching: [ + () => [selectors.status, selectors.isSearchRequest], + (status, isSearchRequest) => Status.LOADING === status && isSearchRequest, ], }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_overview.tsx index 562f587023b27..33bfa6d504bac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_overview.tsx @@ -28,8 +28,9 @@ import { AnalyticsCollectionsLogic } from './analytics_collections_logic'; import { AnalyticsOverviewEmptyPage } from './analytics_overview_empty_page'; export const AnalyticsOverview: React.FC = () => { - const { fetchAnalyticsCollections } = useActions(AnalyticsCollectionsLogic); - const { analyticsCollections, isLoading, hasNoAnalyticsCollections } = + const { fetchAnalyticsCollections, searchAnalyticsCollections } = + useActions(AnalyticsCollectionsLogic); + const { analyticsCollections, hasNoAnalyticsCollections, isFetching, isSearching } = useValues(AnalyticsCollectionsLogic); const { isCloud } = useValues(KibanaLogic); @@ -46,7 +47,7 @@ export const AnalyticsOverview: React.FC = () => { { - ) : hasNoAnalyticsCollections ? ( + ) : hasNoAnalyticsCollections && !isSearching ? ( <> ) : ( - + )} ); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.test.tsx index 03d09f145a4bb..f4883c5e9b4ca 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.test.tsx @@ -13,10 +13,10 @@ import { mount, shallow } from 'enzyme'; import { act } from 'react-dom/test-utils'; -import { DataView } from '@kbn/data-views-plugin/common'; - import { FormulaPublicApi } from '@kbn/lens-plugin/public'; +import { findOrCreateDataView } from '../utils/find_or_create_data_view'; + import { withLensData } from './with_lens_data'; interface MockComponentProps { @@ -29,6 +29,20 @@ interface MockComponentLensProps { const flushPromises = () => new Promise((resolve) => setImmediate(resolve)); +const mockCollection = { + event_retention_day_length: 180, + events_datastream: 'analytics-events-example2', + id: 'example2', + name: 'example2', +}; +const mockDataView = { id: 'test-data-view-id' }; + +jest.mock('../utils/find_or_create_data_view', () => { + return { + findOrCreateDataView: jest.fn(), + }; +}); + describe('withLensData', () => { const MockComponent: React.FC = ({ name }) =>
    {name}
    ; @@ -48,85 +62,91 @@ describe('withLensData', () => { return { data: 'initial data' }; }), getAttributes: jest.fn(), - getDataViewQuery: jest.fn(), initialValues: { data: 'initial data' }, } ); - const props = { name: 'John Doe' }; + const props = { collection: mockCollection, name: 'John Doe' }; const wrapper = shallow( ); expect(wrapper.find(MockComponent).prop('data')).toEqual('initial data'); }); - it('should call getDataViewQuery with props', async () => { - const getDataViewQuery = jest.fn(); - getDataViewQuery.mockReturnValue('title-collection'); - const findMock = jest.spyOn(mockKibanaValues.data.dataViews, 'find').mockResolvedValueOnce([]); + it('should call findOrCreateDataView with collection', async () => { const WrappedComponent = withLensData( MockComponent, { dataLoadTransform: jest.fn(), getAttributes: jest.fn(), - getDataViewQuery, initialValues: { data: 'initial data' }, } ); - const props = { id: 'id', name: 'John Doe', timeRange: { from: 'now-10d', to: 'now' } }; + const props = { + collection: mockCollection, + id: 'id', + name: 'John Doe', + timeRange: { from: 'now-10d', to: 'now' }, + }; mount(); await act(async () => { await flushPromises(); }); - expect(getDataViewQuery).toHaveBeenCalledWith(props); - expect(findMock).toHaveBeenCalledWith('title-collection', 1); + expect(findOrCreateDataView).toHaveBeenCalledWith(mockCollection); }); it('should call getAttributes with the correct arguments when dataView and formula are available', async () => { const getAttributes = jest.fn(); - const dataView = {} as DataView; const formula = {} as FormulaPublicApi; mockKibanaValues.lens.stateHelperApi = jest.fn().mockResolvedValueOnce({ formula }); - jest.spyOn(mockKibanaValues.data.dataViews, 'find').mockResolvedValueOnce([dataView]); + (findOrCreateDataView as jest.Mock).mockResolvedValueOnce(mockDataView); const WrappedComponent = withLensData( MockComponent, { dataLoadTransform: jest.fn(), getAttributes, - getDataViewQuery: jest.fn(), initialValues: { data: 'initial data' }, } ); - const props = { id: 'id', name: 'John Doe', timeRange: { from: 'now-10d', to: 'now' } }; + const props = { + collection: mockCollection, + id: 'id', + name: 'John Doe', + timeRange: { from: 'now-10d', to: 'now' }, + }; mount(); await act(async () => { await flushPromises(); }); - expect(getAttributes).toHaveBeenCalledWith(dataView, formula, props); + expect(getAttributes).toHaveBeenCalledWith(mockDataView, formula, props); }); it('should not call getAttributes when dataView is not available', async () => { const getAttributes = jest.fn(); const formula = {} as FormulaPublicApi; mockKibanaValues.lens.stateHelperApi = jest.fn().mockResolvedValueOnce({ formula }); - jest.spyOn(mockKibanaValues.data.dataViews, 'find').mockResolvedValueOnce([]); + (findOrCreateDataView as jest.Mock).mockResolvedValueOnce(undefined); const WrappedComponent = withLensData( MockComponent, { dataLoadTransform: jest.fn(), getAttributes, - getDataViewQuery: jest.fn(), initialValues: { data: 'initial data' }, } ); - const props = { id: 'id', name: 'John Doe', timeRange: { from: 'now-10d', to: 'now' } }; + const props = { + collection: mockCollection, + id: 'id', + name: 'John Doe', + timeRange: { from: 'now-10d', to: 'now' }, + }; mount(); await act(async () => { await flushPromises(); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx index 7e9ecbbb5ac8c..64df74a011c88 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx @@ -17,9 +17,13 @@ import { TimeRange } from '@kbn/es-query'; import { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; import { FormulaPublicApi, TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import { AnalyticsCollection } from '../../../../common/types/analytics'; + import { KibanaLogic } from '../../shared/kibana'; +import { findOrCreateDataView } from '../utils/find_or_create_data_view'; export interface WithLensDataInputProps { + collection: AnalyticsCollection; id: string; searchSessionId?: string; setTimeRange?(timeRange: TimeRange): void; @@ -36,7 +40,6 @@ interface WithLensDataParams { formulaApi: FormulaPublicApi, props: Props ) => TypedLensByValueInput['attributes']; - getDataViewQuery: (props: Props) => string; initialValues: OutputState; } @@ -45,14 +48,12 @@ export const withLensData = ( { dataLoadTransform, getAttributes, - getDataViewQuery, initialValues, }: WithLensDataParams, OutputState> ) => { const ComponentWithLensData: React.FC = (props) => { const { lens: { EmbeddableComponent, stateHelperApi }, - data: { dataViews }, } = useValues(KibanaLogic); const [dataView, setDataView] = useState(null); const [data, setData] = useState(initialValues); @@ -73,11 +74,7 @@ export const withLensData = ( useEffect(() => { (async () => { - const [target] = await dataViews.find(getDataViewQuery(props), 1); - - if (target) { - setDataView(target); - } + setDataView(await findOrCreateDataView(props.collection)); })(); }, [props]); useEffect(() => { diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/utils/find_or_create_data_view.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/find_or_create_data_view.test.ts new file mode 100644 index 0000000000000..a9362b622176c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/find_or_create_data_view.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { DataView } from '@kbn/data-views-plugin/common'; + +import { AnalyticsCollection } from '../../../../common/types/analytics'; +import { KibanaLogic } from '../../shared/kibana/kibana_logic'; + +import { findOrCreateDataView } from './find_or_create_data_view'; + +jest.mock('../../shared/kibana/kibana_logic', () => ({ + KibanaLogic: { + values: { + data: { + dataViews: { + createAndSave: jest.fn(), + find: jest.fn(() => Promise.resolve([])), + }, + }, + }, + }, +})); + +describe('findOrCreateDataView', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should find and set dataView when analytics collection fetched', async () => { + const dataView = { id: 'test', title: 'events1' } as DataView; + jest.spyOn(KibanaLogic.values.data.dataViews, 'find').mockResolvedValueOnce([dataView]); + + expect( + await findOrCreateDataView({ + events_datastream: 'events1', + name: 'collection1', + } as AnalyticsCollection) + ).toEqual(dataView); + expect(KibanaLogic.values.data.dataViews.createAndSave).not.toHaveBeenCalled(); + }); + + it('should create, save and set dataView when analytics collection fetched but dataView is not found', async () => { + const dataView = { id: 'test21' } as DataView; + jest.spyOn(KibanaLogic.values.data.dataViews, 'createAndSave').mockResolvedValueOnce(dataView); + + expect( + await findOrCreateDataView({ + events_datastream: 'events1', + name: 'collection1', + } as AnalyticsCollection) + ).toEqual(dataView); + expect(KibanaLogic.values.data.dataViews.createAndSave).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/utils/find_or_create_data_view.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/find_or_create_data_view.ts new file mode 100644 index 0000000000000..52ead6c0dd247 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/find_or_create_data_view.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 { AnalyticsCollection } from '../../../../common/types/analytics'; +import { KibanaLogic } from '../../shared/kibana/kibana_logic'; + +export const findOrCreateDataView = async (collection: AnalyticsCollection) => { + const dataView = ( + await KibanaLogic.values.data.dataViews.find(collection.events_datastream, 1) + ).find((result) => result.title === collection.events_datastream); + + if (dataView) { + return dataView; + } + + return await KibanaLogic.values.data.dataViews.createAndSave( + { + allowNoIndex: true, + name: `behavioral_analytics.events-${collection.name}`, + timeFieldName: '@timestamp', + title: collection.events_datastream, + }, + true + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.test.ts index 5ced11f776aab..a50500f751472 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.test.ts @@ -10,12 +10,14 @@ import { FilterBy, getFormulaByFilter } from './get_formula_by_filter'; describe('getFormulaByFilter', () => { test('should return the correct formula for Searches filter without shift', () => { const formula = getFormulaByFilter(FilterBy.Searches); - expect(formula).toBe('count(search.query)'); + expect(formula).toBe("count(search.query, kql='event.action: search')"); }); test('should return the correct formula for NoResults filter with shift', () => { const formula = getFormulaByFilter(FilterBy.NoResults, '1d'); - expect(formula).toBe("count(kql='search.results.total_results : 0', shift='1d')"); + expect(formula).toBe( + "count(kql='search.results.total_results : 0 and event.action: search', shift='1d')" + ); }); test('should return the correct formula for Clicks filter without shift', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.ts index 2ef4ce5790459..9895f3a0ddc55 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/utils/get_formula_by_filter.ts @@ -13,8 +13,8 @@ export enum FilterBy { } export const getFormulaByFilter = (filter: FilterBy, shift?: string): string => { const mapFilterByToFormula: { [key in FilterBy]: string } = { - [FilterBy.Searches]: 'count(search.query', - [FilterBy.NoResults]: "count(kql='search.results.total_results : 0'", + [FilterBy.Searches]: "count(search.query, kql='event.action: search'", + [FilterBy.NoResults]: "count(kql='search.results.total_results : 0 and event.action: search'", [FilterBy.Clicks]: "count(kql='event.action: search_click'", [FilterBy.Sessions]: 'unique_count(session.id', }; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx index 3820f8e334f07..7da4392ef1112 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx @@ -79,7 +79,7 @@ export const ElasticsearchGuide: React.FC = () => { 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchDescription', { defaultMessage: - 'Whether you are building a search-powered application, or designing a large-scale search implementation, Elasticsearch provides the low-level tools to create the most relevant and performant search experience.', + "Elasticsearch provides the low-level tools you need to build fast, relevant search for your website or application. Because it's powerful and flexible, Elasticsearch can handle search use cases of all shapes and sizes.", } )}

    @@ -103,7 +103,7 @@ export const ElasticsearchGuide: React.FC = () => { 'xpack.enterpriseSearch.overview.elasticsearchGuide.connectToElasticsearchDescription', { defaultMessage: - "Elastic builds and maintains clients in several popular languages and our community has contributed many more. They're easy to work with, feel natural to use, and, just like Elasticsearch, don't limit what you might want to do with them.", + 'Elastic builds and maintains clients in several popular languages and our community has contributed many more.', } )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts index 5ae2784b52664..1a4a58558dc5c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts @@ -45,6 +45,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, }, @@ -155,6 +156,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts index 1674f5ca18675..3f9972d1545c5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts @@ -55,6 +55,7 @@ export const connectorIndex: ConnectorViewIndex = { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, }, @@ -169,6 +170,7 @@ export const crawlerIndex: CrawlerViewIndex = { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.test.ts index d5d1b7845f8cc..b24355af98683 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.test.ts @@ -20,7 +20,11 @@ describe('addConnectorApiLogic', () => { it('calls correct api', async () => { const promise = Promise.resolve({ id: 'unique id', index_name: 'indexName' }); http.post.mockReturnValue(promise); - const result = addConnector({ indexName: 'indexName', isNative: false, language: 'en' }); + const result = addConnector({ + indexName: 'indexName', + isNative: false, + language: 'en', + }); await nextTick(); expect(http.post).toHaveBeenCalledWith('/internal/enterprise_search/connectors', { body: JSON.stringify({ index_name: 'indexName', is_native: false, language: 'en' }), diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.ts index 80d31e62d9525..33989ceff579b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/add_connector_api_logic.ts @@ -18,6 +18,7 @@ export interface AddConnectorApiLogicArgs { indexName: string; isNative: boolean; language: string | null; + serviceType?: string; } export interface AddConnectorApiLogicResponse { @@ -30,6 +31,7 @@ export const addConnector = async ({ indexName, isNative, language, + serviceType, }: AddConnectorApiLogicArgs): Promise => { const route = '/internal/enterprise_search/connectors'; @@ -41,6 +43,7 @@ export const addConnector = async ({ index_name: indexName, is_native: isNative, language, + service_type: serviceType, }; const result = await HttpLogic.values.http.post(route, { body: JSON.stringify(params), diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/set_native_connector_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/set_native_connector_api_logic.ts deleted file mode 100644 index d5959134845e9..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/set_native_connector_api_logic.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. - */ - -import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; -import { HttpLogic } from '../../../shared/http'; -import { NativeConnector } from '../../components/search_index/connector/types'; - -export interface SetNativeConnectorArgs { - connectorId: string; - serviceType: string; -} - -export interface SetNativeConnectorResponse { - connectorId: string; - nativeConnector: NativeConnector; -} - -export const setNativeConnector = async ({ connectorId, serviceType }: SetNativeConnectorArgs) => { - await HttpLogic.values.http.put( - `/internal/enterprise_search/connectors/${connectorId}/configure_native`, - { - body: JSON.stringify({ service_type: serviceType }), - } - ); - - return { connectorId }; -}; - -export const SetNativeConnectorLogic = createApiLogic( - ['content', 'service_type_connector_api_logic'], - setNativeConnector -); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx index 22ab72ba3a405..7d504c65bb7ac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_flyout.tsx @@ -45,10 +45,13 @@ export const AddIndicesFlyout: React.FC = ({ onClose }) = const { selectedIndices, updateEngineStatus, updateEngineError } = useValues(AddIndicesLogic); const { setSelectedIndices, submitSelectedIndices } = useActions(AddIndicesLogic); - const selectedOptions = useMemo(() => selectedIndices.map(indexToOption), [selectedIndices]); + const selectedOptions = useMemo( + () => selectedIndices.map((index) => indexToOption(index)), + [selectedIndices] + ); const onIndicesChange = useCallback( (options: IndicesSelectComboBoxOption[]) => { - setSelectedIndices(options.map(({ value }) => value).filter(isNotNullish)); + setSelectedIndices(options.map(({ label }) => label).filter(isNotNullish)); }, [setSelectedIndices] ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts index a60c6e9df756a..7b3e61940f001 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.test.ts @@ -8,7 +8,6 @@ import { LogicMounter } from '../../../__mocks__/kea_logic'; import { Status } from '../../../../../common/types/api'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; import { AddIndicesLogic, AddIndicesLogicValues } from './add_indices_logic'; @@ -18,16 +17,6 @@ const DEFAULT_VALUES: AddIndicesLogicValues = { updateEngineStatus: Status.IDLE, }; -const makeIndexData = (name: string): ElasticsearchIndexWithIngestion => ({ - count: 0, - hidden: false, - name, - total: { - docs: { count: 0, deleted: 0 }, - store: { size_in_bytes: 'n/a' }, - }, -}); - describe('AddIndicesLogic', () => { const { mount: mountAddIndicesLogic } = new LogicMounter(AddIndicesLogic); const { mount: mountEngineIndicesLogic } = new LogicMounter(AddIndicesLogic); @@ -47,31 +36,16 @@ describe('AddIndicesLogic', () => { describe('actions', () => { describe('setSelectedIndices', () => { it('adds the indices to selectedIndices', () => { - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); + AddIndicesLogic.actions.setSelectedIndices(['index-001', 'index-002']); - expect(AddIndicesLogic.values.selectedIndices).toEqual([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); + expect(AddIndicesLogic.values.selectedIndices).toEqual(['index-001', 'index-002']); }); it('replaces any existing indices', () => { - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-003'), - makeIndexData('index-004'), - ]); + AddIndicesLogic.actions.setSelectedIndices(['index-001', 'index-002']); + AddIndicesLogic.actions.setSelectedIndices(['index-003', 'index-004']); - expect(AddIndicesLogic.values.selectedIndices).toEqual([ - makeIndexData('index-003'), - makeIndexData('index-004'), - ]); + expect(AddIndicesLogic.values.selectedIndices).toEqual(['index-003', 'index-004']); }); }); }); @@ -103,10 +77,7 @@ describe('AddIndicesLogic', () => { it('calls addIndicesToEngine when there are selectedIndices', () => { jest.spyOn(AddIndicesLogic.actions, 'addIndicesToEngine'); - AddIndicesLogic.actions.setSelectedIndices([ - makeIndexData('index-001'), - makeIndexData('index-002'), - ]); + AddIndicesLogic.actions.setSelectedIndices(['index-001', 'index-002']); AddIndicesLogic.actions.submitSelectedIndices(); expect(AddIndicesLogic.actions.addIndicesToEngine).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts index 37e5cf43ebd00..add950937b30a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/add_indices_logic.ts @@ -7,8 +7,6 @@ import { kea, MakeLogicType } from 'kea'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; - import { UpdateEngineApiLogic } from '../../api/engines/update_engine_api_logic'; import { EngineIndicesLogic, EngineIndicesLogicActions } from './engine_indices_logic'; @@ -17,21 +15,21 @@ export interface AddIndicesLogicActions { addIndicesToEngine: EngineIndicesLogicActions['addIndicesToEngine']; closeAddIndicesFlyout: EngineIndicesLogicActions['closeAddIndicesFlyout']; engineUpdated: EngineIndicesLogicActions['engineUpdated']; - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => { - indices: ElasticsearchIndexWithIngestion[]; + setSelectedIndices: (indices: string[]) => { + indices: string[]; }; submitSelectedIndices: () => void; } export interface AddIndicesLogicValues { - selectedIndices: ElasticsearchIndexWithIngestion[]; + selectedIndices: string[]; updateEngineError: typeof UpdateEngineApiLogic.values.error | undefined; updateEngineStatus: typeof UpdateEngineApiLogic.values.status; } export const AddIndicesLogic = kea>({ actions: { - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => ({ indices }), + setSelectedIndices: (indices: string[]) => ({ indices }), submitSelectedIndices: () => true, }, connect: { @@ -46,7 +44,7 @@ export const AddIndicesLogic = kea name)); + actions.addIndicesToEngine(selectedIndices); }, }), path: ['enterprise_search', 'content', 'add_indices_logic'], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api_integration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx similarity index 76% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api_integration.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx index 778d69a5ed56b..2fe691e262b64 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api_integration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_integration.tsx @@ -12,23 +12,24 @@ import { useValues } from 'kea'; import { EuiCodeBlock, EuiSpacer, EuiText, EuiTabs, EuiTab } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url'; - +import { useCloudDetails } from '../../../../shared/cloud_details/cloud_details'; import { EngineViewLogic } from '../engine_view_logic'; import { EngineApiLogic } from './engine_api_logic'; -const SearchUISnippet = (enterpriseSearchUrl: string, engineName: string, apiKey: string) => ` +import { elasticsearchUrl } from './search_application_api'; + +const SearchUISnippet = (esUrl: string, engineName: string, apiKey: string) => `6 import EnginesAPIConnector from "@elastic/search-ui-engines-connector"; const connector = new EnginesAPIConnector({ - host: "${enterpriseSearchUrl}", + host: "${esUrl}", engineName: "${engineName}", apiKey: "${apiKey || ''}" });`; -const cURLSnippet = (enterpriseSearchUrl: string, engineName: string, apiKey: string) => ` -curl --location --request GET '${enterpriseSearchUrl}/api/engines/${engineName}/_search' \\ +const cURLSnippet = (esUrl: string, engineName: string, apiKey: string) => ` +curl --location --request GET '${esUrl}/${engineName}/_search' \\ --header 'Authorization: apiKey ${apiKey || ''}' \\ --header 'Content-Type: application/json' \\ --data-raw '{ @@ -47,19 +48,19 @@ interface Tab { export const EngineApiIntegrationStage: React.FC = () => { const [selectedTab, setSelectedTab] = React.useState('curl'); const { engineName } = useValues(EngineViewLogic); - const enterpriseSearchUrl = getEnterpriseSearchUrl(); const { apiKey } = useValues(EngineApiLogic); + const cloudContext = useCloudDetails(); const Tabs: Record = { curl: { - code: cURLSnippet(enterpriseSearchUrl, engineName, apiKey), + code: cURLSnippet(elasticsearchUrl(cloudContext), engineName, apiKey), language: 'bash', title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step3.curlTitle', { defaultMessage: 'cURL', }), }, searchui: { - code: SearchUISnippet(enterpriseSearchUrl, engineName, apiKey), + code: SearchUISnippet(elasticsearchUrl(cloudContext), engineName, apiKey), language: 'javascript', title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step3.searchUITitle', { defaultMessage: 'Search UI', @@ -73,7 +74,7 @@ export const EngineApiIntegrationStage: React.FC = () => {

    {i18n.translate('xpack.enterpriseSearch.content.engine.api.step3.intro', { defaultMessage: - 'Learn how to integrate with your engine with the language clients maintained by Elastic to help build your search experience.', + 'Learn how to integrate with your search application with the language clients maintained by Elastic to help build your search experience.', })}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_logic.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api_logic.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_api_logic.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_connect.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_connect.tsx new file mode 100644 index 0000000000000..a6c29285f4f66 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/engine_connect.tsx @@ -0,0 +1,102 @@ +/* + * 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 { useParams } from 'react-router-dom'; + +import { useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; + +import { generateEncodedPath } from '../../../../shared/encode_path_params'; +import { KibanaLogic } from '../../../../shared/kibana'; +import { + SEARCH_APPLICATION_CONTENT_PATH, + EngineViewTabs, + SearchApplicationConnectTabs, +} from '../../../routes'; +import { EnterpriseSearchEnginesPageTemplate } from '../../layout/engines_page_template'; + +import { EngineError } from '../engine_error'; +import { EngineViewLogic } from '../engine_view_logic'; + +import { SearchApplicationAPI } from './search_application_api'; + +const pageTitle = i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.connect.pageTitle', + { + defaultMessage: 'Connect', + } +); +const API_TAB_TITLE = i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.connect.apiTabTitle', + { + defaultMessage: 'API', + } +); +const ConnectTabs: string[] = Object.values(SearchApplicationConnectTabs); +const getTabBreadCrumb = (tabId: string) => { + switch (tabId) { + case SearchApplicationConnectTabs.API: + return API_TAB_TITLE; + default: + return tabId; + } +}; + +export const EngineConnect: React.FC = () => { + const { engineName, isLoadingEngine } = useValues(EngineViewLogic); + const { connectTabId = SearchApplicationConnectTabs.API } = useParams<{ + connectTabId?: string; + }>(); + const onTabClick = (tab: SearchApplicationConnectTabs) => () => { + KibanaLogic.values.navigateToUrl( + generateEncodedPath(SEARCH_APPLICATION_CONTENT_PATH, { + engineName, + connectTabId: tab, + }) + ); + }; + if (!ConnectTabs.includes(connectTabId)) { + return ( + + + + ); + } + + return ( + + {connectTabId === SearchApplicationConnectTabs.API && } + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.test.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.test.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.test.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.logic.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.test.tsx similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.test.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx similarity index 98% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx index c0cbcea37e93a..b4d03456ff5ec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/generate_engine_api_key_modal/generate_engine_api_key_modal.tsx @@ -59,7 +59,7 @@ export const GenerateEngineApiKeyModal: React.FC {i18n.translate( 'xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title', { - defaultMessage: 'Create Engine read-only API Key', + defaultMessage: 'Create Search application read-only API Key', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx similarity index 56% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx index aad7e84d99084..6934de4051bdb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_api/engine_api.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_connect/search_application_api.tsx @@ -19,58 +19,64 @@ import { EuiSteps, EuiText, } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; import { ANALYTICS_PLUGIN } from '../../../../../../common/constants'; import { COLLECTION_INTEGRATE_PATH } from '../../../../analytics/routes'; +import { CloudDetails, useCloudDetails } from '../../../../shared/cloud_details/cloud_details'; +import { decodeCloudId } from '../../../../shared/decode_cloud_id/decode_cloud_id'; import { docLinks } from '../../../../shared/doc_links'; import { generateEncodedPath } from '../../../../shared/encode_path_params'; -import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url'; import { KibanaLogic } from '../../../../shared/kibana'; -import { EngineViewTabs } from '../../../routes'; -import { EnterpriseSearchEnginesPageTemplate } from '../../layout/engines_page_template'; - -import { EngineIndicesLogic } from '../engine_indices_logic'; import { EngineViewLogic } from '../engine_view_logic'; import { EngineApiIntegrationStage } from './engine_api_integration'; import { EngineApiLogic } from './engine_api_logic'; import { GenerateEngineApiKeyModal } from './generate_engine_api_key_modal/generate_engine_api_key_modal'; -export const EngineAPI: React.FC = () => { - const { engineName, isLoadingEngine } = useValues(EngineViewLogic); - const { engineData } = useValues(EngineIndicesLogic); +export const elasticsearchUrl = (cloudContext: CloudDetails): string => { + const defaultUrl = 'https://localhost:9200'; + const url = + (cloudContext.cloudId && decodeCloudId(cloudContext.cloudId)?.elasticsearchUrl) || defaultUrl; + return url; +}; + +export const SearchApplicationAPI = () => { + const { engineName } = useValues(EngineViewLogic); const { isGenerateModalOpen } = useValues(EngineApiLogic); const { openGenerateModal, closeGenerateModal } = useActions(EngineApiLogic); - const enterpriseSearchUrl = getEnterpriseSearchUrl(); const { navigateToUrl } = useValues(KibanaLogic); - - if (!engineData) return null; + const cloudContext = useCloudDetails(); const steps = [ { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.title', { + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step1.title', { defaultMessage: 'Generate and save API key', }), children: ( <>

    - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning', { - defaultMessage: - "Elastic does not store API keys. Once generated, you'll only be able to view the key one time. Make sure you save it somewhere secure. If you lose access to it you'll need to generate a new API key from this screen.", - })}{' '} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step1.apiKeyWarning', + { + defaultMessage: + "Elastic does not store API keys. Once generated, you'll only be able to view the key one time. Make sure you save it somewhere secure. If you lose access to it you'll need to generate a new API key from this screen.", + } + )}{' '} - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink', { - defaultMessage: 'Learn more about API keys.', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step1.learnMoreLink', + { + defaultMessage: 'Learn more about API keys.', + } + )}

    @@ -81,10 +87,10 @@ export const EngineAPI: React.FC = () => { iconSide="left" iconType="plusInCircleFilled" onClick={openGenerateModal} - data-telemetry-id="entSearchContent-engines-api-step1-createApiKeyButton" + data-telemetry-id="entSearchContent-searchApplications-api-step1-createApiKeyButton" > {i18n.translate( - 'xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton', + 'xpack.enterpriseSearch.content.searchApplication.api.step1.createAPIKeyButton', { defaultMessage: 'Create API Key', } @@ -95,16 +101,19 @@ export const EngineAPI: React.FC = () => { KibanaLogic.values.navigateToUrl('/app/management/security/api_keys', { shouldNotCreateHref: true, }) } > - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step1.viewKeysButton', { - defaultMessage: 'View Keys', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step1.viewKeysButton', + { + defaultMessage: 'View Keys', + } + )} @@ -112,17 +121,17 @@ export const EngineAPI: React.FC = () => { ), }, { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step2.title', { - defaultMessage: "Copy your engine's endpoint", + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step2.title', { + defaultMessage: "Copy your search application's endpoint", }), children: ( <>

    {i18n.translate( - 'xpack.enterpriseSearch.content.engine.api.step2.copyEndpointDescription', + 'xpack.enterpriseSearch.content.searchApplication.api.step2.copyEndpointDescription', { - defaultMessage: "Use this URL to access your engine's API endpoints.", + defaultMessage: "Use this URL to access your search application's API endpoints.", } )}

    @@ -131,7 +140,7 @@ export const EngineAPI: React.FC = () => { - {enterpriseSearchUrl} + {elasticsearchUrl(cloudContext)} @@ -139,22 +148,22 @@ export const EngineAPI: React.FC = () => { ), }, { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step3.title', { + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step3.title', { defaultMessage: 'Learn how to call your endpoints', }), children: , }, { - title: i18n.translate('xpack.enterpriseSearch.content.engine.api.step4.title', { + title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step4.title', { defaultMessage: '(Optional) Power up your analytics', }), children: ( <>

    - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step4.copy', { + {i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step4.copy', { defaultMessage: - 'Your engine provides basic analytics data as part of this installation. To receive more granular and custom metrics, integrate our Behavioral Analytics script on your platform.', + 'Your search application provides basic analytics data as part of this installation. To receive more granular and custom metrics, integrate our Behavioral Analytics script on your platform.', })}

    @@ -162,7 +171,7 @@ export const EngineAPI: React.FC = () => { navigateToUrl( generateEncodedPath(`${ANALYTICS_PLUGIN.URL}${COLLECTION_INTEGRATE_PATH}`, { @@ -174,9 +183,12 @@ export const EngineAPI: React.FC = () => { iconSide="left" iconType="popout" > - {i18n.translate('xpack.enterpriseSearch.content.engine.api.step4.learnHowLink', { - defaultMessage: 'Learn how', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplication.api.step4.learnHowLink', + { + defaultMessage: 'Learn how', + } + )} @@ -186,22 +198,11 @@ export const EngineAPI: React.FC = () => { ]; return ( - + <> {isGenerateModalOpen ? ( ) : null} - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx index 9c71c401fbaf0..0f47d2437e3dc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.test.tsx @@ -40,7 +40,7 @@ describe('EngineError', () => { const notFound = wrapper.find(NotFoundPrompt); expect(notFound.prop('backToLink')).toEqual('/engines'); - expect(notFound.prop('backToContent')).toEqual('Back to Engines'); + expect(notFound.prop('backToContent')).toEqual('Back to Search Applications'); const telemetry = wrapper.find(SendEnterpriseSearchTelemetry); expect(telemetry.prop('action')).toEqual('error'); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx index 4b620c7a3d505..d36ff3e14e7a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_error.tsx @@ -18,15 +18,21 @@ import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry'; import { ENGINES_PATH } from '../../routes'; -export const EngineError: React.FC<{ error: HttpError | undefined }> = ({ error }) => { - if (error?.body?.statusCode === 404) { +export const EngineError: React.FC<{ error?: HttpError; notFound?: boolean }> = ({ + error, + notFound, +}) => { + if (notFound || error?.body?.statusCode === 404) { return ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx index f18fd66ff5f94..285846f7ccb1c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_indices.tsx @@ -11,7 +11,6 @@ import { useActions, useValues } from 'kea'; import { EuiBasicTableColumn, - EuiButton, EuiCallOut, EuiConfirmModal, EuiIcon, @@ -32,20 +31,15 @@ import { KibanaLogic } from '../../../shared/kibana'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { TelemetryLogic } from '../../../shared/telemetry/telemetry_logic'; -import { SEARCH_INDEX_PATH, EngineViewTabs } from '../../routes'; +import { SEARCH_INDEX_PATH } from '../../routes'; -import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; - -import { AddIndicesFlyout } from './add_indices_flyout'; import { EngineIndicesLogic } from './engine_indices_logic'; export const EngineIndices: React.FC = () => { const subduedBackground = useEuiBackgroundColor('subdued'); const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic); - const { engineData, engineName, isLoadingEngine, addIndicesFlyoutOpen } = - useValues(EngineIndicesLogic); - const { removeIndexFromEngine, openAddIndicesFlyout, closeAddIndicesFlyout } = - useActions(EngineIndicesLogic); + const { engineData } = useValues(EngineIndicesLogic); + const { removeIndexFromEngine } = useActions(EngineIndicesLogic); const { navigateToUrl } = useValues(KibanaLogic); const [removeIndexConfirm, setConfirmRemoveIndex] = useState(null); @@ -60,7 +54,7 @@ export const EngineIndices: React.FC = () => { description: i18n.translate( 'xpack.enterpriseSearch.content.engine.indices.actions.removeIndex.title', { - defaultMessage: 'Remove this index from engine', + defaultMessage: 'Remove this index from search application', } ), icon: 'minusInCircle', @@ -173,118 +167,92 @@ export const EngineIndices: React.FC = () => { }, ]; return ( - + {hasUnknownIndices && ( + <> + - {i18n.translate('xpack.enterpriseSearch.content.engine.indices.addNewIndicesButton', { - defaultMessage: 'Add new indices', - })} -
    , - ], - }} - engineName={engineName} - > - <> - {hasUnknownIndices && ( - <> - + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.unknownIndicesCallout.description', + { + defaultMessage: + 'Some data might be unreachable from this search application. Check for any pending operations or errors on affected indices, or remove indices that should no longer be used by this search application.', + } )} - > -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.indices.unknownIndicesCallout.description', - { - defaultMessage: - 'Some data might be unreachable from this engine. Check for any pending operations or errors on affected indices, or remove those that should no longer be used by this engine.', - } - )} -

    -
    - - - )} - { - if (index.health === 'unknown') { - return { style: { backgroundColor: subduedBackground } }; - } +

    + + + + )} + { + if (index.health === 'unknown') { + return { style: { backgroundColor: subduedBackground } }; + } - return {}; - }} - search={{ - box: { - incremental: true, - placeholder: i18n.translate( - 'xpack.enterpriseSearch.content.engine.indices.searchPlaceholder', - { defaultMessage: 'Filter indices' } - ), - schema: true, - }, + return {}; + }} + search={{ + box: { + incremental: true, + placeholder: i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.searchPlaceholder', + { defaultMessage: 'Filter indices' } + ), + schema: true, + }, + }} + pagination + sorting + /> + {removeIndexConfirm !== null && ( + setConfirmRemoveIndex(null)} + onConfirm={() => { + removeIndexFromEngine(removeIndexConfirm); + setConfirmRemoveIndex(null); + sendEnterpriseSearchTelemetry({ + action: 'clicked', + metric: 'entSearchContent-engines-indices-removeIndexConfirm', + }); }} - pagination - sorting - /> - {removeIndexConfirm !== null && ( - setConfirmRemoveIndex(null)} - onConfirm={() => { - removeIndexFromEngine(removeIndexConfirm); - setConfirmRemoveIndex(null); - sendEnterpriseSearchTelemetry({ - action: 'clicked', - metric: 'entSearchContent-engines-indices-removeIndexConfirm', - }); - }} - title={i18n.translate( - 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.title', - { defaultMessage: 'Remove this index from the engine' } - )} - buttonColor="danger" - cancelButtonText={CANCEL_BUTTON_LABEL} - confirmButtonText={i18n.translate( - 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.text', - { - defaultMessage: 'Yes, Remove This Index', - } - )} - defaultFocusedButton="confirm" - maxWidth - > - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.description', - { - defaultMessage: - "This won't delete the index. You may add it back to this engine at a later time.", - } - )} -

    -
    -
    - )} - {addIndicesFlyoutOpen && } - - + title={i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.title', + { defaultMessage: 'Remove this index from the search application' } + )} + buttonColor="danger" + cancelButtonText={CANCEL_BUTTON_LABEL} + confirmButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.text', + { + defaultMessage: 'Yes, Remove This Index', + } + )} + defaultFocusedButton="confirm" + maxWidth + > + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.description', + { + defaultMessage: + "This won't delete the index. You may add it back to this search application at a later time.", + } + )} +

    +
    +
    + )} + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx index b44d644b89336..fd10624188ff0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx @@ -42,8 +42,7 @@ import { docLinks } from '../../../shared/doc_links'; import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; -import { EngineViewTabs, SEARCH_INDEX_TAB_PATH } from '../../routes'; -import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; +import { SEARCH_INDEX_TAB_PATH } from '../../routes'; import { EngineIndicesLogic } from './engine_indices_logic'; @@ -344,127 +343,115 @@ export const EngineSchema: React.FC = () => { ); return ( - - <> - - - - - - {i18n.translate('xpack.enterpriseSearch.content.engine.schema.filters.label', { - defaultMessage: 'Filter By', - })} - - - setIsFilterByPopoverOpen(false)} - panelPaddingSize="none" - anchorPosition="downCenter" + <> + + + + + + {i18n.translate('xpack.enterpriseSearch.content.engine.schema.filters.label', { + defaultMessage: 'Filter By', + })} + + + setIsFilterByPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downCenter" + > + setSelectedEsFieldTypes(options)} > - ( +
    + {search} + {list} +
    + )} +
    + + + setSelectedEsFieldTypes(esFieldTypes)} + > + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.schema.filters.clearAll', { - defaultMessage: 'Filter list ', + defaultMessage: 'Clear all ', } - ), - }} - options={selectedEsFieldTypes} - onChange={(options) => setSelectedEsFieldTypes(options)} - > - {(list, search) => ( -
    - {search} - {list} -
    - )} -
    - - - setSelectedEsFieldTypes(esFieldTypes)} - > - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.schema.filters.clearAll', - { - defaultMessage: 'Clear all ', - } - )} - - - -
    -
    -
    + )} + +
    + +
    +
    - - - {totalConflictsHiddenByTypeFilters > 0 && ( - - } - color="danger" - iconType="iInCircle" - > -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.schema.filters.conflict.callout.subTitle', - { - defaultMessage: - 'In order to see all field conflicts you must clear your field filters', - } - )} -

    - setSelectedEsFieldTypes(esFieldTypes)}> - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.schema.filters.conflict.callout.clearFilters', - { - defaultMessage: 'Clear filters ', - } - )} - -
    - )}
    - -
    + + + {totalConflictsHiddenByTypeFilters > 0 && ( + + } + color="danger" + iconType="iInCircle" + > +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.schema.filters.conflict.callout.subTitle', + { + defaultMessage: + 'In order to see all field conflicts you must clear your field filters', + } + )} +

    + setSelectedEsFieldTypes(esFieldTypes)}> + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.schema.filters.conflict.callout.clearFilters', + { + defaultMessage: 'Clear filters ', + } + )} + +
    + )} + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/api_call_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/api_call_flyout.tsx index e4fa7d5c667dc..ca64d73bf1b64 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/api_call_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/api_call_flyout.tsx @@ -79,7 +79,7 @@ export const APICallFlyout: React.FC = ({ void + private engineName: string // uncomment and add setLastAPICall to constructor when view this API call is needed // private setLastAPICall?: (apiCallData: APICallData) => void ) {} async performRequest(request: SearchRequest) { @@ -64,7 +86,7 @@ class InternalEngineTransporter implements Transporter { body: JSON.stringify(request), }); - this.setLastAPICall({ request, response }); + // this.setLastAPICall({ request, response }); Uncomment when view this API call is needed const withUniqueIds = { ...response, @@ -87,16 +109,184 @@ class InternalEngineTransporter implements Transporter { } } +interface ConfigurationPopOverProps { + engineName: string; + setCloseConfiguration: () => void; + showConfiguration: boolean; +} + +const ConfigurationPopover: React.FC = ({ + engineName, + showConfiguration, + setCloseConfiguration, +}) => { + const { navigateToUrl } = useValues(KibanaLogic); + const { engineData } = useValues(EngineViewLogic); + const { openDeleteEngineModal } = useActions(EngineViewLogic); + const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic); + return ( + <> + + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.buttonTitle', + { + defaultMessage: 'Configuration', + } + )} + + } + > + + + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.contentTitle', + { + defaultMessage: 'Content', + } + )} +

    +
    +
    + + + + navigateToUrl( + generateEncodedPath(SEARCH_APPLICATION_CONTENT_PATH, { + contentTabId: SearchApplicationContentTabs.INDICES, + engineName, + }) + ) + } + > + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.content.Indices', + { + defaultMessage: 'Indices', + } + )} + + + navigateToUrl( + generateEncodedPath(SEARCH_APPLICATION_CONTENT_PATH, { + contentTabId: SearchApplicationContentTabs.SCHEMA, + engineName, + }) + ) + } + > + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.content.Schema', + { + defaultMessage: 'Schema', + } + )} + + + + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.connectTitle', + { + defaultMessage: 'Connect', + } + )} +

    +
    +
    + + + navigateToUrl( + generateEncodedPath(SEARCH_APPLICATION_CONNECT_PATH, { + connectTabId: SearchApplicationConnectTabs.API, + engineName, + }) + ) + } + > + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.connect.Api', + { + defaultMessage: 'API', + } + )} + + + + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.settingsTitle', + { + defaultMessage: 'Settings', + } + )} +

    +
    +
    + + } + onClick={() => { + if (engineData) { + openDeleteEngineModal(); + sendEnterpriseSearchTelemetry({ + action: 'clicked', + metric: 'entSearchContent-engines-engineView-deleteEngine', + }); + } + }} + > + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.settings.delete', + { + defaultMessage: 'Delete this app', + } + )} +

    +
    +
    +
    +
    + + ); +}; export const EngineSearchPreview: React.FC = () => { const { http } = useValues(HttpLogic); - const [showAPICallFlyout, setShowAPICallFlyout] = useState(false); - const [lastAPICall, setLastAPICall] = useState(null); + // const [showAPICallFlyout, setShowAPICallFlyout] = useState(false); Uncomment when view this API call is needed + const [showConfigurationPopover, setShowConfigurationPopover] = useState(false); + // const [lastAPICall, setLastAPICall] = useState(null); Uncomment when view this API call is needed const { engineName, isLoadingEngine } = useValues(EngineViewLogic); const { resultFields, searchableFields, sortableFields } = useValues(EngineSearchPreviewLogic); const { engineData } = useValues(EngineIndicesLogic); const config: SearchDriverOptions = useMemo(() => { - const transporter = new InternalEngineTransporter(http, engineName, setLastAPICall); + const transporter = new InternalEngineTransporter(http, engineName); const connector = new EnginesAPIConnector(transporter); return { @@ -108,29 +298,35 @@ export const EngineSearchPreview: React.FC = () => { search_fields: searchableFields, }, }; - }, [http, engineName, setLastAPICall, resultFields, searchableFields]); + }, [http, engineName, resultFields, searchableFields]); if (!engineData) return null; return ( + ), rightSideItems: [ <> - setShowAPICallFlyout(true)} - isLoading={lastAPICall == null} - > - View this API call - + setShowConfigurationPopover(!showConfigurationPopover)} + /> , ], }} @@ -165,6 +361,9 @@ export const EngineSearchPreview: React.FC = () => { + {/* + Uncomment when view this API call needed + {showAPICallFlyout && lastAPICall && ( setShowAPICallFlyout(false)} @@ -172,6 +371,7 @@ export const EngineSearchPreview: React.FC = () => { engineName={engineName} /> )} + */} ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx index f3934c11311a7..6bbfe9b1de967 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect } from 'react'; -import { useParams, Switch } from 'react-router-dom'; +import { useParams, Redirect, Switch } from 'react-router-dom'; import { useValues, useActions } from 'kea'; @@ -15,29 +15,29 @@ import { Route } from '@kbn/shared-ux-router'; import { Status } from '../../../../../common/types/api'; import { KibanaLogic } from '../../../shared/kibana'; -import { ENGINE_PATH, EngineViewTabs } from '../../routes'; +import { + ENGINE_PATH, + SEARCH_APPLICATION_CONTENT_PATH, + SEARCH_APPLICATION_CONNECT_PATH, + EngineViewTabs, + SearchApplicationConnectTabs, + SearchApplicationContentTabs, +} from '../../routes'; import { DeleteEngineModal } from '../engines/delete_engine_modal'; import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; -import { EngineAPI } from './engine_api/engine_api'; +import { EngineConnect } from './engine_connect/engine_connect'; import { EngineError } from './engine_error'; -import { EngineIndices } from './engine_indices'; -import { EngineSchema } from './engine_schema'; import { EngineSearchPreview } from './engine_search_preview/engine_search_preview'; -import { EngineViewHeaderActions } from './engine_view_header_actions'; import { EngineViewLogic } from './engine_view_logic'; import { EngineHeaderDocsAction } from './header_docs_action'; +import { SearchApplicationContent } from './search_application_content'; export const EngineView: React.FC = () => { const { fetchEngine, closeDeleteEngineModal } = useActions(EngineViewLogic); - const { - engineName, - fetchEngineApiError, - fetchEngineApiStatus, - isDeleteModalVisible, - isLoadingEngine, - } = useValues(EngineViewLogic); + const { engineName, fetchEngineApiError, fetchEngineApiStatus, isDeleteModalVisible } = + useValues(EngineViewLogic); const { tabId = EngineViewTabs.PREVIEW } = useParams<{ tabId?: string; }>(); @@ -75,23 +75,30 @@ export const EngineView: React.FC = () => { path={`${ENGINE_PATH}/${EngineViewTabs.PREVIEW}`} component={EngineSearchPreview} /> - - - - ( - ], - }} - engineName={engineName} - isLoading={isLoadingEngine} - /> - )} + + + + + + + + + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx deleted file mode 100644 index bd48ee8d6d294..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_view_header_actions.tsx +++ /dev/null @@ -1,87 +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, { useState } from 'react'; - -import { useValues, useActions } from 'kea'; - -import { EuiPopover, EuiButtonIcon, EuiText, EuiContextMenu, EuiIcon } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { TelemetryLogic } from '../../../shared/telemetry/telemetry_logic'; - -import { EngineViewLogic } from './engine_view_logic'; - -export const EngineViewHeaderActions: React.FC = () => { - const { engineData } = useValues(EngineViewLogic); - - const { openDeleteEngineModal } = useActions(EngineViewLogic); - const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic); - - const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const toggleActionsPopover = () => setIsActionsPopoverOpen((isPopoverOpen) => !isPopoverOpen); - const closePopover = () => setIsActionsPopoverOpen(false); - return ( - <> - - } - isOpen={isActionsPopoverOpen} - panelPaddingSize="xs" - closePopover={closePopover} - display="block" - > - , - name: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.engine.headerActions.delete', - { defaultMessage: 'Delete this engine' } - )} - - ), - onClick: () => { - if (engineData) { - openDeleteEngineModal(); - sendEnterpriseSearchTelemetry({ - action: 'clicked', - metric: 'entSearchContent-engines-engineView-deleteEngine', - }); - } - }, - size: 's', - }, - ], - }, - ]} - /> - - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx index e20fbc81689a4..4e925b50c63b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/header_docs_action.tsx @@ -21,7 +21,7 @@ export const EngineHeaderDocsAction: React.FC = () => ( target="_blank" iconType="documents" > - Engine Docs + Search Application Docs diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/search_application_content.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/search_application_content.tsx new file mode 100644 index 0000000000000..4bf79b31a1491 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/search_application_content.tsx @@ -0,0 +1,156 @@ +/* + * 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 { useParams } from 'react-router-dom'; + +import { useActions, useValues } from 'kea'; + +import { EuiButton, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { generateEncodedPath } from '../../../shared/encode_path_params'; +import { KibanaLogic } from '../../../shared/kibana'; + +import { + ENGINE_PATH, + SEARCH_APPLICATION_CONTENT_PATH, + EngineViewTabs, + SearchApplicationContentTabs, +} from '../../routes'; +import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; + +import { AddIndicesFlyout } from './add_indices_flyout'; +import { EngineError } from './engine_error'; +import { EngineIndices } from './engine_indices'; +import { EngineIndicesLogic } from './engine_indices_logic'; +import { EngineSchema } from './engine_schema'; +import { EngineViewLogic } from './engine_view_logic'; + +const pageTitle = i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.content.pageTitle', + { + defaultMessage: 'Content', + } +); +const INDICES_TAB_TITLE = i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.content.indicesTabTitle', + { + defaultMessage: 'Indices', + } +); +const SCHEMA_TAB_TITLE = i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.content.schemaTabTitle', + { + defaultMessage: 'Schema', + } +); + +const getTabBreadCrumb = (tabId: string) => { + switch (tabId) { + case SearchApplicationContentTabs.INDICES: + return INDICES_TAB_TITLE; + case SearchApplicationContentTabs.SCHEMA: + return SCHEMA_TAB_TITLE; + default: + return tabId; + } +}; + +const ContentTabs: string[] = Object.values(SearchApplicationContentTabs); + +export const SearchApplicationContent = () => { + const { engineName, isLoadingEngine } = useValues(EngineViewLogic); + const { addIndicesFlyoutOpen } = useValues(EngineIndicesLogic); + const { closeAddIndicesFlyout, openAddIndicesFlyout } = useActions(EngineIndicesLogic); + const { contentTabId = SearchApplicationContentTabs.INDICES } = useParams<{ + contentTabId?: string; + }>(); + + if (!ContentTabs.includes(contentTabId)) { + return ( + + + + ); + } + + const onTabClick = (tab: SearchApplicationContentTabs) => () => { + KibanaLogic.values.navigateToUrl( + generateEncodedPath(SEARCH_APPLICATION_CONTENT_PATH, { + contentTabId: tab, + engineName, + }) + ); + }; + + return ( + + KibanaLogic.values.navigateToUrl( + generateEncodedPath(ENGINE_PATH, { + engineName, + }) + ), + text: ( + <> + {engineName} + + ), + }, + ], + pageTitle, + rightSideItems: [ + + {i18n.translate('xpack.enterpriseSearch.content.engine.indices.addNewIndicesButton', { + defaultMessage: 'Add new indices', + })} + , + ], + tabs: [ + { + isSelected: contentTabId === SearchApplicationContentTabs.INDICES, + label: INDICES_TAB_TITLE, + onClick: onTabClick(SearchApplicationContentTabs.INDICES), + }, + { + isSelected: contentTabId === SearchApplicationContentTabs.SCHEMA, + label: SCHEMA_TAB_TITLE, + onClick: onTabClick(SearchApplicationContentTabs.SCHEMA), + }, + ], + }} + engineName={engineName} + > + {contentTabId === SearchApplicationContentTabs.INDICES && } + {contentTabId === SearchApplicationContentTabs.SCHEMA && } + {addIndicesFlyoutOpen && } + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx index 5e6f7cebd89fc..200192d1e20a4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/indices_select_combobox.tsx @@ -47,7 +47,7 @@ export const IndicesSelectComboBox = (props: IndicesSelectComboBoxProps) => { }, [searchQuery]); const options: Array> = - data?.indices?.map(indexToOption) ?? []; + data?.indices?.map((index) => indexToOption(index.name, index)) ?? []; const renderOption = (option: EuiComboBoxOptionOption) => ( @@ -84,8 +84,9 @@ export const IndicesSelectComboBox = (props: IndicesSelectComboBoxProps) => { }; export const indexToOption = ( - index: ElasticsearchIndexWithIngestion + indexName: string, + index?: ElasticsearchIndexWithIngestion ): IndicesSelectComboBoxOption => ({ - label: index.name, + label: indexName, value: index, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx index f07516313f6e8..de291752639fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/components/tables/engines_table.tsx @@ -56,7 +56,7 @@ export const EnginesListTable: React.FC = ({ { field: 'name', name: i18n.translate('xpack.enterpriseSearch.content.enginesList.table.column.name', { - defaultMessage: 'Engine Name', + defaultMessage: 'Search Application Name', }), mobileOptions: { header: true, @@ -117,7 +117,7 @@ export const EnginesListTable: React.FC = ({ description: i18n.translate( 'xpack.enterpriseSearch.content.enginesList.table.column.actions.view.buttonDescription', { - defaultMessage: 'View this engine', + defaultMessage: 'View this search application', } ), type: 'icon', @@ -134,7 +134,7 @@ export const EnginesListTable: React.FC = ({ description: i18n.translate( 'xpack.enterpriseSearch.content.enginesList.table.column.action.delete.buttonDescription', { - defaultMessage: 'Delete this engine', + defaultMessage: 'Delete this search application', } ), type: 'icon', @@ -144,7 +144,7 @@ export const EnginesListTable: React.FC = ({ i18n.translate( 'xpack.enterpriseSearch.content.engineList.table.column.actions.deleteEngineLabel', { - defaultMessage: 'Delete this engine', + defaultMessage: 'Delete this search application', } ), onClick: (engine) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx index ae7f7423be7ce..2df12a2b9bd8f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_flyout.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; + +import { useLocation } from 'react-router-dom'; import { useActions, useValues } from 'kea'; import { - EuiButton, - EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFieldText, @@ -26,6 +26,8 @@ import { EuiTitle, EuiComboBoxOptionOption, EuiCallOut, + EuiButton, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -33,14 +35,14 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { Status } from '../../../../../common/types/api'; import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; -import { isNotNullish } from '../../../../../common/utils/is_not_nullish'; -import { CANCEL_BUTTON_LABEL } from '../../../shared/constants'; +import { CANCEL_BUTTON_LABEL, ESINDEX_QUERY_PARAMETER } from '../../../shared/constants'; import { docLinks } from '../../../shared/doc_links'; import { getErrorsFromHttpResponse } from '../../../shared/flash_messages/handle_api_errors'; -import { indexToOption, IndicesSelectComboBox } from './components/indices_select_combobox'; +import { parseQueryParams } from '../../../shared/query_params'; +import { indexToOption, IndicesSelectComboBox } from './components/indices_select_combobox'; import { CreateEngineLogic } from './create_engine_logic'; export interface CreateEngineFlyoutProps { @@ -60,11 +62,18 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { selectedIndices, } = useValues(CreateEngineLogic); + const { search } = useLocation() as unknown as Location; + const { ...params } = parseQueryParams(search); + const indexName = params[ESINDEX_QUERY_PARAMETER]; + const onIndicesChange = ( selectedOptions: Array> ) => { - setSelectedIndices(selectedOptions.map((option) => option.value).filter(isNotNullish)); + setSelectedIndices(selectedOptions.map((option) => option.label)); }; + useEffect(() => { + if (indexName && typeof indexName === 'string') setSelectedIndices([indexName]); + }, []); return ( @@ -72,7 +81,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => {

    {i18n.translate('xpack.enterpriseSearch.content.engines.createEngine.headerTitle', { - defaultMessage: 'Create an engine', + defaultMessage: 'Create a Search Application', })}

    @@ -81,7 +90,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => {

    { > {i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.header.docsLink', - { defaultMessage: 'Engines documentation' } + { defaultMessage: 'Search Application documentation' } )} ), @@ -107,7 +116,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { color="danger" title={i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.header.createError.title', - { defaultMessage: 'Error creating engine' } + { defaultMessage: 'Error creating search application' } )} > {getErrorsFromHttpResponse(createEngineError).map((errMessage, i) => ( @@ -126,7 +135,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { fullWidth isDisabled={formDisabled} onChange={onIndicesChange} - selectedOptions={selectedIndices.map(indexToOption)} + selectedOptions={selectedIndices.map((index: string) => indexToOption(index))} /> ), status: indicesStatus, @@ -142,7 +151,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { disabled={formDisabled} placeholder={i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.nameEngine.placeholder', - { defaultMessage: 'Engine name' } + { defaultMessage: 'Search Application name' } )} value={engineName} onChange={(e) => setEngineName(e.target.value)} @@ -151,7 +160,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { status: engineNameStatus, title: i18n.translate( 'xpack.enterpriseSearch.content.engines.createEngine.nameEngine.title', - { defaultMessage: 'Name your engine' } + { defaultMessage: 'Name your Search Application' } ), }, ]} @@ -171,7 +180,7 @@ export const CreateEngineFlyout = ({ onClose }: CreateEngineFlyoutProps) => { { }} > {i18n.translate('xpack.enterpriseSearch.content.engines.createEngine.submit', { - defaultMessage: 'Create this engine', + defaultMessage: 'Create this Search Application', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts index 0d88ca44ff87d..93bdda9c4a2d0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.test.ts @@ -8,10 +8,12 @@ import { LogicMounter } from '../../../__mocks__/kea_logic'; import { Status } from '../../../../../common/types/api'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; +import { KibanaLogic } from '../../../shared/kibana'; import { CreateEngineApiLogic } from '../../api/engines/create_engine_api_logic'; +import { ENGINES_PATH } from '../../routes'; + import { CreateEngineLogic, CreateEngineLogicValues } from './create_engine_logic'; const DEFAULT_VALUES: CreateEngineLogicValues = { @@ -27,7 +29,7 @@ const DEFAULT_VALUES: CreateEngineLogicValues = { const VALID_ENGINE_NAME = 'unit-test-001'; const INVALID_ENGINE_NAME = 'TEST'; -const VALID_INDICES_DATA = [{ name: 'search-index-01' }] as ElasticsearchIndexWithIngestion[]; +const VALID_INDICES_DATA = ['search-index-01']; describe('CreateEngineLogic', () => { const { mount: apiLogicMount } = new LogicMounter(CreateEngineApiLogic); @@ -59,16 +61,17 @@ describe('CreateEngineLogic', () => { indices: ['search-index-01'], }); }); - it('engineCreated is handled', () => { + it('engineCreated is handled and is navigated to Search application list page', () => { jest.spyOn(CreateEngineLogic.actions, 'fetchEngines'); - jest.spyOn(CreateEngineLogic.actions, 'closeEngineCreate'); - + jest + .spyOn(KibanaLogic.values, 'navigateToUrl') + .mockImplementationOnce(() => Promise.resolve()); CreateEngineApiLogic.actions.apiSuccess({ result: 'created', }); + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(ENGINES_PATH); expect(CreateEngineLogic.actions.fetchEngines).toHaveBeenCalledTimes(1); - expect(CreateEngineLogic.actions.closeEngineCreate).toHaveBeenCalledTimes(1); }); }); describe('selectors', () => { @@ -114,7 +117,7 @@ describe('CreateEngineLogic', () => { it('returns true while create request in progress', () => { CreateEngineApiLogic.actions.makeRequest({ engineName: VALID_ENGINE_NAME, - indices: [VALID_INDICES_DATA[0].name], + indices: [VALID_INDICES_DATA[0]], }); expect(CreateEngineLogic.values.formDisabled).toEqual(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts index 266dab05c5441..c939dda096446 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/create_engine_logic.ts @@ -8,27 +8,27 @@ import { kea, MakeLogicType } from 'kea'; import { Status } from '../../../../../common/types/api'; -import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; +import { KibanaLogic } from '../../../shared/kibana'; import { CreateEngineApiLogic, CreateEngineApiLogicActions, } from '../../api/engines/create_engine_api_logic'; +import { ENGINES_PATH } from '../../routes'; import { EnginesListLogic } from './engines_list_logic'; const NAME_VALIDATION = new RegExp(/^[a-z0-9\-]+$/); export interface CreateEngineLogicActions { - closeEngineCreate: () => void; createEngine: () => void; createEngineRequest: CreateEngineApiLogicActions['makeRequest']; engineCreateError: CreateEngineApiLogicActions['apiError']; engineCreated: CreateEngineApiLogicActions['apiSuccess']; fetchEngines: () => void; setEngineName: (engineName: string) => { engineName: string }; - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => { - indices: ElasticsearchIndexWithIngestion[]; + setSelectedIndices: (indices: string[]) => { + indices: string[]; }; } @@ -40,7 +40,7 @@ export interface CreateEngineLogicValues { engineNameStatus: 'complete' | 'incomplete' | 'warning'; formDisabled: boolean; indicesStatus: 'complete' | 'incomplete'; - selectedIndices: ElasticsearchIndexWithIngestion[]; + selectedIndices: string[]; } export const CreateEngineLogic = kea< @@ -49,12 +49,12 @@ export const CreateEngineLogic = kea< actions: { createEngine: true, setEngineName: (engineName: string) => ({ engineName }), - setSelectedIndices: (indices: ElasticsearchIndexWithIngestion[]) => ({ indices }), + setSelectedIndices: (indices: string[]) => ({ indices }), }, connect: { actions: [ EnginesListLogic, - ['closeEngineCreate', 'fetchEngines'], + ['fetchEngines'], CreateEngineApiLogic, [ 'makeRequest as createEngineRequest', @@ -68,12 +68,12 @@ export const CreateEngineLogic = kea< createEngine: () => { actions.createEngineRequest({ engineName: values.engineName, - indices: values.selectedIndices.map((index) => index.name), + indices: values.selectedIndices, }); }, engineCreated: () => { actions.fetchEngines(); - actions.closeEngineCreate(); + KibanaLogic.values.navigateToUrl(ENGINES_PATH); }, }), path: ['enterprise_search', 'content', 'create_engine_logic'], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx index 4203969bb74a2..46d7c8586ed80 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/delete_engine_modal.tsx @@ -28,7 +28,7 @@ export const DeleteEngineModal: React.FC = ({ engineName return ( { @@ -42,7 +42,7 @@ export const DeleteEngineModal: React.FC = ({ engineName confirmButtonText={i18n.translate( 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title', { - defaultMessage: 'Yes, delete this engine ', + defaultMessage: 'Yes, delete this search application', } )} buttonColor="danger" @@ -53,7 +53,7 @@ export const DeleteEngineModal: React.FC = ({ engineName 'xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description', { defaultMessage: - 'Deleting your engine is not a reversible action. Your indices will not be affected. ', + 'Deleting your search application is not a reversible action. Your indices will not be affected. ', } )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx index 7617467bba64f..7b93214e0af8b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list.tsx @@ -31,6 +31,7 @@ import { docLinks } from '../../../shared/doc_links'; import { KibanaLogic } from '../../../shared/kibana'; import { LicensingLogic } from '../../../shared/licensing'; import { EXPLORE_PLATINUM_FEATURES_LINK } from '../../../workplace_search/constants'; +import { ENGINES_PATH, ENGINE_CREATION_PATH } from '../../routes'; import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template'; import { LicensingCallout, LICENSING_FEATURE } from '../shared/licensing_callout/licensing_callout'; @@ -43,9 +44,12 @@ import { EngineListIndicesFlyout } from './engines_list_flyout'; import { EnginesListFlyoutLogic } from './engines_list_flyout_logic'; import { EnginesListLogic } from './engines_list_logic'; -export const CreateEngineButton: React.FC<{ disabled: boolean }> = ({ disabled }) => { +interface CreateEngineButtonProps { + disabled: boolean; +} +export const CreateEngineButton: React.FC = ({ disabled }) => { const [showPopover, setShowPopover] = useState(false); - const { openEngineCreate } = useActions(EnginesListLogic); + return ( = ({ disabled } iconType="plusInCircle" data-test-subj="enterprise-search-content-engines-creation-button" data-telemetry-id="entSearchContent-engines-list-createEngine" - disabled={disabled} - onClick={openEngineCreate} + isDisabled={disabled} + onClick={() => KibanaLogic.values.navigateToUrl(ENGINE_CREATION_PATH)} > - {i18n.translate('xpack.enterpriseSearch.content.engines.createEngineButtonLabel', { - defaultMessage: 'Create Search Application', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.createEngineButtonLabel', + { + defaultMessage: 'Create Search Application', + } + )}
    } > @@ -82,7 +89,7 @@ export const CreateEngineButton: React.FC<{ disabled: boolean }> = ({ disabled } @@ -94,27 +101,27 @@ export const CreateEngineButton: React.FC<{ disabled: boolean }> = ({ disabled } ); }; +interface ListProps { + createEngineFlyoutOpen?: boolean; +} -export const EnginesList: React.FC = () => { +export const EnginesList: React.FC = ({ createEngineFlyoutOpen }) => { const { closeDeleteEngineModal, - closeEngineCreate, fetchEngines, onPaginate, openDeleteEngineModal, setSearchQuery, setIsFirstRequest, } = useActions(EnginesListLogic); - const { openFetchEngineFlyout } = useActions(EnginesListFlyoutLogic); - const { isCloud } = useValues(KibanaLogic); + const { isCloud, navigateToUrl } = useValues(KibanaLogic); const { hasPlatinumLicense } = useValues(LicensingLogic); const isGated = !isCloud && !hasPlatinumLicense; const { - createEngineFlyoutOpen, deleteModalEngineName, hasNoEngines, isDeleteModalVisible, @@ -127,7 +134,7 @@ export const EnginesList: React.FC = () => { const throttledSearchQuery = useThrottle(searchQuery, INPUT_THROTTLE_DELAY_MS); useEffect(() => { - // Don't fetch engines if we don't have a valid license + // Don't fetch search applications if we don't have a valid license if (!isGated) { fetchEngines(); } @@ -148,18 +155,18 @@ export const EnginesList: React.FC = () => { ) : null} - {createEngineFlyoutOpen && } + {createEngineFlyoutOpen && navigateToUrl(ENGINES_PATH)} />} { target="_blank" data-telemetry-id="entSearchContent-engines-documentation-viewDocumentaion" > - {i18n.translate('xpack.enterpriseSearch.content.engines.documentation', { - defaultMessage: 'explore our Search Applications documentation', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.searchApplications.documentation', + { + defaultMessage: 'explore our Search Applications documentation', + } + )} ), }} /> ), - pageTitle: i18n.translate('xpack.enterpriseSearch.content.engines.title', { + pageTitle: i18n.translate('xpack.enterpriseSearch.content.searchApplications.title', { defaultMessage: 'Search Applications', }), rightSideItems: isLoading @@ -185,7 +195,7 @@ export const EnginesList: React.FC = () => { ? [] : [], }} - pageViewTelemetry="Engines" + pageViewTelemetry="Search Applications" isLoading={isLoading && !isGated} > {isGated && ( @@ -200,15 +210,15 @@ export const EnginesList: React.FC = () => { { {i18n.translate( - 'xpack.enterpriseSearch.content.engines.searchPlaceholder.description', + 'xpack.enterpriseSearch.content.searchApplications.searchPlaceholder.description', { - defaultMessage: 'Locate an engine via name or by its included indices.', + defaultMessage: + 'Locate a search application via name or by its included indices.', } )} @@ -230,7 +241,7 @@ export const EnginesList: React.FC = () => { { }); }); }); - describe('openEngineCreate', () => { - it('set createEngineFlyoutOpen to true', () => { - EnginesListLogic.actions.openEngineCreate(); - expect(EnginesListLogic.values).toEqual({ - ...DEFAULT_VALUES, - createEngineFlyoutOpen: true, - }); - }); - }); - describe('closeEngineCreate', () => { - it('set createEngineFlyoutOpen to false', () => { - EnginesListLogic.actions.closeEngineCreate(); - expect(EnginesListLogic.values).toEqual({ - ...DEFAULT_VALUES, - createEngineFlyoutOpen: false, - }); - }); - }); + describe('setSearchQuery', () => { it('set setSearchQuery to search value', () => { EnginesListLogic.actions.setSearchQuery('my-search-query'); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts index 082747612698a..02ac44ae16c8d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_list_logic.ts @@ -39,7 +39,7 @@ export type EnginesListActions = Pick< 'apiError' | 'apiSuccess' | 'makeRequest' > & { closeDeleteEngineModal(): void; - closeEngineCreate(): void; + deleteEngine: DeleteEnginesApiLogicActions['makeRequest']; deleteError: DeleteEnginesApiLogicActions['apiError']; deleteSuccess: DeleteEnginesApiLogicActions['apiSuccess']; @@ -50,13 +50,11 @@ export type EnginesListActions = Pick< openDeleteEngineModal: (engine: EnterpriseSearchEngine | EnterpriseSearchEngineDetails) => { engine: EnterpriseSearchEngine; }; - openEngineCreate(): void; setIsFirstRequest(): void; setSearchQuery(searchQuery: string): { searchQuery: string }; }; interface EngineListValues { - createEngineFlyoutOpen: boolean; data: typeof FetchEnginesAPILogic.values.data; deleteModalEngine: EnterpriseSearchEngine | null; deleteModalEngineName: string; @@ -76,11 +74,9 @@ interface EngineListValues { export const EnginesListLogic = kea>({ actions: { closeDeleteEngineModal: true, - closeEngineCreate: true, fetchEngines: true, onPaginate: (args: EuiBasicTableOnChange) => ({ pageNumber: args.page.index }), openDeleteEngineModal: (engine) => ({ engine }), - openEngineCreate: true, setIsFirstRequest: true, setSearchQuery: (searchQuery: string) => ({ searchQuery }), }, @@ -111,13 +107,6 @@ export const EnginesListLogic = kea ({ - createEngineFlyoutOpen: [ - false, - { - closeEngineCreate: () => false, - openEngineCreate: () => true, - }, - ], deleteModalEngine: [ null, { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx index 662e1d7c21417..bcd5e5ec3bcd2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engines/engines_router.tsx @@ -10,7 +10,7 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; -import { ENGINES_PATH, ENGINE_PATH } from '../../routes'; +import { ENGINES_PATH, ENGINE_CREATION_PATH, ENGINE_PATH } from '../../routes'; import { EngineRouter } from '../engine/engine_router'; import { NotFound } from '../not_found'; @@ -23,6 +23,9 @@ export const EnginesRouter: React.FC = () => { + + + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.test.tsx index 0a7e767617b38..031d3e8da3ecd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.test.tsx @@ -5,11 +5,13 @@ * 2.0. */ +import { setMockValues } from '../../../../__mocks__/kea_logic'; + import React from 'react'; import { shallow } from 'enzyme'; -import { EuiSteps } from '@elastic/eui'; +import { Status } from '../../../../../../common/types/api'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -18,6 +20,7 @@ import { MethodApi } from './method_api'; describe('MethodApi', () => { beforeEach(() => { jest.clearAllMocks(); + setMockValues({ status: Status.IDLE }); }); it('renders API ingestion method tab', () => { @@ -25,6 +28,5 @@ describe('MethodApi', () => { const template = wrapper.find(NewSearchIndexTemplate); expect(template.prop('type')).toEqual('api'); - expect(template.find(EuiSteps)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx index c505428449f8a..3d9afeaede533 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api.tsx @@ -7,59 +7,22 @@ import React from 'react'; -import { useActions } from 'kea'; +import { useActions, useValues } from 'kea'; -import { EuiSteps, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { Status } from '../../../../../../common/types/api'; -import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexTemplate } from '../new_search_index_template'; import { MethodApiLogic } from './method_api_logic'; export const MethodApi: React.FC = () => { const { makeRequest } = useActions(MethodApiLogic); + const { status } = useValues(MethodApiLogic); return ( - } type="api" + buttonLoading={status === Status.LOADING} onSubmit={(indexName, language) => makeRequest({ indexName, language })} - > - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content', - { - defaultMessage: - 'Generate an API key and view the documentation for posting documents to the Elasticsearch API endpoint. Language clients are available for streamlined integration.', - } - )} -

    -
    - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title', - { - defaultMessage: 'Configure ingestion settings', - } - ), - titleSize: 'xs', - }, - BUILD_SEARCH_EXPERIENCE_STEP, - ]} - /> - + /> ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api_logic.ts index 3608395cf5f34..2f0332da0eeab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_api/method_api_logic.ts @@ -24,9 +24,14 @@ type MethodApiActions = Pick< 'apiSuccess' | 'makeRequest' >; -export const MethodApiLogic = kea>({ +interface MethodApiValues { + status: typeof CreateApiIndexApiLogic.values['status']; +} + +export const MethodApiLogic = kea>({ connect: { actions: [CreateApiIndexApiLogic, ['apiSuccess', 'makeRequest']], + values: [CreateApiIndexApiLogic, ['status']], }, listeners: { apiSuccess: ({ indexName }) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.test.tsx index 368c2b7d0c684..fe3126c7fc215 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.test.tsx @@ -11,8 +11,6 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiSteps } from '@elastic/eui'; - import { Status } from '../../../../../../common/types/api'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -27,10 +25,9 @@ describe('MethodConnector', () => { }); it('renders connector ingestion method tab', () => { - const wrapper = shallow(); + const wrapper = shallow(); const template = wrapper.find(NewSearchIndexTemplate); expect(template.prop('type')).toEqual('connector'); - expect(template.find(EuiSteps)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx index 522465e1c6957..badcbe493eb1a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_connector/method_connector.tsx @@ -16,14 +16,10 @@ import { EuiFlexItem, EuiLink, EuiSpacer, - EuiSteps, - EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - import { Status } from '../../../../../../common/types/api'; import { docLinks } from '../../../../shared/doc_links'; import { KibanaLogic } from '../../../../shared/kibana'; @@ -31,11 +27,11 @@ import { LicensingLogic } from '../../../../shared/licensing'; import { AddConnectorApiLogic } from '../../../api/connector/add_connector_api_logic'; import { FetchCloudHealthApiLogic } from '../../../api/stats/fetch_cloud_health_api_logic'; +import { NATIVE_CONNECTORS } from '../../search_index/connector/constants'; import { LicensingCallout, LICENSING_FEATURE, } from '../../shared/licensing_callout/licensing_callout'; -import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexLogic } from '../new_search_index_logic'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -43,7 +39,11 @@ import { errorToText } from '../utils/error_to_text'; import { AddConnectorLogic } from './add_connector_logic'; -export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) => { +interface MethodConnectorProps { + serviceType: string; +} + +export const MethodConnector: React.FC = ({ serviceType }) => { const { apiReset, makeRequest } = useActions(AddConnectorApiLogic); const { error, status } = useValues(AddConnectorApiLogic); const { isModalVisible } = useValues(AddConnectorLogic); @@ -53,6 +53,10 @@ export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) = const { hasPlatinumLicense } = useValues(LicensingLogic); const { data: cloudHealthData } = useValues(FetchCloudHealthApiLogic); + const isNative = + Boolean(NATIVE_CONNECTORS.find((connector) => connector.serviceType === serviceType)) && + isCloud; + const isGated = isNative && !isCloud && !hasPlatinumLicense; const hasLowMemory = isNative && isCloud && cloudHealthData && !cloudHealthData.has_min_connector_memory; @@ -108,145 +112,62 @@ export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) = docsUrl={docLinks.connectors} disabled={isGated || hasLowMemory} error={errorToText(error)} - title={ - isNative - ? i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title', - { - defaultMessage: 'Index using a connector', - } - ) - : i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title', - { - defaultMessage: 'Build a connector', - } - ) - } type="connector" onNameChange={() => { apiReset(); }} - onSubmit={(name, lang) => makeRequest({ indexName: name, isNative, language: lang })} + onSubmit={(name, lang) => + makeRequest({ indexName: name, isNative, language: lang, serviceType }) + } buttonLoading={status === Status.LOADING} - > - -

    - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title', - { - defaultMessage: 'Build a connector', - } - )} - - ), - }} - /> -

    - - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title', - { - defaultMessage: 'Configure a connector', - } - ), - titleSize: 'xs', - } - : { - children: ( - -

    - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink', - { defaultMessage: 'Bulk API' } - )} - - ), - }} - /> -

    -
    - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title', - { - defaultMessage: 'Build and configure a connector', - } - ), - titleSize: 'xs', - }, - BUILD_SEARCH_EXPERIENCE_STEP, - ]} - /> - {isModalVisible && ( - { - event?.preventDefault(); - setIsModalVisible(false); - }} - onConfirm={(event) => { - event.preventDefault(); - makeRequest({ - deleteExistingConnector: true, + /> + + {isModalVisible && ( + { + setIsModalVisible(false); + }} + onConfirm={() => { + makeRequest({ + deleteExistingConnector: true, + indexName: fullIndexName, + isNative, + language, + serviceType, + }); + }} + cancelButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label', + { + defaultMessage: 'Cancel', + } + )} + confirmButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label', + { + defaultMessage: 'Replace configuration', + } + )} + defaultFocusedButton="confirm" + > + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description', + { + defaultMessage: + 'A deleted index named {indexName} was originally tied to an existing connector configuration. Would you like to replace the existing connector configuration with a new one?', + values: { indexName: fullIndexName, - isNative, - language, - }); - }} - cancelButtonText={i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label', - { - defaultMessage: 'Cancel', - } - )} - confirmButtonText={i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label', - { - defaultMessage: 'Replace configuration', - } - )} - defaultFocusedButton="confirm" - > - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description', - { - defaultMessage: - 'A deleted index named {indexName} was originally tied to an existing connector configuration. Would you like to replace the existing connector configuration with a new one?', - values: { - indexName: fullIndexName, - }, - } - )} - - )} - + }, + } + )} + + )}
    ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.test.tsx index ab0d58858d93c..bda59c9ad9fcc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.test.tsx @@ -11,8 +11,6 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiSteps } from '@elastic/eui'; - import { Status } from '../../../../../../common/types/api'; import { NewSearchIndexTemplate } from '../new_search_index_template'; @@ -35,6 +33,5 @@ describe('MethodCrawler', () => { const template = wrapper.find(NewSearchIndexTemplate); expect(template.prop('type')).toEqual('crawler'); - expect(template.find(EuiSteps)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx index 2adbee1515d4e..1264e400fce4e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_crawler/method_crawler.tsx @@ -9,9 +9,7 @@ import React from 'react'; import { useValues, useActions } from 'kea'; -import { EuiFlexGroup, EuiFlexItem, EuiSteps, EuiText } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Status } from '../../../../../../common/types/api'; import { docLinks } from '../../../../shared/doc_links'; @@ -22,7 +20,6 @@ import { LicensingCallout, LICENSING_FEATURE, } from '../../shared/licensing_callout/licensing_callout'; -import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps'; import { NewSearchIndexTemplate } from '../new_search_index_template'; import { MethodCrawlerLogic } from './method_crawler_logic'; @@ -46,48 +43,12 @@ export const MethodCrawler: React.FC = () => { )} makeRequest({ indexName, language })} disabled={isGated} buttonLoading={status === Status.LOADING} docsUrl={docLinks.crawlerOverview} - > - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content', - { - defaultMessage: - 'Configure the domains you’d like to crawl, and when ready trigger your first crawl. Let Enterprise Search do the rest.', - } - )} -

    - - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title', - { - defaultMessage: 'Configure ingestion settings', - } - ), - titleSize: 'xs', - }, - BUILD_SEARCH_EXPERIENCE_STEP, - ]} - /> -
    + />
    ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_steps.tsx deleted file mode 100644 index 20ca643be580d..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/method_steps.tsx +++ /dev/null @@ -1,98 +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 { EuiLink, EuiText } from '@elastic/eui'; - -import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; -import { i18n } from '@kbn/i18n'; - -import { FormattedMessage } from '@kbn/i18n-react'; - -import { - APP_SEARCH_URL, - ENTERPRISE_SEARCH_ELASTICSEARCH_URL, -} from '../../../../../common/constants'; -import { docLinks } from '../../../shared/doc_links'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; - -export const CREATE_ELASTICSEARCH_INDEX_STEP: EuiContainedStepProps = { - children: ( - -

    - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink', - { defaultMessage: 'language analyzer' } - )} - - ), - }} - /> -

    -
    - ), - status: 'incomplete', - title: i18n.translate('xpack.enterpriseSearch.content.newIndex.steps.createIndex.title', { - defaultMessage: 'Create an Elasticsearch index', - }), - - titleSize: 'xs', -}; - -export const BUILD_SEARCH_EXPERIENCE_STEP: EuiContainedStepProps = { - children: ( - -

    - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink', - { defaultMessage: 'App Search' } - )} - - ), - elasticsearchLink: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink', - { defaultMessage: 'Elasticsearch' } - )} - - ), - searchEngineLink: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink', - { defaultMessage: 'search engine' } - )} - - ), - }} - /> -

    -
    - ), - status: 'incomplete', - title: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title', - { - defaultMessage: 'Build a search experience', - } - ), - titleSize: 'xs', -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.scss deleted file mode 100644 index ba464bfd5b145..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.scss +++ /dev/null @@ -1,22 +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. - */ - -.entSearchNewIndexButtonGroupButton { - border: $euiBorderThin; - - &--selected { - border: 1px $euiColorPrimary solid; - } - - .rightArrow { - border-radius: $euiBorderRadiusSmall; - background: transparentize($euiColorPrimary, .8); - color: $euiColorPrimaryText; - padding: $euiSizeXS; - width: $euiSizeL; - } -} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx index 65ef18209fa0b..f9a08a21b0b6d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index.tsx @@ -11,216 +11,121 @@ import { useLocation } from 'react-router-dom'; import { useValues } from 'kea'; -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { INGESTION_METHOD_IDS } from '../../../../../common/constants'; import { ProductFeatures } from '../../../../../common/types'; -import { BETA_LABEL } from '../../../shared/constants/labels'; + +import { CONTINUE_BUTTON_LABEL } from '../../../shared/constants'; + +import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana/kibana_logic'; + import { parseQueryParams } from '../../../shared/query_params'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; - +import { NEW_INDEX_METHOD_PATH, NEW_INDEX_SELECT_CONNECTOR_PATH } from '../../routes'; import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; import { baseBreadcrumbs } from '../search_indices'; -import { ButtonGroup, ButtonGroupOption } from './button_group'; -import { SearchIndexEmptyState } from './empty_state'; -import { MethodApi } from './method_api/method_api'; -import { MethodConnector } from './method_connector/method_connector'; -import { MethodCrawler } from './method_crawler/method_crawler'; - -const betaBadge = ( - - {BETA_LABEL} - -); - -const METHOD_BUTTON_GROUP_OPTIONS: Record = { - [INGESTION_METHOD_IDS.crawler]: { - description: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description', - { - defaultMessage: 'Discover, extract, index, and sync all of your website content', - } - ), - footer: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer', { - defaultMessage: 'No development required', - }), - icon: 'globe', - id: INGESTION_METHOD_IDS.crawler, - label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label', { - defaultMessage: 'Use the web crawler', - }), - }, - [INGESTION_METHOD_IDS.native_connector]: { - badge: betaBadge, - description: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description', - { - defaultMessage: - 'Configure a connector to extract, index, and sync all of your content from supported data sources ', - } - ), - footer: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer', - { - defaultMessage: 'No development required', - } - ), - icon: 'visVega', - id: INGESTION_METHOD_IDS.native_connector, - label: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label', - { - defaultMessage: 'Use a connector', - } - ), - }, - [INGESTION_METHOD_IDS.api]: { - description: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description', - { - defaultMessage: 'Add documents programmatically by connecting with the API', - } - ), - footer: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer', { - defaultMessage: 'Some development required', - }), - icon: 'visVega', - id: INGESTION_METHOD_IDS.api, - label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label', { - defaultMessage: 'Use the API', - }), - }, - [INGESTION_METHOD_IDS.connector]: { - badge: betaBadge, - description: i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description', - { - defaultMessage: - 'Use the connector framework to quickly build connectors for custom data sources', - } - ), - footer: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer', { - defaultMessage: 'Development required', - }), - icon: 'package', - id: INGESTION_METHOD_IDS.connector, - label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label', { - defaultMessage: 'Build a connector', - }), - }, -}; +import { NewIndexCard } from './new_index_card'; -const getAvailableMethodOptions = (productFeatures: ProductFeatures): ButtonGroupOption[] => { +const getAvailableMethodOptions = (productFeatures: ProductFeatures): INGESTION_METHOD_IDS[] => { return [ - ...(productFeatures.hasWebCrawler - ? [METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.crawler]] - : []), - ...(productFeatures.hasNativeConnectors - ? [METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.native_connector]] - : []), - METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.api], - ...(productFeatures.hasConnectors - ? [METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.connector]] - : []), + ...(productFeatures.hasWebCrawler ? [INGESTION_METHOD_IDS.CRAWLER] : []), + INGESTION_METHOD_IDS.API, + ...(productFeatures.hasConnectors ? [INGESTION_METHOD_IDS.CONNECTOR] : []), ]; }; export const NewIndex: React.FC = () => { const { search } = useLocation(); + const { method } = parseQueryParams(search); const { capabilities, productFeatures } = useValues(KibanaLogic); - const { method: methodParam } = parseQueryParams(search); const availableIngestionMethodOptions = getAvailableMethodOptions(productFeatures); - const initialSelectedMethod = - availableIngestionMethodOptions.find((option) => option.id === methodParam) ?? - availableIngestionMethodOptions[0]; - - const [selectedMethod, setSelectedMethod] = useState(initialSelectedMethod); - + const [selectedMethod, setSelectedMethod] = useState( + Array.isArray(method) ? method[0] : method ?? INGESTION_METHOD_IDS.CRAWLER + ); return ( - - - - -

    - {i18n.translate('xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title', { - defaultMessage: 'Select an ingestion method', - })} -

    -
    - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description', - { - defaultMessage: - 'Create a search optimized Elasticsearch index by selecting an ingestion method for your use case.', - } + + <> + + + {availableIngestionMethodOptions.map((type) => ( + + { + setSelectedMethod(type); + }} + isSelected={selectedMethod === type} + /> + + ))} + + + + + + {capabilities.navLinks.integrations && ( + <> + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.viewIntegrationsLink', + { + defaultMessage: 'View additional integrations', + } + )} + + )} -

    -
    - - - {capabilities.navLinks.integrations && ( - <> - - - {i18n.translate('xpack.enterpriseSearch.content.newIndex.viewIntegrationsLink', { - defaultMessage: 'View additional integrations', - })} - - - )} -
    -
    - - {selectedMethod ? ( - <> - {selectedMethod.id === INGESTION_METHOD_IDS.crawler && } - {selectedMethod.id === INGESTION_METHOD_IDS.api && } - {selectedMethod.id === INGESTION_METHOD_IDS.connector && ( - - )} - {selectedMethod.id === INGESTION_METHOD_IDS.native_connector && ( - - )} - - ) : ( - - )} - + + + ).includes( + selectedMethod + ) + } + fill + onClick={() => { + if (selectedMethod === INGESTION_METHOD_IDS.CONNECTOR) { + KibanaLogic.values.navigateToUrl(NEW_INDEX_SELECT_CONNECTOR_PATH); + } else { + KibanaLogic.values.navigateToUrl( + generateEncodedPath(NEW_INDEX_METHOD_PATH, { type: selectedMethod }) + ); + } + }} + > + {CONTINUE_BUTTON_LABEL} + + +
    + +
    ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_card.tsx new file mode 100644 index 0000000000000..31a15a9a13275 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_card.tsx @@ -0,0 +1,132 @@ +/* + * 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, { MouseEventHandler } from 'react'; + +import { EuiCardProps, EuiIconProps, EuiTextColor } from '@elastic/eui'; +import { EuiBadge, EuiButton, EuiCard, EuiIcon, EuiSpacer } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { INGESTION_METHOD_IDS } from '../../../../../common/constants'; + +import { getIngestionMethodIconType } from './utils'; + +export interface NewIndexCardProps { + isSelected?: boolean; + onSelect?: MouseEventHandler; + type: INGESTION_METHOD_IDS; +} + +export interface MethodCardOptions { + description: EuiCardProps['description']; + footer: Record; + icon: EuiIconProps['type']; + title: EuiCardProps['title']; +} + +const NO_DEVELOPMENT_LABEL = i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCard.noDevelopment.label', + { + defaultMessage: 'No development required', + } +); + +const METHOD_CARD_OPTIONS: Record = { + [INGESTION_METHOD_IDS.CRAWLER]: { + description: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCard.crawler.description', + { + defaultMessage: 'Discover, extract, index, and sync all of your website content', + } + ), + footer: { + buttonLabel: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCard.crawler.label', + { + defaultMessage: 'Use a web crawler', + } + ), + label: NO_DEVELOPMENT_LABEL, + }, + icon: getIngestionMethodIconType(INGESTION_METHOD_IDS.CRAWLER), + title: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.crawler.title', { + defaultMessage: 'Web crawler', + }), + }, + [INGESTION_METHOD_IDS.CONNECTOR]: { + description: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCard.connector.description', + { + defaultMessage: + 'Use the connector framework to quickly build connectors for custom data sources', + } + ), + footer: { + buttonLabel: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCard.connector.label', + { + defaultMessage: 'Use a connector', + } + ), + label: NO_DEVELOPMENT_LABEL, + }, + icon: getIngestionMethodIconType(INGESTION_METHOD_IDS.CONNECTOR), + title: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.connector.title', { + defaultMessage: 'Connector', + }), + }, + [INGESTION_METHOD_IDS.API]: { + description: i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.methodCard.api.description', + { + defaultMessage: 'Add documents programmatically by connecting with the API', + } + ), + footer: { + buttonLabel: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.api.label', { + defaultMessage: 'Use the API', + }), + label: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.api.footer', { + defaultMessage: 'Some development required', + }), + }, + icon: getIngestionMethodIconType(INGESTION_METHOD_IDS.API), + title: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.api.title', { + defaultMessage: 'API', + }), + }, +}; +export const NewIndexCard: React.FC = ({ onSelect, isSelected, type }) => { + if (!METHOD_CARD_OPTIONS[type]) { + return null; + } + const { icon, title, description, footer } = METHOD_CARD_OPTIONS[type]; + + return ( + } + title={title} + description={{description}} + footer={ + <> + {footer.label} + + + {footer.buttonLabel} + + + } + /> + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx index f1d120dee4e46..ac5bb69706146 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_created_toast.tsx @@ -5,37 +5,9 @@ * 2.0. */ -import React from 'react'; - -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; -import { APP_SEARCH_URL } from '../../../../../common/constants'; - import { flashSuccessToast } from '../../../shared/flash_messages'; -import { EuiButtonTo } from '../../../shared/react_router_helpers'; - -const SuccessToast = ( - <> - - {i18n.translate('xpack.enterpriseSearch.content.new_index.successToast.description', { - defaultMessage: - 'You can use App Search engines to build a search experience for your new Elasticsearch index.', - })} - - - - - - {i18n.translate('xpack.enterpriseSearch.content.new_index.successToast.button.label', { - defaultMessage: 'Create an engine', - })} - - - - -); export function flashIndexCreatedToast(): void { flashSuccessToast( @@ -44,7 +16,6 @@ export function flashIndexCreatedToast(): void { }), { iconType: 'cheer', - text: SuccessToast, } ); } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_router.tsx new file mode 100644 index 0000000000000..2388ab7ffa407 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_index_router.tsx @@ -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 React from 'react'; +import { Switch } from 'react-router-dom'; + +import { Route } from '@kbn/shared-ux-router'; + +import { + NEW_INDEX_PATH, + NEW_INDEX_SELECT_CONNECTOR_PATH, + NEW_INDEX_METHOD_PATH, +} from '../../routes'; + +import { NewIndex } from './new_index'; +import { NewSearchIndexPage } from './new_search_index_page'; +import { SelectConnector } from './select_connector/select_connector'; + +export const NewIndexRouter: React.FC = () => { + return ( + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_page.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_page.tsx new file mode 100644 index 0000000000000..756875c9811cb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_page.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 from 'react'; + +import { useLocation, useParams } from 'react-router-dom'; + +import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { INGESTION_METHOD_IDS } from '../../../../../common/constants'; +import { parseQueryParams } from '../../../shared/query_params'; + +import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; +import { CONNECTORS } from '../search_index/connector/constants'; +import { baseBreadcrumbs } from '../search_indices'; + +import { MethodApi } from './method_api/method_api'; +import { MethodConnector } from './method_connector/method_connector'; +import { MethodCrawler } from './method_crawler/method_crawler'; +import { getIngestionMethodIconType } from './utils'; + +function getTitle(method: string, serviceType: string): string { + switch (method) { + case INGESTION_METHOD_IDS.API: + return i18n.translate('xpack.enterpriseSearch.content.new_index.apiTitle', { + defaultMessage: 'New search index', + }); + case INGESTION_METHOD_IDS.CONNECTOR: { + const connector = + Boolean(serviceType) && CONNECTORS.find((item) => item.serviceType === serviceType); + return connector + ? i18n.translate('xpack.enterpriseSearch.content.new_index.connectorTitleWithServiceType', { + defaultMessage: 'New {name} search index', + values: { + name: connector.name, + }, + }) + : i18n.translate('xpack.enterpriseSearch.content.new_index.connectorTitle', { + defaultMessage: 'New connector search index', + }); + } + case INGESTION_METHOD_IDS.CRAWLER: + return i18n.translate('xpack.enterpriseSearch.content.new_index.crawlerTitle', { + defaultMessage: 'New web crawler search index', + }); + default: + return i18n.translate('xpack.enterpriseSearch.content.new_index.genericTitle', { + defaultMessage: 'New search index', + }); + } +} + +function getDescription(method: string, serviceType: string): string { + switch (method) { + case INGESTION_METHOD_IDS.API: + return i18n.translate('xpack.enterpriseSearch.content.new_index.apiDescription', { + defaultMessage: 'A search index stores your data.', + }); + case INGESTION_METHOD_IDS.CONNECTOR: { + const connector = + Boolean(serviceType) && CONNECTORS.find((item) => item.serviceType === serviceType); + return connector + ? i18n.translate( + 'xpack.enterpriseSearch.content.new_index.connectorDescriptionWithServiceType', + { + defaultMessage: 'A search index stores the data for your {name} connector.', + values: { + name: connector.name, + }, + } + ) + : i18n.translate('xpack.enterpriseSearch.content.new_index.connectorDescription', { + defaultMessage: 'A search index stores the data for your connector.', + }); + } + case INGESTION_METHOD_IDS.CRAWLER: + return i18n.translate('xpack.enterpriseSearch.content.new_index.crawlerDescription', { + defaultMessage: 'A search index stores the data for your web crawler.', + }); + default: + return i18n.translate('xpack.enterpriseSearch.content.new_index.defaultDescription', { + defaultMessage: 'A search index stores your data.', + }); + } +} + +export const NewSearchIndexPage: React.FC = () => { + const type = decodeURIComponent(useParams<{ type: string }>().type); + const { search } = useLocation(); + const { service_type: inputServiceType } = parseQueryParams(search); + const serviceType = Array.isArray(inputServiceType) + ? inputServiceType[0] + : inputServiceType || ''; + + return ( + + + + + {getTitle(type, serviceType)} + + ), + }} + > + { + <> + {type === INGESTION_METHOD_IDS.CRAWLER && } + {type === INGESTION_METHOD_IDS.API && } + {type === INGESTION_METHOD_IDS.CONNECTOR && } + + } + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.test.tsx index 2626074285a77..12fc021a0ae11 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.test.tsx @@ -20,7 +20,6 @@ import { describe('NewSearchIndexTemplate', () => { const mockProps: NewSearchIndexTemplateProps = { onSubmit: jest.fn(), - title: 'Index using the API', type: 'api', }; @@ -35,13 +34,9 @@ describe('NewSearchIndexTemplate', () => { setMockActions({ makeRequest: jest.fn(), setLanguageSelectValue: jest.fn() }); }); - it('renders children', () => { - const wrapper = shallow( - -
    - - ); + it('renders', () => { + const wrapper = shallow(); - expect(wrapper.find('[data-test-subj="ChildComponent"]')).toHaveLength(1); + expect(wrapper.find('EuiForm')).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx index 8142a26ceff93..c7b65b28f0a99 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_template.tsx @@ -16,16 +16,19 @@ import { EuiFlexItem, EuiForm, EuiFormRow, - EuiHorizontalRule, EuiLink, - EuiPanel, EuiSelect, EuiSpacer, EuiText, - EuiTitle, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; +import { INGESTION_METHOD_IDS } from '../../../../../common/constants'; + +import { BACK_BUTTON_LABEL } from '../../../shared/constants'; +import { docLinks } from '../../../shared/doc_links'; + import { SUPPORTED_LANGUAGES } from './constants'; import { NewSearchIndexLogic } from './new_search_index_logic'; import { LanguageForOptimization } from './types'; @@ -37,19 +40,15 @@ export interface Props { error?: string | React.ReactNode; onNameChange?(name: string): void; onSubmit(name: string, language: LanguageForOptimization): void; - title: React.ReactNode; type: string; } export const NewSearchIndexTemplate: React.FC = ({ buttonLoading, - children, disabled, - docsUrl, error, onNameChange, onSubmit, - title, type, }) => { const { @@ -102,26 +101,21 @@ export const NewSearchIndexTemplate: React.FC = ({ }; return ( - + <> { event.preventDefault(); onSubmit(fullIndexName, language); }} > - - -

    {title}

    -
    -
    = ({ + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreIndices.linkText', + { + defaultMessage: 'Learn more about indices', + } + )} + + + + {type === INGESTION_METHOD_IDS.CONNECTOR && ( + + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreConnectors.linkText', + { + defaultMessage: 'Learn more about connectors', + } + )} + + + )} + {type === INGESTION_METHOD_IDS.CRAWLER && ( + + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreCrawler.linkText', + { + defaultMessage: 'Learn more about the Elastic web crawler', + } + )} + + + )} + {type === INGESTION_METHOD_IDS.API && ( + + + {i18n.translate( + 'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreApis.linkText', + { + defaultMessage: 'Learn more about ingestion APIs', + } + )} + + + )} + + + + history.back()} + > + {BACK_BUTTON_LABEL} + + = ({ )} - {!!docsUrl && ( - - - {i18n.translate( - 'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText', - { - defaultMessage: 'View the documentation', - } - )} - - - )}
    - - {children} -
    + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/connector_checkable.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx similarity index 70% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/connector_checkable.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx index 0fc03da850596..0019b843678de 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/connector_checkable.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx @@ -8,38 +8,40 @@ import React from 'react'; import { + EuiBadge, EuiCheckableCard, EuiCheckableCardProps, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, - EuiSpacer, EuiText, EuiTitle, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { NATIVE_CONNECTOR_ICONS } from '../../../../../../assets/source_icons/native_connector_icons'; +import { i18n } from '@kbn/i18n'; -import './connector_checkable.scss'; +import { BETA_LABEL } from '../../../../shared/constants'; export type ConnectorCheckableProps = Omit< EuiCheckableCardProps, 'id' | 'label' | 'name' | 'value' > & { - documentationUrl: string; + documentationUrl: string | undefined; + icon: string; + isBeta: boolean; name: string; serviceType: string; }; export const ConnectorCheckable: React.FC = ({ documentationUrl, + icon, + isBeta, name, serviceType, ...props }) => { - const icon = NATIVE_CONNECTOR_ICONS[serviceType]; return ( = ({ )} - {name} +

    {name}

    @@ -63,17 +65,7 @@ export const ConnectorCheckable: React.FC = ({ name={name} value={serviceType} > -
    - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description', - { - defaultMessage: 'Search over your {name} content with Enterprise Search.', - values: { name }, - } - )} - - + {documentationUrl && ( {i18n.translate( 'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel', @@ -82,7 +74,12 @@ export const ConnectorCheckable: React.FC = ({ } )} -
    + )} + {isBeta && ( + + {BETA_LABEL} + + )}
    ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx new file mode 100644 index 0000000000000..c25c2a54b0082 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.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, { useState } from 'react'; + +import { useLocation } from 'react-router-dom'; + +import { + EuiButton, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormFieldset, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { INGESTION_METHOD_IDS } from '../../../../../../common/constants'; + +import { BACK_BUTTON_LABEL, CONTINUE_BUTTON_LABEL } from '../../../../shared/constants'; +import { generateEncodedPath } from '../../../../shared/encode_path_params'; + +import { KibanaLogic } from '../../../../shared/kibana'; +import { parseQueryParams } from '../../../../shared/query_params'; + +import { NEW_INDEX_METHOD_PATH, NEW_INDEX_PATH } from '../../../routes'; +import { EnterpriseSearchContentPageTemplate } from '../../layout'; + +import { CONNECTORS } from '../../search_index/connector/constants'; + +import { baseBreadcrumbs } from '../../search_indices'; + +import { ConnectorCheckable } from './connector_checkable'; + +export const SelectConnector: React.FC = () => { + const { search } = useLocation(); + const { service_type: serviceType } = parseQueryParams(search); + const [selectedConnector, setSelectedConnector] = useState( + Array.isArray(serviceType) ? serviceType[0] : serviceType ?? null + ); + + return ( + + { + event.preventDefault(); + KibanaLogic.values.navigateToUrl( + `${generateEncodedPath(NEW_INDEX_METHOD_PATH, { + type: INGESTION_METHOD_IDS.CONNECTOR, + })}?service_type=${selectedConnector}` + ); + }} + > + +

    + + ), + }} + > + + + {CONNECTORS.map((connector) => ( + + { + setSelectedConnector(connector.serviceType); + }} + documentationUrl={connector.docsUrl} + checked={selectedConnector === connector.serviceType} + /> + + ))} + + + + + + KibanaLogic.values.navigateToUrl(NEW_INDEX_PATH)} + > + {BACK_BUTTON_LABEL} + + + + + + + {CONTINUE_BUTTON_LABEL} + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/utils.ts index 8568301c4a597..e4bdceb5dcbdf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/utils.ts @@ -5,6 +5,12 @@ * 2.0. */ +import { INGESTION_METHOD_IDS } from '../../../../../common/constants'; + +import connectorLogo from '../../../../assets/source_icons/connector.svg'; + +import crawlerLogo from '../../../../assets/source_icons/crawler.svg'; + import { UNIVERSAL_LANGUAGE_VALUE } from './constants'; import { LanguageForOptimization } from './types'; @@ -12,3 +18,14 @@ import { LanguageForOptimization } from './types'; // but we can't use null as the value for an EuiSelectOption export const getLanguageForOptimization = (language: string): LanguageForOptimization => language === UNIVERSAL_LANGUAGE_VALUE ? null : language; + +export function getIngestionMethodIconType(type: string): string { + switch (type) { + case INGESTION_METHOD_IDS.CRAWLER: + return crawlerLogo; + case INGESTION_METHOD_IDS.CONNECTOR: + return connectorLogo; + default: + return 'consoleApp'; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx index 61f090f8e20ad..059fa91c8ac0b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/create_engine_menu_item.tsx @@ -11,11 +11,11 @@ import { EuiContextMenuItem, EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic import { i18n } from '@kbn/i18n'; -import { APP_SEARCH_PLUGIN } from '../../../../../../../common/constants'; -import { ENGINE_CREATION_PATH } from '../../../../../app_search/routes'; +import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../../../common/constants'; import { ESINDEX_QUERY_PARAMETER } from '../../../../../shared/constants'; import { generateEncodedPath } from '../../../../../shared/encode_path_params'; import { KibanaLogic } from '../../../../../shared/kibana'; +import { ENGINE_CREATION_PATH } from '../../../../routes'; export interface CreateEngineMenuItemProps { indexName?: string; @@ -28,12 +28,15 @@ export const CreateEngineMenuItem: React.FC = ({ ingestionMethod, isHiddenIndex, }) => { - const engineCreationPath = !indexName - ? `${APP_SEARCH_PLUGIN.URL}${ENGINE_CREATION_PATH}` - : generateEncodedPath(`${APP_SEARCH_PLUGIN.URL}${ENGINE_CREATION_PATH}?:indexKey=:indexName`, { - indexKey: ESINDEX_QUERY_PARAMETER, - indexName, - }); + const searchApplicationCreationPath = !indexName + ? `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINE_CREATION_PATH}` + : generateEncodedPath( + `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINE_CREATION_PATH}?:indexKey=:indexName`, + { + indexKey: ESINDEX_QUERY_PARAMETER, + indexName, + } + ); return ( @@ -43,7 +46,7 @@ export const CreateEngineMenuItem: React.FC = ({ size="s" icon="plusInCircle" onClick={() => { - KibanaLogic.values.navigateToUrl(engineCreationPath, { + KibanaLogic.values.navigateToUrl(searchApplicationCreationPath, { shouldNotCreateHref: true, }); }} @@ -51,9 +54,12 @@ export const CreateEngineMenuItem: React.FC = ({ >

    - {i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.createEngine', { - defaultMessage: 'Create an App Search engine', - })} + {i18n.translate( + 'xpack.enterpriseSearch.content.index.searchApplication.createSearchApplication', + { + defaultMessage: 'Create a Search Application', + } + )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx index 47584d3021ada..611c63c276750 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/search_engines_popover.tsx @@ -20,9 +20,11 @@ import { import { i18n } from '@kbn/i18n'; -import { APP_SEARCH_PLUGIN } from '../../../../../../../common/constants'; +import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../../../common/constants'; import { KibanaLogic } from '../../../../../shared/kibana'; +import { ENGINES_PATH } from '../../../../routes'; + import { CreateEngineMenuItem } from './create_engine_menu_item'; import { SearchEnginesPopoverLogic } from './search_engines_popover_logic'; @@ -52,7 +54,7 @@ export const SearchEnginesPopover: React.FC = ({ onClick={toggleSearchEnginesPopover} > {i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.label', { - defaultMessage: 'Search engines', + defaultMessage: 'Search Applications', })} } @@ -64,15 +66,18 @@ export const SearchEnginesPopover: React.FC = ({ data-telemetry-id={`entSearchContent-${ingestionMethod}-header-searchEngines-viewEngines`} icon="eye" onClick={() => { - KibanaLogic.values.navigateToUrl(APP_SEARCH_PLUGIN.URL, { - shouldNotCreateHref: true, - }); + KibanaLogic.values.navigateToUrl( + ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + ENGINES_PATH, + { + shouldNotCreateHref: true, + } + ); }} >

    {i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.viewEngines', { - defaultMessage: 'View App Search engines', + defaultMessage: 'View Search Applications', })}

    @@ -82,7 +87,7 @@ export const SearchEnginesPopover: React.FC = ({ content={i18n.translate( 'xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip', { - defaultMessage: 'You cannot create engines from hidden indices.', + defaultMessage: 'You cannot create search applications from hidden indices.', } )} > diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx index 2e53d7651885c..0abd1ba5b4afe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx @@ -43,6 +43,7 @@ import { SearchIndexTabId } from '../search_index'; import { ApiKeyConfig } from './api_key_configuration'; import { ConnectorConfigurationConfig } from './connector_configuration_config'; import { ConnectorNameAndDescription } from './connector_name_and_description/connector_name_and_description'; +import { CONNECTORS } from './constants'; import { NativeConnectorConfiguration } from './native_connector_configuration/native_connector_configuration'; export const ConnectorConfiguration: React.FC = () => { @@ -59,6 +60,9 @@ export const ConnectorConfiguration: React.FC = () => { } const hasApiKey = !!(index.connector.api_key_id ?? apiKeyData); + const docsUrl = CONNECTORS.find( + ({ serviceType }) => serviceType === index.connector.service_type + )?.docsUrl; return ( <> @@ -120,7 +124,7 @@ export const ConnectorConfiguration: React.FC = () => { values={{ link: ( @@ -141,7 +145,7 @@ export const ConnectorConfiguration: React.FC = () => { values={{ link: ( @@ -352,7 +356,7 @@ export const ConnectorConfiguration: React.FC = () => { {i18n.translate( @@ -378,7 +382,7 @@ export const ConnectorConfiguration: React.FC = () => { {i18n.translate( @@ -422,6 +426,31 @@ export const ConnectorConfiguration: React.FC = () => { )} + {docsUrl && ( + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.configurationConnector.support.dockerDeploy.label', + { + defaultMessage: 'Deploy with Docker', + } + )} + + + )} + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.configurationConnector.support.deploy.label', + { + defaultMessage: 'Deploy without Docker', + } + )} + + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_field.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_field.tsx index 1030cbdcfa5d9..e154785a33531 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_field.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_field.tsx @@ -95,7 +95,14 @@ export const ConnectorConfigurationField: React.FC + +

    {label}

    + + } + > {textarea}
    ) : ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_form.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_form.tsx index 2ef04c8e2c18b..2c40eb0beafa4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_form.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_form.tsx @@ -17,22 +17,19 @@ import { EuiForm, EuiFormRow, EuiPanel, + EuiSpacer, EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Status } from '../../../../../../common/types/api'; -import { DependencyLookup, DisplayType } from '../../../../../../common/types/connectors'; +import { DisplayType } from '../../../../../../common/types/connectors'; import { ConnectorConfigurationApiLogic } from '../../../api/connector/update_connector_configuration_api_logic'; import { ConnectorConfigurationField } from './connector_configuration_field'; -import { - ConfigEntry, - ConnectorConfigurationLogic, - dependenciesSatisfied, -} from './connector_configuration_logic'; +import { ConnectorConfigurationLogic } from './connector_configuration_logic'; export const ConnectorConfigurationForm = () => { const { status } = useValues(ConnectorConfigurationApiLogic); @@ -40,14 +37,6 @@ export const ConnectorConfigurationForm = () => { const { localConfigView } = useValues(ConnectorConfigurationLogic); const { saveConfig, setIsEditing } = useActions(ConnectorConfigurationLogic); - const dependencyLookup: DependencyLookup = localConfigView.reduce( - (prev: Record, configEntry: ConfigEntry) => ({ - ...prev, - [configEntry.key]: configEntry.value, - }), - {} - ); - return ( { @@ -56,17 +45,16 @@ export const ConnectorConfigurationForm = () => { }} component="form" > - {localConfigView.map((configEntry) => { + {localConfigView.map((configEntry, index) => { const { default_value: defaultValue, depends_on: dependencies, key, display, label, + sensitive, tooltip, } = configEntry; - // toggle label goes next to the element, not in the row - const hasDependencies = dependencies.length > 0; const helpText = defaultValue ? i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.config.defaultValue', @@ -76,26 +64,41 @@ export const ConnectorConfigurationForm = () => { } ) : ''; + // toggle and sensitive textarea labels go next to the element, not in the row const rowLabel = - display !== DisplayType.TOGGLE ? ( + display === DisplayType.TOGGLE || (display === DisplayType.TEXTAREA && sensitive) ? ( + <> + ) : (

    {label}

    - ) : ( - <> ); - return hasDependencies ? ( - dependenciesSatisfied(dependencies, dependencyLookup) ? ( - - - - - - ) : ( - <> - ) - ) : ( + if (dependencies.length > 0) { + // dynamic spacing without CSS + const previousField = localConfigView[index - 1]; + const nextField = localConfigView[index + 1]; + + const topSpacing = + !previousField || previousField.depends_on.length <= 0 ? : <>; + + const bottomSpacing = + !nextField || nextField.depends_on.length <= 0 ? : <>; + + return ( + <> + {topSpacing} + + + + + + {bottomSpacing} + + ); + } + + return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts index 9ff1fc60db168..f87d73b882ecd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.test.ts @@ -61,6 +61,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'oldBar', }, }, @@ -79,6 +80,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'oldBar', }, }, @@ -94,6 +96,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'oldBar', }, ], @@ -111,6 +114,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, }); @@ -127,6 +131,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, }, @@ -142,13 +147,14 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, ], }); }); describe('setLocalConfigEntry', () => { - it('should set local config entry and sort keys', () => { + it('should set local config entry, and sort and filter keys', () => { ConnectorConfigurationLogic.actions.setConfigState({ bar: { default_value: '', @@ -160,6 +166,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'foofoo', }, password: { @@ -172,8 +179,80 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, + restricted: { + default_value: '', + depends_on: [], + display: DisplayType.TEXTBOX, + label: 'Restricted', + options: [], + order: 3, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: ['advanced'], + value: 'I am restricted', + }, + shownDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'foofoo' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 4, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (one dependency)', + }, + shownDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'foofoo' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 1', + options: [], + order: 5, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (multiple dependencies)', + }, + hiddenDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'fafa' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 2', + options: [], + order: 6, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (one dependency)', + }, + hiddenDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'fafa' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 7, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (multiple dependencies)', + }, }); ConnectorConfigurationLogic.actions.setLocalConfigState({ bar: { @@ -186,6 +265,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'foofoo', }, password: { @@ -198,8 +278,80 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, + restricted: { + default_value: '', + depends_on: [], + display: DisplayType.TEXTBOX, + label: 'Restricted', + options: [], + order: 3, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: ['advanced'], + value: 'I am restricted', + }, + shownDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'foofoo' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 4, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (one dependency)', + }, + shownDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'foofoo' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 1', + options: [], + order: 5, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (multiple dependencies)', + }, + hiddenDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'fafa' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 2', + options: [], + order: 6, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (one dependency)', + }, + hiddenDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'fafa' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 7, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (multiple dependencies)', + }, }); ConnectorConfigurationLogic.actions.setLocalConfigEntry({ default_value: '', @@ -212,6 +364,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'fafa', }); expect(ConnectorConfigurationLogic.values).toEqual({ @@ -227,6 +380,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'foofoo', }, password: { @@ -239,8 +393,80 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, + restricted: { + default_value: '', + depends_on: [], + display: DisplayType.TEXTBOX, + label: 'Restricted', + options: [], + order: 3, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: ['advanced'], + value: 'I am restricted', + }, + shownDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'foofoo' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 4, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (one dependency)', + }, + shownDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'foofoo' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 1', + options: [], + order: 5, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (multiple dependencies)', + }, + hiddenDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'fafa' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 2', + options: [], + order: 6, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (one dependency)', + }, + hiddenDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'fafa' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 7, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (multiple dependencies)', + }, }, configView: [ { @@ -254,6 +480,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'foofoo', }, { @@ -267,8 +494,40 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, + { + default_value: '', + depends_on: [{ field: 'bar', value: 'foofoo' }], + display: DisplayType.TEXTBOX, + key: 'shownDependent1', + label: 'Shown Dependent', + options: [], + order: 4, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (one dependency)', + }, + { + default_value: '', + depends_on: [ + { field: 'bar', value: 'foofoo' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + key: 'shownDependent2', + label: 'Shown Dependent 1', + options: [], + order: 5, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (multiple dependencies)', + }, ], localConfigState: { bar: { @@ -281,6 +540,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'fafa', }, password: { @@ -293,8 +553,80 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, + restricted: { + default_value: '', + depends_on: [], + display: DisplayType.TEXTBOX, + label: 'Restricted', + options: [], + order: 3, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: ['advanced'], + value: 'I am restricted', + }, + shownDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'foofoo' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 4, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (one dependency)', + }, + shownDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'foofoo' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 1', + options: [], + order: 5, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should appear (multiple dependencies)', + }, + hiddenDependent1: { + default_value: '', + depends_on: [{ field: 'bar', value: 'fafa' }], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent 2', + options: [], + order: 6, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (one dependency)', + }, + hiddenDependent2: { + default_value: '', + depends_on: [ + { field: 'bar', value: 'fafa' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + label: 'Shown Dependent', + options: [], + order: 7, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (multiple dependencies)', + }, }, localConfigView: [ { @@ -308,6 +640,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'fafa', }, { @@ -321,8 +654,40 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'fourthBar', }, + { + default_value: '', + depends_on: [{ field: 'bar', value: 'fafa' }], + display: DisplayType.TEXTBOX, + key: 'hiddenDependent1', + label: 'Shown Dependent 2', + options: [], + order: 6, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (one dependency)', + }, + { + default_value: '', + depends_on: [ + { field: 'bar', value: 'fafa' }, + { field: 'password', value: 'fourthBar' }, + ], + display: DisplayType.TEXTBOX, + key: 'hiddenDependent2', + label: 'Shown Dependent', + options: [], + order: 7, + required: false, + sensitive: true, + tooltip: '', + ui_restrictions: [], + value: 'I should hide (multiple dependencies)', + }, ], }); }); @@ -345,6 +710,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, ], @@ -381,6 +747,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, ], @@ -402,6 +769,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: false, tooltip: '', + ui_restrictions: [], value: 'barbar', }, ], @@ -424,6 +792,7 @@ describe('ConnectorConfigurationLogic', () => { required: false, sensitive: true, tooltip: '', + ui_restrictions: [], value: 'Barbara', }, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts index 02ebb7888b7b8..861ab90079229 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_logic.ts @@ -66,17 +66,23 @@ export interface ConfigEntry { required: boolean; sensitive: boolean; tooltip: string; + ui_restrictions: string[]; value: string | number | boolean | null; } /** * - * Sorts the connector configuration by specified order (if present) + * Sorts and filters the connector configuration + * + * Sorting is done by specified order (if present) * otherwise by alphabetic order of keys * + * Filtering is done on any fields with ui_restrictions + * or that have not had their dependencies met + * */ -function sortConnectorConfiguration(config: ConnectorConfiguration): ConfigEntry[] { - return Object.keys(config) +function sortAndFilterConnectorConfiguration(config: ConnectorConfiguration): ConfigEntry[] { + const sortedConfig = Object.keys(config) .map( (key) => ({ @@ -97,6 +103,20 @@ function sortConnectorConfiguration(config: ConnectorConfiguration): ConfigEntry } return a.key.localeCompare(b.key); }); + + const dependencyLookup: DependencyLookup = sortedConfig.reduce( + (prev: Record, configEntry: ConfigEntry) => ({ + ...prev, + [configEntry.key]: configEntry.value, + }), + {} + ); + + return sortedConfig.filter( + (configEntry) => + configEntry.ui_restrictions.length <= 0 && + dependenciesSatisfied(configEntry.depends_on, dependencyLookup) + ); } export function ensureStringType(value: string | number | boolean | null): string { @@ -245,6 +265,8 @@ export const ConnectorConfigurationLogic = kea< required, sensitive, tooltip, + // eslint-disable-next-line @typescript-eslint/naming-convention + ui_restrictions, value, } ) => ({ @@ -259,6 +281,7 @@ export const ConnectorConfigurationLogic = kea< required, sensitive, tooltip, + ui_restrictions, value, }, }), @@ -276,11 +299,11 @@ export const ConnectorConfigurationLogic = kea< selectors: ({ selectors }) => ({ configView: [ () => [selectors.configState], - (configState: ConnectorConfiguration) => sortConnectorConfiguration(configState), + (configState: ConnectorConfiguration) => sortAndFilterConnectorConfiguration(configState), ], localConfigView: [ () => [selectors.localConfigState], - (configState) => sortConnectorConfiguration(configState), + (configState) => sortAndFilterConnectorConfiguration(configState), ], }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts index 11a6db518281c..b6269bb91aa87 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts @@ -5,28 +5,84 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; +import { CONNECTOR_DEFINITIONS } from '../../../../../../common/connectors/connectors'; import { docLinks } from '../../../../shared/doc_links'; +import { CONNECTOR_ICONS } from '../../../../shared/icons/connector_icons'; -import { NativeConnector } from './types'; +import { ConnectorClientSideDefinition } from './types'; -export const NATIVE_CONNECTORS: NativeConnector[] = [ - { +export const CONNECTORS_DICT: Record = { + azure_blob_storage: { + docsUrl: docLinks.connectorsAzureBlobStorage, + externalAuthDocsUrl: 'https://learn.microsoft.com/azure/storage/common/authorize-data-access', + externalDocsUrl: 'https://learn.microsoft.com/azure/storage/blobs/', + icon: CONNECTOR_ICONS.azure_blob_storage, + }, + custom: { + docsUrl: docLinks.connectors, + externalAuthDocsUrl: '', + externalDocsUrl: '', + icon: CONNECTOR_ICONS.custom, + }, + google_cloud_storage: { + docsUrl: docLinks.connectorsGoogleCloudStorage, + externalAuthDocsUrl: 'https://cloud.google.com/storage/docs/authentication', + externalDocsUrl: 'https://cloud.google.com/storage/docs', + icon: CONNECTOR_ICONS.google_cloud_storage, + }, + mongodb: { docsUrl: docLinks.connectorsMongoDB, externalAuthDocsUrl: 'https://www.mongodb.com/docs/atlas/app-services/authentication/', externalDocsUrl: 'https://www.mongodb.com/docs/', - name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.mongodb.name', { - defaultMessage: 'MongoDB', - }), - serviceType: 'mongodb', + icon: CONNECTOR_ICONS.mongodb, }, - { + mssql: { + docsUrl: docLinks.connectorsMicrosoftSQL, + externalAuthDocsUrl: + 'https://learn.microsoft.com/sql/relational-databases/security/authentication-access/getting-started-with-database-engine-permissions', + externalDocsUrl: 'https://learn.microsoft.com/sql/', + icon: CONNECTOR_ICONS.microsoft_sql, + }, + mysql: { docsUrl: docLinks.connectorsMySQL, externalDocsUrl: 'https://dev.mysql.com/doc/', - name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.mysql.name', { - defaultMessage: 'MySQL', - }), - serviceType: 'mysql', + icon: CONNECTOR_ICONS.mysql, + }, + network_drive: { + docsUrl: docLinks.connectorsNetworkDrive, + externalAuthDocsUrl: '', + externalDocsUrl: '', + icon: CONNECTOR_ICONS.network_drive, + }, + oracle: { + docsUrl: docLinks.connectorsOracle, + externalAuthDocsUrl: + 'https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/index.html', + externalDocsUrl: 'https://docs.oracle.com/database/oracle/oracle-database/', + icon: CONNECTOR_ICONS.oracle, }, -]; + postgresql: { + docsUrl: docLinks.connectorsPostgreSQL, + externalAuthDocsUrl: 'https://www.postgresql.org/docs/15/auth-methods.html', + externalDocsUrl: 'https://www.postgresql.org/docs/', + icon: CONNECTOR_ICONS.postgresql, + }, + s3: { + docsUrl: docLinks.connectorsS3, + externalAuthDocsUrl: 'https://docs.aws.amazon.com/s3/index.html', + externalDocsUrl: '', + icon: CONNECTOR_ICONS.amazon_s3, + }, +}; + +export const CONNECTORS = CONNECTOR_DEFINITIONS.map((connector) => ({ + ...connector, + ...(connector.serviceType && CONNECTORS_DICT[connector.serviceType] + ? CONNECTORS_DICT[connector.serviceType] + : CONNECTORS_DICT.custom), +})); + +export const CUSTOM_CONNECTORS = CONNECTORS.filter(({ isNative }) => !isNative); + +export const NATIVE_CONNECTORS = CONNECTORS.filter(({ isNative }) => isNative); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx index 58be80f2caa15..6eb991df1cdf9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/custom_connector_configuration_config.tsx @@ -18,16 +18,16 @@ export const ConnectorConfigurationConfig: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.config.connectorClientLink', - { defaultMessage: 'example connector client' } + { defaultMessage: 'connectors' } )}
    ), @@ -56,7 +56,7 @@ export const ConnectorConfigurationConfig: React.FC = () => {
    ), issuesLink: ( - + {i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.config.issuesLink', { defaultMessage: 'issue' } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx index 4deedd6768a1a..c4c4af581ef5a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx @@ -23,7 +23,6 @@ import { import { i18n } from '@kbn/i18n'; -import { NATIVE_CONNECTOR_ICONS } from '../../../../../../assets/source_icons/native_connector_icons'; import { docLinks } from '../../../../../shared/doc_links'; import { hasConfiguredConfiguration } from '../../../../utils/has_configured_configuration'; @@ -55,8 +54,8 @@ export const NativeConnectorConfiguration: React.FC = () => { const hasConfigured = hasConfiguredConfiguration(index.connector.configuration); const hasConfiguredAdvanced = index.connector.last_synced || index.connector.scheduling.enabled; const hasResearched = hasDescription || hasConfigured || hasConfiguredAdvanced; + const icon = nativeConnector.icon; - const icon = NATIVE_CONNECTOR_ICONS[nativeConnector.serviceType]; return ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx index 1cecf7af2fc79..c0c47545860aa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration_config.tsx @@ -16,10 +16,10 @@ import { ConnectorStatus } from '../../../../../../../common/types/connectors'; import { docLinks } from '../../../../../shared/doc_links'; import { ConnectorConfigurationConfig } from '../connector_configuration_config'; -import { NativeConnector } from '../types'; +import { ConnectorDefinition } from '../types'; interface NativeConnectorConfigurationConfigProps { - nativeConnector: NativeConnector; + nativeConnector: ConnectorDefinition; status: ConnectorStatus; } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/research_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/research_configuration.tsx index 8ba06ce5e50e3..b76d89396384e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/research_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/research_configuration.tsx @@ -11,10 +11,10 @@ import { EuiText, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic import { i18n } from '@kbn/i18n'; -import { NativeConnector } from '../types'; +import { ConnectorDefinition } from '../types'; interface ResearchConfigurationProps { - nativeConnector: NativeConnector; + nativeConnector: ConnectorDefinition; } export const ResearchConfiguration: React.FC = ({ nativeConnector, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/connector_checkable.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/connector_checkable.scss deleted file mode 100644 index e8f65d5846e91..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/connector_checkable.scss +++ /dev/null @@ -1,9 +0,0 @@ -.connectorCheckable { - .euiRadio { - margin-top: 4px; - } - - .connectorCheckableContent { - margin-left: 20px; - } -} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/select_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/select_connector.tsx deleted file mode 100644 index 219ad30282121..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/select_connector.tsx +++ /dev/null @@ -1,187 +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, { useEffect } from 'react'; - -import { useActions, useValues } from 'kea'; - -import { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiFormFieldset, - EuiLink, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { FormattedMessage } from '@kbn/i18n-react'; - -import { Status } from '../../../../../../../common/types/api'; -import { docLinks } from '../../../../../shared/doc_links'; -import { generateEncodedPath } from '../../../../../shared/encode_path_params'; - -import { flashSuccessToast } from '../../../../../shared/flash_messages'; -import { KibanaLogic } from '../../../../../shared/kibana'; -import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; -import { CachedFetchIndexApiLogic } from '../../../../api/index/cached_fetch_index_api_logic'; -import { NEW_INDEX_PATH, SEARCH_INDEX_TAB_PATH } from '../../../../routes'; -import { isConnectorIndex } from '../../../../utils/indices'; -import { EnterpriseSearchContentPageTemplate } from '../../../layout'; -import { baseBreadcrumbs } from '../../../search_indices'; -import { IndexNameLogic } from '../../index_name_logic'; - -import { NATIVE_CONNECTORS } from '../constants'; - -import { ConnectorCheckable } from './connector_checkable'; -import { SelectConnectorLogic } from './select_connector_logic'; - -export const SelectConnector: React.FC = () => { - const { indexData, status: indexApiStatus } = useValues(CachedFetchIndexApiLogic); - const { selectedNativeConnector } = useValues(SelectConnectorLogic); - const { saveNativeConnector, setSelectedConnector } = useActions(SelectConnectorLogic); - - const { indexName } = useValues(IndexNameLogic); - - useEffect(() => { - if (isConnectorIndex(indexData) && indexData.connector.service_type) { - flashSuccessToast( - i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.successToast.title', - { - defaultMessage: 'Your index will now use the {connectorName} native connector.', - values: { - connectorName: NATIVE_CONNECTORS.find( - (connector) => connector.serviceType === indexData.connector.service_type - )?.name, - }, - } - ) - ); - KibanaLogic.values.navigateToUrl( - generateEncodedPath(SEARCH_INDEX_TAB_PATH, { - indexName, - tabId: 'configuration', - }) - ); - } - }, [indexData]); - - return ( - -
    { - event.preventDefault(); - saveNativeConnector(); - }} - > - - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.title', - { - defaultMessage: 'Select a connector', - } - )} - - - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.description', - { - defaultMessage: - "Get started by selecting the connector you'd like to configure to extract, index and sync data from your data source into your newly created search index.", - } - )} -

    -
    - - ), - }} - > - - - {NATIVE_CONNECTORS.map((nativeConnector) => ( - - setSelectedConnector(nativeConnector)} - documentationUrl={nativeConnector.docsUrl} - checked={nativeConnector === selectedNativeConnector} - /> - - ))} - - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel', - { - defaultMessage: 'Select and configure', - } - )} - - - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel', - { - defaultMessage: 'build your own', - } - )} - - ), - workplaceSearchLink: ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel', - { - defaultMessage: 'View additional integrations in Workplace Search', - } - )} - - ), - }} - /> - -
    -
    -
    - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/select_connector_logic.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/select_connector_logic.tsx deleted file mode 100644 index fec06e823a4db..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/select_connector/select_connector_logic.tsx +++ /dev/null @@ -1,96 +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 { kea, MakeLogicType } from 'kea'; - -import { Actions } from '../../../../../shared/api_logic/create_api_logic'; -import { generateEncodedPath } from '../../../../../shared/encode_path_params'; - -import { KibanaLogic } from '../../../../../shared/kibana'; -import { - SetNativeConnectorArgs, - SetNativeConnectorLogic, - SetNativeConnectorResponse, -} from '../../../../api/connector/set_native_connector_api_logic'; - -import { CachedFetchIndexApiLogic } from '../../../../api/index/cached_fetch_index_api_logic'; -import { FetchIndexApiResponse } from '../../../../api/index/fetch_index_api_logic'; - -import { SEARCH_INDEX_TAB_PATH } from '../../../../routes'; -import { isConnectorIndex } from '../../../../utils/indices'; -import { NATIVE_CONNECTORS } from '../constants'; -import { NativeConnector } from '../types'; - -type SelectConnectorActions = Pick< - Actions, - 'apiSuccess' | 'makeRequest' -> & { - saveNativeConnector(): void; - setSelectedConnector(nativeConnector: NativeConnector): { - nativeConnector: NativeConnector; - }; -}; - -interface SelectConnectorValues { - index: FetchIndexApiResponse; - selectedNativeConnector: NativeConnector | null; -} - -export const SelectConnectorLogic = kea< - MakeLogicType ->({ - actions: { - saveNativeConnector: true, - setSelectedConnector: (nativeConnector) => ({ nativeConnector }), - }, - connect: { - actions: [SetNativeConnectorLogic, ['apiError', 'apiSuccess', 'makeRequest']], - values: [CachedFetchIndexApiLogic, ['indexData as index']], - }, - events: ({ actions, values }) => ({ - afterMount: () => { - if (isConnectorIndex(values.index)) { - const serviceType = values.index.connector.service_type; - const nativeConnector = NATIVE_CONNECTORS.find( - (connector) => connector.serviceType === serviceType - ); - if (nativeConnector) { - actions.setSelectedConnector(nativeConnector); - } - } - }, - }), - listeners: ({ actions, values }) => ({ - apiSuccess: () => { - CachedFetchIndexApiLogic.actions.makeRequest({ indexName: values.index.name }); - }, - saveNativeConnector: () => { - if (!isConnectorIndex(values.index) || values.selectedNativeConnector === null) { - KibanaLogic.values.navigateToUrl( - generateEncodedPath(SEARCH_INDEX_TAB_PATH, { - indexName: values.index.name, - tabId: 'configuration', - }) - ); - } else { - actions.makeRequest({ - connectorId: values.index.connector.id, - serviceType: values.selectedNativeConnector.serviceType, - }); - } - }, - }), - path: ['enterprise_search', 'content', 'select_connector'], - reducers: () => ({ - selectedNativeConnector: [ - null, - { - setSelectedConnector: (_, { nativeConnector }) => nativeConnector, - }, - ], - }), -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/types.ts index 7324e32c7eea6..68d990650a175 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/types.ts @@ -5,10 +5,13 @@ * 2.0. */ -export interface NativeConnector { - docsUrl: string; +import { ConnectorServerSideDefinition } from '../../../../../../common/connectors/connectors'; + +export interface ConnectorClientSideDefinition { + docsUrl?: string; externalAuthDocsUrl?: string; externalDocsUrl: string; - name: string; - serviceType: string; + icon: string; } + +export type ConnectorDefinition = ConnectorClientSideDefinition & ConnectorServerSideDefinition; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx index 40cdda3aaae32..365593c4699c3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/generate_api_key_panel.tsx @@ -9,7 +9,15 @@ import React, { useState } from 'react'; import { useActions, useValues } from 'kea'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiSwitch, EuiTitle } from '@elastic/eui'; +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiSwitch, + EuiTitle, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,7 +35,7 @@ import { OverviewLogic } from './overview.logic'; export const GenerateApiKeyPanel: React.FC = () => { const { apiKey, isGenerateModalOpen } = useValues(OverviewLogic); - const { indexName, ingestionMethod } = useValues(IndexViewLogic); + const { indexName, ingestionMethod, isHiddenIndex } = useValues(IndexViewLogic); const { closeGenerateModal } = useActions(OverviewLogic); const { defaultPipeline } = useValues(SettingsLogic); @@ -41,11 +49,29 @@ export const GenerateApiKeyPanel: React.FC = () => { - - - - - {indexName[0] !== '.' && ( + {isHiddenIndex ? ( + + {i18n.translate('xpack.enterpriseSearch.content.overview.emptyPrompt.body', { + defaultMessage: + 'We do not recommend adding documents to an externally managed index.', + })} +

    + } + title={ +

    + {i18n.translate('xpack.enterpriseSearch.content.overview.emptyPrompt.title', { + defaultMessage: 'Index managed externally', + })} +

    + } + /> + ) : ( + + + +

    {i18n.translate( @@ -54,45 +80,42 @@ export const GenerateApiKeyPanel: React.FC = () => { )}

    - )} -
    - - setOptimizedRequest(event.target.checked)} - label={i18n.translate( - 'xpack.enterpriseSearch.content.overview.optimizedRequest.label', - { defaultMessage: 'View Enterprise Search optimized request' } - )} - checked={optimizedRequest} - /> - - - - - - - - - - - -
    -
    - {indexName[0] !== '.' && ( - <> - - - - - - )} -
    +
    + + setOptimizedRequest(event.target.checked)} + label={i18n.translate( + 'xpack.enterpriseSearch.content.overview.optimizedRequest.label', + { defaultMessage: 'View Enterprise Search optimized request' } + )} + checked={optimizedRequest} + /> + + + + + + + + + + + +
    +
    + + + + + +
    + )}
    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts index 5e4256f92a97b..e440947cdfb5c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.test.ts @@ -51,6 +51,7 @@ const DEFAULT_VALUES = { ingestionStatus: IngestionStatus.CONNECTED, isCanceling: false, isConnectorIndex: false, + isHiddenIndex: false, isInitialLoading: false, isSyncing: false, isWaitingForSync: false, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts index b89e122f03c7f..cd33656062fdb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts @@ -77,6 +77,7 @@ export interface IndexViewValues { ingestionMethod: IngestionMethod; ingestionStatus: IngestionStatus; isCanceling: boolean; + isHiddenIndex: boolean; isInitialLoading: typeof CachedFetchIndexApiLogic.values.isInitialLoading; isSyncing: boolean; isWaitingForSync: boolean; @@ -234,6 +235,11 @@ export const IndexViewLogic = kea [selectors.indexData], (data: FetchIndexApiResponse | undefined) => isConnectorIndex(data), ], + isHiddenIndex: [ + () => [selectors.indexData], + (data: FetchIndexApiResponse | undefined) => + data?.hidden || (data?.name ?? '').startsWith('.'), + ], isSyncing: [ () => [selectors.indexData, selectors.syncStatus], (indexData: FetchIndexApiResponse | null, syncStatus: SyncStatus) => diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx index 445769211aca2..6b4e1c475f9fb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx @@ -19,11 +19,7 @@ import { i18n } from '@kbn/i18n'; import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; -import { - SEARCH_INDEX_PATH, - SEARCH_INDEX_SELECT_CONNECTOR_PATH, - SEARCH_INDEX_TAB_PATH, -} from '../../routes'; +import { SEARCH_INDEX_PATH, SEARCH_INDEX_TAB_PATH } from '../../routes'; import { isConnectorIndex, isCrawlerIndex } from '../../utils/indices'; import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; @@ -99,19 +95,6 @@ export const SearchIndex: React.FC = () => { } }, [isAppGuideActive, isWebsiteGuideActive, isDatabaseGuideActive, index?.count]); - useEffect(() => { - if ( - isConnectorIndex(index) && - index.name === indexName && - index.connector.is_native && - index.connector.service_type === null - ) { - KibanaLogic.values.navigateToUrl( - generateEncodedPath(SEARCH_INDEX_SELECT_CONNECTOR_PATH, { indexName }) - ); - } - }, [index]); - const ALL_INDICES_TABS: EuiTabbedContentTab[] = [ { content: , diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index_router.tsx index 128fb3ea8fc6e..16e3443de8271 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index_router.tsx @@ -15,12 +15,10 @@ import { Route } from '@kbn/shared-ux-router'; import { OLD_SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH, SEARCH_INDEX_PATH, - SEARCH_INDEX_SELECT_CONNECTOR_PATH, SEARCH_INDEX_TAB_DETAIL_PATH, SEARCH_INDEX_TAB_PATH, } from '../../routes'; -import { SelectConnector } from './connector/select_connector/select_connector'; import { IndexNameLogic } from './index_name_logic'; import { IndexViewLogic } from './index_view_logic'; import { SearchIndex } from './search_index'; @@ -48,9 +46,6 @@ export const SearchIndexRouter: React.FC = () => { - - - diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/documents_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/documents_panel.tsx index 5323620776026..27b18d48b1743 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/documents_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/documents_panel.tsx @@ -45,7 +45,23 @@ export const SyncJobDocumentsPanel: React.FC = (sync name: i18n.translate('xpack.enterpriseSearch.content.index.syncJobs.documents.volume', { defaultMessage: 'Volume', }), - render: (volume: number) => new ByteSizeValue(volume).toString(), + render: (volume: number) => + volume < 1 + ? i18n.translate( + 'xpack.enterpriseSearch.content.index.syncJobs.documents.volume.lessThanOneMBLabel', + { + defaultMessage: 'Less than 1mb', + } + ) + : i18n.translate( + 'xpack.enterpriseSearch.content.index.syncJobs.documents.volume.aboutLabel', + { + defaultMessage: 'About {volume}', + values: { + volume: new ByteSizeValue(volume * 1024 * 1024).toString(), + }, + } + ), }, ]; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx index 90e1da7c26eb2..38875821aeccf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx @@ -80,7 +80,7 @@ export const DeleteIndexModal: React.FC = () => { 'xpack.enterpriseSearch.content.searchIndices.deleteModal.delete.description', { defaultMessage: - 'Deleting this index will also delete all of its data and its {ingestionMethod} configuration. Any associated search engines will no longer be able to access any data stored in this index.', + 'Deleting this index will also delete all of its data and its {ingestionMethod} configuration. Any associated search applications will no longer be able to access any data stored in this index.', values: { ingestionMethod: ingestionMethodToText(ingestionMethod), }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.test.tsx index 047292927245e..d5b98d0fe0351 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.test.tsx @@ -12,7 +12,7 @@ import { Switch } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { NewIndex } from '../new_index'; +import { NewIndexRouter } from '../new_index/new_index_router'; import { SearchIndexRouter } from '../search_index/search_index_router'; import { SearchIndices } from './search_indices'; @@ -25,7 +25,7 @@ describe('SearchIndicesRouter', () => { const routeSwitch = wrapper.find(Switch); - expect(routeSwitch.find(NewIndex)).toHaveLength(1); + expect(routeSwitch.find(NewIndexRouter)).toHaveLength(1); expect(routeSwitch.find(SearchIndices)).toHaveLength(1); expect(routeSwitch.find(SearchIndexRouter)).toHaveLength(1); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.tsx index ca86d6932de0a..739eb262932c8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices_router.tsx @@ -12,7 +12,7 @@ import { Route } from '@kbn/shared-ux-router'; import { SEARCH_INDICES_PATH, SEARCH_INDEX_PATH, NEW_INDEX_PATH } from '../../routes'; -import { NewIndex } from '../new_index'; +import { NewIndexRouter } from '../new_index/new_index_router'; import { SearchIndexRouter } from '../search_index/search_index_router'; import { SearchIndices } from './search_indices'; @@ -20,8 +20,8 @@ import { SearchIndices } from './search_indices'; export const SearchIndicesRouter: React.FC = () => { return ( - - + + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx index a1bbe6c39956f..840010d3aa219 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings.tsx @@ -12,6 +12,8 @@ import { useActions, useValues } from 'kea'; import { EuiButton, EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + import { docLinks } from '../../../shared/doc_links'; import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; @@ -36,6 +38,21 @@ export const Settings: React.FC = () => { }), ]} pageHeader={{ + description: ( + + {i18n.translate('xpack.enterpriseSearch.content.settings.ingestLink', { + defaultMessage: 'ingest pipelines', + })} +
    + ), + }} + /> + ), pageTitle: i18n.translate('xpack.enterpriseSearch.content.settings.headerTitle', { defaultMessage: 'Content Settings', }), @@ -69,19 +86,12 @@ export const Settings: React.FC = () => { 'xpack.enterpriseSearch.content.settings.contentExtraction.description', { defaultMessage: - 'Allow all ingestion mechanisms on your Enterprise Search deployment to extract searchable content from binary files, like PDFs and Word documents. This setting applies to all new Elasticsearch indices created by an Enterprise Search ingestion mechanism.', + 'Extract searchable content from binary files, like PDFs and Word documents.', } )} label={i18n.translate('xpack.enterpriseSearch.content.settings.contactExtraction.label', { defaultMessage: 'Content extraction', })} - link={ - - {i18n.translate('xpack.enterpriseSearch.content.settings.contactExtraction.link', { - defaultMessage: 'Learn more about content extraction', - })} - - } onChange={() => setPipeline({ ...pipelineState, @@ -105,13 +115,6 @@ export const Settings: React.FC = () => { label={i18n.translate('xpack.enterpriseSearch.content.settings.whitespaceReduction.label', { defaultMessage: 'Whitespace reduction', })} - link={ - - {i18n.translate('xpack.enterpriseSearch.content.settings.whitespaceReduction.link', { - defaultMessage: 'Learn more about whitespace reduction', - })} - - } onChange={() => setPipeline({ ...pipelineState, @@ -139,9 +142,9 @@ export const Settings: React.FC = () => { defaultMessage: 'ML Inference', })} link={ - + {i18n.translate('xpack.enterpriseSearch.content.settings.mlInference.link', { - defaultMessage: 'Learn more about content extraction', + defaultMessage: 'Learn more about document enrichment with ML', })} } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings_panel.tsx index fc0f3cca3e06c..673861d80e6ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/settings/settings_panel.tsx @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; interface SettingsPanelProps { description: string; label: string; - link: React.ReactNode; + link?: React.ReactNode; onChange: (event: EuiSwitchEvent) => void; title: string; value: boolean; @@ -61,7 +61,7 @@ export const SettingsPanel: React.FC = ({ - {link} + {link && {link}} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts index 02a1ef68c31f1..7b5bc7bf286f5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts @@ -13,27 +13,36 @@ export const SEARCH_INDICES_PATH = `${ROOT_PATH}search_indices`; export const SETTINGS_PATH = `${ROOT_PATH}settings`; export const NEW_INDEX_PATH = `${SEARCH_INDICES_PATH}/new_index`; +export const NEW_INDEX_METHOD_PATH = `${NEW_INDEX_PATH}/:type`; export const NEW_API_PATH = `${NEW_INDEX_PATH}/api`; export const NEW_ES_INDEX_PATH = `${NEW_INDEX_PATH}/elasticsearch`; export const NEW_DIRECT_UPLOAD_PATH = `${NEW_INDEX_PATH}/upload`; +export const NEW_INDEX_SELECT_CONNECTOR_PATH = `${NEW_INDEX_PATH}/select_connector`; export const SEARCH_INDEX_PATH = `${SEARCH_INDICES_PATH}/:indexName`; export const SEARCH_INDEX_TAB_PATH = `${SEARCH_INDEX_PATH}/:tabId`; export const SEARCH_INDEX_TAB_DETAIL_PATH = `${SEARCH_INDEX_TAB_PATH}/:detailId`; export const SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/domain_management/:domainId`; export const OLD_SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/crawler/domains/:domainId`; -export const SEARCH_INDEX_SELECT_CONNECTOR_PATH = `${SEARCH_INDEX_PATH}/select_connector`; export const ENGINES_PATH = `${ROOT_PATH}engines`; +export enum EngineViewTabs { + PREVIEW = 'preview', + CONTENT = 'content', + CONNECT = 'connect', +} export const ENGINE_CREATION_PATH = `${ENGINES_PATH}/new`; export const ENGINE_PATH = `${ENGINES_PATH}/:engineName`; export const ENGINE_TAB_PATH = `${ENGINE_PATH}/:tabId`; -export enum EngineViewTabs { - PREVIEW = 'preview', +export const SEARCH_APPLICATION_CONNECT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONNECT}/:connectTabId`; +export enum SearchApplicationConnectTabs { + API = 'api', +} +export const SEARCH_APPLICATION_CONTENT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONTENT}/:contentTabId`; +export enum SearchApplicationContentTabs { INDICES = 'indices', SCHEMA = 'schema', - API = 'api', } export const ML_MANAGE_TRAINED_MODELS_PATH = '/app/ml/trained_models'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts index 8955c2d505a43..cb6663eebf6ab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts @@ -60,8 +60,15 @@ class DocLinks { public clientsRustOverview: string; public cloudIndexManagement: string; public connectors: string; + public connectorsAzureBlobStorage: string; + public connectorsGoogleCloudStorage: string; + public connectorsMicrosoftSQL: string; public connectorsMongoDB: string; public connectorsMySQL: string; + public connectorsNetworkDrive: string; + public connectorsOracle: string; + public connectorsPostgreSQL: string; + public connectorsS3: string; public connectorsWorkplaceSearch: string; public crawlerExtractionRules: string; public crawlerManaging: string; @@ -78,12 +85,14 @@ class DocLinks { public enterpriseSearchMailService: string; public enterpriseSearchTroubleshootSetup: string; public enterpriseSearchUsersAccess: string; + public ingestionApis: string; public ingestPipelines: string; public kibanaSecurity: string; public languageAnalyzers: string; public languageClients: string; public licenseManagement: string; public machineLearningStart: string; + public mlDocumentEnrichment: string; public pluginsIngestAttachment: string; public queryDsl: string; public searchUIAppSearch: string; @@ -179,8 +188,15 @@ class DocLinks { this.clientsRustOverview = ''; this.cloudIndexManagement = ''; this.connectors = ''; + this.connectorsAzureBlobStorage = ''; + this.connectorsGoogleCloudStorage = ''; + this.connectorsMicrosoftSQL = ''; this.connectorsMongoDB = ''; this.connectorsMySQL = ''; + this.connectorsNetworkDrive = ''; + this.connectorsOracle = ''; + this.connectorsPostgreSQL = ''; + this.connectorsS3 = ''; this.connectorsWorkplaceSearch = ''; this.crawlerExtractionRules = ''; this.crawlerManaging = ''; @@ -197,12 +213,14 @@ class DocLinks { this.enterpriseSearchMailService = ''; this.enterpriseSearchTroubleshootSetup = ''; this.enterpriseSearchUsersAccess = ''; + this.ingestionApis = ''; this.ingestPipelines = ''; this.kibanaSecurity = ''; this.languageAnalyzers = ''; this.languageClients = ''; this.licenseManagement = ''; this.machineLearningStart = ''; + this.mlDocumentEnrichment = ''; this.pluginsIngestAttachment = ''; this.queryDsl = ''; this.searchUIAppSearch = ''; @@ -317,12 +335,14 @@ class DocLinks { this.enterpriseSearchMailService = docLinks.links.enterpriseSearch.mailService; this.enterpriseSearchTroubleshootSetup = docLinks.links.enterpriseSearch.troubleshootSetup; this.enterpriseSearchUsersAccess = docLinks.links.enterpriseSearch.usersAccess; + this.ingestionApis = docLinks.links.enterpriseSearch.ingestionApis; this.ingestPipelines = docLinks.links.enterpriseSearch.ingestPipelines; this.kibanaSecurity = docLinks.links.kibana.xpackSecurity; this.languageAnalyzers = docLinks.links.enterpriseSearch.languageAnalyzers; this.languageClients = docLinks.links.enterpriseSearch.languageClients; this.licenseManagement = docLinks.links.enterpriseSearch.licenseManagement; this.machineLearningStart = docLinks.links.enterpriseSearch.machineLearningStart; + this.mlDocumentEnrichment = docLinks.links.enterpriseSearch.mlDocumentEnrichment; this.pluginsIngestAttachment = docLinks.links.plugins.ingestAttachment; this.queryDsl = docLinks.links.query.queryDsl; this.searchUIAppSearch = docLinks.links.searchUI.appSearch; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts b/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts new file mode 100644 index 0000000000000..f9d9eb5df9799 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts @@ -0,0 +1,30 @@ +/* + * 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 azure_blob_storage from '../../../assets/source_icons/azure_blob_storage.svg'; +import custom from '../../../assets/source_icons/custom.svg'; +import google_cloud_storage from '../../../assets/source_icons/google_cloud_storage.svg'; +import mongodb from '../../../assets/source_icons/mongodb.svg'; +import microsoft_sql from '../../../assets/source_icons/mssql.svg'; +import mysql from '../../../assets/source_icons/mysql.svg'; +import network_drive from '../../../assets/source_icons/network_drive.svg'; +import oracle from '../../../assets/source_icons/oracle.svg'; +import postgresql from '../../../assets/source_icons/postgresql.svg'; +import amazon_s3 from '../../../assets/source_icons/s3.svg'; + +export const CONNECTOR_ICONS = { + amazon_s3, + azure_blob_storage, + custom, + google_cloud_storage, + microsoft_sql, + mongodb, + mysql, + network_drive, + oracle, + postgresql, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index 87282007f5bbd..ff2c05c4c8566 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -10,7 +10,6 @@ import { useValues } from 'kea'; import { EuiBreadcrumb } from '@elastic/eui'; import { - ENGINES_PLUGIN, ENTERPRISE_SEARCH_OVERVIEW_PLUGIN, ANALYTICS_PLUGIN, APP_SEARCH_PLUGIN, @@ -139,7 +138,4 @@ export const useSearchExperiencesBreadcrumbs = (breadcrumbs: Breadcrumbs = []) = ]); export const useEnterpriseSearchEnginesBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => - useEnterpriseSearchBreadcrumbs([ - { text: ENGINES_PLUGIN.NAV_TITLE, path: '/engines' }, - ...breadcrumbs, - ]); + useEnterpriseSearchBreadcrumbs(breadcrumbs); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index 9c22e64127326..bd042c4b6ac90 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -9,6 +9,8 @@ import React, { useEffect } from 'react'; import { useValues } from 'kea'; +import { ENGINES_PLUGIN } from '../../../../common/constants'; + import { KibanaLogic } from '../kibana'; import { @@ -176,8 +178,9 @@ export const SetEnterpriseSearchEnginesChrome: React.FC = ({ tra const title = reverseArray(trail); const docTitle = appSearchTitle(title); - const crumbs = useGenerateBreadcrumbs(trail); - const breadcrumbs = useEnterpriseSearchEnginesBreadcrumbs(crumbs); + const breadcrumbs = useEnterpriseSearchEnginesBreadcrumbs( + useGenerateBreadcrumbs([ENGINES_PLUGIN.NAV_TITLE, ...trail]) + ); useEffect(() => { setBreadcrumbs(breadcrumbs); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx index 299f803a6e131..dcb343a2beec4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx @@ -290,19 +290,14 @@ describe('useEnterpriseSearchEngineNav', () => { name: 'Preview', }, { - href: `/app/enterprise_search/content/engines/${engineName}/indices`, - id: 'enterpriseSearchEngineIndices', - name: 'Indices', - }, - { - href: `/app/enterprise_search/content/engines/${engineName}/schema`, - id: 'enterpriseSearchEngineSchema', - name: 'Schema', + href: `/app/enterprise_search/content/engines/${engineName}/content`, + id: 'enterpriseSearchApplicationsContent', + name: 'Content', }, { - href: `/app/enterprise_search/content/engines/${engineName}/api`, - id: 'enterpriseSearchEngineAPI', - name: 'API', + href: `/app/enterprise_search/content/engines/${engineName}/connect`, + id: 'enterpriseSearchApplicationConnect', + name: 'Connect', }, ], name: engineName, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx index 0798de9870f94..7fbc354ff725f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx @@ -189,6 +189,7 @@ export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?: name: engineName, ...generateNavLink({ shouldNotCreateHref: true, + shouldShowActiveForSubroutes: true, to: enginePath, }), items: [ @@ -203,33 +204,28 @@ export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?: }), }, { - id: 'enterpriseSearchEngineIndices', - name: i18n.translate('xpack.enterpriseSearch.nav.engine.indicesTitle', { - defaultMessage: 'Indices', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: `${enginePath}/${EngineViewTabs.INDICES}`, - }), - }, - { - id: 'enterpriseSearchEngineSchema', - name: i18n.translate('xpack.enterpriseSearch.nav.engine.schemaTitle', { - defaultMessage: 'Schema', + id: 'enterpriseSearchApplicationsContent', + name: i18n.translate('xpack.enterpriseSearch.nav.engine.contentTitle', { + defaultMessage: 'Content', }), ...generateNavLink({ shouldNotCreateHref: true, - to: `${enginePath}/${EngineViewTabs.SCHEMA}`, + shouldShowActiveForSubroutes: true, + to: `${enginePath}/${EngineViewTabs.CONTENT}`, }), }, { - id: 'enterpriseSearchEngineAPI', - name: i18n.translate('xpack.enterpriseSearch.nav.engine.apiTitle', { - defaultMessage: 'API', - }), + id: 'enterpriseSearchApplicationConnect', + name: i18n.translate( + 'xpack.enterpriseSearch.nav.applications.searchApplications.connectTitle', + { + defaultMessage: 'Connect', + } + ), ...generateNavLink({ shouldNotCreateHref: true, - to: `${enginePath}/${EngineViewTabs.API}`, + shouldShowActiveForSubroutes: true, + to: `${enginePath}/${EngineViewTabs.CONNECT}`, }), }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg index cc07fbbc50877..b3bd8474ff5cc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/azure_blob.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/azure_blob_storage.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/assets/source_icons/azure_blob.svg rename to x-pack/plugins/enterprise_search/public/assets/source_icons/azure_blob_storage.svg diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/connector.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/connector.svg new file mode 100644 index 0000000000000..3b37b963f435f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/connector.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/crawler.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/crawler.svg new file mode 100644 index 0000000000000..94aafafddf68b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/crawler.svg @@ -0,0 +1,4 @@ + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/custom.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/custom.svg new file mode 100644 index 0000000000000..b3bd8474ff5cc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/custom.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/google_cloud.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/google_cloud_storage.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/assets/source_icons/google_cloud.svg rename to x-pack/plugins/enterprise_search/public/assets/source_icons/google_cloud_storage.svg diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/microsoft_sql.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/mssql.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/assets/source_icons/microsoft_sql.svg rename to x-pack/plugins/enterprise_search/public/assets/source_icons/mssql.svg diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/amazon_s3.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/s3.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/assets/source_icons/amazon_s3.svg rename to x-pack/plugins/enterprise_search/public/assets/source_icons/s3.svg diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index 53ed6f5096320..8d68f1c3e1c58 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -372,7 +372,7 @@ export const registerEnterpriseSearchIntegrations = ( defaultMessage: 'Add search to your website with the Enterprise Search web crawler.', }), categories: ['enterprise_search', 'app_search', 'web', 'elastic_stack', 'crawler'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=crawler', + uiInternalPath: '/app/enterprise_search/content/search_indices/new_index/crawler', icons: [ { type: 'eui', @@ -393,7 +393,7 @@ export const registerEnterpriseSearchIntegrations = ( defaultMessage: "Add search to your application with Elasticsearch's robust APIs.", }), categories: ['enterprise_search', 'custom', 'elastic_stack', 'sdk_search', 'language_client'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=api', + uiInternalPath: '/app/enterprise_search/content/search_indices/new_index/api', icons: [ { type: 'eui', @@ -421,8 +421,7 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'custom', 'elastic_stack', 'connector', 'native_search'], - uiInternalPath: - '/app/enterprise_search/content/search_indices/new_index?method=native_connector', + uiInternalPath: '/app/enterprise_search/content/search_indices/new_index/connector', icons: [ { type: 'eui', @@ -453,7 +452,7 @@ export const registerEnterpriseSearchIntegrations = ( 'connector_client', ], uiInternalPath: - '/app/enterprise_search/content/search_indices/new_index?method=native_connector', + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=mongodb', icons: [ { type: 'svg', @@ -484,7 +483,7 @@ export const registerEnterpriseSearchIntegrations = ( 'connector_client', ], uiInternalPath: - '/app/enterprise_search/content/search_indices/new_index?method=native_connector', + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=mysql', icons: [ { type: 'svg', @@ -509,7 +508,8 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'custom', 'elastic_stack', 'connector_client'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=custom', icons: [ { type: 'eui', @@ -532,7 +532,8 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'elastic_stack', 'custom', 'datastore'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=postgresql', icons: [ { type: 'svg', @@ -557,7 +558,8 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'elastic_stack', 'custom', 'datastore'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=oracle', icons: [ { type: 'svg', @@ -569,7 +571,7 @@ export const registerEnterpriseSearchIntegrations = ( }); customIntegrations.registerCustomIntegration({ - id: 'ms_sql', + id: 'mssql', title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.msSqlName', { defaultMessage: 'Microsoft SQL', }), @@ -581,13 +583,12 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'custom', 'elastic_stack', 'datastore'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=mssql', icons: [ { type: 'svg', - src: http.basePath.prepend( - '/plugins/enterpriseSearch/assets/source_icons/microsoft_sql.svg' - ), + src: http.basePath.prepend('/plugins/enterpriseSearch/assets/source_icons/mssql.svg'), }, ], shipper: 'enterprise_search', @@ -617,7 +618,8 @@ export const registerEnterpriseSearchIntegrations = ( 'connector_client', 'connector_package', ], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=network_drive', icons: [ { type: 'svg', @@ -642,11 +644,12 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'datastore', 'elastic_stack'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=s3', icons: [ { type: 'svg', - src: http.basePath.prepend('/plugins/enterpriseSearch/assets/source_icons/amazon_s3.svg'), + src: http.basePath.prepend('/plugins/enterpriseSearch/assets/source_icons/s3.svg'), }, ], shipper: 'enterprise_search', @@ -666,12 +669,13 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'elastic_stack', 'custom'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=google_cloud_storage', icons: [ { type: 'svg', src: http.basePath.prepend( - '/plugins/enterpriseSearch/assets/source_icons/google_cloud.svg' + '/plugins/enterpriseSearch/assets/source_icons/google_cloud_storage.svg' ), }, ], @@ -691,12 +695,13 @@ export const registerEnterpriseSearchIntegrations = ( } ), categories: ['enterprise_search', 'elastic_stack', 'custom'], - uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector', + uiInternalPath: + '/app/enterprise_search/content/search_indices/new_index/connector?service_type=azure_blob_storage', icons: [ { type: 'svg', src: http.basePath.prepend( - '/plugins/enterpriseSearch/assets/source_icons/azure_blob.svg' + '/plugins/enterpriseSearch/assets/source_icons/azure_blob_storage.svg' ), }, ], diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts index 2019cd06e0089..cebb673fba038 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts @@ -68,7 +68,7 @@ export const addConnector = async ( index_name: string; is_native: boolean; language: string | null; - service_type?: string | null; + service_type?: string; } ): Promise<{ id: string; index_name: string }> => { const connectorsIndexExists = await client.asCurrentUser.indices.exists({ @@ -96,7 +96,7 @@ export const addConnector = async ( run_ml_inference: pipeline.default_run_ml_inference, } : null, - serviceType: input.service_type, + serviceType: input.service_type ?? null, }); return await createConnector(document, client, input.language, !!input.delete_existing_connector); diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 729258fe4e97c..77dc6d4c839f5 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -18,6 +18,7 @@ import { import { CustomIntegrationsPluginSetup } from '@kbn/custom-integrations-plugin/server'; import { DataPluginStart } from '@kbn/data-plugin/server/plugin'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; +import { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; import { InfraPluginSetup } from '@kbn/infra-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; @@ -76,31 +77,34 @@ import { workplaceSearchTelemetryType } from './saved_objects/workplace_search/t import { uiSettings as enterpriseSearchUISettings } from './ui_settings'; +import { getSearchResultProvider } from './utils/search_result_provider'; + import { ConfigType } from '.'; interface PluginsSetup { - usageCollection?: UsageCollectionSetup; - security: SecurityPluginSetup; + customIntegrations?: CustomIntegrationsPluginSetup; features: FeaturesPluginSetup; + globalSearch: GlobalSearchPluginSetup; + guidedOnboarding: GuidedOnboardingPluginSetup; infra: InfraPluginSetup; - customIntegrations?: CustomIntegrationsPluginSetup; ml?: MlPluginSetup; - guidedOnboarding: GuidedOnboardingPluginSetup; + security: SecurityPluginSetup; + usageCollection?: UsageCollectionSetup; } interface PluginsStart { - spaces?: SpacesPluginStart; - security: SecurityPluginStart; data: DataPluginStart; + security: SecurityPluginStart; + spaces?: SpacesPluginStart; } export interface RouteDependencies { - router: IRouter; config: ConfigType; - log: Logger; enterpriseSearchRequestHandler: IEnterpriseSearchRequestHandler; getSavedObjectsService?(): SavedObjectsServiceStart; + log: Logger; ml?: MlPluginSetup; + router: IRouter; } export class EnterpriseSearchPlugin implements Plugin { @@ -118,6 +122,7 @@ export class EnterpriseSearchPlugin implements Plugin { usageCollection, security, features, + globalSearch, infra, customIntegrations, ml, @@ -284,6 +289,14 @@ export class EnterpriseSearchPlugin implements Plugin { if (config.hasNativeConnectors) { guidedOnboarding.registerGuideConfig(databaseSearchGuideId, databaseSearchGuideConfig); } + + /** + * Register our integrations in the global search bar + */ + + if (globalSearch) { + globalSearch.registerResultProvider(getSearchResultProvider(http.basePath, config)); + } } public start() {} diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts index 2f97e6362a9ee..690379fc0c4c3 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts @@ -25,6 +25,80 @@ describe('Enterprise Search Analytics API', () => { let mockRouter: MockRouter; const mockClient = {}; + describe('GET /internal/enterprise_search/analytics/collections', () => { + beforeEach(() => { + const context = { + core: Promise.resolve({ elasticsearch: { client: mockClient } }), + } as jest.Mocked; + + mockRouter = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/analytics/collections', + }); + + const mockDataPlugin = { + indexPatterns: { + dataViewsServiceFactory: jest.fn(), + }, + }; + + const mockedSavedObjects = { + getScopedClient: jest.fn(), + }; + + registerAnalyticsRoutes({ + ...mockDependencies, + data: mockDataPlugin as unknown as DataPluginStart, + savedObjects: mockedSavedObjects as unknown as SavedObjectsServiceStart, + router: mockRouter.router, + }); + }); + + it('fetches a defined analytics collections', async () => { + const mockData: AnalyticsCollection[] = [ + { + events_datastream: 'logs-elastic_analytics.events-example', + name: 'my_collection', + }, + { + events_datastream: 'logs-elastic_analytics.events-example2', + name: 'my_collection2', + }, + { + events_datastream: 'logs-elastic_analytics.events-example2', + name: 'my_collection3', + }, + ]; + + (fetchAnalyticsCollections as jest.Mock).mockImplementationOnce(() => { + return Promise.resolve(mockData); + }); + await mockRouter.callRoute({}); + + expect(mockRouter.response.ok).toHaveBeenCalledWith({ + body: mockData, + }); + }); + + it('passes the query string to the fetch function', async () => { + await mockRouter.callRoute({ query: { query: 'my_collection2' } }); + + expect(fetchAnalyticsCollections).toHaveBeenCalledWith(mockClient, 'my_collection2*'); + }); + + it('returns an empty obj when fetchAnalyticsCollections returns not found error', async () => { + (fetchAnalyticsCollections as jest.Mock).mockImplementationOnce(() => { + throw new Error(ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND); + }); + await mockRouter.callRoute({}); + + expect(mockRouter.response.ok).toHaveBeenCalledWith({ + body: [], + }); + }); + }); + describe('GET /internal/enterprise_search/analytics/collections/{id}', () => { beforeEach(() => { const context = { diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts index 4bc99895a6d44..abdf0673665c8 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts @@ -12,6 +12,7 @@ import { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; import { DataPluginStart } from '@kbn/data-plugin/server/plugin'; import { i18n } from '@kbn/i18n'; +import { AnalyticsEventsExist } from '../../../common/types/analytics'; import { ErrorCode } from '../../../common/types/error_codes'; import { addAnalyticsCollection } from '../../lib/analytics/add_analytics_collection'; import { analyticsEventsExist } from '../../lib/analytics/analytics_events_exist'; @@ -50,12 +51,25 @@ export function registerAnalyticsRoutes({ router.get( { path: '/internal/enterprise_search/analytics/collections', - validate: {}, + validate: { + query: schema.object({ + query: schema.maybe(schema.string()), + }), + }, }, elasticsearchErrorHandler(log, async (context, request, response) => { const { client } = (await context.core).elasticsearch; - const collections = await fetchAnalyticsCollections(client); - return response.ok({ body: collections }); + try { + const query = request.query.query && request.query.query + '*'; + const collections = await fetchAnalyticsCollections(client, query); + return response.ok({ body: collections }); + } catch (error) { + if ((error as Error).message === ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND) { + return response.ok({ body: [] }); + } + + throw error; + } }) ); @@ -193,11 +207,9 @@ export function registerAnalyticsRoutes({ const eventsIndexExists = await analyticsEventsExist(client, request.params.name); - if (!eventsIndexExists) { - return response.ok({ body: { exists: false } }); - } + const body: AnalyticsEventsExist = { exists: eventsIndexExists }; - return response.ok({ body: { exists: true } }); + return response.ok({ body }); }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts index 24fc921e6e584..32e7cb79a2597 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts @@ -20,7 +20,6 @@ import { ErrorCode } from '../../../common/types/error_codes'; import { addConnector } from '../../lib/connectors/add_connector'; import { fetchSyncJobsByConnectorId } from '../../lib/connectors/fetch_sync_jobs'; import { cancelSyncs } from '../../lib/connectors/post_cancel_syncs'; -import { configureNativeConnector } from '../../lib/connectors/put_configure_native'; import { updateFiltering } from '../../lib/connectors/put_update_filtering'; import { updateFilteringDraft } from '../../lib/connectors/put_update_filtering_draft'; import { startConnectorSync } from '../../lib/connectors/start_sync'; @@ -48,6 +47,7 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { index_name: schema.string(), is_native: schema.boolean(), language: schema.nullable(schema.string()), + service_type: schema.maybe(schema.string()), }), }, }, @@ -276,23 +276,6 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { }) ); - router.put( - { - path: '/internal/enterprise_search/connectors/{connectorId}/configure_native', - validate: { - body: schema.object({ service_type: schema.string() }), - params: schema.object({ - connectorId: schema.string(), - }), - }, - }, - elasticsearchErrorHandler(log, async (context, request, response) => { - const { client } = (await context.core).elasticsearch; - await configureNativeConnector(client, request.params.connectorId, request.body.service_type); - return response.ok(); - }) - ); - router.put( { path: '/internal/enterprise_search/connectors/{connectorId}/name_and_description', diff --git a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts index 08e18e0907532..4574039c61485 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts @@ -22,6 +22,7 @@ describe('createConnectorDocument', () => { reduce_whitespace: true, run_ml_inference: false, }, + serviceType: null, }) ).toEqual({ api_key_id: null, @@ -114,6 +115,7 @@ describe('createConnectorDocument', () => { reduce_whitespace: true, run_ml_inference: false, }, + serviceType: null, }) ).toEqual({ api_key_id: null, diff --git a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts index 507559b65440d..6f548b0879283 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts @@ -5,6 +5,9 @@ * 2.0. */ +import { NATIVE_CONNECTOR_DEFINITIONS } from '../../common/connectors/native_connectors'; +import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../common/constants'; + import { ConnectorDocument, ConnectorStatus, @@ -25,9 +28,31 @@ export function createConnectorDocument({ isNative: boolean; language: string | null; pipeline?: IngestPipelineParams | null; - serviceType?: string | null; + serviceType: string | null; }): ConnectorDocument { const currentTimestamp = new Date().toISOString(); + const nativeConnector = + isNative && serviceType ? NATIVE_CONNECTOR_DEFINITIONS[serviceType] : undefined; + + if ( + isNative && + serviceType && + !nativeConnector && + serviceType !== ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE + ) { + throw new Error(`Could not find connector definition for service type ${serviceType}`); + } + + const nativeFields = nativeConnector + ? { + configuration: nativeConnector.configuration, + features: nativeConnector.features, + name: nativeConnector.name, + service_type: serviceType, + status: ConnectorStatus.NEEDS_CONFIGURATION, + } + : {}; + return { api_key_id: null, configuration: {}, @@ -100,5 +125,6 @@ export function createConnectorDocument({ service_type: serviceType || null, status: ConnectorStatus.CREATED, sync_now: false, + ...nativeFields, }; } diff --git a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts new file mode 100644 index 0000000000000..d5ecee88f80cf --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts @@ -0,0 +1,196 @@ +/* + * 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 { NEVER } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; + +import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../common/constants'; + +import { getSearchResultProvider } from './search_result_provider'; + +const getTestScheduler = () => { + return new TestScheduler((actual, expected) => { + return expect(actual).toEqual(expected); + }); +}; + +describe('Enterprise Search search provider', () => { + const basePathMock = { + prepend: (input: string) => `/kbn${input}`, + } as any; + + const crawlerResult = { + icon: '/kbn/plugins/enterpriseSearch/assets/source_icons/crawler.svg', + id: 'elastic-crawler', + score: 75, + title: 'Elastic Web Crawler', + type: 'Enterprise Search', + url: { + path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/crawler`, + prependBasePath: true, + }, + }; + + const mongoResult = { + icon: '/kbn/plugins/enterpriseSearch/assets/source_icons/mongodb.svg', + id: 'mongodb', + score: 75, + title: 'MongoDB', + type: 'Enterprise Search', + url: { + path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/connector?service_type=mongodb`, + prependBasePath: true, + }, + }; + + const searchResultProvider = getSearchResultProvider(basePathMock, { + hasConnectors: true, + hasWebCrawler: true, + } as any); + + beforeEach(() => {}); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('find', () => { + it('returns formatted results', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { term: 'crawler' }, + { aborted$: NEVER, maxResults: 100, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: [crawlerResult], + }); + }); + }); + + it('returns everything on empty string', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { term: '' }, + { aborted$: NEVER, maxResults: 100, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: expect.arrayContaining([ + { ...crawlerResult, score: 80 }, + { ...mongoResult, score: 80 }, + ]), + }); + }); + }); + + it('respect maximum results', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { term: '' }, + { aborted$: NEVER, maxResults: 1, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: [{ ...crawlerResult, score: 80 }], + }); + }); + }); + + it('omits crawler if config has crawler disabled', () => { + const searchProvider = getSearchResultProvider(basePathMock, { + hasConnectors: true, + hasWebCrawler: false, + } as any); + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchProvider.find( + { term: '' }, + { aborted$: NEVER, maxResults: 100, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: expect.not.arrayContaining([{ ...crawlerResult, score: 80 }]), + }); + }); + }); + + it('omits connectors if config has connectors disabled', () => { + const searchProvider = getSearchResultProvider(basePathMock, { + hasConnectors: false, + hasWebCrawler: true, + } as any); + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchProvider.find( + { term: '' }, + { aborted$: NEVER, maxResults: 100, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: expect.not.arrayContaining([{ mongoResult, score: 80 }]), + }); + }); + }); + + it('returns nothing if tag is specified', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { tags: ['tag'], term: '' }, + { aborted$: NEVER, maxResults: 1, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: [], + }); + }); + }); + it('returns nothing if unknown type is specified', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { term: '', types: ['tag'] }, + { aborted$: NEVER, maxResults: 1, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: [], + }); + }); + }); + it('returns results for integrations tag', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { term: 'crawler', types: ['integration'] }, + { aborted$: NEVER, maxResults: 1, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: [crawlerResult], + }); + }); + }); + it('returns results for enterprise search tag', () => { + getTestScheduler().run(({ expectObservable }) => { + expectObservable( + searchResultProvider.find( + { term: 'crawler', types: ['enterprise search'] }, + { aborted$: NEVER, maxResults: 1, preference: '' }, + {} as any + ) + ).toBe('(a|)', { + a: [crawlerResult], + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts new file mode 100644 index 0000000000000..29f791dfd5ca1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts @@ -0,0 +1,103 @@ +/* + * 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 { from, takeUntil } from 'rxjs'; + +import { IBasePath } from '@kbn/core-http-server'; +import { GlobalSearchResultProvider } from '@kbn/global-search-plugin/server'; + +import { ConfigType } from '..'; +import { CONNECTOR_DEFINITIONS } from '../../common/connectors/connectors'; +import { + ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE, + ENTERPRISE_SEARCH_CONTENT_PLUGIN, +} from '../../common/constants'; + +export function toSearchResult({ + basePath, + iconPath, + name, + score, + serviceType, +}: { + basePath: IBasePath; + iconPath: string; + name: string; + score: number; + serviceType: string; +}) { + return { + icon: iconPath + ? basePath.prepend(`/plugins/enterpriseSearch/assets/source_icons/${iconPath}`) + : 'logoEnterpriseSearch', + id: serviceType, + score, + title: name, + type: 'Enterprise Search', + url: { + path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/${ + serviceType === ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE + ? 'crawler' + : `connector?service_type=${serviceType}` + }`, + prependBasePath: true, + }, + }; +} + +export function getSearchResultProvider( + basePath: IBasePath, + config: ConfigType +): GlobalSearchResultProvider { + return { + find: ({ term, types, tags }, { aborted$, maxResults }) => { + if ( + tags || + (types && !(types.includes('integration') || types.includes('enterprise search'))) + ) { + return from([[]]); + } + const result = [ + ...(config.hasWebCrawler + ? [ + { + iconPath: 'crawler.svg', + keywords: ['crawler', 'web', 'website', 'internet', 'google'], + name: 'Elastic Web Crawler', + serviceType: ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE, + }, + ] + : []), + ...(config.hasConnectors ? CONNECTOR_DEFINITIONS : []), + ] + .map(({ iconPath, keywords, name, serviceType }) => { + let score = 0; + const searchTerm = (term || '').toLowerCase(); + const searchName = name.toLowerCase(); + if (!searchTerm) { + score = 80; + } else if (searchName === searchTerm) { + score = 100; + } else if (searchName.startsWith(searchTerm)) { + score = 90; + } else if (searchName.includes(searchTerm)) { + score = 75; + } else if (serviceType === searchTerm) { + score = 65; + } else if (keywords.some((keyword) => keyword.includes(searchTerm))) { + score = 50; + } + return toSearchResult({ basePath, iconPath, name, score, serviceType }); + }) + .filter(({ score }) => score > 0) + .slice(0, maxResults); + return from([result]).pipe(takeUntil(aborted$)); + }, + getSearchableTypes: () => ['enterprise search', 'integration'], + id: 'enterpriseSearch', + }; +} diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index af660c1169d5a..3bd55202cfe52 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -58,5 +58,7 @@ "@kbn/react-field", "@kbn/field-types", "@kbn/core-elasticsearch-server-mocks", + "@kbn/shared-ux-link-redirect-app", + "@kbn/global-search-plugin", ] } diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index c9758df202a3d..e802567b2ab7f 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -304,6 +304,13 @@ "flapping": { "type": "boolean" }, + "maintenance_window_ids": { + "type": "keyword", + "ignore_above": 1024, + "meta": { + "isArray": "true" + } + }, "uuid": { "type": "keyword", "ignore_above": 1024 @@ -391,6 +398,9 @@ } } }, + "revision": { + "type": "long" + }, "rule_type_id": { "type": "keyword", "ignore_above": 1024 diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 7471390a7bc88..8621df23020bd 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -140,6 +140,7 @@ export const EventSchema = schema.maybe( alert: schema.maybe( schema.object({ flapping: ecsBoolean(), + maintenance_window_ids: ecsStringMulti(), uuid: ecsString(), rule: schema.maybe( schema.object({ @@ -177,6 +178,7 @@ export const EventSchema = schema.maybe( ), }) ), + revision: ecsStringOrNumber(), rule_type_id: ecsString(), }) ), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 2e236d7b4eff8..768988aa3c07b 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -86,6 +86,10 @@ exports.EcsCustomPropertyMappings = { flapping: { type: 'boolean', }, + maintenance_window_ids: { + type: 'keyword', + ignore_above: 1024, + }, uuid: { type: 'keyword', ignore_above: 1024, @@ -173,6 +177,9 @@ exports.EcsCustomPropertyMappings = { }, }, }, + revision: { + type: 'long', + }, rule_type_id: { type: 'keyword', ignore_above: 1024, @@ -274,4 +281,5 @@ exports.EcsEventLogMultiValuedProperties = [ 'event.type', 'rule.author', 'kibana.space_ids', + 'kibana.alert.maintenance_window_ids', ]; diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index 98ddd770ac962..d5bf14a1259c1 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -61,13 +61,11 @@ export class Plugin implements CorePlugin { dataView.addRuntimeField(name, field); @@ -170,6 +175,7 @@ export class ObservabilityDataViews { timeFieldName: '@timestamp', fieldFormats: this.getFieldFormats(app), name: DataTypesLabels[app], + allowNoIndex: true, }); } // we want to make sure field formats remain same diff --git a/x-pack/plugins/fleet/common/authz.test.ts b/x-pack/plugins/fleet/common/authz.test.ts index 9602fabdce7c1..22bfeb6c04cef 100644 --- a/x-pack/plugins/fleet/common/authz.test.ts +++ b/x-pack/plugins/fleet/common/authz.test.ts @@ -7,6 +7,8 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common'; +import { TRANSFORM_PLUGIN_ID } from './constants/plugin'; + import { calculatePackagePrivilegesFromCapabilities, calculatePackagePrivilegesFromKibanaPrivileges, @@ -39,16 +41,33 @@ describe('fleet authz', () => { writeHostIsolationExceptions: true, writeHostIsolation: false, }; + + const transformCapabilities = { + canCreateTransform: false, + canDeleteTransform: false, + canGetTransform: true, + canStartStopTransform: false, + }; + const expected = { endpoint: { actions: generateActions(ENDPOINT_PRIVILEGES, endpointCapabilities), }, + transform: { + actions: { + canCreateTransform: { executePackageAction: false }, + canDeleteTransform: { executePackageAction: false }, + canGetTransform: { executePackageAction: true }, + canStartStopTransform: { executePackageAction: false }, + }, + }, }; const actual = calculatePackagePrivilegesFromCapabilities({ navLinks: {}, management: {}, catalogue: {}, siem: endpointCapabilities, + transform: transformCapabilities, }); expect(actual).toEqual(expected); @@ -65,6 +84,8 @@ describe('fleet authz', () => { { privilege: `${SECURITY_SOLUTION_ID}-writeHostIsolationExceptions`, authorized: true }, { privilege: `${SECURITY_SOLUTION_ID}-writeHostIsolation`, authorized: false }, { privilege: `${SECURITY_SOLUTION_ID}-ignoreMe`, authorized: true }, + { privilege: `${TRANSFORM_PLUGIN_ID}-admin`, authorized: true }, + { privilege: `${TRANSFORM_PLUGIN_ID}-read`, authorized: true }, ]; const expected = { endpoint: { @@ -77,6 +98,14 @@ describe('fleet authz', () => { writeHostIsolation: false, }), }, + transform: { + actions: { + canCreateTransform: { executePackageAction: true }, + canDeleteTransform: { executePackageAction: true }, + canGetTransform: { executePackageAction: true }, + canStartStopTransform: { executePackageAction: true }, + }, + }, }; const actual = calculatePackagePrivilegesFromKibanaPrivileges(endpointPrivileges); expect(actual).toEqual(expected); diff --git a/x-pack/plugins/fleet/common/authz.ts b/x-pack/plugins/fleet/common/authz.ts index fa30f2b8f7f33..83d337c00368b 100644 --- a/x-pack/plugins/fleet/common/authz.ts +++ b/x-pack/plugins/fleet/common/authz.ts @@ -7,8 +7,16 @@ import type { Capabilities } from '@kbn/core-capabilities-common'; +import { TRANSFORM_PLUGIN_ID } from './constants/plugin'; + import { ENDPOINT_PRIVILEGES } from './constants'; +export type TransformPrivilege = + | 'canGetTransform' + | 'canCreateTransform' + | 'canDeleteTransform' + | 'canStartStopTransform'; + export interface FleetAuthz { fleet: { all: boolean; @@ -106,10 +114,22 @@ export function calculatePackagePrivilegesFromCapabilities( {} ); + const transformActions = Object.keys(capabilities.transform).reduce((acc, privilegeName) => { + return { + ...acc, + [privilegeName]: { + executePackageAction: capabilities.transform[privilegeName] || false, + }, + }; + }, {}); + return { endpoint: { actions: endpointActions, }, + transform: { + actions: transformActions, + }, }; } @@ -158,9 +178,40 @@ export function calculatePackagePrivilegesFromKibanaPrivileges( {} ); + const hasTransformAdmin = getAuthorizationFromPrivileges( + kibanaPrivileges, + `${TRANSFORM_PLUGIN_ID}-`, + `admin` + ); + const transformActions: { + [key in TransformPrivilege]: { + executePackageAction: boolean; + }; + } = { + canCreateTransform: { + executePackageAction: hasTransformAdmin, + }, + canDeleteTransform: { + executePackageAction: hasTransformAdmin, + }, + canStartStopTransform: { + executePackageAction: hasTransformAdmin, + }, + canGetTransform: { + executePackageAction: getAuthorizationFromPrivileges( + kibanaPrivileges, + `${TRANSFORM_PLUGIN_ID}-`, + `read` + ), + }, + }; + return { endpoint: { actions: endpointActions, }, + transform: { + actions: transformActions, + }, }; } diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index a1d73b452cf72..2635dbc05399f 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -16,6 +16,7 @@ export const FLEET_ENDPOINT_PACKAGE = 'endpoint'; export const FLEET_APM_PACKAGE = 'apm'; export const FLEET_SYNTHETICS_PACKAGE = 'synthetics'; export const FLEET_KUBERNETES_PACKAGE = 'kubernetes'; +export const FLEET_UNIVERSAL_PROFILING_SYMBOLIZER_PACKAGE = 'profiler_symbolizer'; export const FLEET_CLOUD_SECURITY_POSTURE_PACKAGE = 'cloud_security_posture'; export const FLEET_CLOUD_SECURITY_POSTURE_KSPM_POLICY_TEMPLATE = 'kspm'; diff --git a/x-pack/plugins/fleet/common/constants/index.ts b/x-pack/plugins/fleet/common/constants/index.ts index 9892c883805f7..bfc67cb682c87 100644 --- a/x-pack/plugins/fleet/common/constants/index.ts +++ b/x-pack/plugins/fleet/common/constants/index.ts @@ -6,6 +6,7 @@ */ export { INTEGRATIONS_PLUGIN_ID, PLUGIN_ID } from './plugin'; +export { INGEST_SAVED_OBJECT_INDEX } from './saved_objects'; export * from './routes'; export * from './agent'; export * from './agent_policy'; diff --git a/x-pack/plugins/fleet/common/constants/plugin.ts b/x-pack/plugins/fleet/common/constants/plugin.ts index 86211ba3727eb..f9f71fb1608fa 100644 --- a/x-pack/plugins/fleet/common/constants/plugin.ts +++ b/x-pack/plugins/fleet/common/constants/plugin.ts @@ -7,3 +7,4 @@ export const PLUGIN_ID = 'fleet' as const; export const INTEGRATIONS_PLUGIN_ID = 'integrations' as const; +export const TRANSFORM_PLUGIN_ID = 'transform' as const; diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 7eb00dd89ab0c..7b77296c4e52b 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -40,6 +40,8 @@ export const EPM_API_ROUTES = { INFO_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED, INSTALL_FROM_REGISTRY_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED, DELETE_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED, + + REAUTHORIZE_TRANSFORMS: `${EPM_PACKAGES_ONE}/transforms/authorize`, }; // Data stream API routes @@ -169,6 +171,11 @@ export const AGENTS_SETUP_API_ROUTES = { CREATE_PATTERN: `${API_ROOT}/agents/setup`, }; +// Message signing service +export const MESSAGE_SIGNING_SERVICE_API_ROUTES = { + ROTATE_KEY_PAIR: `${API_ROOT}/message_signing_service/rotate_key_pair`, +}; + export const SETUP_API_ROUTE = `${API_ROOT}/setup`; export const INSTALL_SCRIPT_API_ROUTES = `${API_ROOT}/install/{osType}`; diff --git a/x-pack/plugins/fleet/common/constants/saved_objects.ts b/x-pack/plugins/fleet/common/constants/saved_objects.ts new file mode 100644 index 0000000000000..3bca180cb32db --- /dev/null +++ b/x-pack/plugins/fleet/common/constants/saved_objects.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 const INGEST_SAVED_OBJECT_INDEX = '.kibana_ingest'; diff --git a/x-pack/plugins/fleet/common/http_authorization_header.ts b/x-pack/plugins/fleet/common/http_authorization_header.ts new file mode 100644 index 0000000000000..0a209f5bc4eba --- /dev/null +++ b/x-pack/plugins/fleet/common/http_authorization_header.ts @@ -0,0 +1,58 @@ +/* + * 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 { KibanaRequest } from '@kbn/core/server'; + +// Extended version of x-pack/plugins/security/server/authentication/http_authentication/http_authorization_header.ts +// to prevent bundle being required in security_solution +export class HTTPAuthorizationHeader { + /** + * The authentication scheme. Should be consumed in a case-insensitive manner. + * https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml#authschemes + */ + readonly scheme: string; + + /** + * The authentication credentials for the scheme. + */ + readonly credentials: string; + + /** + * The authentication credentials for the scheme. + */ + readonly username: string | undefined; + + constructor(scheme: string, credentials: string, username?: string) { + this.scheme = scheme; + this.credentials = credentials; + this.username = username; + } + + /** + * Parses request's `Authorization` HTTP header if present. + * @param request Request instance to extract the authorization header from. + */ + static parseFromRequest(request: KibanaRequest, username?: string) { + const authorizationHeaderValue = request.headers.authorization; + if (!authorizationHeaderValue || typeof authorizationHeaderValue !== 'string') { + return null; + } + + const [scheme] = authorizationHeaderValue.split(/\s+/); + const credentials = authorizationHeaderValue.substring(scheme.length + 1); + + return new HTTPAuthorizationHeader(scheme, credentials, username); + } + + toString() { + return `${this.scheme} ${this.credentials}`; + } + + getUsername() { + return this.username; + } +} diff --git a/x-pack/plugins/fleet/common/mocks.ts b/x-pack/plugins/fleet/common/mocks.ts index eb45689a73bb6..0784a5a0b36e8 100644 --- a/x-pack/plugins/fleet/common/mocks.ts +++ b/x-pack/plugins/fleet/common/mocks.ts @@ -94,6 +94,14 @@ export const createFleetAuthzMock = (): FleetAuthz => { endpoint: { actions: endpointActions, }, + transform: { + actions: { + canCreateTransform: { executePackageAction: true }, + canDeleteTransform: { executePackageAction: true }, + canGetTransform: { executePackageAction: true }, + canStartStopTransform: { executePackageAction: true }, + }, + }, }, }; }; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 90e0459cb2e54..6e7c9d8606955 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -3402,6 +3402,46 @@ }, "parameters": [] }, + "/message_signing_service/rotate_key_pair": { + "post": { + "summary": "Rotate key pair", + "tags": [ + "Message Signing Service" + ], + "operationId": "rotate-key-pair", + "parameters": [ + { + "schema": { + "type": "boolean" + }, + "in": "query", + "name": "acknowledge", + "required": true, + "description": "When set to true, rotate key pair is done. If set to false or missing, it returns an error." + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + } + } + } + }, "/data_streams": { "get": { "summary": "List data streams", diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index e50525db886d0..a62444586efeb 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -2113,6 +2113,33 @@ paths: parameters: - $ref: '#/components/parameters/kbn_xsrf' parameters: [] + /message_signing_service/rotate_key_pair: + post: + summary: Rotate key pair + tags: + - Message Signing Service + operationId: rotate-key-pair + parameters: + - schema: + type: boolean + in: query + name: acknowledge + required: true + description: >- + When set to true, rotate key pair is done. If set to false or + missing, it returns an error. + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + message: + type: string + '400': + $ref: '#/components/responses/error' /data_streams: get: summary: List data streams diff --git a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml index ca9d1cd3c8e19..ee95d9899a570 100644 --- a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml +++ b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml @@ -105,6 +105,10 @@ paths: /agent_policies/delete: $ref: paths/agent_policies@delete.yaml + # Message signing service + /message_signing_service/rotate_key_pair: + $ref: paths/message_signing_service@rotate_key_pair.yaml + # Data streams endpoints /data_streams: $ref: paths/data_streams.yaml diff --git a/x-pack/plugins/fleet/common/openapi/paths/message_signing_service@rotate_key_pair.yaml b/x-pack/plugins/fleet/common/openapi/paths/message_signing_service@rotate_key_pair.yaml new file mode 100644 index 0000000000000..477967d3cb2b1 --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/paths/message_signing_service@rotate_key_pair.yaml @@ -0,0 +1,24 @@ +post: + summary: Rotate key pair + tags: + - Message Signing Service + operationId: rotate-key-pair + parameters: + - schema: + type: boolean + in: query + name: acknowledge + required: true + description: When set to true, rotate key pair is done. If set to false or missing, it returns an error. + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + message: + type: string + '400': + $ref: ../components/responses/error.yaml \ No newline at end of file diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index 310e53b616d50..70d98abdfac91 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -83,6 +83,12 @@ export const epmRouteService = { pkgVersion ); }, + + getReauthorizeTransformsPath: (pkgName: string, pkgVersion: string) => { + return EPM_API_ROUTES.REAUTHORIZE_TRANSFORMS.replace('{pkgName}', pkgName) + .replace('{pkgVersion}', pkgVersion) + .replace(/\/$/, ''); // trim trailing slash + }, }; export const packagePolicyRouteService = { diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 86dc732e9648e..b425eb11d9918 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -453,6 +453,7 @@ export interface IntegrationCardItem { id: string; categories: string[]; fromIntegrations?: string; + isReauthorizationRequired?: boolean; isUnverified?: boolean; isUpdateAvailable?: boolean; showLabels?: boolean; @@ -539,6 +540,7 @@ export type KibanaAssetReference = Pick & { }; export type EsAssetReference = Pick & { type: ElasticsearchAssetType; + deferred?: boolean; }; export type PackageAssetReference = Pick & { diff --git a/x-pack/plugins/fleet/common/types/models/transform_api_key.ts b/x-pack/plugins/fleet/common/types/models/transform_api_key.ts new file mode 100644 index 0000000000000..6a6a3635b4f68 --- /dev/null +++ b/x-pack/plugins/fleet/common/types/models/transform_api_key.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 type { GrantAPIKeyResult } from '@kbn/security-plugin/server'; + +export interface TransformAPIKey extends GrantAPIKeyResult { + /** + * Generated encoded API key used for headers + */ + encoded: string; +} + +export interface SecondaryAuthorizationHeader { + headers?: { 'es-secondary-authorization': string | string[] }; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index 57154ecb6b00a..e2f2ec5e908f7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -308,6 +308,15 @@ describe('when on the package policy create page', () => { fireEvent.click(renderResult.getByText(/Save and continue/).closest('button')!); }); + await waitFor( + async () => { + expect( + await renderResult.findByText(/Add Elastic Agent to your hosts/) + ).toBeInTheDocument(); + }, + { timeout: 10000 } + ); + await act(async () => { fireEvent.click( renderResult.getByText(/Add Elastic Agent to your hosts/).closest('button')! diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index 9940f17d11492..81c1c518ccd4f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -22,6 +22,11 @@ import { } from '@elastic/eui'; import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; +import { + getNumTransformAssets, + TransformInstallWithCurrentUserPermissionCallout, +} from '../../../../../../components/transform_install_as_current_user_callout'; + import { useCancelAddPackagePolicy } from '../hooks'; import { splitPkgKey } from '../../../../../../../common/services'; @@ -266,6 +271,11 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ ] ); + const numTransformAssets = useMemo( + () => getNumTransformAssets(packageInfo?.assets), + [packageInfo?.assets] + ); + const extensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-create'); const replaceDefineStepView = useUIExtension( packagePolicy.package?.name ?? '', @@ -406,6 +416,12 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ integration={integrationInfo?.name} /> )} + {numTransformAssets > 0 ? ( + <> + + + + ) : null} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx index 2a3d29226371c..d4710e71de9c2 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useState, useMemo } from 'react'; +import React, { memo, useState, useMemo, useCallback } from 'react'; import { EuiPortal, EuiContextMenuItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -24,6 +24,8 @@ import { isAgentUpgradeable, policyHasFleetServer } from '../../../../services'; import { AgentRequestDiagnosticsModal } from '../../components/agent_request_diagnostics_modal'; import { ExperimentalFeaturesService } from '../../../../services'; +import { AgentDetailsJsonFlyout } from './agent_details_json_flyout'; + export const AgentDetailsActionMenu: React.FunctionComponent<{ agent: Agent; agentPolicy?: AgentPolicy; @@ -37,8 +39,17 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false); const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false); const [isRequestDiagnosticsModalOpen, setIsRequestDiagnosticsModalOpen] = useState(false); + const [isAgentDetailsJsonFlyoutOpen, setIsAgentDetailsJsonFlyoutOpen] = useState(false); const isUnenrolling = agent.status === 'unenrolling'; + const [isContextMenuOpen, setIsContextMenuOpen] = useState(false); + const onContextMenuChange = useCallback( + (open: boolean) => { + setIsContextMenuOpen(open); + }, + [setIsContextMenuOpen] + ); + const hasFleetServer = agentPolicy && policyHasFleetServer(agentPolicy); const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get(); @@ -70,6 +81,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ onClick={() => { setIsUnenrollModalOpen(true); }} + key="unenrollAgent" > {isUnenrolling ? ( { setIsUpgradeModalOpen(true); }} + key="upgradeAgent" > , + { + setIsContextMenuOpen(false); + setIsAgentDetailsJsonFlyoutOpen(!isAgentDetailsJsonFlyoutOpen); + }} + key="agentDetailsJson" + > + + , ]; if (diagnosticFileUploadEnabled) { @@ -105,6 +131,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ onClick={() => { setIsRequestDiagnosticsModalOpen(true); }} + key="requestDiagnostics" > )} + {isAgentDetailsJsonFlyoutOpen && ( + + setIsAgentDetailsJsonFlyoutOpen(false)} + /> + + )} { + const agent: Agent = { + id: '123', + packages: [], + type: 'PERMANENT', + active: true, + enrolled_at: `${Date.now()}`, + user_provided_metadata: {}, + local_metadata: {}, + }; + + beforeEach(() => { + mockUseStartServices.mockReturnValue({ + docLinks: { links: { fleet: { troubleshooting: 'https://elastic.co' } } }, + }); + }); + + const renderComponent = () => { + return render(); + }; + + it('renders a title with the agent id if host name is not defined', () => { + const result = renderComponent(); + expect(result.getByText("'123' agent details")).toBeInTheDocument(); + }); + + it('renders a title with the agent host name if defined', () => { + agent.local_metadata = { + host: { + hostname: '456', + }, + }; + const result = renderComponent(); + expect(result.getByText("'456' agent details")).toBeInTheDocument(); + }); + + it('does not add a link to the page after clicking Download', () => { + const result = renderComponent(); + const downloadButton = result.getByRole('button', { name: 'Download JSON' }); + const anchorMocked = { + href: '', + click: jest.fn(), + download: '', + setAttribute: jest.fn(), + } as any; + const createElementSpyOn = jest + .spyOn(document, 'createElement') + .mockReturnValueOnce(anchorMocked); + + downloadButton.click(); + expect(createElementSpyOn).toBeCalledWith('a'); + expect(result.queryAllByRole('link')).toHaveLength(1); // The only link is the one from the flyout's description. + expect(result.getByRole('link')).toHaveAttribute('href', 'https://elastic.co'); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details_json_flyout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details_json_flyout.tsx new file mode 100644 index 0000000000000..093823f108ac6 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details_json_flyout.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiButton, + EuiButtonEmpty, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import type { Agent } from '../../../../types'; +import { useStartServices } from '../../../../hooks'; + +export const AgentDetailsJsonFlyout = memo<{ agent: Agent; onClose: () => void }>( + ({ agent, onClose }) => { + const agentToJson = JSON.stringify(agent, null, 2); + const agentName = + typeof agent.local_metadata?.host?.hostname === 'string' + ? agent.local_metadata.host.hostname + : agent.id; + + const downloadJson = () => { + const link = document.createElement('a'); + link.href = `data:text/json;charset=utf-8,${encodeURIComponent(agentToJson)}`; + link.download = `${agentName}-agent-details.json`; + link.click(); + }; + + const { docLinks } = useStartServices(); + + return ( + + + +

    + +

    +
    +
    + + +

    + + + + ), + }} + /> +

    +
    + + {agentToJson} +
    + + + + + + + + + + + + + + +
    + ); + } +); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 809b87b614148..4af6b30c99fb4 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -30,7 +30,7 @@ import semverCoerce from 'semver/functions/coerce'; import { createStateContainerReactHelpers } from '@kbn/kibana-utils-plugin/public'; import { RedirectAppLinks } from '@kbn/kibana-react-plugin/public'; import type { TimeRange } from '@kbn/es-query'; -import { LogStream } from '@kbn/infra-plugin/public'; +import { LogStream, type LogStreamProps } from '@kbn/infra-plugin/public'; import type { Agent, AgentPolicy } from '../../../../../types'; import { useLink, useStartServices } from '../../../../../hooks'; @@ -50,6 +50,11 @@ const DatePickerFlexItem = styled(EuiFlexItem)` max-width: 312px; `; +const LOG_VIEW_SETTINGS: LogStreamProps['logView'] = { + type: 'log-view-reference', + logViewId: 'default', +}; + export interface AgentLogsProps { agent: Agent; agentPolicy?: AgentPolicy; @@ -350,7 +355,7 @@ export const AgentLogsUI: React.FunctionComponent = memo( + + + + {DEFERRED_ASSETS_WARNING_LABEL} + + + + ); + } + let updateAvailableBadge: React.ReactNode | null = null; if (isUpdateAvailable && showLabels) { @@ -135,10 +160,11 @@ export function PackageCard({ } onClick={onCardClick} > - + {verifiedBadge} {updateAvailableBadge} {releaseBadge} + {hasDeferredInstallationsBadge} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index 8d130c04bac5d..7f852ec15a5da 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -5,18 +5,20 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { Fragment, useEffect, useState } from 'react'; import { Redirect } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, EuiCallOut } from '@elastic/eui'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { groupBy } from 'lodash'; import type { ResolvedSimpleSavedObject } from '@kbn/core/public'; -import { Loading, Error, ExtensionWrapper } from '../../../../../components'; +import type { EsAssetReference } from '../../../../../../../../common'; + +import { Error, ExtensionWrapper, Loading } from '../../../../../components'; import type { PackageInfo } from '../../../../../types'; -import { InstallStatus } from '../../../../../types'; +import { ElasticsearchAssetType, InstallStatus } from '../../../../../types'; import { useGetPackageInstallStatus, @@ -25,11 +27,14 @@ import { useUIExtension, } from '../../../../../hooks'; +import { DeferredAssetsSection } from './deferred_assets_accordion'; + import type { AssetSavedObject } from './types'; import { allowedAssetTypes } from './constants'; import { AssetsAccordion } from './assets_accordion'; const allowedAssetTypesLookup = new Set(allowedAssetTypes); + interface AssetsPanelProps { packageInfo: PackageInfo; } @@ -50,6 +55,8 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { // assume assets are installed in this space until we find otherwise const [assetsInstalledInCurrentSpace, setAssetsInstalledInCurrentSpace] = useState(true); const [assetSavedObjects, setAssetsSavedObjects] = useState(); + const [deferredInstallations, setDeferredInstallations] = useState(); + const [fetchError, setFetchError] = useState(); const [isLoading, setIsLoading] = useState(true); const [hasPermissionError, setHasPermissionError] = useState(false); @@ -73,19 +80,33 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { } = packageInfo; if ( - !packageAttributes.installed_kibana || - packageAttributes.installed_kibana.length === 0 + Array.isArray(packageAttributes.installed_es) && + packageAttributes.installed_es?.length > 0 + ) { + const deferredAssets = packageAttributes.installed_es.filter( + (asset) => asset.deferred === true + ); + setDeferredInstallations(deferredAssets); + } + + const authorizedTransforms = packageAttributes.installed_es.filter( + (asset) => asset.type === ElasticsearchAssetType.transform && !asset.deferred + ); + + if ( + authorizedTransforms.length === 0 && + (!packageAttributes.installed_kibana || packageAttributes.installed_kibana.length === 0) ) { setIsLoading(false); return; } - try { - const objectsToGet = packageAttributes.installed_kibana.map(({ id, type }) => ({ - id, - type, - })); - + const objectsToGet = [...authorizedTransforms, ...packageAttributes.installed_kibana].map( + ({ id, type }) => ({ + id, + type, + }) + ); // We don't have an API to know which SO types a user has access to, so instead we make a request for each // SO type and ignore the 403 errors const objectsByType = await Promise.all( @@ -118,7 +139,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { ) ) ); - setAssetsSavedObjects(objectsByType.flat()); + setAssetsSavedObjects([...objectsByType.flat()]); } catch (e) { setFetchError(e); } finally { @@ -137,7 +158,10 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { return ; } - let content: JSX.Element | Array; + const showDeferredInstallations = + Array.isArray(deferredInstallations) && deferredInstallations.length > 0; + + let content: JSX.Element | Array | null; if (isLoading) { content = ; } else if (fetchError) { @@ -190,7 +214,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { ); } else { - content = ( + content = !showDeferredInstallations ? (

    { />

    - ); + ) : null; } } else { content = [ @@ -211,10 +235,14 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { } return ( - <> - + + - + ); }), // Ensure we add any custom assets provided via UI extension to the end of the list of other assets @@ -225,11 +253,23 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { ) : null, ]; } + const deferredInstallationsContent = showDeferredInstallations ? ( + <> + + + + ) : null; return ( - {content} + + {deferredInstallationsContent} + {content} + ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx index 4a2d64fb84017..ade15c316d1fd 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx @@ -5,26 +5,27 @@ * 2.0. */ -import React from 'react'; import type { FunctionComponent } from 'react'; +import { Fragment } from 'react'; +import React from 'react'; import { EuiAccordion, EuiFlexGroup, EuiFlexItem, - EuiSplitPanel, - EuiSpacer, - EuiText, - EuiLink, EuiHorizontalRule, + EuiLink, EuiNotificationBadge, + EuiSpacer, + EuiSplitPanel, + EuiText, } from '@elastic/eui'; import { AssetTitleMap } from '../../../constants'; import { getHrefToObjectInKibanaApp, useStartServices } from '../../../../../hooks'; -import { KibanaAssetType } from '../../../../../types'; +import { ElasticsearchAssetType, KibanaAssetType } from '../../../../../types'; import type { AllowedAssetType, AssetSavedObject } from './types'; @@ -60,8 +61,8 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } <> - {savedObjects.map(({ id, attributes: { title, description } }, idx) => { - // Ignore custom asset views + {savedObjects.map(({ id, attributes: { title: soTitle, description } }, idx) => { + // Ignore custom asset views or if not a Kibana asset if (type === 'view') { return; } @@ -69,10 +70,11 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } const pathToObjectInApp = getHrefToObjectInKibanaApp({ http, id, - type, + type: type === ElasticsearchAssetType.transform ? undefined : type, }); + const title = soTitle ?? id; return ( - <> +

    @@ -93,7 +95,7 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } )} {idx + 1 < savedObjects.length && } - + ); })} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts index d6d88f7935eb4..e4c773b445baa 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { KibanaAssetType } from '../../../../../types'; +import { ElasticsearchAssetType, KibanaAssetType } from '../../../../../types'; import type { AllowedAssetTypes } from './types'; @@ -13,4 +13,5 @@ export const allowedAssetTypes: AllowedAssetTypes = [ KibanaAssetType.dashboard, KibanaAssetType.search, KibanaAssetType.visualization, + ElasticsearchAssetType.transform, ]; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_assets_accordion.tsx new file mode 100644 index 0000000000000..4a10a360f31de --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_assets_accordion.tsx @@ -0,0 +1,66 @@ +/* + * 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 type { FunctionComponent } from 'react'; + +import { EuiSpacer, EuiCallOut, EuiTitle } from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { useAuthz } from '../../../../../../../hooks'; + +import type { EsAssetReference } from '../../../../../../../../common'; + +import type { PackageInfo } from '../../../../../types'; +import { ElasticsearchAssetType } from '../../../../../types'; + +import { getDeferredInstallationMsg } from './deferred_assets_warning'; + +import { DeferredTransformAccordion } from './deferred_transforms_accordion'; + +interface Props { + packageInfo: PackageInfo; + deferredInstallations: EsAssetReference[]; +} + +export const DeferredAssetsSection: FunctionComponent = ({ + deferredInstallations, + packageInfo, +}) => { + const authz = useAuthz(); + + const deferredTransforms = deferredInstallations.filter( + (asset) => asset.type === ElasticsearchAssetType.transform + ); + return ( + <> + +

    + +

    + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_assets_warning.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_assets_warning.tsx new file mode 100644 index 0000000000000..30fcab820f9b7 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_assets_warning.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiIcon, EuiToolTip } from '@elastic/eui'; + +import type { FleetAuthz } from '../../../../../../../../common'; + +import { useAuthz } from '../../../../../../../hooks'; + +export const DEFERRED_ASSETS_WARNING_LABEL = i18n.translate( + 'xpack.fleet.packageCard.reauthorizationRequiredLabel', + { + defaultMessage: 'Reauthorization required', + } +); + +export const DEFERRED_ASSETS_WARNING_MSG = i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.deferredInstallationsMsg', + { + defaultMessage: + 'This package has at least one deferred installation which requires additional permissions to install and operate correctly.', + } +); + +export const getDeferredInstallationMsg = ( + numOfDeferredInstallations: number | undefined | null, + { authz }: { authz: FleetAuthz } +) => { + const canReauthorizeTransforms = + authz?.packagePrivileges?.transform?.actions?.canStartStopTransform?.executePackageAction ?? + false; + + if (!numOfDeferredInstallations) return DEFERRED_ASSETS_WARNING_MSG; + + if (canReauthorizeTransforms) { + return i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.reauthorizeDeferredInstallationsMsg', + { + defaultMessage: + 'This package has {numOfDeferredInstallations, plural, one {one deferred installation} other {# deferred installations}}. Complete the installation to operate the package correctly.', + values: { numOfDeferredInstallations }, + } + ); + } + + return i18n.translate('xpack.fleet.epm.packageDetails.assets.deferredInstallationsWarning', { + defaultMessage: + 'This package has {numOfDeferredInstallations, plural, one {one deferred installation which requires} other {# deferred installations which require}} additional permissions to install and operate correctly.', + values: { numOfDeferredInstallations }, + }); +}; + +export const DeferredAssetsWarning = ({ + numOfDeferredInstallations, +}: { + numOfDeferredInstallations?: number; +}) => { + const authz = useAuthz(); + + const tooltipContent = useMemo( + () => getDeferredInstallationMsg(numOfDeferredInstallations, { authz }), + [numOfDeferredInstallations, authz] + ); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_transforms_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_transforms_accordion.tsx new file mode 100644 index 0000000000000..42b39f966e836 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/deferred_transforms_accordion.tsx @@ -0,0 +1,278 @@ +/* + * 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, { Fragment, useCallback, useMemo, useState } from 'react'; +import type { FunctionComponent, MouseEvent } from 'react'; + +import { + EuiAccordion, + EuiFlexGroup, + EuiFlexItem, + EuiSplitPanel, + EuiSpacer, + EuiText, + EuiHorizontalRule, + EuiNotificationBadge, + EuiButton, + EuiToolTip, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import type { ElasticsearchErrorDetails } from '@kbn/es-errors'; + +import type { EsAssetReference } from '../../../../../../../../common'; + +import { + sendRequestReauthorizeTransforms, + useAuthz, + useStartServices, +} from '../../../../../../../hooks'; + +import { AssetTitleMap } from '../../../constants'; + +import type { PackageInfo } from '../../../../../types'; +import { ElasticsearchAssetType } from '../../../../../types'; + +interface Props { + packageInfo: PackageInfo; + type: ElasticsearchAssetType.transform; + deferredInstallations: EsAssetReference[]; +} + +export const getDeferredAssetDescription = ( + assetType: string, + assetCount: number, + permissions: { canReauthorizeTransforms: boolean } +) => { + switch (assetType) { + case ElasticsearchAssetType.transform: + if (permissions.canReauthorizeTransforms) { + return i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.deferredTransformReauthorizeDescription', + { + defaultMessage: + '{assetCount, plural, one {Transform was installed but requires} other {# transforms were installed but require}} additional permissions to run. Reauthorize the {assetCount, plural, one {transform} other {transforms}} to start operations.', + values: { assetCount: assetCount ?? 1 }, + } + ); + } + return i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.deferredTransformRequestPermissionDescription', + { + defaultMessage: + '{assetCount, plural, one {Transform was installed but requires} other {# transforms were installed but require}} additional permissions to run. Contact your administrator to request the required privileges.', + values: { assetCount: assetCount ?? 1 }, + } + ); + default: + return i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.deferredInstallationsDescription', + { + defaultMessage: 'Asset requires additional permissions.', + } + ); + } +}; + +export const DeferredTransformAccordion: FunctionComponent = ({ + packageInfo, + type, + deferredInstallations, +}) => { + const { notifications } = useStartServices(); + const [isLoading, setIsLoading] = useState(false); + const deferredTransforms = useMemo( + () => + deferredInstallations.map((i) => ({ + id: i.id, + attributes: { + title: i.id, + description: i.type, + }, + })), + [deferredInstallations] + ); + + const canReauthorizeTransforms = + useAuthz().packagePrivileges?.transform?.actions?.canStartStopTransform?.executePackageAction ?? + false; + + const authorizeTransforms = useCallback( + async (transformIds: Array<{ transformId: string }>) => { + setIsLoading(true); + notifications.toasts.addInfo( + i18n.translate('xpack.fleet.epm.packageDetails.assets.authorizeTransformsAcknowledged', { + defaultMessage: + 'Request to authorize {count, plural, one {# transform} other {# transforms}} acknowledged.', + values: { count: transformIds.length }, + }), + { toastLifeTimeMs: 500 } + ); + + try { + const reauthorizeTransformResp = await sendRequestReauthorizeTransforms( + packageInfo.name, + packageInfo.version, + transformIds + ); + if (reauthorizeTransformResp.error) { + throw reauthorizeTransformResp.error; + } + if (Array.isArray(reauthorizeTransformResp.data)) { + const error = reauthorizeTransformResp.data.find((d) => d.error)?.error; + + const cntAuthorized = reauthorizeTransformResp.data.filter((d) => d.success).length; + if (error) { + const errorBody = error.meta?.body as ElasticsearchErrorDetails; + const errorMsg = errorBody + ? `${errorBody.error?.type}: ${errorBody.error?.reason}` + : `${error.message}`; + + notifications.toasts.addError( + { name: errorMsg, message: errorMsg }, + { + title: i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.authorizeTransformsUnsuccessful', + { + defaultMessage: + 'Unable to authorize {cntUnauthorized, plural, one {# transform} other {# transforms}}.', + values: { cntUnauthorized: transformIds.length - cntAuthorized }, + } + ), + toastLifeTimeMs: 1000, + } + ); + } else { + notifications.toasts.addSuccess( + i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.authorizeTransformsSuccessful', + { + defaultMessage: + 'Successfully authorized {count, plural, one {# transform} other {# transforms}}.', + values: { count: cntAuthorized }, + } + ), + { toastLifeTimeMs: 1000 } + ); + } + } + } catch (e) { + if (e) { + notifications.toasts.addError(e, { + title: i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.unableToAuthorizeAllTransformsError', + { + defaultMessage: 'An error occurred authorizing and starting transforms.', + } + ), + }); + } + } + setIsLoading(false); + }, + [notifications.toasts, packageInfo.name, packageInfo.version] + ); + if (deferredTransforms.length === 0) return null; + return ( + + + +

    {AssetTitleMap[type]}

    +
    +
    + + +

    {deferredTransforms.length}

    +
    +
    +
    + } + id={type} + > + <> + + + + {getDeferredAssetDescription(type, deferredInstallations.length, { + canReauthorizeTransforms, + })}{' '} + + + + ) => { + e.preventDefault(); + authorizeTransforms(deferredTransforms.map((t) => ({ transformId: t.id }))); + }} + aria-label={getDeferredAssetDescription(type, deferredInstallations.length, { + canReauthorizeTransforms, + })} + > + {i18n.translate('xpack.fleet.epm.packageDetails.assets.reauthorizeAllButton', { + defaultMessage: 'Reauthorize all', + })} + + + + + + {deferredTransforms.map(({ id: transformId }, idx) => { + return ( + + + + + +

    {transformId}

    +
    +
    + + + ) => { + e.preventDefault(); + authorizeTransforms([{ transformId }]); + }} + > + {i18n.translate( + 'xpack.fleet.epm.packageDetails.assets.reauthorizeButton', + { + defaultMessage: 'Reauthorize', + } + )} + + + +
    +
    + {idx + 1 < deferredTransforms.length && } +
    + ); + })} +
    + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts index 59ae6636aa8ff..03cf8be0b5c9b 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts @@ -8,13 +8,15 @@ import type { SimpleSavedObject } from '@kbn/core/public'; import type { KibanaAssetType } from '../../../../../types'; +import type { ElasticsearchAssetType } from '../../../../../types'; export type AssetSavedObject = SimpleSavedObject<{ title: string; description?: string }>; export type AllowedAssetTypes = [ KibanaAssetType.dashboard, KibanaAssetType.search, - KibanaAssetType.visualization + KibanaAssetType.visualization, + ElasticsearchAssetType.transform ]; export type AllowedAssetType = AllowedAssetTypes[number] | 'view'; 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 98e4d2c9cce52..0a5a39789a1ec 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 @@ -27,6 +27,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import semverLt from 'semver/functions/lt'; +import { getDeferredInstallationsCnt } from '../../../../../../services/has_deferred_installations'; + import { getPackageReleaseLabel, isPackagePrerelease, @@ -65,6 +67,7 @@ import { import type { WithHeaderLayoutProps } from '../../../../layouts'; import { WithHeaderLayout } from '../../../../layouts'; +import { DeferredAssetsWarning } from './assets/deferred_assets_warning'; import { useIsFirstTimeAgentUserQuery } from './hooks'; import { getInstallPkgRouteOptions } from './utils'; import { @@ -274,6 +277,11 @@ export function Detail() { ? getHref('integrations_installed') : getHref('integrations_all'); + const numOfDeferredInstallations = useMemo( + () => getDeferredInstallationsCnt(packageInfo), + [packageInfo] + ); + const headerLeftContent = useMemo( () => ( @@ -570,10 +578,16 @@ export function Detail() { tabs.push({ id: 'assets', name: ( - +
    + +   + {numOfDeferredInstallations > 0 ? ( + + ) : null} +
    ), isSelected: panel === 'assets', 'data-test-subj': `tab-assets`, @@ -645,6 +659,7 @@ export function Detail() { getHref, integration, canReadIntegrationPolicies, + numOfDeferredInstallations, isInstalled, CustomAssets, canReadPackageSettings, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index f3a89b4e11038..b4eb7a035fe26 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -26,6 +26,11 @@ import { i18n } from '@kbn/i18n'; import type { Observable } from 'rxjs'; import type { CoreTheme } from '@kbn/core/public'; +import { + getNumTransformAssets, + TransformInstallWithCurrentUserPermissionCallout, +} from '../../../../../../../components/transform_install_as_current_user_callout'; + import type { PackageInfo } from '../../../../../types'; import { InstallStatus } from '../../../../../types'; import { @@ -227,9 +232,10 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop const isUpdating = installationStatus === InstallStatus.installing && installedVersion; - const numOfAssets = useMemo( - () => - Object.entries(packageInfo.assets).reduce( + const { numOfAssets, numTransformAssets } = useMemo( + () => ({ + numTransformAssets: getNumTransformAssets(packageInfo.assets), + numOfAssets: Object.entries(packageInfo.assets).reduce( (acc, [serviceName, serviceNameValue]) => acc + Object.entries(serviceNameValue).reduce( @@ -238,6 +244,7 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop ), 0 ), + }), [packageInfo.assets] ); @@ -351,6 +358,15 @@ export const SettingsPage: React.FC = memo(({ packageInfo, theme$ }: Prop + + {numTransformAssets > 0 ? ( + <> + + + + ) : null}

    !!c), + isReauthorizationRequired, isUnverified, isUpdateAvailable, }; diff --git a/x-pack/plugins/fleet/public/components/custom_assets_accordion.tsx b/x-pack/plugins/fleet/public/components/custom_assets_accordion.tsx index 1194cd496c1dc..7c8ac417e066d 100644 --- a/x-pack/plugins/fleet/public/components/custom_assets_accordion.tsx +++ b/x-pack/plugins/fleet/public/components/custom_assets_accordion.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { Fragment } from 'react'; import type { FunctionComponent } from 'react'; import { EuiAccordion, @@ -62,7 +62,7 @@ export const CustomAssetsAccordion: FunctionComponent {views.map((view, index) => ( - <> +

    @@ -78,7 +78,7 @@ export const CustomAssetsAccordion: FunctionComponent {index + 1 < views.length && } - + ))} diff --git a/x-pack/plugins/fleet/public/components/transform_install_as_current_user_callout.tsx b/x-pack/plugins/fleet/public/components/transform_install_as_current_user_callout.tsx new file mode 100644 index 0000000000000..bc2af74ccbb96 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/transform_install_as_current_user_callout.tsx @@ -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 { EuiCallOut } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { uniqBy } from 'lodash'; + +import type { PackageInfo } from '../../common'; + +export const getNumTransformAssets = (assets?: PackageInfo['assets']) => { + if ( + !assets || + !(Array.isArray(assets.elasticsearch?.transform) && assets.elasticsearch?.transform?.length > 0) + ) { + return 0; + } + + return uniqBy(assets.elasticsearch?.transform, 'file').length; +}; +export const TransformInstallWithCurrentUserPermissionCallout: React.FunctionComponent<{ + count: number; +}> = ({ count }) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/constants/index.ts b/x-pack/plugins/fleet/public/constants/index.ts index 2c682af1fcbcc..ccf93339c38d2 100644 --- a/x-pack/plugins/fleet/public/constants/index.ts +++ b/x-pack/plugins/fleet/public/constants/index.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { INGEST_SAVED_OBJECT_INDEX } from '../../common/constants'; + export { PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, @@ -27,7 +29,7 @@ export { export * from './page_paths'; -export const INDEX_NAME = '.kibana'; +export const INDEX_NAME = INGEST_SAVED_OBJECT_INDEX; export const CUSTOM_LOGS_INTEGRATION_NAME = 'log'; diff --git a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts index 7ab1b17b77caa..739fd078fe4db 100644 --- a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts @@ -32,7 +32,7 @@ export const getHrefToObjectInKibanaApp = ({ id, http, }: { - type: KibanaAssetType; + type: KibanaAssetType | undefined; id: string; http: HttpStart; }): undefined | string => { diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index 1a50be67099a5..f050820508d98 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -224,6 +224,18 @@ export const sendRemovePackage = (pkgName: string, pkgVersion: string, force: bo }); }; +export const sendRequestReauthorizeTransforms = ( + pkgName: string, + pkgVersion: string, + transforms: Array<{ transformId: string }> +) => { + return sendRequest({ + path: epmRouteService.getReauthorizeTransformsPath(pkgName, pkgVersion), + method: 'post', + body: { transforms }, + }); +}; + interface UpdatePackageArgs { pkgName: string; pkgVersion: string; diff --git a/x-pack/plugins/fleet/public/services/has_deferred_installations.test.ts b/x-pack/plugins/fleet/public/services/has_deferred_installations.test.ts new file mode 100644 index 0000000000000..f4f39ed7115d1 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/has_deferred_installations.test.ts @@ -0,0 +1,99 @@ +/* + * 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 { EsAssetReference } from '../../common/types'; +import type { PackageInfo } from '../types'; + +import { ElasticsearchAssetType } from '../../common/types'; + +import { hasDeferredInstallations } from './has_deferred_installations'; + +import { ExperimentalFeaturesService } from '.'; + +const mockGet = jest.spyOn(ExperimentalFeaturesService, 'get'); + +const createPackage = ({ + installedEs = [], +}: { + installedEs?: EsAssetReference[]; +} = {}): PackageInfo => ({ + name: 'test-package', + description: 'Test Package', + title: 'Test Package', + version: '0.0.1', + latestVersion: '0.0.1', + release: 'experimental', + format_version: '1.0.0', + owner: { github: 'elastic/fleet' }, + policy_templates: [], + // @ts-ignore + assets: {}, + savedObject: { + id: '1234', + type: 'epm-package', + references: [], + attributes: { + installed_kibana: [], + installed_es: installedEs ?? [], + es_index_patterns: {}, + name: 'test-package', + version: '0.0.1', + install_status: 'installed', + install_version: '0.0.1', + install_started_at: new Date().toString(), + install_source: 'registry', + verification_status: 'verified', + verification_key_id: '', + }, + }, +}); + +describe('isPackageUnverified', () => { + describe('When experimental feature is disabled', () => { + beforeEach(() => { + // @ts-ignore don't want to define all experimental features here + mockGet.mockReturnValue({ + packageVerification: false, + } as ReturnType); + }); + + it('Should return false for a package with no saved object', () => { + const noSoPkg = createPackage(); + // @ts-ignore we know pkg has savedObject but ts doesn't + delete noSoPkg.savedObject; + expect(hasDeferredInstallations(noSoPkg)).toEqual(false); + }); + + it('Should return true for a package with at least one asset deferred', () => { + const pkgWithDeferredInstallations = createPackage({ + installedEs: [ + { id: '', type: ElasticsearchAssetType.ingestPipeline }, + { id: '', type: ElasticsearchAssetType.transform, deferred: true }, + ], + }); + // @ts-ignore we know pkg has savedObject but ts doesn't + expect(hasDeferredInstallations(pkgWithDeferredInstallations)).toEqual(true); + }); + + it('Should return false for a package that has no asset deferred', () => { + const pkgWithoutDeferredInstallations = createPackage({ + installedEs: [ + { id: '', type: ElasticsearchAssetType.ingestPipeline }, + { id: '', type: ElasticsearchAssetType.transform, deferred: false }, + ], + }); + expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false); + }); + + it('Should return false for a package that has no asset', () => { + const pkgWithoutDeferredInstallations = createPackage({ + installedEs: [], + }); + expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false); + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/services/has_deferred_installations.ts b/x-pack/plugins/fleet/public/services/has_deferred_installations.ts new file mode 100644 index 0000000000000..8026ca0ae39a0 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/has_deferred_installations.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 type { PackageInfo, PackageListItem } from '../../common'; + +export const getDeferredInstallationsCnt = (pkg?: PackageInfo | PackageListItem | null): number => { + return pkg && 'savedObject' in pkg && pkg.savedObject + ? pkg.savedObject.attributes?.installed_es?.filter((d) => d.deferred).length + : 0; +}; + +export const hasDeferredInstallations = (pkg?: PackageInfo | PackageListItem | null): boolean => + getDeferredInstallationsCnt(pkg) > 0; diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index ff7c70d8f64e8..a2192cccaea96 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -35,6 +35,8 @@ export { PRECONFIGURATION_API_ROUTES, DOWNLOAD_SOURCE_API_ROOT, DOWNLOAD_SOURCE_API_ROUTES, + // Saved Object indices + INGEST_SAVED_OBJECT_INDEX, // Saved object types SO_SEARCH_LIMIT, AGENTS_PREFIX, @@ -72,6 +74,8 @@ export { FLEET_PROXY_SAVED_OBJECT_TYPE, // Authz ENDPOINT_PRIVILEGES, + // Message signing service + MESSAGE_SIGNING_SERVICE_API_ROUTES, } from '../../common/constants'; export { diff --git a/x-pack/plugins/fleet/server/integration_tests/output_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/output_preconfiguration.test.ts new file mode 100644 index 0000000000000..81a14f6ddec40 --- /dev/null +++ b/x-pack/plugins/fleet/server/integration_tests/output_preconfiguration.test.ts @@ -0,0 +1,144 @@ +/* + * 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 Path from 'path'; + +import { + type TestElasticsearchUtils, + type TestKibanaUtils, + createRootWithCorePlugins, + createTestServers, +} from '@kbn/core-test-helpers-kbn-server'; + +import type { OutputSOAttributes } from '../types'; + +import { useDockerRegistry, waitForFleetSetup } from './helpers'; + +const logFilePath = Path.join(__dirname, 'logs.log'); + +describe('Fleet preconfigured outputs', () => { + let esServer: TestElasticsearchUtils; + let kbnServer: TestKibanaUtils; + + const registryUrl = useDockerRegistry(); + + const startServers = async (outputs: any) => { + const { startES } = createTestServers({ + adjustTimeout: (t) => jest.setTimeout(t), + settings: { + es: { + license: 'trial', + }, + kbn: {}, + }, + }); + + esServer = await startES(); + if (kbnServer) { + await kbnServer.stop(); + } + + const root = createRootWithCorePlugins( + { + xpack: { + fleet: { + outputs, + registryUrl, + }, + }, + logging: { + appenders: { + file: { + type: 'file', + fileName: logFilePath, + layout: { + type: 'json', + }, + }, + }, + loggers: [ + { + name: 'root', + appenders: ['file'], + }, + { + name: 'plugins.fleet', + level: 'all', + }, + ], + }, + }, + { oss: false } + ); + + await root.preboot(); + const coreSetup = await root.setup(); + const coreStart = await root.start(); + + kbnServer = { + root, + coreSetup, + coreStart, + stop: async () => await root.shutdown(), + }; + await waitForFleetSetup(kbnServer.root); + }; + + const stopServers = async () => { + if (kbnServer) { + await kbnServer.stop(); + } + + if (esServer) { + await esServer.stop(); + } + + await new Promise((res) => setTimeout(res, 10000)); + }; + + describe('Preconfigured outputs', () => { + describe('With a preconfigured monitoring output', () => { + beforeAll(async () => { + await startServers([ + { + name: 'Test output', + is_default_monitoring: true, + type: 'elasticsearch', + id: 'output-default-monitoring', + hosts: ['http://elasticsearch-alternative-url:9200'], + }, + ]); + }); + + afterAll(async () => { + await stopServers(); + }); + + it('Should create a default output and the default preconfigured output', async () => { + const outputs = await kbnServer.coreStart.savedObjects + .createInternalRepository() + .find({ + type: 'ingest-outputs', + perPage: 10000, + }); + + expect(outputs.total).toBe(2); + expect(outputs.saved_objects.filter((so) => so.attributes.is_default)).toHaveLength(1); + expect( + outputs.saved_objects.filter((so) => so.attributes.is_default_monitoring) + ).toHaveLength(1); + + const defaultDataOutput = outputs.saved_objects.find((so) => so.attributes.is_default); + const defaultMonitoringOutput = outputs.saved_objects.find( + (so) => so.attributes.is_default_monitoring + ); + expect(defaultDataOutput!.id).not.toBe(defaultMonitoringOutput!.id); + expect(defaultDataOutput!.attributes.is_default_monitoring).toBeFalsy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 8e228f445977e..29489f8b4d877 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -167,5 +167,7 @@ export function createMessageSigningServiceMock() { generateKeyPair: jest.fn(), sign: jest.fn(), getPublicKey: jest.fn(), + removeKeyPair: jest.fn(), + rotateKeyPair: jest.fn(), }; } diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index a31ba8fa5cb70..9601052fc415c 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -15,6 +15,8 @@ import type { import pMap from 'p-map'; import { safeDump } from 'js-yaml'; +import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; + import { fullAgentPolicyToYaml } from '../../../common/services'; import { appContextService, agentPolicyService } from '../../services'; import { getAgentsByKuery } from '../../services/agents'; @@ -175,6 +177,8 @@ export const createAgentPolicyHandler: FleetRequestHandler< const monitoringEnabled = request.body.monitoring_enabled; const { has_fleet_server: hasFleetServer, ...newPolicy } = request.body; const spaceId = fleetContext.spaceId; + const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, user?.username); + try { const body: CreateAgentPolicyResponse = { item: await createAgentPolicyWithPackages({ @@ -186,6 +190,7 @@ export const createAgentPolicyHandler: FleetRequestHandler< monitoringEnabled, spaceId, user, + authorizationHeader, }), }; diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 8bacf87284271..0f2232593fd5e 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -12,6 +12,11 @@ import mime from 'mime-types'; import semverValid from 'semver/functions/valid'; import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/core/server'; +import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; + +import { generateTransformSecondaryAuthHeaders } from '../../services/api_keys/transform_api_keys'; +import { handleTransformReauthorizeAndStart } from '../../services/epm/elasticsearch/transform/reauthorize'; + import type { GetInfoResponse, InstallPackageResponse, @@ -54,12 +59,13 @@ import { } from '../../services/epm/packages'; import type { BulkInstallResponse } from '../../services/epm/packages'; import { defaultFleetErrorHandler, fleetErrorToResponseOptions, FleetError } from '../../errors'; -import { checkAllowedPackages, licenseService } from '../../services'; +import { appContextService, checkAllowedPackages, licenseService } from '../../services'; import { getArchiveEntry } from '../../services/epm/archive/cache'; import { getAsset } from '../../services/epm/archive/storage'; import { getPackageUsageStats } from '../../services/epm/packages/get'; import { updatePackage } from '../../services/epm/packages/update'; import { getGpgKeyIdOrUndefined } from '../../services/epm/packages/package_verification'; +import type { ReauthorizeTransformRequestSchema } from '../../types'; const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = { 'cache-control': 'max-age=600', @@ -282,8 +288,12 @@ export const installPackageFromRegistryHandler: FleetRequestHandler< const fleetContext = await context.fleet; const savedObjectsClient = fleetContext.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; + const { pkgName, pkgVersion } = request.params; + const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, user?.username); + const spaceId = fleetContext.spaceId; const res = await installPackage({ installSource: 'registry', @@ -294,6 +304,7 @@ export const installPackageFromRegistryHandler: FleetRequestHandler< force: request.body?.force, ignoreConstraints: request.body?.ignore_constraints, prerelease: request.query?.prerelease, + authorizationHeader, }); if (!res.error) { @@ -334,6 +345,7 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler< const savedObjectsClient = fleetContext.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const spaceId = fleetContext.spaceId; + const bulkInstalledResponses = await bulkInstallPackages({ savedObjectsClient, esClient, @@ -361,6 +373,7 @@ export const installPackageByUploadHandler: FleetRequestHandler< body: { message: 'Requires Enterprise license' }, }); } + const coreContext = await context.core; const fleetContext = await context.fleet; const savedObjectsClient = fleetContext.internalSoClient; @@ -368,6 +381,10 @@ export const installPackageByUploadHandler: FleetRequestHandler< const contentType = request.headers['content-type'] as string; // from types it could also be string[] or undefined but this is checked later const archiveBuffer = Buffer.from(request.body); const spaceId = fleetContext.spaceId; + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; + + const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, user?.username); + const res = await installPackage({ installSource: 'upload', savedObjectsClient, @@ -375,6 +392,7 @@ export const installPackageByUploadHandler: FleetRequestHandler< archiveBuffer, spaceId, contentType, + authorizationHeader, }); if (!res.error) { const body: InstallPackageResponse = { @@ -432,3 +450,60 @@ export const getVerificationKeyIdHandler: FleetRequestHandler = async ( return defaultFleetErrorHandler({ error, response }); } }; + +/** + * Create transform and optionally start transform + * Note that we want to add the current user's roles/permissions to the es-secondary-auth with a API Key. + * If API Key has insufficient permissions, it should still create the transforms but not start it + * Instead of failing, we need to allow package to continue installing other assets + * and prompt for users to authorize the transforms with the appropriate permissions after package is done installing + */ +export const reauthorizeTransformsHandler: FleetRequestHandler< + TypeOf, + TypeOf, + TypeOf +> = async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = (await context.fleet).internalSoClient; + + const esClient = coreContext.elasticsearch.client.asInternalUser; + const { pkgName, pkgVersion } = request.params; + const { transforms } = request.body; + + let username; + try { + const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); + if (user) { + username = user.username; + } + } catch (e) { + // User might not have permission to get username, or security is not enabled, and that's okay. + } + + try { + const logger = appContextService.getLogger(); + const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, username); + const secondaryAuth = await generateTransformSecondaryAuthHeaders({ + authorizationHeader, + logger, + username, + pkgName, + pkgVersion, + }); + + const resp = await handleTransformReauthorizeAndStart({ + esClient, + savedObjectsClient, + logger, + pkgName, + pkgVersion, + transforms, + secondaryAuth, + username, + }); + + return response.ok({ body: resp }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index 5c2a34cd90551..b707a8b80e582 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -39,6 +39,7 @@ import { GetStatsRequestSchema, UpdatePackageRequestSchema, UpdatePackageRequestSchemaDeprecated, + ReauthorizeTransformRequestSchema, } from '../../types'; import { @@ -54,6 +55,7 @@ import { getStatsHandler, updatePackageHandler, getVerificationKeyIdHandler, + reauthorizeTransformsHandler, } from './handlers'; const MAX_FILE_SIZE_BYTES = 104857600; // 100MB @@ -294,4 +296,26 @@ export const registerRoutes = (router: FleetAuthzRouter) => { return resp; } ); + + // Update transforms with es-secondary-authorization headers, + // append authorized_by to transform's _meta, and start transforms + router.post( + { + path: EPM_API_ROUTES.REAUTHORIZE_TRANSFORMS, + validate: ReauthorizeTransformRequestSchema, + fleetAuthz: { + integrations: { installPackages: true }, + packagePrivileges: { + transform: { + actions: { + canStartStopTransform: { + executePackageAction: true, + }, + }, + }, + }, + }, + }, + reauthorizeTransformsHandler + ); }; diff --git a/x-pack/plugins/fleet/server/routes/index.ts b/x-pack/plugins/fleet/server/routes/index.ts index b5f82f9923535..af577ba7fb29b 100644 --- a/x-pack/plugins/fleet/server/routes/index.ts +++ b/x-pack/plugins/fleet/server/routes/index.ts @@ -24,6 +24,7 @@ import { registerRoutes as registerDownloadSourcesRoutes } from './download_sour import { registerRoutes as registerHealthCheckRoutes } from './health_check'; import { registerRoutes as registerFleetServerHostRoutes } from './fleet_server_policy_config'; import { registerRoutes as registerFleetProxiesRoutes } from './fleet_proxies'; +import { registerRoutes as registerMessageSigningServiceRoutes } from './message_signing_service'; export async function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: FleetConfigType) { // Always register app routes for permissions checking @@ -43,6 +44,7 @@ export async function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: registerFleetProxiesRoutes(fleetAuthzRouter); registerDownloadSourcesRoutes(fleetAuthzRouter); registerHealthCheckRoutes(fleetAuthzRouter); + registerMessageSigningServiceRoutes(fleetAuthzRouter); // Conditional config routes if (config.agents.enabled) { diff --git a/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts new file mode 100644 index 0000000000000..b20a29121f81b --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts @@ -0,0 +1,99 @@ +/* + * 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 { AwaitedProperties } from '@kbn/utility-types'; +import { httpServerMock, coreMock } from '@kbn/core/server/mocks'; +import type { KibanaRequest } from '@kbn/core/server'; + +import { createAppContextStartContractMock, xpackMocks } from '../../mocks'; +import { appContextService } from '../../services/app_context'; +import type { FleetRequestHandlerContext } from '../../types'; + +import { rotateKeyPairHandler } from './handlers'; + +describe('FleetMessageSigningServiceHandler', () => { + let context: AwaitedProperties>; + let response: ReturnType; + let request: KibanaRequest< + undefined, + Readonly<{} & { acknowledge: boolean }> | undefined, + undefined, + any + >; + + beforeEach(async () => { + context = xpackMocks.createRequestHandlerContext(); + request = httpServerMock.createKibanaRequest({ + method: 'post', + path: '/api/fleet/message_signing_service/rotate_key_pair', + query: { acknowledge: true }, + params: {}, + body: {}, + }); + response = httpServerMock.createResponseFactory(); + // prevents `Logger not set.` and other appContext errors + appContextService.start(createAppContextStartContractMock()); + }); + + afterEach(async () => { + jest.clearAllMocks(); + appContextService.stop(); + }); + + it('POST /message_signing_service/rotate_key_pair?acknowledge=true succeeds with an 200 with `acknowledge=true`', async () => { + (appContextService.getMessageSigningService()?.rotateKeyPair as jest.Mock).mockReturnValue( + true + ); + + await rotateKeyPairHandler( + coreMock.createCustomRequestHandlerContext(context), + request, + response + ); + expect(response.ok).toHaveBeenCalledWith({ + body: { + message: 'Key pair rotated successfully.', + }, + }); + }); + + it(`POST /message_signing_service/rotate_key_pair?acknowledge=true fails with an 500 with "acknowledge=true" when rotateKeyPair doesn't succeed`, async () => { + (appContextService.getMessageSigningService()?.rotateKeyPair as jest.Mock).mockReturnValue( + false + ); + + await rotateKeyPairHandler( + coreMock.createCustomRequestHandlerContext(context), + request, + response + ); + expect(response.customError).toHaveBeenCalledWith({ + statusCode: 500, + body: { + message: 'Failed to rotate key pair!', + }, + }); + }); + + it(`POST /message_signing_service/rotate_key_pair?acknowledge=true fails with an 500 with "acknowledge=true" when no messaging service`, async () => { + (appContextService.getMessageSigningService()?.rotateKeyPair as jest.Mock).mockReturnValue( + undefined + ); + + await rotateKeyPairHandler( + coreMock.createCustomRequestHandlerContext(context), + request, + response + ); + expect(response.customError).toHaveBeenCalledWith({ + statusCode: 500, + body: { + message: 'Failed to rotate key pair!', + }, + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts new file mode 100644 index 0000000000000..18a00ca86fdd8 --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts @@ -0,0 +1,42 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; + +import type { FleetRequestHandler } from '../../types'; + +import { defaultFleetErrorHandler } from '../../errors'; +import { appContextService } from '../../services'; +import type { RotateKeyPairSchema } from '../../types/rest_spec/message_signing_service'; + +export const rotateKeyPairHandler: FleetRequestHandler< + undefined, + TypeOf, + undefined +> = async (_, __, response) => { + try { + const rotateKeyPairResponse = await appContextService + .getMessageSigningService() + ?.rotateKeyPair(); + + if (!rotateKeyPairResponse) { + return response.customError({ + statusCode: 500, + body: { + message: 'Failed to rotate key pair!', + }, + }); + } + return response.ok({ + body: { + message: 'Key pair rotated successfully.', + }, + }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/message_signing_service/index.ts b/x-pack/plugins/fleet/server/routes/message_signing_service/index.ts new file mode 100644 index 0000000000000..56c64fb2b4ddf --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/message_signing_service/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 type { FleetAuthzRouter } from '../../services/security'; +import { MESSAGE_SIGNING_SERVICE_API_ROUTES } from '../../constants'; +import { RotateKeyPairSchema } from '../../types'; + +import { rotateKeyPairHandler } from './handlers'; + +export const registerRoutes = (router: FleetAuthzRouter) => { + // Rotate fleet message signing key pair + router.post( + { + path: MESSAGE_SIGNING_SERVICE_API_ROUTES.ROTATE_KEY_PAIR, + validate: RotateKeyPairSchema, + fleetAuthz: { + fleet: { all: true }, + }, + }, + rotateKeyPairHandler + ); +}; diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index 51b4056843d25..9edfad74b7c5b 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -13,6 +13,8 @@ import type { RequestHandler } from '@kbn/core/server'; import { groupBy, keyBy } from 'lodash'; +import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; + import { populatePackagePolicyAssignedAgentsCount } from '../../services/package_policies/populate_package_policy_assigned_agents_count'; import { @@ -219,6 +221,8 @@ export const createPackagePolicyHandler: FleetRequestHandler< const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; const { force, package: pkg, ...newPolicy } = request.body; + const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, user?.username); + if ('output_id' in newPolicy) { // TODO Remove deprecated APIs https://github.com/elastic/kibana/issues/121485 delete newPolicy.output_id; @@ -248,6 +252,7 @@ export const createPackagePolicyHandler: FleetRequestHandler< } // Create package policy + const packagePolicy = await fleetContext.packagePolicyService.asCurrentUser.create( soClient, esClient, @@ -256,6 +261,7 @@ export const createPackagePolicyHandler: FleetRequestHandler< user, force, spaceId, + authorizationHeader, }, context, request diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 8f0db94d1d31c..36fb30eb97893 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -21,6 +21,7 @@ import { FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, FLEET_PROXY_SAVED_OBJECT_TYPE, MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, + INGEST_SAVED_OBJECT_INDEX, } from '../constants'; import { @@ -54,10 +55,8 @@ import { migrateInstallationToV860, migratePackagePolicyToV860, } from './migrations/to_v8_6_0'; -import { - migratePackagePolicyToV870, - migratePackagePolicyToV880, -} from './migrations/security_solution'; +import { migratePackagePolicyToV870 } from './migrations/security_solution'; +import { migratePackagePolicyToV880 } from './migrations/to_v8_8_0'; /* * Saved object types and mappings @@ -69,6 +68,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ // Deprecated [GLOBAL_SETTINGS_SAVED_OBJECT_TYPE]: { name: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -89,6 +89,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [AGENT_POLICY_SAVED_OBJECT_TYPE]: { name: AGENT_POLICY_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -132,6 +133,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [OUTPUT_SAVED_OBJECT_TYPE]: { name: OUTPUT_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -165,6 +167,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [PACKAGE_POLICY_SAVED_OBJECT_TYPE]: { name: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -220,6 +223,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [PACKAGES_SAVED_OBJECT_TYPE]: { name: PACKAGES_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -243,6 +247,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ id: { type: 'keyword' }, type: { type: 'keyword' }, version: { type: 'keyword' }, + deferred: { type: 'boolean' }, }, }, installed_kibana: { @@ -287,6 +292,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [ASSETS_SAVED_OBJECT_TYPE]: { name: ASSETS_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -306,6 +312,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE]: { name: PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -319,6 +326,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE]: { name: DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -335,6 +343,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [FLEET_SERVER_HOST_SAVED_OBJECT_TYPE]: { name: FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -352,6 +361,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [FLEET_PROXY_SAVED_OBJECT_TYPE]: { name: FLEET_PROXY_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', management: { @@ -371,6 +381,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, [MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE]: { name: MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'agnostic', management: { diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts new file mode 100644 index 0000000000000..2c24678184094 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/fixtures/8.7.0.ts @@ -0,0 +1,1100 @@ +/* + * 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 { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../../../common'; + +export const httpPolicy = { + type: 'ingest-package-policies', + id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid http monitor with 4 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'http', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { + value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { value: 'Invalid http monitor with 4 minute schedule', type: 'text' }, + schedule: { value: '"@every 4m"', type: 'text' }, + urls: { value: 'https://elastic.co', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + max_redirects: { value: '0', type: 'integer' }, + proxy_url: { value: '', type: 'text' }, + tags: { value: null, type: 'yaml' }, + username: { value: '', type: 'text' }, + password: { value: '', type: 'password' }, + 'response.include_headers': { value: true, type: 'bool' }, + 'response.include_body': { value: 'on_error', type: 'text' }, + 'check.request.method': { value: 'GET', type: 'text' }, + 'check.request.headers': { value: null, type: 'yaml' }, + 'check.request.body': { value: null, type: 'yaml' }, + 'check.response.status': { value: null, type: 'yaml' }, + 'check.response.headers': { value: null, type: 'yaml' }, + 'check.response.body.positive': { value: null, type: 'yaml' }, + 'check.response.body.negative': { value: null, type: 'yaml' }, + 'ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'ssl.certificate': { value: null, type: 'yaml' }, + 'ssl.key': { value: null, type: 'yaml' }, + 'ssl.key_passphrase': { value: null, type: 'text' }, + 'ssl.verification_mode': { value: null, type: 'text' }, + 'ssl.supported_protocols': { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', type: 'text' }, + config_id: { value: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/http-http-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + type: 'http', + name: 'Invalid http monitor with 4 minute schedule', + id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + urls: 'https://elastic.co', + schedule: '@every 4m', + timeout: '16s', + max_redirects: 0, + 'response.include_headers': true, + 'response.include_body': 'on_error', + 'check.request.method': 'GET', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: 'ce2f0cc6-b082-4080-9ed3-82f38743a3ed', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-ce2f0cc6-b082-4080-9ed3-82f38743a3ed-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:34:51.655Z', + created_by: 'system', + updated_at: '2023-04-19T15:34:51.655Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:34:51.714Z', + created_at: '2023-04-19T15:34:51.714Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; +export const tcpPolicy = { + type: 'ingest-package-policies', + id: '77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid tcp monitor with 8 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'tcp', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { + value: '{"is_tls_enabled":false,"is_zip_url_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { value: 'Invalid tcp monitor with 8 minute schedule', type: 'text' }, + schedule: { value: '"@every 8m"', type: 'text' }, + hosts: { value: 'localhost:5601', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + proxy_url: { value: '', type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { value: null, type: 'yaml' }, + 'check.send': { value: '', type: 'text' }, + 'check.receive': { value: '', type: 'text' }, + 'ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'ssl.certificate': { value: null, type: 'yaml' }, + 'ssl.key': { value: null, type: 'yaml' }, + 'ssl.key_passphrase': { value: null, type: 'text' }, + 'ssl.verification_mode': { value: null, type: 'text' }, + 'ssl.supported_protocols': { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: '77f25200-7cf3-450d-abfa-4f95faae1907', type: 'text' }, + config_id: { value: '77f25200-7cf3-450d-abfa-4f95faae1907', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/tcp-tcp-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + type: 'tcp', + name: 'Invalid tcp monitor with 8 minute schedule', + id: '77f25200-7cf3-450d-abfa-4f95faae1907', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + hosts: 'localhost:5601', + schedule: '@every 8m', + timeout: '16s', + proxy_use_local_resolver: false, + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: '77f25200-7cf3-450d-abfa-4f95faae1907', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-77f25200-7cf3-450d-abfa-4f95faae1907-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:35:26.839Z', + created_by: 'system', + updated_at: '2023-04-19T15:35:26.839Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:35:26.884Z', + created_at: '2023-04-19T15:35:26.884Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; +export const icmpPolicy = { + type: 'ingest-package-policies', + id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + attributes: { + name: 'Invalid ICMP monitor with 11 minute schedule-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'icmp', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { value: 'Invalid ICMP monitor with 11 minute schedule', type: 'text' }, + schedule: { value: '"@every 16m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { value: '1.1.1.1', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: '16s', type: 'text' }, + tags: { value: null, type: 'yaml' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', type: 'text' }, + config_id: { value: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/icmp-icmp-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'icmp', + name: 'Invalid ICMP monitor with 11 minute schedule', + id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + hosts: '1.1.1.1', + schedule: '@every 16m', + wait: '1s', + timeout: '16s', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: 'e437c11c-5cb0-42d1-966f-d2ba231550fd', + }, + }, + }, + ], + }, + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, + 'source.inline.script': { type: 'yaml' }, + 'source.project.content': { type: 'text' }, + params: { type: 'yaml' }, + playwright_options: { type: 'yaml' }, + screenshots: { type: 'text' }, + synthetics_args: { type: 'text' }, + ignore_https_errors: { type: 'bool' }, + 'throttling.config': { type: 'text' }, + 'filter_journeys.tags': { type: 'yaml' }, + 'filter_journeys.match': { type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/browser-browser-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + __ui: null, + type: 'browser', + name: null, + 'run_from.id': 'Fleet managed', + 'run_from.geo.name': 'Fleet managed', + enabled: true, + schedule: '@every 3m', + timeout: null, + throttling: null, + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.network-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { privileges: { indices: ['auto_configure', 'create_doc', 'read'] } }, + }, + id: 'synthetics/browser-browser.screenshot-e437c11c-5cb0-42d1-966f-d2ba231550fd-fa2e69b0-dec6-11ed-8746-c5b1a1a12ec1-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T15:35:53.763Z', + created_by: 'system', + updated_at: '2023-04-19T15:35:53.763Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T15:35:53.793Z', + created_at: '2023-04-19T15:35:53.793Z', + typeMigrationVersion: '8.7.0', +} as unknown as SavedObjectUnsanitizedDoc; + +export const getBrowserPolicy = (throttling = '5d/3u/20l') => + ({ + type: 'ingest-package-policies', + id: '420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + attributes: { + name: 'https://elastic.co-A private location-default', + namespace: 'default', + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.8' }, + enabled: true, + policy_id: 'fe200580-dee2-11ed-933e-0f85f8c5dd40', + inputs: [ + { + type: 'synthetics/http', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'http' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'http', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + urls: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + max_redirects: { type: 'integer' }, + proxy_url: { type: 'text' }, + tags: { type: 'yaml' }, + username: { type: 'text' }, + password: { type: 'password' }, + 'response.include_headers': { type: 'bool' }, + 'response.include_body': { type: 'text' }, + 'check.request.method': { type: 'text' }, + 'check.request.headers': { type: 'yaml' }, + 'check.request.body': { type: 'yaml' }, + 'check.response.status': { type: 'yaml' }, + 'check.response.headers': { type: 'yaml' }, + 'check.response.body.positive': { type: 'yaml' }, + 'check.response.body.negative': { type: 'yaml' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/http-http-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/tcp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'tcp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'tcp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + proxy_url: { type: 'text' }, + proxy_use_local_resolver: { value: false, type: 'bool' }, + tags: { type: 'yaml' }, + 'check.send': { type: 'text' }, + 'check.receive': { type: 'text' }, + 'ssl.certificate_authorities': { type: 'yaml' }, + 'ssl.certificate': { type: 'yaml' }, + 'ssl.key': { type: 'yaml' }, + 'ssl.key_passphrase': { type: 'text' }, + 'ssl.verification_mode': { type: 'text' }, + 'ssl.supported_protocols': { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/tcp-tcp-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/icmp', + policy_template: 'synthetics', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { type: 'synthetics', dataset: 'icmp' }, + vars: { + __ui: { type: 'yaml' }, + enabled: { value: true, type: 'bool' }, + type: { value: 'icmp', type: 'text' }, + name: { type: 'text' }, + schedule: { value: '"@every 3m"', type: 'text' }, + wait: { value: '1s', type: 'text' }, + hosts: { type: 'text' }, + 'service.name': { type: 'text' }, + timeout: { type: 'text' }, + tags: { type: 'yaml' }, + location_name: { value: 'Fleet managed', type: 'text' }, + id: { type: 'text' }, + config_id: { type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { type: 'text' }, + 'monitor.project.id': { type: 'text' }, + 'monitor.project.name': { type: 'text' }, + }, + id: 'synthetics/icmp-icmp-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + }, + ], + }, + { + type: 'synthetics/browser', + policy_template: 'synthetics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + vars: { + __ui: { + value: + '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false,"is_tls_enabled":false}', + type: 'yaml', + }, + enabled: { value: true, type: 'bool' }, + type: { value: 'browser', type: 'text' }, + name: { value: 'https://elastic.co', type: 'text' }, + schedule: { value: '"@every 2m"', type: 'text' }, + 'service.name': { value: '', type: 'text' }, + timeout: { value: null, type: 'text' }, + tags: { value: null, type: 'yaml' }, + 'source.zip_url.url': { value: '', type: 'text' }, + 'source.zip_url.username': { value: '', type: 'text' }, + 'source.zip_url.folder': { value: '', type: 'text' }, + 'source.zip_url.password': { value: '', type: 'password' }, + 'source.inline.script': { + value: + "\"step('Go to https://elastic.co', async () => {\\n await page.goto('https://elastic.co');\\n});\"", + type: 'yaml', + }, + 'source.project.content': { value: '', type: 'text' }, + params: { value: '', type: 'yaml' }, + playwright_options: { value: '', type: 'yaml' }, + screenshots: { value: 'on', type: 'text' }, + synthetics_args: { value: null, type: 'text' }, + ignore_https_errors: { value: false, type: 'bool' }, + 'throttling.config': { value: throttling, type: 'text' }, + 'filter_journeys.tags': { value: null, type: 'yaml' }, + 'filter_journeys.match': { value: null, type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.certificate': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.key': { value: null, type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { value: null, type: 'text' }, + 'source.zip_url.ssl.verification_mode': { value: null, type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { value: null, type: 'yaml' }, + 'source.zip_url.proxy_url': { value: '', type: 'text' }, + location_name: { value: 'A private location', type: 'text' }, + id: { value: '420754e9-40f2-486c-bc2e-265bafd735c5', type: 'text' }, + config_id: { value: '420754e9-40f2-486c-bc2e-265bafd735c5', type: 'text' }, + run_once: { value: false, type: 'bool' }, + origin: { value: 'ui', type: 'text' }, + 'monitor.project.id': { value: null, type: 'text' }, + 'monitor.project.name': { value: null, type: 'text' }, + }, + id: 'synthetics/browser-browser-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + is_tls_enabled: false, + }, + type: 'browser', + name: 'https://elastic.co', + id: '420754e9-40f2-486c-bc2e-265bafd735c5', + origin: 'ui', + 'run_from.id': 'A private location', + 'run_from.geo.name': 'A private location', + enabled: true, + schedule: '@every 2m', + timeout: null, + throttling: throttling === 'false' ? false : throttling, + 'source.inline.script': + "step('Go to https://elastic.co', async () => {\n await page.goto('https://elastic.co');\n});", + screenshots: 'on', + processors: [ + { + add_fields: { + target: '', + fields: { + 'monitor.fleet_managed': true, + config_id: '420754e9-40f2-486c-bc2e-265bafd735c5', + }, + }, + }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.network', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + id: 'synthetics/browser-browser.network-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + { + enabled: true, + data_stream: { + type: 'synthetics', + dataset: 'browser.screenshot', + elasticsearch: { + privileges: { indices: ['auto_configure', 'create_doc', 'read'] }, + }, + }, + id: 'synthetics/browser-browser.screenshot-420754e9-40f2-486c-bc2e-265bafd735c5-fe200580-dee2-11ed-933e-0f85f8c5dd40-default', + compiled_stream: { + processors: [ + { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, + ], + }, + }, + ], + }, + ], + is_managed: true, + revision: 1, + created_at: '2023-04-19T18:55:35.126Z', + created_by: 'system', + updated_at: '2023-04-19T18:55:35.126Z', + updated_by: 'system', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-04-19T18:55:35.250Z', + created_at: '2023-04-19T18:55:35.250Z', + typeMigrationVersion: '8.7.0', + } as unknown as SavedObjectUnsanitizedDoc); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/index.ts new file mode 100644 index 0000000000000..b38d03e10a104 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/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 { migratePackagePolicyToV880 } from './to_v8_8_0'; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts new file mode 100644 index 0000000000000..23d7462872c39 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.test.ts @@ -0,0 +1,167 @@ +/* + * 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 { SavedObjectMigrationContext } from '@kbn/core/server'; + +import { getBrowserPolicy, httpPolicy, icmpPolicy, tcpPolicy } from './fixtures/8.7.0'; + +import { migratePackagePolicyToV880 as migration } from './to_v8_8_0'; + +describe('8.8.0 Synthetics Package Policy migration', () => { + describe('schedule migration', () => { + const testSchedules = [ + ['4', '3'], + ['4.5', '5'], + ['7', '5'], + ['8', '10'], + ['9.5', '10'], + ['12', '10'], + ['13', '15'], + ['16', '15'], + ['18', '20'], + ['21', '20'], + ['25', '20'], + ['26', '30'], + ['31', '30'], + ['45', '30'], + ['46', '60'], + ['61', '60'], + ['90', '60'], + ['91', '120'], + ['121', '120'], + ['195', '240'], + ['600', '240'], + ]; + + it.each(testSchedules)('handles a variety of schedules', (invalidSchedule, validSchedule) => { + const actual = migration( + { + ...httpPolicy, + attributes: { + ...httpPolicy.attributes, + inputs: [ + { + ...httpPolicy.attributes.inputs[0], + streams: [ + { + ...httpPolicy.attributes.inputs[0].streams[0], + vars: { + ...httpPolicy.attributes.inputs[0].streams[0].vars, + schedule: { + value: `"@every ${invalidSchedule}m"`, + type: 'text', + }, + }, + }, + ], + }, + ], + }, + }, + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.vars?.schedule?.value).toEqual( + `"@every ${validSchedule}m"` + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every ${validSchedule}m` + ); + }); + + it('handles browserPolicy with 2 minute', () => { + const actual = migration(getBrowserPolicy(), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 1m"' + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 1m` + ); + }); + + it('handles httpPolicy with 4 minute schedule', () => { + const actual = migration(httpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[0]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 3m"' + ); + expect(actual.attributes?.inputs[0]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 3m` + ); + }); + + it('handles tcp with 8 minute schedule', () => { + const actual = migration(tcpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[1]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 10m"' + ); + expect(actual.attributes?.inputs[1]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 10m` + ); + }); + + it('handles icmpPolicy with 16 minute schedule', () => { + const actual = migration(icmpPolicy, {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[2]?.streams[0]?.vars?.schedule?.value).toEqual( + '"@every 15m"' + ); + expect(actual.attributes?.inputs[2]?.streams[0]?.compiled_stream?.schedule).toEqual( + `@every 15m` + ); + }); + }); + + describe('throttling migration', () => { + it('handles throtling config for throttling: false', () => { + const actual = migration(getBrowserPolicy('false'), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + 'false' + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream?.throttling).toEqual(false); + }); + + it('handles throttling config for default throttling', () => { + const actual = migration(getBrowserPolicy(), {} as SavedObjectMigrationContext); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 5, upload: 3, latency: 20 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 5, + upload: 3, + latency: 20, + }); + }); + + it('handles throttling config for custom throttling', () => { + const actual = migration( + getBrowserPolicy('1.6d/0.75u/150l'), + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 1.6, upload: 0.75, latency: 150 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 1.6, + upload: 0.75, + latency: 150, + }); + }); + + it('handles edge cases', () => { + const actual = migration( + getBrowserPolicy('not a valid value'), + {} as SavedObjectMigrationContext + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.vars?.['throttling.config']?.value).toEqual( + JSON.stringify({ download: 5, upload: 3, latency: 20 }) + ); + expect(actual.attributes?.inputs[3]?.streams[0]?.compiled_stream.throttling).toEqual({ + download: 5, + upload: 3, + latency: 20, + }); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts new file mode 100644 index 0000000000000..872313471ae9b --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/synthetics/to_v8_8_0.ts @@ -0,0 +1,107 @@ +/* + * 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 { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../../common'; + +export const ALLOWED_SCHEDULES_IN_MINUTES = [ + '1', + '3', + '5', + '10', + '15', + '20', + '30', + '60', + '120', + '240', +]; + +export const migratePackagePolicyToV880: SavedObjectMigrationFn = ( + packagePolicyDoc +) => { + if (packagePolicyDoc.attributes.package?.name !== 'synthetics') { + return packagePolicyDoc; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = packagePolicyDoc; + + const enabledInput = updatedPackagePolicyDoc.attributes.inputs.find( + (input) => input.enabled === true + ); + const enabledStream = enabledInput?.streams.find((stream) => { + return ['browser', 'http', 'icmp', 'tcp'].includes(stream.data_stream.dataset); + }); + if (!enabledStream) { + return updatedPackagePolicyDoc; + } + + if ( + enabledStream.vars && + enabledStream.vars.schedule?.value && + enabledStream.compiled_stream?.schedule + ) { + const schedule = enabledStream.vars.schedule.value.match(/\d+\.?\d*/g)?.[0]; + const updatedSchedule = getNearestSupportedSchedule(schedule); + const formattedUpdatedSchedule = `@every ${updatedSchedule}m`; + enabledStream.vars.schedule.value = `"${formattedUpdatedSchedule}"`; + enabledStream.compiled_stream.schedule = formattedUpdatedSchedule; + } + + if ( + enabledStream.data_stream.dataset === 'browser' && + enabledStream.vars?.['throttling.config'] && + enabledStream.compiled_stream?.throttling + ) { + const throttling = enabledStream.vars['throttling.config'].value; + if (throttling) { + const formattedThrottling = handleThrottling(throttling); + enabledStream.vars['throttling.config'].value = JSON.stringify(formattedThrottling); + enabledStream.compiled_stream.throttling = formattedThrottling; + } + } + + return updatedPackagePolicyDoc; +}; + +const handleThrottling = ( + throttling: string +): { download: number; upload: number; latency: number } => { + try { + const [download = 5, upload = 3, latency = 20] = throttling.match(/\d+\.?\d*/g) || []; + return { + download: Number(download), + upload: Number(upload), + latency: Number(latency), + }; + } catch { + return { + download: 5, + upload: 3, + latency: 20, + }; + } +}; + +const getNearestSupportedSchedule = (currentSchedule: string): string => { + try { + const closest = ALLOWED_SCHEDULES_IN_MINUTES.reduce(function (prev, curr) { + const supportedSchedule = parseFloat(curr); + const currSchedule = parseFloat(currentSchedule); + const prevSupportedSchedule = parseFloat(prev); + return Math.abs(supportedSchedule - currSchedule) < + Math.abs(prevSupportedSchedule - currSchedule) + ? curr + : prev; + }); + + return closest; + } catch { + return '10'; + } +}; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts new file mode 100644 index 0000000000000..ab68e4e1c1429 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_8_0.ts @@ -0,0 +1,35 @@ +/* + * 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 { SavedObjectMigrationFn } from '@kbn/core/server'; + +import type { PackagePolicy } from '../../../common'; + +import { migratePackagePolicyToV880 as SecSolMigratePackagePolicyToV880 } from './security_solution'; +import { migratePackagePolicyToV880 as SyntheticsMigratePackagePolicyToV880 } from './synthetics'; + +export const migratePackagePolicyToV880: SavedObjectMigrationFn = ( + packagePolicyDoc, + migrationContext +) => { + let updatedPackagePolicyDoc = packagePolicyDoc; + + // Endpoint specific migrations + if (packagePolicyDoc.attributes.package?.name === 'endpoint') { + updatedPackagePolicyDoc = SecSolMigratePackagePolicyToV880(packagePolicyDoc, migrationContext); + } + + // Synthetics specific migrations + if (packagePolicyDoc.attributes.package?.name === 'synthetics') { + updatedPackagePolicyDoc = SyntheticsMigratePackagePolicyToV880( + packagePolicyDoc, + migrationContext + ); + } + + return updatedPackagePolicyDoc; +}; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.test.ts index 1f5ea87c6d6f9..f093b20eaaeb4 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.test.ts @@ -13,6 +13,7 @@ import type { DataStreamMeta } from './package_policies_to_agent_permissions'; import { getDataStreamPrivileges, storedPackagePoliciesToAgentPermissions, + UNIVERSAL_PROFILING_PERMISSIONS, } from './package_policies_to_agent_permissions'; const packageInfoCache = new Map(); @@ -137,6 +138,56 @@ packageInfoCache.set('osquery_manager-0.3.0', { }, }, }); +packageInfoCache.set('profiler_symbolizer-8.8.0-preview', { + format_version: '2.7.0', + name: 'profiler_symbolizer', + title: 'Universal Profiling Symbolizer', + version: '8.8.0-preview', + license: 'basic', + description: + ' Fleet-wide, whole-system, continuous profiling with zero instrumentation. Symbolize native frames.', + type: 'integration', + release: 'beta', + categories: ['monitoring', 'elastic_stack'], + icons: [ + { + src: '/img/logo_profiling_symbolizer.svg', + title: 'logo symbolizer', + size: '32x32', + type: 'image/svg+xml', + }, + ], + owner: { github: 'elastic/profiling' }, + data_streams: [], + latestVersion: '8.8.0-preview', + notice: undefined, + status: 'not_installed', + assets: { + kibana: { + csp_rule_template: [], + dashboard: [], + visualization: [], + search: [], + index_pattern: [], + map: [], + lens: [], + security_rule: [], + ml_module: [], + tag: [], + osquery_pack_asset: [], + osquery_saved_query: [], + }, + elasticsearch: { + component_template: [], + ingest_pipeline: [], + ilm_policy: [], + transform: [], + index_template: [], + data_stream_ilm_policy: [], + ml_model: [], + }, + }, +}); describe('storedPackagePoliciesToAgentPermissions()', () => { it('Returns `undefined` if there are no package policies', async () => { @@ -363,6 +414,47 @@ describe('storedPackagePoliciesToAgentPermissions()', () => { }, }); }); + + it('Returns the Universal Profiling permissions for profiler_symbolizer package', async () => { + const packagePolicies: PackagePolicy[] = [ + { + id: 'package-policy-uuid-test-123', + name: 'test-policy', + namespace: '', + enabled: true, + package: { name: 'profiler_symbolizer', version: '8.8.0-preview', title: 'Test Package' }, + inputs: [ + { + type: 'pf-elastic-symbolizer', + enabled: true, + streams: [], + }, + ], + created_at: '', + updated_at: '', + created_by: '', + updated_by: '', + revision: 1, + policy_id: '', + }, + ]; + + const permissions = await storedPackagePoliciesToAgentPermissions( + packageInfoCache, + packagePolicies + ); + + expect(permissions).toMatchObject({ + 'package-policy-uuid-test-123': { + indices: [ + { + names: ['profiling-*'], + privileges: UNIVERSAL_PROFILING_PERMISSIONS, + }, + ], + }, + }); + }); }); describe('getDataStreamPrivileges()', () => { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts index 02c44024421ce..f8cd73901e0d7 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { FLEET_UNIVERSAL_PROFILING_SYMBOLIZER_PACKAGE } from '../../../common/constants'; + import { getNormalizedDataStreams } from '../../../common/services'; import type { @@ -19,6 +21,16 @@ import { pkgToPkgKey } from '../epm/registry'; export const DEFAULT_CLUSTER_PERMISSIONS = ['monitor']; +export const UNIVERSAL_PROFILING_PERMISSIONS = [ + 'auto_configure', + 'read', + 'create_doc', + 'create', + 'write', + 'index', + 'view_index_metadata', +]; + export async function storedPackagePoliciesToAgentPermissions( packageInfoCache: Map, packagePolicies?: PackagePolicy[] @@ -42,6 +54,12 @@ export async function storedPackagePoliciesToAgentPermissions( const pkg = packageInfoCache.get(pkgToPkgKey(packagePolicy.package))!; + // Special handling for Universal Profiling packages, as it does not use data streams _only_, + // but also indices that do not adhere to the convention. + if (pkg.name === FLEET_UNIVERSAL_PROFILING_SYMBOLIZER_PACKAGE) { + return Promise.resolve(universalProfilingPermissions(packagePolicy.id)); + } + const dataStreams = getNormalizedDataStreams(pkg); if (!dataStreams || dataStreams.length === 0) { return [packagePolicy.name, undefined]; @@ -175,3 +193,18 @@ export function getDataStreamPrivileges(dataStream: DataStreamMeta, namespace: s privileges, }; } + +async function universalProfilingPermissions(packagePolicyId: string): Promise<[string, any]> { + const profilingIndexPattern = 'profiling-*'; + return [ + packagePolicyId, + { + indices: [ + { + names: [profilingIndexPattern], + privileges: UNIVERSAL_PROFILING_PERMISSIONS, + }, + ], + }, + ]; +} diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 96b8fd55e3b84..aade7b382a7d3 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -22,6 +22,8 @@ import type { BulkResponseItem } from '@elastic/elasticsearch/lib/api/typesWithB import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; +import type { HTTPAuthorizationHeader } from '../../common/http_authorization_header'; + import { AGENT_POLICY_SAVED_OBJECT_TYPE, AGENTS_PREFIX, @@ -209,7 +211,11 @@ class AgentPolicyService { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, agentPolicy: NewAgentPolicy, - options: { id?: string; user?: AuthenticatedUser } = {} + options: { + id?: string; + user?: AuthenticatedUser; + authorizationHeader?: HTTPAuthorizationHeader | null; + } = {} ): Promise { // Ensure an ID is provided, so we can include it in the audit logs below if (!options.id) { @@ -444,7 +450,12 @@ class AgentPolicyService { esClient: ElasticsearchClient, id: string, agentPolicy: Partial, - options?: { user?: AuthenticatedUser; force?: boolean; spaceId?: string } + options?: { + user?: AuthenticatedUser; + force?: boolean; + spaceId?: string; + authorizationHeader?: HTTPAuthorizationHeader | null; + } ): Promise { if (agentPolicy.name) { await this.requireUniqueName(soClient, { @@ -479,6 +490,7 @@ class AgentPolicyService { esClient, packagesToInstall, spaceId: options?.spaceId || DEFAULT_SPACE_ID, + authorizationHeader: options?.authorizationHeader, }); } diff --git a/x-pack/plugins/fleet/server/services/agent_policy_create.ts b/x-pack/plugins/fleet/server/services/agent_policy_create.ts index 1f7aeb2bc985f..11cb82123a347 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy_create.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy_create.ts @@ -9,6 +9,8 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/ import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { HTTPAuthorizationHeader } from '../../common/http_authorization_header'; + import { FLEET_ELASTIC_AGENT_PACKAGE, FLEET_SERVER_PACKAGE, @@ -48,7 +50,11 @@ async function createPackagePolicy( esClient: ElasticsearchClient, agentPolicy: AgentPolicy, packageToInstall: string, - options: { spaceId: string; user: AuthenticatedUser | undefined } + options: { + spaceId: string; + user: AuthenticatedUser | undefined; + authorizationHeader?: HTTPAuthorizationHeader | null; + } ) { const newPackagePolicy = await packagePolicyService .buildPackagePolicyFromPackage(soClient, packageToInstall) @@ -71,6 +77,7 @@ async function createPackagePolicy( spaceId: options.spaceId, user: options.user, bumpRevision: false, + authorizationHeader: options.authorizationHeader, }); } @@ -83,6 +90,7 @@ interface CreateAgentPolicyParams { monitoringEnabled?: string[]; spaceId: string; user?: AuthenticatedUser; + authorizationHeader?: HTTPAuthorizationHeader | null; } export async function createAgentPolicyWithPackages({ @@ -94,6 +102,7 @@ export async function createAgentPolicyWithPackages({ monitoringEnabled, spaceId, user, + authorizationHeader, }: CreateAgentPolicyParams) { let agentPolicyId = newPolicy.id; const packagesToInstall = []; @@ -118,6 +127,7 @@ export async function createAgentPolicyWithPackages({ esClient, packagesToInstall, spaceId, + authorizationHeader, }); } @@ -126,6 +136,7 @@ export async function createAgentPolicyWithPackages({ const agentPolicy = await agentPolicyService.create(soClient, esClient, policy, { user, id: agentPolicyId, + authorizationHeader, }); // Create the fleet server package policy and add it to agent policy. @@ -133,6 +144,7 @@ export async function createAgentPolicyWithPackages({ await createPackagePolicy(soClient, esClient, agentPolicy, FLEET_SERVER_PACKAGE, { spaceId, user, + authorizationHeader, }); } @@ -141,6 +153,7 @@ export async function createAgentPolicyWithPackages({ await createPackagePolicy(soClient, esClient, agentPolicy, FLEET_SYSTEM_PACKAGE, { spaceId, user, + authorizationHeader, }); } diff --git a/x-pack/plugins/fleet/server/services/api_keys/transform_api_keys.ts b/x-pack/plugins/fleet/server/services/api_keys/transform_api_keys.ts new file mode 100644 index 0000000000000..f75c8d5677b28 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/api_keys/transform_api_keys.ts @@ -0,0 +1,123 @@ +/* + * 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 { CreateAPIKeyParams } from '@kbn/security-plugin/server'; +import type { FakeRawRequest, Headers } from '@kbn/core-http-server'; +import { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; + +import type { Logger } from '@kbn/logging'; + +import { appContextService } from '..'; + +import type { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; + +import type { + TransformAPIKey, + SecondaryAuthorizationHeader, +} from '../../../common/types/models/transform_api_key'; + +export function isTransformApiKey(arg: any): arg is TransformAPIKey { + return ( + arg && + arg.hasOwnProperty('api_key') && + arg.hasOwnProperty('encoded') && + typeof arg.encoded === 'string' + ); +} + +function createKibanaRequestFromAuth(authorizationHeader: HTTPAuthorizationHeader) { + const requestHeaders: Headers = { + authorization: authorizationHeader.toString(), + }; + const fakeRawRequest: FakeRawRequest = { + headers: requestHeaders, + path: '/', + }; + + // Since we're using API keys and accessing elasticsearch can only be done + // via a request, we're faking one with the proper authorization headers. + const fakeRequest = CoreKibanaRequest.from(fakeRawRequest); + + return fakeRequest; +} + +/** This function generates a new API based on current Kibana's user request.headers.authorization + * then formats it into a es-secondary-authorization header object + * @param authorizationHeader: + * @param createParams + */ +export async function generateTransformSecondaryAuthHeaders({ + authorizationHeader, + createParams, + logger, + username, + pkgName, + pkgVersion, +}: { + authorizationHeader: HTTPAuthorizationHeader | null | undefined; + logger: Logger; + createParams?: CreateAPIKeyParams; + username?: string; + pkgName?: string; + pkgVersion?: string; +}): Promise { + if (!authorizationHeader) { + return; + } + + const fakeKibanaRequest = createKibanaRequestFromAuth(authorizationHeader); + + const user = username ?? authorizationHeader.getUsername(); + + const name = pkgName + ? `${pkgName}${pkgVersion ? '-' + pkgVersion : ''}-transform${user ? '-by-' + user : ''}` + : `fleet-transform-api-key`; + + const security = appContextService.getSecurity(); + + // If security is not enabled or available, we can't generate api key + // but that's ok, cause all the index and transform commands should work + if (!security) return; + + try { + const apiKeyWithCurrentUserPermission = await security?.authc.apiKeys.grantAsInternalUser( + fakeKibanaRequest, + createParams ?? { + name, + metadata: { + managed_by: 'fleet', + managed: true, + type: 'transform', + }, + role_descriptors: {}, + } + ); + logger.debug(`Created api_key name: ${name}`); + let encodedApiKey: TransformAPIKey['encoded'] | null = null; + + // Property 'encoded' does exist in the resp coming back from request + // and is required to use in authentication headers + // It's just not defined in returned GrantAPIKeyResult type + if (isTransformApiKey(apiKeyWithCurrentUserPermission)) { + encodedApiKey = apiKeyWithCurrentUserPermission.encoded; + } + + const secondaryAuth = + encodedApiKey !== null + ? { + headers: { + 'es-secondary-authorization': `ApiKey ${encodedApiKey}`, + }, + } + : undefined; + + return secondaryAuth; + } catch (e) { + logger.debug(`Failed to create api_key: ${name} because ${e}`); + return undefined; + } +} diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/index/update_settings.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/index/update_settings.ts index a381f19a3d9fa..766d03f6a776c 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/index/update_settings.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/index/update_settings.ts @@ -9,6 +9,8 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { IndicesIndexSettings } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { appContextService } from '../../..'; + import { retryTransientEsErrors } from '../retry'; export async function updateIndexSettings( @@ -16,6 +18,8 @@ export async function updateIndexSettings( index: string, settings: IndicesIndexSettings ): Promise { + const logger = appContextService.getLogger(); + if (index) { try { await retryTransientEsErrors(() => @@ -25,7 +29,8 @@ export async function updateIndexSettings( }) ); } catch (err) { - throw new Error(`could not update index settings for ${index}`); + // No need to throw error and block installation process + logger.debug(`Could not update index settings for ${index} because ${err}`); } } } diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts index dc775d6f52e01..6f4724642e9f3 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/install.ts @@ -11,6 +11,12 @@ import { safeLoad } from 'js-yaml'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { uniqBy } from 'lodash'; +import type { HTTPAuthorizationHeader } from '../../../../../common/http_authorization_header'; + +import type { SecondaryAuthorizationHeader } from '../../../../../common/types/models/transform_api_key'; + +import { generateTransformSecondaryAuthHeaders } from '../../../api_keys/transform_api_keys'; + import { PACKAGE_TEMPLATE_SUFFIX, USER_SETTINGS_TEMPLATE_SUFFIX, @@ -36,7 +42,8 @@ import { getInstallation } from '../../packages'; import { retryTransientEsErrors } from '../retry'; import { deleteTransforms } from './remove'; -import { getAsset, TRANSFORM_DEST_IDX_ALIAS_LATEST_SFX } from './common'; +import { getAsset } from './common'; +import { getDestinationIndexAliases } from './transform_utils'; const DEFAULT_TRANSFORM_TEMPLATES_PRIORITY = 250; enum TRANSFORM_SPECS_TYPES { @@ -58,6 +65,7 @@ interface TransformInstallation extends TransformModuleBase { content: any; transformVersion?: string; installationOrder?: number; + runAsKibanaSystem?: boolean; } const installLegacyTransformsAssets = async ( @@ -137,7 +145,9 @@ const processTransformAssetsPerModule = ( installablePackage: InstallablePackage, installNameSuffix: string, transformPaths: string[], - previousInstalledTransformEsAssets: EsAssetReference[] = [] + previousInstalledTransformEsAssets: EsAssetReference[] = [], + force?: boolean, + username?: string ) => { const transformsSpecifications = new Map(); const destinationIndexTemplates: DestinationIndexTemplateInstallation[] = []; @@ -195,10 +205,6 @@ const processTransformAssetsPerModule = ( const installationOrder = isFinite(content._meta?.order) && content._meta?.order >= 0 ? content._meta?.order : 0; const transformVersion = content._meta?.fleet_transform_version ?? '0.1.0'; - // The “all” alias for the transform destination indices will be adjusted to include the new transform destination index as well as everything it previously included - const allIndexAliasName = `${content.dest.index}.all`; - // The “latest” alias for the transform destination indices will point solely to the new transform destination index - const latestIndexAliasName = `${content.dest.index}.latest`; transformsSpecifications .get(transformModuleId) @@ -206,24 +212,30 @@ const processTransformAssetsPerModule = ( // Create two aliases associated with the destination index // for better handling during upgrades - const alias = { - [allIndexAliasName]: {}, - [latestIndexAliasName]: {}, - }; + const aliases = getDestinationIndexAliases(content.dest.aliases); + const aliasNames = aliases.map((a) => a.alias); + // Override yml settings with alia format for transform's dest.aliases + content.dest.aliases = aliases; - const versionedIndexName = `${content.dest.index}-${installNameSuffix}`; - content.dest.index = versionedIndexName; indicesToAddRefs.push({ - id: versionedIndexName, + id: content.dest.index, type: ElasticsearchAssetType.index, }); + + // If run_as_kibana_system is not set, or is set to true, then run as kibana_system user + // else, run with user's secondary credentials + const runAsKibanaSystem = content._meta?.run_as_kibana_system !== false; + transformsSpecifications.get(transformModuleId)?.set('destinationIndex', content.dest); - transformsSpecifications.get(transformModuleId)?.set('destinationIndexAlias', alias); + transformsSpecifications.get(transformModuleId)?.set('destinationIndexAlias', aliases); transformsSpecifications.get(transformModuleId)?.set('transform', content); transformsSpecifications.get(transformModuleId)?.set('transformVersion', transformVersion); + content._meta = { ...(content._meta ?? {}), ...getESAssetMetadata({ packageName: installablePackage.name }), + ...(username ? { installed_by: username } : {}), + run_as_kibana_system: runAsKibanaSystem, }; const installationName = getTransformAssetNameForInstallation( @@ -233,43 +245,56 @@ const processTransformAssetsPerModule = ( `default-${transformVersion}` ); - const currentTransformSameAsPrev = - previousInstalledTransformEsAssets.find((t) => t.id === installationName) !== undefined; + // Here, we track if fleet_transform_version (not package version) has changed based on installation name + // if version has changed, install transform and update es assets + // else, don't delete the dest index and install transform as it can be an expensive operation + const matchingTransformFromPrevInstall = previousInstalledTransformEsAssets.find( + (t) => t.id === installationName + ); + + const currentTransformSameAsPrev = matchingTransformFromPrevInstall !== undefined; if (previousInstalledTransformEsAssets.length === 0) { - aliasesRefs.push(allIndexAliasName, latestIndexAliasName); + aliasesRefs.push(...aliasNames); transforms.push({ transformModuleId, installationName, installationOrder, transformVersion, content, + runAsKibanaSystem, }); transformsSpecifications.get(transformModuleId)?.set('transformVersionChanged', true); } else { - if (!currentTransformSameAsPrev) { - // If upgrading from old json schema to new yml schema - // We need to make sure to delete those transforms by matching the legacy naming convention - const versionFromOldJsonSchema = previousInstalledTransformEsAssets.find((t) => - t.id.startsWith( - getLegacyTransformNameForInstallation( - installablePackage, - `${transformModuleId}/default.json` + if (force || !currentTransformSameAsPrev) { + // If we are reinstalling the package (i.e. force = true), + // force delete old transforms so we can reinstall the same transforms again + if (force && matchingTransformFromPrevInstall) { + transformsToRemoveWithDestIndex.push(matchingTransformFromPrevInstall); + } else { + // If upgrading from old json schema to new yml schema + // We need to make sure to delete those transforms by matching the legacy naming convention + const versionFromOldJsonSchema = previousInstalledTransformEsAssets.find((t) => + t.id.startsWith( + getLegacyTransformNameForInstallation( + installablePackage, + `${transformModuleId}/default.json` + ) ) - ) - ); + ); - if (versionFromOldJsonSchema !== undefined) { - transformsToRemoveWithDestIndex.push(versionFromOldJsonSchema); - } + if (versionFromOldJsonSchema !== undefined) { + transformsToRemoveWithDestIndex.push(versionFromOldJsonSchema); + } - // If upgrading from yml to newer version of yaml - // Match using new naming convention - const installNameWithoutVersion = installationName.split(transformVersion)[0]; - const prevVersion = previousInstalledTransformEsAssets.find((t) => - t.id.startsWith(installNameWithoutVersion) - ); - if (prevVersion !== undefined) { - transformsToRemove.push(prevVersion); + // If upgrading from yml to newer version of yaml + // Match using new naming convention + const installNameWithoutVersion = installationName.split(transformVersion)[0]; + const prevVersion = previousInstalledTransformEsAssets.find((t) => + t.id.startsWith(installNameWithoutVersion) + ); + if (prevVersion !== undefined) { + transformsToRemove.push(prevVersion); + } } transforms.push({ transformModuleId, @@ -277,9 +302,12 @@ const processTransformAssetsPerModule = ( installationOrder, transformVersion, content, + runAsKibanaSystem, }); transformsSpecifications.get(transformModuleId)?.set('transformVersionChanged', true); - aliasesRefs.push(allIndexAliasName, latestIndexAliasName); + if (aliasNames.length > 0) { + aliasesRefs.push(...aliasNames); + } } else { transformsSpecifications.get(transformModuleId)?.set('transformVersionChanged', false); } @@ -371,9 +399,13 @@ const installTransformsAssets = async ( savedObjectsClient: SavedObjectsClientContract, logger: Logger, esReferences: EsAssetReference[] = [], - previousInstalledTransformEsAssets: EsAssetReference[] = [] + previousInstalledTransformEsAssets: EsAssetReference[] = [], + force?: boolean, + authorizationHeader?: HTTPAuthorizationHeader | null ) => { let installedTransforms: EsAssetReference[] = []; + const username = authorizationHeader?.getUsername(); + if (transformPaths.length > 0) { const { indicesToAddRefs, @@ -383,23 +415,31 @@ const installTransformsAssets = async ( transforms, destinationIndexTemplates, transformsSpecifications, - aliasesRefs, transformsToRemove, transformsToRemoveWithDestIndex, } = processTransformAssetsPerModule( installablePackage, installNameSuffix, transformPaths, - previousInstalledTransformEsAssets + previousInstalledTransformEsAssets, + force, + username ); - // ensure the .latest alias points to only the latest - // by removing any associate of old destination indices - await Promise.all( - aliasesRefs - .filter((a) => a.endsWith(TRANSFORM_DEST_IDX_ALIAS_LATEST_SFX)) - .map((alias) => deleteAliasFromIndices({ esClient, logger, alias })) - ); + // By default, for internal Elastic packages that touch system indices, we want to run as internal user + // so we set runAsKibanaSystem: true by default (e.g. when run_as_kibana_system set to true/not defined in yml file). + // If package should be installed as the logged in user, set run_as_kibana_system: false, + // generate api key, and pass es-secondary-authorization in header when creating the transforms. + const secondaryAuth = transforms.some((t) => t.runAsKibanaSystem === false) + ? await generateTransformSecondaryAuthHeaders({ + authorizationHeader, + logger, + pkgName: installablePackage.name, + pkgVersion: installablePackage.version, + username, + }) + : // No need to generate api key/secondary auth if all transforms are run as kibana_system user + undefined; // delete all previous transform await Promise.all([ @@ -407,13 +447,15 @@ const installTransformsAssets = async ( esClient, transformsToRemoveWithDestIndex.map((asset) => asset.id), // Delete destination indices if specified or if from old json schema - true + true, + secondaryAuth ), deleteTransforms( esClient, transformsToRemove.map((asset) => asset.id), // Else, keep destination indices by default - false + false, + secondaryAuth ), ]); @@ -492,58 +534,6 @@ const installTransformsAssets = async ( .filter((p) => p !== undefined) ); - // create destination indices - await Promise.all( - transforms.map(async (transform) => { - const index = transform.content.dest.index; - - const aliases = transformsSpecifications - .get(transform.transformModuleId) - ?.get('destinationIndexAlias'); - try { - const resp = await retryTransientEsErrors( - () => - esClient.indices.create( - { - index, - aliases, - }, - { ignore: [400] } - ), - { logger } - ); - logger.debug(`Created destination index: ${index}`); - - // If index already exists, we still need to update the destination index alias - // to point '{destinationIndexName}.latest' to the versioned index - // @ts-ignore status is a valid field of resp - if (resp.status === 400 && aliases) { - await retryTransientEsErrors( - () => - esClient.indices.updateAliases({ - body: { - actions: Object.keys(aliases).map((alias) => ({ add: { index, alias } })), - }, - }), - { logger } - ); - logger.debug(`Created aliases for destination index: ${index}`); - } - } catch (err) { - logger.error( - `Error creating destination index: ${JSON.stringify({ - index, - aliases: transformsSpecifications - .get(transform.transformModuleId) - ?.get('destinationIndexAlias'), - })} with error ${err}` - ); - - throw new Error(err.message); - } - }) - ); - // If the transforms have specific installation order, install & optionally start transforms sequentially const shouldInstallSequentially = uniqBy(transforms, 'installationOrder').length === transforms.length; @@ -555,6 +545,7 @@ const installTransformsAssets = async ( logger, transform, startTransform: transformsSpecifications.get(transform.transformModuleId)?.get('start'), + secondaryAuth: transform.runAsKibanaSystem !== false ? undefined : secondaryAuth, }); installedTransforms.push(installTransform); } @@ -566,23 +557,62 @@ const installTransformsAssets = async ( logger, transform, startTransform: transformsSpecifications.get(transform.transformModuleId)?.get('start'), + secondaryAuth: transform.runAsKibanaSystem !== false ? undefined : secondaryAuth, }); }); installedTransforms = await Promise.all(transformsPromises).then((results) => results.flat()); } + + // If user does not have sufficient permissions to start the transforms, + // we need to mark them as deferred installations without blocking full package installation + // so that they can be updated/re-authorized later + + if (installedTransforms.length > 0) { + // get and save refs associated with the transforms before installing + esReferences = await updateEsAssetReferences( + savedObjectsClient, + installablePackage.name, + esReferences, + { + assetsToRemove: installedTransforms, + assetsToAdd: installedTransforms, + } + ); + } } return { installedTransforms, esReferences }; }; -export const installTransforms = async ( - installablePackage: InstallablePackage, - paths: string[], - esClient: ElasticsearchClient, - savedObjectsClient: SavedObjectsClientContract, - logger: Logger, - esReferences?: EsAssetReference[] -) => { + +interface InstallTransformsParams { + installablePackage: InstallablePackage; + paths: string[]; + esClient: ElasticsearchClient; + savedObjectsClient: SavedObjectsClientContract; + logger: Logger; + esReferences?: EsAssetReference[]; + /** + * Force transforms to install again even though fleet_transform_version might be same + * Should be true when package is re-installing + */ + force?: boolean; + /** + * Authorization header parsed from original Kibana request, used to generate API key from user + * to pass in secondary authorization info to transform + */ + authorizationHeader?: HTTPAuthorizationHeader | null; +} +export const installTransforms = async ({ + installablePackage, + paths, + esClient, + savedObjectsClient, + logger, + force, + esReferences, + authorizationHeader, +}: InstallTransformsParams) => { const transformPaths = paths.filter((path) => isTransform(path)); const installation = await getInstallation({ @@ -620,6 +650,7 @@ export const installTransforms = async ( ); } + // If package contains yml transform specifications return await installTransformsAssets( installablePackage, installNameSuffix, @@ -628,7 +659,9 @@ export const installTransforms = async ( savedObjectsClient, logger, esReferences, - previousInstalledTransformEsAssets + previousInstalledTransformEsAssets, + force, + authorizationHeader ); }; @@ -637,65 +670,59 @@ export const isTransform = (path: string) => { return !path.endsWith('/') && pathParts.type === ElasticsearchAssetType.transform; }; -async function deleteAliasFromIndices({ - esClient, - logger, - alias, -}: { - esClient: ElasticsearchClient; - logger: Logger; - alias: string; -}) { - try { - const resp = await esClient.indices.getAlias({ name: alias }); - const indicesMatchingAlias = Object.keys(resp); - logger.debug(`Deleting alias: '${alias}' matching indices ${indicesMatchingAlias}`); - - if (indicesMatchingAlias.length > 0) { - await retryTransientEsErrors( - () => - // defer validation on put if the source index is not available - esClient.indices.deleteAlias( - { index: indicesMatchingAlias, name: alias }, - { ignore: [404] } - ), - { logger } - ); - logger.debug(`Deleted alias: '${alias}' matching indices ${indicesMatchingAlias}`); - } - } catch (err) { - logger.error(`Error deleting alias: ${alias}`); - } +interface TransformEsAssetReference extends EsAssetReference { + version?: string; } +/** + * Create transform and optionally start transform + * Note that we want to add the current user's roles/permissions to the es-secondary-auth with a API Key. + * If API Key has insufficient permissions, it should still create the transforms but not start it + * Instead of failing, we need to allow package to continue installing other assets + * and prompt for users to authorize the transforms with the appropriate permissions after package is done installing + */ async function handleTransformInstall({ esClient, logger, transform, startTransform, + secondaryAuth, }: { esClient: ElasticsearchClient; logger: Logger; transform: TransformInstallation; startTransform?: boolean; -}): Promise { + secondaryAuth?: SecondaryAuthorizationHeader; +}): Promise { + let isUnauthorizedAPIKey = false; try { await retryTransientEsErrors( () => - // defer validation on put if the source index is not available - esClient.transform.putTransform({ - transform_id: transform.installationName, - defer_validation: true, - body: transform.content, - }), + // defer_validation: true on put if the source index is not available + // but will check if API Key has sufficient permission + esClient.transform.putTransform( + { + transform_id: transform.installationName, + defer_validation: true, + body: transform.content, + }, + // add '{ headers: { es-secondary-authorization: 'ApiKey {encodedApiKey}' } }' + secondaryAuth ? { ...secondaryAuth } : undefined + ), { logger } ); logger.debug(`Created transform: ${transform.installationName}`); } catch (err) { - // swallow the error if the transform already exists. + const isResponseError = err instanceof errors.ResponseError; + isUnauthorizedAPIKey = + isResponseError && + err?.body?.error?.type === 'security_exception' && + err?.body?.error?.reason?.includes('unauthorized for API key'); + const isAlreadyExistError = - err instanceof errors.ResponseError && - err?.body?.error?.type === 'resource_already_exists_exception'; - if (!isAlreadyExistError) { + isResponseError && err?.body?.error?.type === 'resource_already_exists_exception'; + + // swallow the error if the transform already exists or if API key has insufficient permissions + if (!isUnauthorizedAPIKey && !isAlreadyExistError) { throw err; } } @@ -703,18 +730,71 @@ async function handleTransformInstall({ // start transform by default if not set in yml file // else, respect the setting if (startTransform === undefined || startTransform === true) { - await retryTransientEsErrors( - () => - esClient.transform.startTransform( - { transform_id: transform.installationName }, - { ignore: [409] } - ), - { logger, additionalResponseStatuses: [400] } - ); - logger.debug(`Started transform: ${transform.installationName}`); + try { + await retryTransientEsErrors( + () => + esClient.transform.startTransform( + { transform_id: transform.installationName }, + { ignore: [409] } + ), + { logger, additionalResponseStatuses: [400] } + ); + logger.debug(`Started transform: ${transform.installationName}`); + } catch (err) { + const isResponseError = err instanceof errors.ResponseError; + isUnauthorizedAPIKey = + isResponseError && + // if transform was created with insufficient permission, + // _start will yield an error + err?.body?.error?.type === 'security_exception' && + err?.body?.error?.reason?.includes('lacks the required permissions'); + + // swallow the error if the transform can't be started if API key has insufficient permissions + if (!isUnauthorizedAPIKey) { + throw err; + } + } + } else { + // if transform was not set to start automatically in yml config, + // we need to check using _stats if the transform had insufficient permissions + try { + const transformStats = await retryTransientEsErrors( + () => + esClient.transform.getTransformStats( + { transform_id: transform.installationName }, + { ignore: [409] } + ), + { logger, additionalResponseStatuses: [400] } + ); + if (Array.isArray(transformStats.transforms) && transformStats.transforms.length === 1) { + // @ts-expect-error TransformGetTransformStatsTransformStats should have 'health' + const transformHealth = transformStats.transforms[0].health; + if ( + transformHealth.status === 'red' && + Array.isArray(transformHealth.issues) && + transformHealth.issues.find( + (i: { issue: string }) => i.issue === 'Privileges check failed' + ) + ) { + isUnauthorizedAPIKey = true; + } + } + } catch (err) { + logger.debug( + `Error getting transform stats for transform: ${transform.installationName} cause ${err}` + ); + } } - return { id: transform.installationName, type: ElasticsearchAssetType.transform }; + return { + id: transform.installationName, + type: ElasticsearchAssetType.transform, + // If isUnauthorizedAPIKey: true (due to insufficient user permission at transform creation) + // that means the transform is created but not started. + // Note in saved object this is a deferred installation so user can later reauthorize + deferred: isUnauthorizedAPIKey, + version: transform.transformVersion, + }; } const getLegacyTransformNameForInstallation = ( diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/legacy_transforms.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/legacy_transforms.test.ts index 124004dee94ac..08c0fc0cf4ed2 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/legacy_transforms.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/legacy_transforms.test.ts @@ -122,8 +122,8 @@ describe('test transform install with legacy schema', () => { ], }); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', data_streams: [ @@ -157,16 +157,16 @@ describe('test transform install with legacy schema', () => { }, ], } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/data_stream/policy/elasticsearch/ingest_pipeline/default.json', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata/default.json', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); expect(esClient.transform.getTransform.mock.calls).toEqual([ [ @@ -320,8 +320,8 @@ describe('test transform install with legacy schema', () => { } as unknown as SavedObject) ); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', data_streams: [ @@ -341,12 +341,12 @@ describe('test transform install with legacy schema', () => { }, ], } as unknown as RegistryPackage, - ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], + paths: ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); const meta = getESAssetMetadata({ packageName: 'endpoint' }); @@ -422,8 +422,8 @@ describe('test transform install with legacy schema', () => { ], }); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', data_streams: [ @@ -457,12 +457,12 @@ describe('test transform install with legacy schema', () => { }, ], } as unknown as RegistryPackage, - [], + paths: [], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); expect(esClient.transform.getTransform.mock.calls).toEqual([ [ @@ -556,8 +556,8 @@ describe('test transform install with legacy schema', () => { ) ); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', data_streams: [ @@ -577,12 +577,12 @@ describe('test transform install with legacy schema', () => { }, ], } as unknown as RegistryPackage, - ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], + paths: ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); const meta = getESAssetMetadata({ packageName: 'endpoint' }); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/reauthorize.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/reauthorize.ts new file mode 100644 index 0000000000000..7bba68d84bcf8 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/reauthorize.ts @@ -0,0 +1,179 @@ +/* + * 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 } from '@kbn/core-elasticsearch-server'; +import type { Logger } from '@kbn/logging'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; + +import { sortBy, uniqBy } from 'lodash'; + +import type { SecondaryAuthorizationHeader } from '../../../../../common/types/models/transform_api_key'; +import { updateEsAssetReferences } from '../../packages/install'; +import type { Installation } from '../../../../../common'; +import { ElasticsearchAssetType, PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common'; + +import { retryTransientEsErrors } from '../retry'; + +interface FleetTransformMetadata { + fleet_transform_version?: string; + order?: number; + package?: { name: string }; + managed?: boolean; + managed_by?: string; + installed_by?: string; + last_authorized_by?: string; + run_as_kibana_system?: boolean; + transformId: string; +} + +async function reauthorizeAndStartTransform({ + esClient, + logger, + transformId, + secondaryAuth, + meta, +}: { + esClient: ElasticsearchClient; + logger: Logger; + transformId: string; + secondaryAuth?: SecondaryAuthorizationHeader; + shouldInstallSequentially?: boolean; + meta?: object; +}): Promise<{ transformId: string; success: boolean; error: null | any }> { + try { + await retryTransientEsErrors( + () => + esClient.transform.updateTransform( + { + transform_id: transformId, + body: { _meta: meta }, + }, + { ...(secondaryAuth ? secondaryAuth : {}) } + ), + { logger, additionalResponseStatuses: [400] } + ); + + logger.debug(`Updated transform: ${transformId}`); + } catch (err) { + logger.error(`Failed to update transform: ${transformId} because ${err}`); + return { transformId, success: false, error: err }; + } + + try { + const startedTransform = await retryTransientEsErrors( + () => esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] }), + { logger, additionalResponseStatuses: [400] } + ); + logger.debug(`Started transform: ${transformId}`); + return { transformId, success: startedTransform.acknowledged, error: null }; + } catch (err) { + logger.error(`Failed to start transform: ${transformId} because ${err}`); + return { transformId, success: false, error: err }; + } +} +export async function handleTransformReauthorizeAndStart({ + esClient, + savedObjectsClient, + logger, + pkgName, + pkgVersion, + transforms, + secondaryAuth, + username, +}: { + esClient: ElasticsearchClient; + savedObjectsClient: SavedObjectsClientContract; + logger: Logger; + transforms: Array<{ transformId: string }>; + pkgName: string; + pkgVersion?: string; + secondaryAuth?: SecondaryAuthorizationHeader; + username?: string; +}) { + if (!secondaryAuth) { + throw Error( + 'A valid secondary authorization with sufficient `manage_transform` permission is needed to re-authorize and start transforms. ' + + 'This could be because security is not enabled, or API key cannot be generated.' + ); + } + + const transformInfos = await Promise.all( + transforms.map(({ transformId }) => + retryTransientEsErrors( + () => + esClient.transform.getTransform( + { + transform_id: transformId, + }, + { ...(secondaryAuth ? secondaryAuth : {}) } + ), + { logger, additionalResponseStatuses: [400] } + ) + ) + ); + + const transformsMetadata: FleetTransformMetadata[] = transformInfos + .flat() + .map((t) => { + const transform = t.transforms?.[0]; + return { ...transform._meta, transformId: transform?.id }; + }) + .filter((t) => t?.run_as_kibana_system === false); + + const shouldInstallSequentially = + uniqBy(transformsMetadata, 'order').length === transforms.length; + + let authorizedTransforms = []; + + if (shouldInstallSequentially) { + const sortedTransformsMetadata = sortBy(transformsMetadata, [ + (t) => t.package?.name, + (t) => t.fleet_transform_version, + (t) => t.order, + ]); + + for (const { transformId, ...meta } of sortedTransformsMetadata) { + const authorizedTransform = await reauthorizeAndStartTransform({ + esClient, + logger, + transformId, + secondaryAuth, + meta: { ...meta, last_authorized_by: username }, + }); + + authorizedTransforms.push(authorizedTransform); + } + } else { + // Else, create & start all the transforms at once for speed + const transformsPromises = transformsMetadata.map(async ({ transformId, ...meta }) => { + return await reauthorizeAndStartTransform({ + esClient, + logger, + transformId, + secondaryAuth, + meta: { ...meta, last_authorized_by: username }, + }); + }); + + authorizedTransforms = await Promise.all(transformsPromises).then((results) => results.flat()); + } + + const so = await savedObjectsClient.get(PACKAGES_SAVED_OBJECT_TYPE, pkgName); + const esReferences = so.attributes.installed_es ?? []; + + const successfullyAuthorizedTransforms = authorizedTransforms.filter((t) => t.success); + const authorizedTransformsRefs = successfullyAuthorizedTransforms.map((t) => ({ + type: ElasticsearchAssetType.transform, + id: t.transformId, + version: pkgVersion, + })); + await updateEsAssetReferences(savedObjectsClient, pkgName, esReferences, { + assetsToRemove: authorizedTransformsRefs, + assetsToAdd: authorizedTransformsRefs, + }); + return authorizedTransforms; +} diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts index 77996674f402e..5de22e050483a 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts @@ -7,6 +7,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import type { SecondaryAuthorizationHeader } from '../../../../../common/types/models/transform_api_key'; import { ElasticsearchAssetType } from '../../../../types'; import type { EsAssetReference } from '../../../../types'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common/constants'; @@ -24,7 +25,8 @@ export const stopTransforms = async (transformIds: string[], esClient: Elasticse export const deleteTransforms = async ( esClient: ElasticsearchClient, transformIds: string[], - deleteDestinationIndices = false + deleteDestinationIndices = false, + secondaryAuth?: SecondaryAuthorizationHeader ) => { const logger = appContextService.getLogger(); if (transformIds.length) { @@ -41,7 +43,7 @@ export const deleteTransforms = async ( await stopTransforms([transformId], esClient); await esClient.transform.deleteTransform( { force: true, transform_id: transformId }, - { ignore: [404] } + { ...(secondaryAuth ? secondaryAuth : {}), ignore: [404] } ); logger.info(`Deleted: ${transformId}`); if (deleteDestinationIndices && transformResponse?.transforms) { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform_utils.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform_utils.test.ts new file mode 100644 index 0000000000000..7458f49ed826d --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform_utils.test.ts @@ -0,0 +1,66 @@ +/* + * 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 { getDestinationIndexAliases } from './transform_utils'; + +describe('test transform_utils', () => { + describe('getDestinationIndexAliases()', function () { + test('return transform alias settings when input is an object', () => { + expect( + getDestinationIndexAliases({ + 'alias1.latest': { move_on_creation: true }, + 'alias1.all': { move_on_creation: false }, + }) + ).toStrictEqual([ + { alias: 'alias1.latest', move_on_creation: true }, + { alias: 'alias1.all', move_on_creation: false }, + ]); + + expect( + getDestinationIndexAliases({ + 'alias1.latest': null, + 'alias1.all': { move_on_creation: false }, + alias2: { move_on_creation: true }, + alias3: undefined, + alias4: '', + alias5: 'invalid string', + }) + ).toStrictEqual([ + { alias: 'alias1.latest', move_on_creation: false }, + { alias: 'alias1.all', move_on_creation: false }, + { alias: 'alias2', move_on_creation: true }, + { alias: 'alias3', move_on_creation: false }, + { alias: 'alias4', move_on_creation: false }, + { alias: 'alias5', move_on_creation: false }, + ]); + }); + + test('return transform alias settings when input is an array', () => { + const aliasSettings = ['alias1.latest', 'alias1.all']; + expect(getDestinationIndexAliases(aliasSettings)).toStrictEqual([ + { alias: 'alias1.latest', move_on_creation: true }, + { alias: 'alias1.all', move_on_creation: false }, + ]); + }); + + test('return transform alias settings when input is a string', () => { + expect(getDestinationIndexAliases('alias1.latest')).toStrictEqual([ + { alias: 'alias1.latest', move_on_creation: true }, + ]); + + expect(getDestinationIndexAliases('alias1.all')).toStrictEqual([ + { alias: 'alias1.all', move_on_creation: false }, + ]); + }); + + test('return empty array when input is invalid', () => { + expect(getDestinationIndexAliases(undefined)).toStrictEqual([]); + + expect(getDestinationIndexAliases({})).toStrictEqual([]); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform_utils.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform_utils.ts new file mode 100644 index 0000000000000..a0dac305e23a8 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform_utils.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +interface TransformAliasSetting { + alias: string; + // When move_on_creation: true, all the other indices are removed from the alias, + // ensuring that the alias points at only one index (i.e.: the destination index of the current transform). + move_on_creation?: boolean; +} + +export const getDestinationIndexAliases = (aliasSettings: unknown): TransformAliasSetting[] => { + let aliases: TransformAliasSetting[] = []; + + if (!aliasSettings) return aliases; + + // Can be in form of { + // 'alias1': null, + // 'alias2': { move_on_creation: false } + // } + if (isPopulatedObject(aliasSettings)) { + Object.keys(aliasSettings).forEach((alias) => { + if (aliasSettings.hasOwnProperty(alias) && typeof alias === 'string') { + const moveOnCreation = aliasSettings[alias]?.move_on_creation === true; + aliases.push({ alias, move_on_creation: moveOnCreation }); + } + }); + } + if (Array.isArray(aliasSettings)) { + aliases = aliasSettings.reduce((acc, alias) => { + if (typeof alias === 'string') { + acc.push({ alias, move_on_creation: alias.endsWith('.latest') ? true : false }); + } + return acc; + }, []); + } + if (typeof aliasSettings === 'string') { + aliases = [ + { alias: aliasSettings, move_on_creation: aliasSettings.endsWith('.latest') ? true : false }, + ]; + } + return aliases; +}; diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts index 77537f200628d..547dfe1a504fb 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transforms.test.ts @@ -5,25 +5,14 @@ * 2.0. */ -// eslint-disable-next-line import/order -import { createAppContextStartContractMock } from '../../../../mocks'; - -jest.mock('../../packages/get', () => { - return { getInstallation: jest.fn(), getInstallationObject: jest.fn() }; -}); - -jest.mock('./common', () => { - return { - getAsset: jest.fn(), - }; -}); - import type { SavedObject, SavedObjectsClientContract } from '@kbn/core/server'; import { loggerMock } from '@kbn/logging-mocks'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { HTTPAuthorizationHeader } from '../../../../../common/http_authorization_header'; + import { getInstallation, getInstallationObject } from '../../packages'; import type { Installation, RegistryPackage } from '../../../../types'; import { ElasticsearchAssetType } from '../../../../types'; @@ -33,15 +22,31 @@ import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../constants'; import { getESAssetMetadata } from '../meta'; +import { createAppContextStartContractMock } from '../../../../mocks'; + import { installTransforms } from './install'; import { getAsset } from './common'; +jest.mock('../../packages/get', () => { + return { getInstallation: jest.fn(), getInstallationObject: jest.fn() }; +}); + +jest.mock('./common', () => { + return { + getAsset: jest.fn(), + }; +}); + const meta = getESAssetMetadata({ packageName: 'endpoint' }); describe('test transform install', () => { let esClient: ReturnType; let savedObjectsClient: jest.Mocked; + const authorizationHeader = new HTTPAuthorizationHeader( + 'Basic', + 'bW9uaXRvcmluZ191c2VyOm1scWFfYWRtaW4=' + ); const getYamlTestData = ( autoStart: boolean | undefined = undefined, transformVersion: string = '0.1.0' @@ -113,7 +118,8 @@ _meta: body: { description: 'Merges latest endpoint and Agent metadata documents.', dest: { - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', + index: '.metrics-endpoint.metadata_united_default', + aliases: [], }, frequency: '1s', pivot: { @@ -145,7 +151,7 @@ _meta: field: 'updated_at', }, }, - _meta: { fleet_transform_version: transformVersion, ...meta }, + _meta: { fleet_transform_version: transformVersion, ...meta, run_as_kibana_system: true }, }, }, }; @@ -235,21 +241,21 @@ _meta: ], }); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/fields.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/manifest.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); // Stop and delete previously installed transforms expect(esClient.transform.stopTransform.mock.calls).toEqual([ @@ -336,7 +342,7 @@ _meta: 'logs-endpoint.metadata_current-template@package', 'logs-endpoint.metadata_current-template@custom', ], - index_patterns: ['.metrics-endpoint.metadata_united_default-0.16.0-dev.0'], + index_patterns: ['.metrics-endpoint.metadata_united_default'], priority: 250, template: { mappings: undefined, settings: undefined }, }, @@ -346,19 +352,8 @@ _meta: ], ]); - // Destination index is created before transform is created - expect(esClient.indices.create.mock.calls).toEqual([ - [ - { - aliases: { - '.metrics-endpoint.metadata_united_default.all': {}, - '.metrics-endpoint.metadata_united_default.latest': {}, - }, - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', - }, - { ignore: [400] }, - ], - ]); + // Destination index is not created before transform is created + expect(esClient.indices.create.mock.calls).toEqual([]); expect(esClient.transform.putTransform.mock.calls).toEqual([[expectedData.TRANSFORM]]); expect(esClient.transform.startTransform.mock.calls).toEqual([ @@ -382,7 +377,7 @@ _meta: type: ElasticsearchAssetType.ingestPipeline, }, { - id: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', + id: '.metrics-endpoint.metadata_united_default', type: ElasticsearchAssetType.index, }, { @@ -411,6 +406,48 @@ _meta: refresh: false, }, ], + // After transforms are installed, es asset reference needs to be updated if they are deferred or not + [ + 'epm-packages', + 'endpoint', + { + installed_es: [ + { + id: 'metrics-endpoint.policy-0.16.0-dev.0', + type: ElasticsearchAssetType.ingestPipeline, + }, + { + id: '.metrics-endpoint.metadata_united_default', + type: ElasticsearchAssetType.index, + }, + { + id: 'logs-endpoint.metadata_current-template', + type: ElasticsearchAssetType.indexTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-template@custom', + type: ElasticsearchAssetType.componentTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-template@package', + type: ElasticsearchAssetType.componentTemplate, + version: '0.2.0', + }, + { + // After transforms are installed, es asset reference needs to be updated if they are deferred or not + deferred: false, + id: 'logs-endpoint.metadata_current-default-0.2.0', + type: ElasticsearchAssetType.transform, + version: '0.2.0', + }, + ], + }, + { + refresh: false, + }, + ], ]); }); @@ -479,21 +516,21 @@ _meta: ], }); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/fields.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/manifest.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); // Stop and delete previously installed transforms expect(esClient.transform.stopTransform.mock.calls).toEqual([ @@ -588,7 +625,7 @@ _meta: 'logs-endpoint.metadata_current-template@package', 'logs-endpoint.metadata_current-template@custom', ], - index_patterns: ['.metrics-endpoint.metadata_united_default-0.16.0-dev.0'], + index_patterns: ['.metrics-endpoint.metadata_united_default'], priority: 250, template: { mappings: undefined, settings: undefined }, }, @@ -598,19 +635,8 @@ _meta: ], ]); - // Destination index is created before transform is created - expect(esClient.indices.create.mock.calls).toEqual([ - [ - { - aliases: { - '.metrics-endpoint.metadata_united_default.all': {}, - '.metrics-endpoint.metadata_united_default.latest': {}, - }, - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', - }, - { ignore: [400] }, - ], - ]); + // Destination index is not created before transform is created + expect(esClient.indices.create.mock.calls).toEqual([]); expect(esClient.transform.putTransform.mock.calls).toEqual([[expectedData.TRANSFORM]]); expect(esClient.transform.startTransform.mock.calls).toEqual([ @@ -634,7 +660,7 @@ _meta: type: ElasticsearchAssetType.ingestPipeline, }, { - id: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', + id: '.metrics-endpoint.metadata_united_default', type: ElasticsearchAssetType.index, }, { @@ -663,6 +689,47 @@ _meta: refresh: false, }, ], + [ + 'epm-packages', + 'endpoint', + { + installed_es: [ + { + id: 'metrics-endpoint.policy-0.1.0-dev.0', + type: ElasticsearchAssetType.ingestPipeline, + }, + { + id: '.metrics-endpoint.metadata_united_default', + type: ElasticsearchAssetType.index, + }, + { + id: 'logs-endpoint.metadata_current-template', + type: ElasticsearchAssetType.indexTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-template@custom', + type: ElasticsearchAssetType.componentTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-template@package', + type: ElasticsearchAssetType.componentTemplate, + version: '0.2.0', + }, + { + // After transforms are installed, es asset reference needs to be updated if they are deferred or not + deferred: false, + id: 'logs-endpoint.metadata_current-default-0.2.0', + type: ElasticsearchAssetType.transform, + version: '0.2.0', + }, + ], + }, + { + refresh: false, + }, + ], ]); }); @@ -731,20 +798,20 @@ _meta: ], }); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/fields.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); // Stop and delete previously installed transforms expect(esClient.transform.stopTransform.mock.calls).toEqual([ @@ -809,7 +876,7 @@ _meta: 'logs-endpoint.metadata_current-template@package', 'logs-endpoint.metadata_current-template@custom', ], - index_patterns: ['.metrics-endpoint.metadata_united_default-0.16.0-dev.0'], + index_patterns: ['.metrics-endpoint.metadata_united_default'], priority: 250, template: { mappings: undefined, settings: undefined }, }, @@ -819,19 +886,8 @@ _meta: ], ]); - // Destination index is created before transform is created - expect(esClient.indices.create.mock.calls).toEqual([ - [ - { - aliases: { - '.metrics-endpoint.metadata_united_default.all': {}, - '.metrics-endpoint.metadata_united_default.latest': {}, - }, - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', - }, - { ignore: [400] }, - ], - ]); + // Destination index is not created before transform is created + expect(esClient.indices.create.mock.calls).toEqual([]); expect(esClient.transform.putTransform.mock.calls).toEqual([[expectedData.TRANSFORM]]); expect(esClient.transform.startTransform.mock.calls).toEqual([ @@ -855,7 +911,46 @@ _meta: type: ElasticsearchAssetType.ingestPipeline, }, { - id: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', + id: '.metrics-endpoint.metadata_united_default', + type: ElasticsearchAssetType.index, + }, + { + id: 'logs-endpoint.metadata_current-template', + type: ElasticsearchAssetType.indexTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-template@custom', + type: ElasticsearchAssetType.componentTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-template@package', + type: ElasticsearchAssetType.componentTemplate, + version: '0.2.0', + }, + { + id: 'logs-endpoint.metadata_current-default-0.2.0', + type: ElasticsearchAssetType.transform, + version: '0.2.0', + }, + ], + }, + { + refresh: false, + }, + ], + [ + 'epm-packages', + 'endpoint', + { + installed_es: [ + { + id: 'metrics-endpoint.policy-0.16.0-dev.0', + type: ElasticsearchAssetType.ingestPipeline, + }, + { + id: '.metrics-endpoint.metadata_united_default', type: ElasticsearchAssetType.index, }, { @@ -874,6 +969,7 @@ _meta: version: '0.2.0', }, { + deferred: false, id: 'logs-endpoint.metadata_current-default-0.2.0', type: ElasticsearchAssetType.transform, version: '0.2.0', @@ -919,20 +1015,21 @@ _meta: } as unknown as SavedObject) ); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/manifest.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + authorizationHeader, + }); expect(esClient.transform.putTransform.mock.calls).toEqual([[expectedData.TRANSFORM]]); // Does not start transform because start is set to false in manifest.yml @@ -1011,58 +1108,26 @@ _meta: }) ); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/manifest.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); - expect(esClient.indices.create.mock.calls).toEqual([ - [ - { - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', - aliases: { - '.metrics-endpoint.metadata_united_default.all': {}, - '.metrics-endpoint.metadata_united_default.latest': {}, - }, - }, - { ignore: [400] }, - ], - ]); + expect(esClient.indices.create.mock.calls).toEqual([]); // If downgrading to and older version, and destination index already exists // aliases should still be updated to point .latest to this index - expect(esClient.indices.updateAliases.mock.calls).toEqual([ - [ - { - body: { - actions: [ - { - add: { - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', - alias: '.metrics-endpoint.metadata_united_default.all', - }, - }, - { - add: { - index: '.metrics-endpoint.metadata_united_default-0.16.0-dev.0', - alias: '.metrics-endpoint.metadata_united_default.latest', - }, - }, - ], - }, - }, - ], - ]); + expect(esClient.indices.updateAliases.mock.calls).toEqual([]); expect(esClient.transform.deleteTransform.mock.calls).toEqual([ [ @@ -1124,21 +1189,21 @@ _meta: } as unknown as SavedObject) ); - await installTransforms( - { + await installTransforms({ + installablePackage: { name: 'endpoint', version: '0.16.0-dev.0', } as unknown as RegistryPackage, - [ + paths: [ 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/fields/fields.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/manifest.yml', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/transform.yml', ], esClient, savedObjectsClient, - loggerMock.create(), - previousInstallation.installed_es - ); + logger: loggerMock.create(), + esReferences: previousInstallation.installed_es, + }); // Transform from old version is neither stopped nor deleted expect(esClient.transform.stopTransform.mock.calls).toEqual([]); diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts index 69e1217b0493c..4007ad7545ece 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts @@ -11,6 +11,7 @@ const createClientMock = (): jest.Mocked => ({ getInstallation: jest.fn(), ensureInstalledPackage: jest.fn(), fetchFindLatestPackage: jest.fn(), + readBundledPackage: jest.fn(), getPackage: jest.fn(), getPackages: jest.fn(), reinstallEsAssets: jest.fn(), diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts index 779f0dad02c8c..6e5d2a69ee001 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts @@ -26,6 +26,7 @@ import * as epmPackagesGet from './packages/get'; import * as epmPackagesInstall from './packages/install'; import * as epmRegistry from './registry'; import * as epmTransformsInstall from './elasticsearch/transform/install'; +import * as epmArchiveParse from './archive/parse'; const testKeys = [ 'getInstallation', @@ -33,6 +34,7 @@ const testKeys = [ 'fetchFindLatestPackage', 'getPackage', 'reinstallEsAssets', + 'readBundledPackage', ]; function getTest( @@ -129,7 +131,21 @@ function getTest( method: mocks.packageClient.reinstallEsAssets.bind(mocks.packageClient), args: [pkg, paths], spy: jest.spyOn(epmTransformsInstall, 'installTransforms'), - spyArgs: [pkg, paths, mocks.esClient, mocks.soClient, mocks.logger], + spyArgs: [ + { + installablePackage: pkg, + paths, + esClient: mocks.esClient, + savedObjectsClient: mocks.soClient, + logger: mocks.logger, + // package reinstall means we need to force transforms to reinstall + force: true, + // Undefined es references + esReferences: undefined, + // Undefined secondary authorization + authorizationHeader: undefined, + }, + ], spyResponse: { installedTransforms: [ { @@ -144,6 +160,23 @@ function getTest( ], }; break; + case testKeys[5]: + const bundledPackage = { name: 'package name', version: '8.0.0', buffer: Buffer.from([]) }; + test = { + method: mocks.packageClient.readBundledPackage.bind(mocks.packageClient), + args: [bundledPackage], + spy: jest.spyOn(epmArchiveParse, 'generatePackageInfoFromArchiveBuffer'), + spyArgs: [bundledPackage.buffer, 'application/zip'], + spyResponse: { + packageInfo: { name: 'readBundledPackage test' }, + paths: ['/some/test/path'], + }, + expectedReturnValue: { + packageInfo: { name: 'readBundledPackage test' }, + paths: ['/some/test/path'], + }, + }; + break; default: throw new Error('invalid test key'); } diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.ts b/x-pack/plugins/fleet/server/services/epm/package_service.ts index b0dd6e9dc38b4..c42b345d545a7 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -14,6 +14,8 @@ import type { Logger, } from '@kbn/core/server'; +import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; + import type { PackageList } from '../../../common'; import type { @@ -32,6 +34,7 @@ import { installTransforms, isTransform } from './elasticsearch/transform/instal import type { FetchFindLatestPackageOptions } from './registry'; import { fetchFindLatestPackageOrThrow, getPackage } from './registry'; import { ensureInstalledPackage, getInstallation, getPackages } from './packages'; +import { generatePackageInfoFromArchiveBuffer } from './archive'; export type InstalledAssetType = EsAssetReference; @@ -54,6 +57,10 @@ export interface PackageClient { options?: FetchFindLatestPackageOptions ): Promise; + readBundledPackage( + bundledPackage: BundledPackage + ): Promise<{ packageInfo: ArchivePackage; paths: string[] }>; + getPackage( packageName: string, packageVersion: string @@ -91,7 +98,8 @@ export class PackageServiceImpl implements PackageService { this.internalEsClient, this.internalSoClient, this.logger, - preflightCheck + preflightCheck, + request ); } @@ -101,13 +109,23 @@ export class PackageServiceImpl implements PackageService { } class PackageClientImpl implements PackageClient { + private authorizationHeader?: HTTPAuthorizationHeader | null = undefined; + constructor( private readonly internalEsClient: ElasticsearchClient, private readonly internalSoClient: SavedObjectsClientContract, private readonly logger: Logger, - private readonly preflightCheck?: () => void | Promise + private readonly preflightCheck?: () => void | Promise, + private readonly request?: KibanaRequest ) {} + private getAuthorizationHeader() { + if (this.request) { + this.authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(this.request); + return this.authorizationHeader; + } + } + public async getInstallation(pkgName: string) { await this.#runPreflight(); return getInstallation({ @@ -122,6 +140,7 @@ class PackageClientImpl implements PackageClient { spaceId?: string; }): Promise { await this.#runPreflight(); + return ensureInstalledPackage({ ...options, esClient: this.internalEsClient, @@ -137,6 +156,11 @@ class PackageClientImpl implements PackageClient { return fetchFindLatestPackageOrThrow(packageName, options); } + public async readBundledPackage(bundledPackage: BundledPackage) { + await this.#runPreflight(); + return generatePackageInfoFromArchiveBuffer(bundledPackage.buffer, 'application/zip'); + } + public async getPackage( packageName: string, packageVersion: string, @@ -183,13 +207,18 @@ class PackageClientImpl implements PackageClient { } async #reinstallTransforms(packageInfo: InstallablePackage, paths: string[]) { - const { installedTransforms } = await installTransforms( - packageInfo, + const authorizationHeader = await this.getAuthorizationHeader(); + + const { installedTransforms } = await installTransforms({ + installablePackage: packageInfo, paths, - this.internalEsClient, - this.internalSoClient, - this.logger - ); + esClient: this.internalEsClient, + savedObjectsClient: this.internalSoClient, + logger: this.logger, + force: true, + esReferences: undefined, + authorizationHeader, + }); return installedTransforms; } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index 68c981a308f82..b884e8c893de8 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -16,6 +16,8 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { IAssignmentService, ITagsClient } from '@kbn/saved-objects-tagging-plugin/server'; +import type { HTTPAuthorizationHeader } from '../../../../common/http_authorization_header'; + import { getNormalizedDataStreams } from '../../../../common/services'; import { @@ -73,7 +75,9 @@ export async function _installPackage({ installType, installSource, spaceId, + force, verificationResult, + authorizationHeader, }: { savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; @@ -87,7 +91,9 @@ export async function _installPackage({ installType: InstallType; installSource: InstallSource; spaceId: string; + force?: boolean; verificationResult?: PackageVerificationResult; + authorizationHeader?: HTTPAuthorizationHeader | null; }): Promise { const { name: pkgName, version: pkgVersion, title: pkgTitle } = packageInfo; @@ -245,7 +251,16 @@ export async function _installPackage({ ); ({ esReferences } = await withPackageSpan('Install transforms', () => - installTransforms(packageInfo, paths, esClient, savedObjectsClient, logger, esReferences) + installTransforms({ + installablePackage: packageInfo, + paths, + esClient, + savedObjectsClient, + logger, + esReferences, + force, + authorizationHeader, + }) )); // If this is an update or retrying an update, delete the previous version's pipelines diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts index 659d8d1a1c5db..a95560fbd9cbb 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts @@ -7,6 +7,8 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import type { HTTPAuthorizationHeader } from '../../../../common/http_authorization_header'; + import { appContextService } from '../../app_context'; import * as Registry from '../registry'; @@ -23,6 +25,7 @@ interface BulkInstallPackagesParams { spaceId: string; preferredSource?: 'registry' | 'bundled'; prerelease?: boolean; + authorizationHeader?: HTTPAuthorizationHeader | null; } export async function bulkInstallPackages({ @@ -32,6 +35,7 @@ export async function bulkInstallPackages({ spaceId, force, prerelease, + authorizationHeader, }: BulkInstallPackagesParams): Promise { const logger = appContextService.getLogger(); @@ -94,6 +98,7 @@ export async function bulkInstallPackages({ spaceId, force, prerelease, + authorizationHeader, }); if (installResult.error) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index af56737f7f8b5..19105860deabf 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -25,6 +25,8 @@ import { uniqBy } from 'lodash'; import type { LicenseType } from '@kbn/licensing-plugin/server'; +import type { HTTPAuthorizationHeader } from '../../../../common/http_authorization_header'; + import { isPackagePrerelease, getNormalizedDataStreams } from '../../../../common/services'; import { FLEET_INSTALL_FORMAT_VERSION } from '../../../constants/fleet_es_assets'; @@ -120,6 +122,7 @@ export async function ensureInstalledPackage(options: { pkgVersion?: string; spaceId?: string; force?: boolean; + authorizationHeader?: HTTPAuthorizationHeader | null; }): Promise { const { savedObjectsClient, @@ -128,6 +131,7 @@ export async function ensureInstalledPackage(options: { pkgVersion, force = false, spaceId = DEFAULT_SPACE_ID, + authorizationHeader, } = options; // If pkgVersion isn't specified, find the latest package version @@ -152,6 +156,7 @@ export async function ensureInstalledPackage(options: { esClient, neverIgnoreVerificationError: !force, force: true, // Always force outdated packages to be installed if a later version isn't installed + authorizationHeader, }); if (installResult.error) { @@ -188,6 +193,7 @@ export async function handleInstallPackageFailure({ installedPkg, esClient, spaceId, + authorizationHeader, }: { savedObjectsClient: SavedObjectsClientContract; error: FleetError | Boom.Boom | Error; @@ -196,6 +202,7 @@ export async function handleInstallPackageFailure({ installedPkg: SavedObject | undefined; esClient: ElasticsearchClient; spaceId: string; + authorizationHeader?: HTTPAuthorizationHeader | null; }) { if (error instanceof FleetError) { return; @@ -232,6 +239,7 @@ export async function handleInstallPackageFailure({ esClient, spaceId, force: true, + authorizationHeader, }); } } catch (e) { @@ -255,6 +263,7 @@ interface InstallRegistryPackageParams { neverIgnoreVerificationError?: boolean; ignoreConstraints?: boolean; prerelease?: boolean; + authorizationHeader?: HTTPAuthorizationHeader | null; } interface InstallUploadedArchiveParams { savedObjectsClient: SavedObjectsClientContract; @@ -263,6 +272,7 @@ interface InstallUploadedArchiveParams { contentType: string; spaceId: string; version?: string; + authorizationHeader?: HTTPAuthorizationHeader | null; } function getTelemetryEvent(pkgName: string, pkgVersion: string): PackageUpdateEvent { @@ -290,6 +300,7 @@ async function installPackageFromRegistry({ pkgkey, esClient, spaceId, + authorizationHeader, force = false, ignoreConstraints = false, neverIgnoreVerificationError = false, @@ -366,6 +377,7 @@ async function installPackageFromRegistry({ packageInfo, paths, verificationResult, + authorizationHeader, }); } catch (e) { sendEvent({ @@ -400,6 +412,7 @@ async function installPackageCommon(options: { paths: string[]; verificationResult?: PackageVerificationResult; telemetryEvent?: PackageUpdateEvent; + authorizationHeader?: HTTPAuthorizationHeader | null; }): Promise { const { pkgName, @@ -414,6 +427,7 @@ async function installPackageCommon(options: { packageInfo, paths, verificationResult, + authorizationHeader, } = options; let { telemetryEvent } = options; const logger = appContextService.getLogger(); @@ -496,6 +510,8 @@ async function installPackageCommon(options: { spaceId, verificationResult, installSource, + authorizationHeader, + force, }) .then(async (assets) => { await removeOldAssets({ @@ -519,6 +535,7 @@ async function installPackageCommon(options: { installedPkg, spaceId, esClient, + authorizationHeader, }); sendEvent({ ...telemetryEvent!, @@ -548,6 +565,7 @@ async function installPackageByUpload({ contentType, spaceId, version, + authorizationHeader, }: InstallUploadedArchiveParams): Promise { // if an error happens during getInstallType, report that we don't know let installType: InstallType = 'unknown'; @@ -595,6 +613,7 @@ async function installPackageByUpload({ force: true, // upload has implicit force packageInfo, paths, + authorizationHeader, }); } catch (e) { return { @@ -622,6 +641,8 @@ export async function installPackage(args: InstallPackageParams): Promise; diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 06c206ee77dc8..350909d3de0a7 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -80,7 +80,6 @@ export async function ensurePreconfiguredPackagesAndPolicies( const packagesToInstall = packages.map((pkg) => pkg.version === PRECONFIGURATION_LATEST_KEYWORD ? pkg.name : pkg ); - // Preinstall packages specified in Kibana config const preconfiguredPackages = await bulkInstallPackages({ savedObjectsClient: soClient, diff --git a/x-pack/plugins/fleet/server/services/security/message_signing_service.test.ts b/x-pack/plugins/fleet/server/services/security/message_signing_service.test.ts index e3ad1d82bdee7..6503f2f08d566 100644 --- a/x-pack/plugins/fleet/server/services/security/message_signing_service.test.ts +++ b/x-pack/plugins/fleet/server/services/security/message_signing_service.test.ts @@ -35,6 +35,23 @@ describe('MessageSigningService', () => { }); } + function mockCreatePointInTimeFinderAsInternalUserOnce(savedObjects: unknown[] = []) { + esoClientMock.createPointInTimeFinderDecryptedAsInternalUser = jest + .fn() + .mockResolvedValueOnce({ + close: jest.fn(), + find: function* asyncGenerator() { + yield { saved_objects: savedObjects }; + }, + }) + .mockResolvedValueOnce({ + close: jest.fn(), + find: function* asyncGenerator() { + yield { saved_objects: [] }; + }, + }); + } + function setupMocks(canEncrypt = true) { const mockContext = createAppContextStartContractMock(); mockContext.encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup({ @@ -74,19 +91,49 @@ describe('MessageSigningService', () => { it('can correctly generate key pair if none exist', async () => { mockCreatePointInTimeFinderAsInternalUser(); - await messageSigningService.generateKeyPair(); + const generateKeyPairResponse = await messageSigningService.generateKeyPair(); expect(soClientMock.create).toBeCalledWith(MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, { private_key: expect.any(String), public_key: expect.any(String), passphrase: expect.any(String), }); + + expect(generateKeyPairResponse).toEqual({ + passphrase: expect.any(String), + privateKey: expect.any(String), + publicKey: expect.any(String), + }); + }); + + it('can correctly rotate existing key pair', async () => { + mockCreatePointInTimeFinderAsInternalUserOnce([keyPairObj]); + + const rotateKeyPairResponse = await messageSigningService.rotateKeyPair(); + + expect(soClientMock.delete).toBeCalledWith( + MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, + keyPairObj.id + ); + expect(soClientMock.create).toBeCalledWith(MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, { + private_key: expect.any(String), + public_key: expect.any(String), + passphrase: expect.any(String), + }); + + expect(rotateKeyPairResponse).toEqual(true); }); it('does not generate key pair if one exists', async () => { mockCreatePointInTimeFinderAsInternalUser([keyPairObj]); - await messageSigningService.generateKeyPair(); + const generateKeyPairResponse = await messageSigningService.generateKeyPair(); expect(soClientMock.create).not.toBeCalled(); + + expect(generateKeyPairResponse).toEqual({ + passphrase: expect.any(String), + privateKey: expect.any(String), + publicKey: expect.any(String), + }); }); it('can correctly sign messages', async () => { @@ -153,7 +200,8 @@ describe('MessageSigningService', () => { MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, keyPairObj.id, { - passphrase: expect.any(String), + ...keyPairObj.attributes, + passphrase: keyPairObj.attributes.passphrase_plain, passphrase_plain: '', } ); diff --git a/x-pack/plugins/fleet/server/services/security/message_signing_service.ts b/x-pack/plugins/fleet/server/services/security/message_signing_service.ts index 443f35ec06ed2..fc5583dec01d0 100644 --- a/x-pack/plugins/fleet/server/services/security/message_signing_service.ts +++ b/x-pack/plugins/fleet/server/services/security/message_signing_service.ts @@ -8,7 +8,10 @@ import { generateKeyPairSync, createSign, randomBytes } from 'crypto'; import type { KibanaRequest } from '@kbn/core-http-server'; -import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import type { + SavedObjectsClientContract, + SavedObjectsFindResult, +} from '@kbn/core-saved-objects-api-server'; import type { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; @@ -27,6 +30,7 @@ export interface MessageSigningServiceInterface { generateKeyPair( providedPassphrase?: string ): Promise<{ privateKey: string; publicKey: string; passphrase: string }>; + rotateKeyPair(): Promise; sign(message: Buffer | Record): Promise<{ data: Buffer; signature: string }>; getPublicKey(): Promise; } @@ -45,35 +49,12 @@ export class MessageSigningService implements MessageSigningServiceInterface { publicKey: string; passphrase: string; }> { - let passphrase = providedPassphrase || this.generatePassphrase(); - - const currentKeyPair = await this.getCurrentKeyPair(); - if ( - currentKeyPair.privateKey && - currentKeyPair.publicKey && - (currentKeyPair.passphrase || currentKeyPair.passphrasePlain) - ) { - passphrase = currentKeyPair.passphrase || currentKeyPair.passphrasePlain; - - // newly configured encryption key, encrypt the passphrase - if (currentKeyPair.passphrasePlain && this.isEncryptionAvailable) { - await this.soClient.update( - MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, - currentKeyPair.id, - { - passphrase, - passphrase_plain: '', - } - ); - } - - return { - privateKey: currentKeyPair.privateKey, - publicKey: currentKeyPair.publicKey, - passphrase, - }; + const existingKeyPair = await this.checkForExistingKeyPair(); + if (existingKeyPair) { + return existingKeyPair; } + const passphrase = providedPassphrase || this.generatePassphrase(); const keyPair = generateKeyPairSync('ec', { namedCurve: 'prime256v1', privateKeyEncoding: { @@ -154,6 +135,25 @@ export class MessageSigningService implements MessageSigningServiceInterface { return publicKey; } + public async rotateKeyPair(): Promise { + const isRemoved = await this.removeKeyPair(); + if (isRemoved) { + await this.generateKeyPair(); + return true; + } + return false; + // TODO: Apply changes to all policies + } + + private async removeKeyPair(): Promise { + const currentKeyPair = await this.getCurrentKeyPairObj(); + if (currentKeyPair) { + await this.soClient.delete(MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, currentKeyPair.id); + return true; + } + return false; + } + private get soClient() { if (this._soClient) { return this._soClient; @@ -176,13 +176,9 @@ export class MessageSigningService implements MessageSigningServiceInterface { return this._soClient; } - private async getCurrentKeyPair(): Promise<{ - id: string; - privateKey: string; - publicKey: string; - passphrase: string; - passphrasePlain: string; - }> { + private async getCurrentKeyPairObj(): Promise< + SavedObjectsFindResult | undefined + > { const finder = await this.esoClient.createPointInTimeFinderDecryptedAsInternalUser({ type: MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, @@ -190,30 +186,59 @@ export class MessageSigningService implements MessageSigningServiceInterface { sortField: 'created_at', sortOrder: 'desc', }); - let keyPair = { - id: '', - privateKey: '', - publicKey: '', - passphrase: '', - passphrasePlain: '', - }; + let soDoc: SavedObjectsFindResult | undefined; for await (const result of finder.find()) { - const savedObject = result.saved_objects[0]; - const attributes = savedObject?.attributes; - if (!attributes?.private_key) { - break; - } - keyPair = { - id: savedObject.id, - privateKey: attributes.private_key, - publicKey: attributes.public_key, - passphrase: attributes.passphrase, - passphrasePlain: attributes.passphrase_plain, - }; + soDoc = result.saved_objects[0]; break; } + finder.close(); + + return soDoc; + } - return keyPair; + private async checkForExistingKeyPair(): Promise< + | { + privateKey: string; + publicKey: string; + passphrase: string; + } + | undefined + > { + const currentKeyPair = await this.getCurrentKeyPairObj(); + if (!currentKeyPair) { + return; + } + + const { attributes } = currentKeyPair; + if (!attributes) { + return; + } + + const { + private_key: privateKey, + public_key: publicKey, + passphrase: passphraseEncrypted, + passphrase_plain: passphrasePlain, + } = attributes; + const passphrase = passphraseEncrypted || passphrasePlain; + if (!privateKey || !publicKey || !passphrase) { + return; + } + + // newly configured encryption key, encrypt the passphrase + if (passphrasePlain && this.isEncryptionAvailable) { + await this.soClient.update( + MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, + currentKeyPair?.id, + { ...attributes, passphrase, passphrase_plain: '' } + ); + } + + return { + privateKey, + publicKey, + passphrase, + }; } private generatePassphrase(): string { diff --git a/x-pack/plugins/fleet/server/services/security/security.ts b/x-pack/plugins/fleet/server/services/security/security.ts index 0f72aeabfd17e..715d8d966484f 100644 --- a/x-pack/plugins/fleet/server/services/security/security.ts +++ b/x-pack/plugins/fleet/server/services/security/security.ts @@ -9,6 +9,8 @@ import { pick } from 'lodash'; import type { KibanaRequest } from '@kbn/core/server'; +import { TRANSFORM_PLUGIN_ID } from '../../../common/constants/plugin'; + import type { FleetAuthz } from '../../../common'; import { INTEGRATIONS_PLUGIN_ID } from '../../../common'; import { @@ -36,6 +38,7 @@ export function checkSuperuser(req: KibanaRequest) { const security = appContextService.getSecurity(); const user = security.authc.getCurrentUser(req); + if (!user) { return false; } @@ -79,6 +82,10 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise { + it('should throw on `false` values for acknowledge', () => { + expect(() => + RotateKeyPairSchema.query.validate({ + acknowledge: false, + }) + ).toThrowError( + 'You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters.' + ); + }); + + it('should allow without any query', () => { + expect(() => RotateKeyPairSchema.query.validate({})).toThrowError( + 'You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters.' + ); + }); + + it.each([1, 'string'])('should not allow non-boolean `%s` values for acknowledge', (value) => { + expect(() => + RotateKeyPairSchema.query.validate({ + acknowledge: value, + }) + ).toThrowError(`[acknowledge]: expected value of type [boolean] but got [${typeof value}]`); + }); + + it('should not throw on `true` values for acknowledge', () => { + expect(() => + RotateKeyPairSchema.query.validate({ + acknowledge: true, + }) + ).not.toThrow(); + }); +}); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts b/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts new file mode 100644 index 0000000000000..d6037748e4682 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts @@ -0,0 +1,30 @@ +/* + * 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 } from '@kbn/config-schema'; + +export const RotateKeyPairSchema = { + query: schema.maybe( + schema.object( + { + acknowledge: schema.boolean({ + defaultValue: false, + }), + }, + { + defaultValue: { acknowledge: false }, + validate: (value: { acknowledge: boolean }) => { + if (!value || !value.acknowledge) { + throw new Error( + 'You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters.' + ); + } + }, + } + ) + ), +}; diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 84f88ffea366f..f80d7d51686d8 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -95,5 +95,6 @@ "@kbn/core-http-request-handler-context-server", "@kbn/shared-ux-router", "@kbn/shared-ux-link-redirect-app", + "@kbn/core-http-router-server-internal", ] } diff --git a/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts b/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts index 4a53bba847543..c9ff8edae3b1c 100644 --- a/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts +++ b/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts @@ -5,11 +5,13 @@ * 2.0. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { graphMigrations } from './migrations'; export const graphWorkspace: SavedObjectsType = { name: 'graph-workspace', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', hidden: false, diff --git a/x-pack/plugins/graph/tsconfig.json b/x-pack/plugins/graph/tsconfig.json index 3979bb5b4d9a0..579cda78b0fed 100644 --- a/x-pack/plugins/graph/tsconfig.json +++ b/x-pack/plugins/graph/tsconfig.json @@ -40,6 +40,7 @@ "@kbn/shared-ux-router", "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-finder-plugin", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/grokdebugger/kibana.jsonc b/x-pack/plugins/grokdebugger/kibana.jsonc index 340088efc772f..aa0bdc864142f 100644 --- a/x-pack/plugins/grokdebugger/kibana.jsonc +++ b/x-pack/plugins/grokdebugger/kibana.jsonc @@ -16,8 +16,7 @@ "devTools" ], "requiredBundles": [ - "kibanaReact", - "esUiShared" + "kibanaReact" ] } } diff --git a/x-pack/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js b/x-pack/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js index cac942fa44694..def839c3f9b09 100644 --- a/x-pack/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js +++ b/x-pack/plugins/grokdebugger/public/components/custom_patterns_input/custom_patterns_input.js @@ -6,11 +6,11 @@ */ import React from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiCallOut, EuiCodeBlock, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EDITOR } from '../../../common/constants'; -import { EuiCodeEditor } from '../../shared_imports'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; export function CustomPatternsInput({ value, onChange }) { const sampleCustomPatterns = `POSTFIX_QUEUEID [0-9A-F]{10,11} @@ -43,18 +43,18 @@ MSG message-id=<%{GREEDYDATA}>`; - diff --git a/x-pack/plugins/grokdebugger/public/components/event_input/event_input.js b/x-pack/plugins/grokdebugger/public/components/event_input/event_input.js index 35b3be399fdce..2bfdce4f0a893 100644 --- a/x-pack/plugins/grokdebugger/public/components/event_input/event_input.js +++ b/x-pack/plugins/grokdebugger/public/components/event_input/event_input.js @@ -6,11 +6,10 @@ */ import React from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; - -import { EDITOR } from '../../../common/constants'; -import { EuiCodeEditor } from '../../shared_imports'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; export function EventInput({ value, onChange }) { return ( @@ -19,20 +18,20 @@ export function EventInput({ value, onChange }) { } fullWidth - data-test-subj="aceEventInput" + data-test-subj="eventInput" > - ); diff --git a/x-pack/plugins/grokdebugger/public/components/event_output/event_output.js b/x-pack/plugins/grokdebugger/public/components/event_output/event_output.js index a2a02259c3fdf..e26672e467c3e 100644 --- a/x-pack/plugins/grokdebugger/public/components/event_output/event_output.js +++ b/x-pack/plugins/grokdebugger/public/components/event_output/event_output.js @@ -6,11 +6,9 @@ */ import React from 'react'; -import { EuiFormRow } from '@elastic/eui'; +import { EuiFormRow, EuiCodeBlock } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiCodeEditor } from '../../shared_imports'; - export function EventOutput({ value }) { return ( } fullWidth - data-test-subj="aceEventOutput" > - + + {JSON.stringify(value, null, 2)} + ); } diff --git a/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js index f40948730872b..7eb4e9412edaa 100644 --- a/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js +++ b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js @@ -11,7 +11,6 @@ import { i18n } from '@kbn/i18n'; // eslint-disable-next-line no-restricted-imports import isEmpty from 'lodash/isEmpty'; -import './brace_imports'; import { EuiForm, EuiButton, diff --git a/x-pack/plugins/grokdebugger/public/components/pattern_input/pattern_input.js b/x-pack/plugins/grokdebugger/public/components/pattern_input/pattern_input.js index 75af1453c6b40..096537bd8a6bf 100644 --- a/x-pack/plugins/grokdebugger/public/components/pattern_input/pattern_input.js +++ b/x-pack/plugins/grokdebugger/public/components/pattern_input/pattern_input.js @@ -6,12 +6,10 @@ */ import React from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; - -import { EDITOR } from '../../../common/constants'; -import { EuiCodeEditor } from '../../shared_imports'; -import { GrokMode } from '../../lib/ace'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; export function PatternInput({ value, onChange }) { return ( @@ -20,20 +18,20 @@ export function PatternInput({ value, onChange }) { } fullWidth - data-test-subj="acePatternInput" + data-test-subj="patternInput" > - ); diff --git a/x-pack/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js b/x-pack/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js deleted file mode 100644 index 5987cd672ec05..0000000000000 --- a/x-pack/plugins/grokdebugger/public/lib/ace/grok_highlight_rules.js +++ /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 ace from 'brace'; - -const { TextHighlightRules } = ace.acequire('ace/mode/text_highlight_rules'); - -export class GrokHighlightRules extends TextHighlightRules { - constructor() { - super(); - this.$rules = { - start: [ - { - token: ['grokStart', 'grokPatternName', 'grokSeparator', 'grokFieldName', 'grokEnd'], - regex: '(%{)([^:]+)(:)([^:]+)(})', - }, - { - token: [ - 'grokStart', - 'grokPatternName', - 'grokSeparator', - 'grokFieldName', - 'grokSeparator', - 'grokFieldType', - 'grokEnd', - ], - regex: '(%{)([^:]+)(:)([^:]+)(:)([^:]+)(})', - }, - { - token: (escapeToken /* regexToken */) => { - if (escapeToken) { - return ['grokEscape', 'grokEscaped']; - } - return 'grokRegex'; - }, - regex: '(\\\\)?([\\[\\]\\(\\)\\?\\:\\|])', - }, - ], - }; - } -} diff --git a/x-pack/plugins/grokdebugger/public/lib/ace/grok_mode.js b/x-pack/plugins/grokdebugger/public/lib/ace/grok_mode.js deleted file mode 100644 index a3f97de6dd934..0000000000000 --- a/x-pack/plugins/grokdebugger/public/lib/ace/grok_mode.js +++ /dev/null @@ -1,18 +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 ace from 'brace'; -import { GrokHighlightRules } from './grok_highlight_rules'; - -const TextMode = ace.acequire('ace/mode/text').Mode; - -export class GrokMode extends TextMode { - constructor() { - super(); - this.HighlightRules = GrokHighlightRules; - } -} diff --git a/x-pack/plugins/grokdebugger/public/shared_imports.ts b/x-pack/plugins/grokdebugger/public/shared_imports.ts index b2e82bce637f1..0bfbb3e05f933 100644 --- a/x-pack/plugins/grokdebugger/public/shared_imports.ts +++ b/x-pack/plugins/grokdebugger/public/shared_imports.ts @@ -5,6 +5,4 @@ * 2.0. */ -export { EuiCodeEditor } from '@kbn/es-ui-shared-plugin/public'; - export { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx index 1727caa9eaff0..2eb7403177257 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx @@ -21,6 +21,23 @@ import { import { setup } from './template_create.helpers'; import { TemplateFormTestBed } from './template_form.helpers'; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + ) => { + props.onChange(e.currentTarget.getAttribute('data-currentvalue')); + }} + /> + ), + }; +}); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx index 99565222ffa7b..6d1224abf3529 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx @@ -28,6 +28,23 @@ const MAPPING = { }, }; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + ) => { + props.onChange(e.currentTarget.getAttribute('data-currentvalue')); + }} + /> + ), + }; +}); + jest.mock('@elastic/eui', () => { const origial = jest.requireActual('@elastic/eui'); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts index 64ce73ee8161b..bf16e8e5e803d 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts @@ -219,18 +219,13 @@ export const formSetup = async (initTestBed: SetupFunc) => { const completeStepThree = async (settings?: string) => { const { find, component } = testBed; - await act(async () => { - if (settings) { - find('settingsEditor').simulate('change', { - jsonString: settings, - }); // Using mocked EuiCodeEditor - jest.advanceTimersByTime(0); - } - }); + if (settings) { + find('settingsEditor').getDOMNode().setAttribute('data-currentvalue', settings); + find('settingsEditor').simulate('change'); + } await act(async () => { clickNextButton(); - jest.advanceTimersByTime(0); }); component.update(); @@ -258,13 +253,8 @@ export const formSetup = async (initTestBed: SetupFunc) => { const { find, component } = testBed; if (aliases) { - await act(async () => { - find('aliasesEditor').simulate('change', { - jsonString: aliases, - }); // Using mocked EuiCodeEditor - jest.advanceTimersByTime(0); // advance timers to allow the form to validate - }); - component.update(); + find('aliasesEditor').getDOMNode().setAttribute('data-currentvalue', aliases); + find('aliasesEditor').simulate('change'); } await act(async () => { diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_create.test.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_create.test.tsx index 5e915c526dc44..8309db0699fc6 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_create.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_create.test.tsx @@ -14,6 +14,23 @@ import { setupEnvironment } from './helpers'; import { API_BASE_PATH } from './helpers/constants'; import { setup, ComponentTemplateCreateTestBed } from './helpers/component_template_create.helpers'; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + ) => { + props.onChange(e.currentTarget.getAttribute('data-currentvalue')); + }} + /> + ), + }; +}); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_edit.test.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_edit.test.tsx index 94beecf441b07..1da8027c59497 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_edit.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_edit.test.tsx @@ -13,6 +13,23 @@ import { setupEnvironment } from './helpers'; import { API_BASE_PATH } from './helpers/constants'; import { setup, ComponentTemplateEditTestBed } from './helpers/component_template_edit.helpers'; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + ) => { + props.onChange(e.currentTarget.getAttribute('data-currentvalue')); + }} + /> + ), + }; +}); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts index 2aaadf3c06f11..0db29dffff510 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts +++ b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/helpers/component_template_form.helpers.ts @@ -66,14 +66,14 @@ export const getFormActions = (testBed: TestBed) => { const completeStepSettings = async (settings?: { [key: string]: any }) => { const { find, component } = testBed; + const settingsValue = JSON.stringify(settings); - await act(async () => { - if (settings) { - find('settingsEditor').simulate('change', { - jsonString: JSON.stringify(settings), - }); // Using mocked EuiCodeEditor - } + if (settingsValue) { + find('settingsEditor').getDOMNode().setAttribute('data-currentvalue', settingsValue); + find('settingsEditor').simulate('change'); + } + await act(async () => { clickNextButton(); }); @@ -119,14 +119,14 @@ export const getFormActions = (testBed: TestBed) => { const completeStepAliases = async (aliases?: { [key: string]: any }) => { const { find, component } = testBed; + const aliasesValue = JSON.stringify(aliases); - await act(async () => { - if (aliases) { - find('aliasesEditor').simulate('change', { - jsonString: JSON.stringify(aliases), - }); // Using mocked EuiCodeEditor - } + if (aliasesValue) { + find('aliasesEditor').getDOMNode().setAttribute('data-currentvalue', aliasesValue); + find('aliasesEditor').simulate('change'); + } + await act(async () => { clickNextButton(); }); diff --git a/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_aliases.tsx b/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_aliases.tsx index b22030be2d30b..a948b9a999fa8 100644 --- a/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_aliases.tsx +++ b/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_aliases.tsx @@ -18,8 +18,9 @@ import { EuiCode, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; -import { EuiCodeEditor, Forms } from '../../../../../shared_imports'; +import { Forms } from '../../../../../shared_imports'; import { useJsonStep } from './use_json_step'; interface Props { @@ -105,29 +106,23 @@ export const StepAliases: React.FunctionComponent = React.memo( error={error} fullWidth > -

    diff --git a/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx b/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx index f370ea3642491..0fd889de03921 100644 --- a/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx +++ b/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx @@ -18,8 +18,9 @@ import { EuiCode, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; -import { EuiCodeEditor, Forms } from '../../../../../shared_imports'; +import { Forms } from '../../../../../shared_imports'; import { useJsonStep } from './use_json_step'; interface Props { @@ -99,29 +100,23 @@ export const StepSettings: React.FunctionComponent = React.memo( error={error} fullWidth > -
    diff --git a/x-pack/plugins/infra/common/alerting/metrics/metric_value_formatter.test.ts b/x-pack/plugins/infra/common/alerting/metrics/metric_value_formatter.test.ts new file mode 100644 index 0000000000000..b6413b37b0380 --- /dev/null +++ b/x-pack/plugins/infra/common/alerting/metrics/metric_value_formatter.test.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 { metricValueFormatter } from './metric_value_formatter'; + +describe('metricValueFormatter', () => { + const testData = [ + { value: null, metric: undefined, result: '[NO DATA]' }, + { value: null, metric: 'system.cpu.user.pct', result: '[NO DATA]' }, + { value: 50, metric: undefined, result: '50' }, + { value: 0.7, metric: 'system.cpu.user.pct', result: '70%' }, + { value: 0.7012345, metric: 'system.cpu.user.pct', result: '70.1%' }, + { value: 208, metric: 'system.cpu.user.ticks', result: '208' }, + { value: 0.8, metric: 'system.cpu.user.ticks', result: '0.8' }, + ]; + + it.each(testData)( + 'metricValueFormatter($value, $metric) = $result', + ({ value, metric, result }) => { + expect(metricValueFormatter(value, metric)).toBe(result); + } + ); +}); diff --git a/x-pack/plugins/infra/common/alerting/metrics/metric_value_formatter.ts b/x-pack/plugins/infra/common/alerting/metrics/metric_value_formatter.ts new file mode 100644 index 0000000000000..2049a3667d0e5 --- /dev/null +++ b/x-pack/plugins/infra/common/alerting/metrics/metric_value_formatter.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 { i18n } from '@kbn/i18n'; +import { createFormatter } from '../../formatters'; + +export const metricValueFormatter = (value: number | null, metric: string = '') => { + const noDataValue = i18n.translate('xpack.infra.metrics.alerting.noDataFormattedValue', { + defaultMessage: '[NO DATA]', + }); + + const formatter = metric.endsWith('.pct') + ? createFormatter('percent') + : createFormatter('highPrecision'); + + return value == null ? noDataValue : formatter(value); +}; diff --git a/x-pack/plugins/infra/common/constants.ts b/x-pack/plugins/infra/common/constants.ts index 9fcb488e02fda..d428ebd3e4909 100644 --- a/x-pack/plugins/infra/common/constants.ts +++ b/x-pack/plugins/infra/common/constants.ts @@ -16,6 +16,7 @@ export const LOGS_FEATURE_ID = 'logs'; export type InfraFeatureId = typeof METRICS_FEATURE_ID | typeof LOGS_FEATURE_ID; export const TIMESTAMP_FIELD = '@timestamp'; +export const MESSAGE_FIELD = 'message'; export const TIEBREAKER_FIELD = '_doc'; export const HOST_FIELD = 'host.name'; export const CONTAINER_FIELD = 'container.id'; diff --git a/x-pack/plugins/infra/common/http_api/index.ts b/x-pack/plugins/infra/common/http_api/index.ts index 52906bcabb65a..355c5925702f7 100644 --- a/x-pack/plugins/infra/common/http_api/index.ts +++ b/x-pack/plugins/infra/common/http_api/index.ts @@ -13,3 +13,10 @@ export * from './metrics_api'; export * from './log_alerts'; export * from './snapshot_api'; export * from './host_details'; +export * from './infra'; + +/** + * Exporting versioned APIs types + */ +export * from './latest'; +export * as inventoryViewsV1 from './inventory_views/v1'; diff --git a/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts b/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts new file mode 100644 index 0000000000000..80e5e501169d6 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts @@ -0,0 +1,82 @@ +/* + * 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 { createLiteralValueFromUndefinedRT, inRangeRt, dateRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const InfraMetricTypeRT = rt.keyof({ + cpu: null, + diskLatency: null, + memory: null, + memoryTotal: null, + rx: null, + tx: null, +}); + +export const RangeRT = rt.type({ + from: dateRt, + to: dateRt, +}); + +export const InfraAssetMetadataTypeRT = rt.keyof({ + 'cloud.provider': null, + 'host.ip': null, + 'host.os.name': null, +}); + +export const InfraAssetMetricsRT = rt.type({ + name: InfraMetricTypeRT, + value: rt.union([rt.number, rt.null]), +}); + +export const InfraAssetMetadataRT = rt.type({ + // keep the actual field name from the index mappings + name: InfraAssetMetadataTypeRT, + value: rt.union([rt.string, rt.null]), +}); + +export const GetInfraMetricsRequestBodyPayloadRT = rt.intersection([ + rt.partial({ + query: rt.UnknownRecord, + }), + rt.type({ + type: rt.literal('host'), + limit: rt.union([inRangeRt(1, 500), createLiteralValueFromUndefinedRT(20)]), + metrics: rt.array(rt.type({ type: InfraMetricTypeRT })), + sourceId: rt.string, + range: RangeRT, + }), +]); + +export const InfraAssetMetricsItemRT = rt.type({ + name: rt.string, + metrics: rt.array(InfraAssetMetricsRT), + metadata: rt.array(InfraAssetMetadataRT), +}); + +export const GetInfraMetricsResponsePayloadRT = rt.type({ + type: rt.literal('host'), + nodes: rt.array(InfraAssetMetricsItemRT), +}); + +export type InfraAssetMetrics = rt.TypeOf; +export type InfraAssetMetadata = rt.TypeOf; +export type InfraAssetMetadataType = rt.TypeOf; +export type InfraAssetMetricType = rt.TypeOf; +export type InfraAssetMetricsItem = rt.TypeOf; + +export type GetInfraMetricsRequestBodyPayload = Omit< + rt.TypeOf, + 'limit' | 'range' +> & { + limit?: number; + range: { + from: string; + to: string; + }; +}; +export type GetInfraMetricsResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/grokdebugger/public/lib/ace/index.js b/x-pack/plugins/infra/common/http_api/infra/index.ts similarity index 86% rename from x-pack/plugins/grokdebugger/public/lib/ace/index.js rename to x-pack/plugins/infra/common/http_api/infra/index.ts index 3152baa94f0ec..75b58fbcf2442 100644 --- a/x-pack/plugins/grokdebugger/public/lib/ace/index.js +++ b/x-pack/plugins/infra/common/http_api/infra/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { GrokMode } from './grok_mode'; +export * from './get_infra_metrics'; diff --git a/x-pack/plugins/infra/common/http_api/inventory_views/v1/common.ts b/x-pack/plugins/infra/common/http_api/inventory_views/v1/common.ts new file mode 100644 index 0000000000000..c229170b8007b --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/inventory_views/v1/common.ts @@ -0,0 +1,66 @@ +/* + * 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 { nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; +import { either } from 'fp-ts/Either'; + +export const INVENTORY_VIEW_URL = '/api/infra/inventory_views'; +export const INVENTORY_VIEW_URL_ENTITY = `${INVENTORY_VIEW_URL}/{inventoryViewId}`; +export const getInventoryViewUrl = (inventoryViewId?: string) => + [INVENTORY_VIEW_URL, inventoryViewId].filter(Boolean).join('/'); + +const inventoryViewIdRT = new rt.Type( + 'InventoryViewId', + rt.string.is, + (u, c) => + either.chain(rt.string.validate(u, c), (id) => { + return id === '0' + ? rt.failure(u, c, `The inventory view with id ${id} is not configurable.`) + : rt.success(id); + }), + String +); + +export const inventoryViewRequestParamsRT = rt.type({ + inventoryViewId: inventoryViewIdRT, +}); + +export type InventoryViewRequestParams = rt.TypeOf; + +export const inventoryViewRequestQueryRT = rt.partial({ + sourceId: rt.string, +}); + +export type InventoryViewRequestQuery = rt.TypeOf; + +const inventoryViewAttributesResponseRT = rt.intersection([ + rt.strict({ + name: nonEmptyStringRt, + isDefault: rt.boolean, + isStatic: rt.boolean, + }), + rt.UnknownRecord, +]); + +const inventoryViewResponseRT = rt.exact( + rt.intersection([ + rt.type({ + id: rt.string, + attributes: inventoryViewAttributesResponseRT, + }), + rt.partial({ + updatedAt: rt.number, + version: rt.string, + }), + ]) +); + +export const inventoryViewResponsePayloadRT = rt.type({ + data: inventoryViewResponseRT, +}); + +export type GetInventoryViewResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/http_api/inventory_views/v1/create_inventory_view.ts b/x-pack/plugins/infra/common/http_api/inventory_views/v1/create_inventory_view.ts new file mode 100644 index 0000000000000..99350daa358b0 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/inventory_views/v1/create_inventory_view.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 { nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const createInventoryViewAttributesRequestPayloadRT = rt.intersection([ + rt.type({ + name: nonEmptyStringRt, + }), + rt.UnknownRecord, + rt.exact(rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined })), +]); + +export type CreateInventoryViewAttributesRequestPayload = rt.TypeOf< + typeof createInventoryViewAttributesRequestPayloadRT +>; + +export const createInventoryViewRequestPayloadRT = rt.type({ + attributes: createInventoryViewAttributesRequestPayloadRT, +}); + +export type CreateInventoryViewRequestPayload = rt.TypeOf< + typeof createInventoryViewRequestPayloadRT +>; diff --git a/x-pack/plugins/infra/common/http_api/inventory_views/v1/find_inventory_view.ts b/x-pack/plugins/infra/common/http_api/inventory_views/v1/find_inventory_view.ts new file mode 100644 index 0000000000000..24812ccb43585 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/inventory_views/v1/find_inventory_view.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 { nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const findInventoryViewAttributesResponseRT = rt.strict({ + name: nonEmptyStringRt, + isDefault: rt.boolean, + isStatic: rt.boolean, +}); + +const findInventoryViewResponseRT = rt.exact( + rt.intersection([ + rt.type({ + id: rt.string, + attributes: findInventoryViewAttributesResponseRT, + }), + rt.partial({ + updatedAt: rt.number, + version: rt.string, + }), + ]) +); + +export const findInventoryViewResponsePayloadRT = rt.type({ + data: rt.array(findInventoryViewResponseRT), +}); + +export type FindInventoryViewResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/http_api/inventory_views/v1/get_inventory_view.ts b/x-pack/plugins/infra/common/http_api/inventory_views/v1/get_inventory_view.ts new file mode 100644 index 0000000000000..3e862bdaa3388 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/inventory_views/v1/get_inventory_view.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. + */ + +import * as rt from 'io-ts'; + +export const getInventoryViewRequestParamsRT = rt.type({ + inventoryViewId: rt.string, +}); + +export type GetInventoryViewRequestParams = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/http_api/inventory_views/v1/index.ts b/x-pack/plugins/infra/common/http_api/inventory_views/v1/index.ts new file mode 100644 index 0000000000000..74f0d3b6962a1 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/inventory_views/v1/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. + */ + +export * from './common'; +export * from './get_inventory_view'; +export * from './find_inventory_view'; +export * from './create_inventory_view'; +export * from './update_inventory_view'; diff --git a/x-pack/plugins/infra/common/http_api/inventory_views/v1/update_inventory_view.ts b/x-pack/plugins/infra/common/http_api/inventory_views/v1/update_inventory_view.ts new file mode 100644 index 0000000000000..7a2d33ebd6138 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/inventory_views/v1/update_inventory_view.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 { nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const updateInventoryViewAttributesRequestPayloadRT = rt.intersection([ + rt.type({ + name: nonEmptyStringRt, + }), + rt.UnknownRecord, + rt.exact(rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined })), +]); + +export type UpdateInventoryViewAttributesRequestPayload = rt.TypeOf< + typeof updateInventoryViewAttributesRequestPayloadRT +>; + +export const updateInventoryViewRequestPayloadRT = rt.type({ + attributes: updateInventoryViewAttributesRequestPayloadRT, +}); + +export type UpdateInventoryViewRequestPayload = rt.TypeOf< + typeof updateInventoryViewRequestPayloadRT +>; diff --git a/x-pack/plugins/infra/common/http_api/latest.ts b/x-pack/plugins/infra/common/http_api/latest.ts new file mode 100644 index 0000000000000..519da4a60dec1 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/latest.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 * from './inventory_views/v1'; diff --git a/x-pack/plugins/infra/common/inventory_views/defaults.ts b/x-pack/plugins/infra/common/inventory_views/defaults.ts new file mode 100644 index 0000000000000..ae78000968995 --- /dev/null +++ b/x-pack/plugins/infra/common/inventory_views/defaults.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { NonEmptyString } from '@kbn/io-ts-utils'; +import type { InventoryViewAttributes } from './types'; + +export const staticInventoryViewId = '0'; + +export const staticInventoryViewAttributes: InventoryViewAttributes = { + name: i18n.translate('xpack.infra.savedView.defaultViewNameHosts', { + defaultMessage: 'Default view', + }) as NonEmptyString, + isDefault: false, + isStatic: true, + metric: { + type: 'cpu', + }, + groupBy: [], + nodeType: 'host', + view: 'map', + customOptions: [], + boundsOverride: { + max: 1, + min: 0, + }, + autoBounds: true, + accountId: '', + region: '', + customMetrics: [], + legend: { + palette: 'cool', + steps: 10, + reverseColors: false, + }, + source: 'default', + sort: { + by: 'name', + direction: 'desc', + }, + timelineOpen: false, + filterQuery: { + kind: 'kuery', + expression: '', + }, + time: Date.now(), + autoReload: false, +}; diff --git a/x-pack/plugins/infra/common/inventory_views/index.ts b/x-pack/plugins/infra/common/inventory_views/index.ts new file mode 100644 index 0000000000000..ae809a6c7c615 --- /dev/null +++ b/x-pack/plugins/infra/common/inventory_views/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 * from './defaults'; +export * from './types'; diff --git a/x-pack/plugins/infra/common/inventory_views/inventory_view.mock.ts b/x-pack/plugins/infra/common/inventory_views/inventory_view.mock.ts new file mode 100644 index 0000000000000..a8f5ef6ce181b --- /dev/null +++ b/x-pack/plugins/infra/common/inventory_views/inventory_view.mock.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { staticInventoryViewAttributes } from './defaults'; +import type { InventoryView, InventoryViewAttributes } from './types'; + +export const createInventoryViewMock = ( + id: string, + attributes: InventoryViewAttributes, + updatedAt?: number, + version?: string +): InventoryView => ({ + id, + attributes: { + ...staticInventoryViewAttributes, + ...attributes, + }, + updatedAt, + version, +}); diff --git a/x-pack/plugins/infra/common/inventory_views/types.ts b/x-pack/plugins/infra/common/inventory_views/types.ts new file mode 100644 index 0000000000000..49979c1063efa --- /dev/null +++ b/x-pack/plugins/infra/common/inventory_views/types.ts @@ -0,0 +1,35 @@ +/* + * 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 { nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const inventoryViewAttributesRT = rt.intersection([ + rt.strict({ + name: nonEmptyStringRt, + isDefault: rt.boolean, + isStatic: rt.boolean, + }), + rt.UnknownRecord, +]); + +export type InventoryViewAttributes = rt.TypeOf; + +export const inventoryViewRT = rt.exact( + rt.intersection([ + rt.type({ + id: rt.string, + attributes: inventoryViewAttributesRT, + }), + rt.partial({ + updatedAt: rt.number, + version: rt.string, + }), + ]) +); + +export type InventoryView = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/log_views/resolved_log_view.ts b/x-pack/plugins/infra/common/log_views/resolved_log_view.ts index 2fc2fd7aa2374..391c6be18fb9d 100644 --- a/x-pack/plugins/infra/common/log_views/resolved_log_view.ts +++ b/x-pack/plugins/infra/common/log_views/resolved_log_view.ts @@ -55,6 +55,7 @@ const resolveLegacyReference = async ( .create( { id: `log-view-${logViewId}`, + name: logViewAttributes.name, title: indices, timeFieldName: TIMESTAMP_FIELD, allowNoIndex: true, diff --git a/x-pack/plugins/infra/common/metrics_explorer_views/defaults.ts b/x-pack/plugins/infra/common/metrics_explorer_views/defaults.ts new file mode 100644 index 0000000000000..88771d1a76fcb --- /dev/null +++ b/x-pack/plugins/infra/common/metrics_explorer_views/defaults.ts @@ -0,0 +1,51 @@ +/* + * 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 { NonEmptyString } from '@kbn/io-ts-utils'; +import type { MetricsExplorerViewAttributes } from './types'; + +export const staticMetricsExplorerViewId = 'static'; + +export const staticMetricsExplorerViewAttributes: MetricsExplorerViewAttributes = { + name: i18n.translate('xpack.infra.savedView.defaultViewNameHosts', { + defaultMessage: 'Default view', + }) as NonEmptyString, + isDefault: false, + isStatic: true, + options: { + aggregation: 'avg', + metrics: [ + { + aggregation: 'avg', + field: 'system.cpu.total.norm.pct', + color: 'color0', + }, + { + aggregation: 'avg', + field: 'kubernetes.pod.cpu.usage.node.pct', + color: 'color1', + }, + { + aggregation: 'avg', + field: 'docker.cpu.total.pct', + color: 'color2', + }, + ], + source: 'default', + }, + chartOptions: { + type: 'line', + yAxisMode: 'fromZero', + stack: false, + }, + currentTimerange: { + from: 'now-1h', + to: 'now', + interval: '>=10s', + }, +}; diff --git a/x-pack/plugins/infra/common/metrics_explorer_views/index.ts b/x-pack/plugins/infra/common/metrics_explorer_views/index.ts new file mode 100644 index 0000000000000..6cc0ccaa93a6d --- /dev/null +++ b/x-pack/plugins/infra/common/metrics_explorer_views/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 * from './types'; diff --git a/x-pack/plugins/infra/common/metrics_explorer_views/metric_explorer_view.mock.ts b/x-pack/plugins/infra/common/metrics_explorer_views/metric_explorer_view.mock.ts new file mode 100644 index 0000000000000..e921c37dd21f8 --- /dev/null +++ b/x-pack/plugins/infra/common/metrics_explorer_views/metric_explorer_view.mock.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { staticMetricsExplorerViewAttributes } from './defaults'; +import type { MetricsExplorerView, MetricsExplorerViewAttributes } from './types'; + +export const createmetricsExplorerViewMock = ( + id: string, + attributes: MetricsExplorerViewAttributes, + updatedAt?: number, + version?: string +): MetricsExplorerView => ({ + id, + attributes: { + ...staticMetricsExplorerViewAttributes, + ...attributes, + }, + updatedAt, + version, +}); diff --git a/x-pack/plugins/infra/common/metrics_explorer_views/types.ts b/x-pack/plugins/infra/common/metrics_explorer_views/types.ts new file mode 100644 index 0000000000000..47ecb06ceace5 --- /dev/null +++ b/x-pack/plugins/infra/common/metrics_explorer_views/types.ts @@ -0,0 +1,35 @@ +/* + * 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 { nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const metricsExplorerViewAttributesRT = rt.intersection([ + rt.strict({ + name: nonEmptyStringRt, + isDefault: rt.boolean, + isStatic: rt.boolean, + }), + rt.UnknownRecord, +]); + +export type MetricsExplorerViewAttributes = rt.TypeOf; + +export const metricsExplorerViewRT = rt.exact( + rt.intersection([ + rt.type({ + id: rt.string, + attributes: metricsExplorerViewAttributesRT, + }), + rt.partial({ + updatedAt: rt.number, + version: rt.string, + }), + ]) +); + +export type MetricsExplorerView = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/plugin_config_types.ts b/x-pack/plugins/infra/common/plugin_config_types.ts index 59ed36c9b3279..2c3a3bd2efeb7 100644 --- a/x-pack/plugins/infra/common/plugin_config_types.ts +++ b/x-pack/plugins/infra/common/plugin_config_types.ts @@ -17,6 +17,9 @@ export interface InfraConfig { inventory: { compositeSize: number; }; + logs: { + app_target: 'logs-ui' | 'discover'; + }; sources?: { default?: { fields?: { @@ -28,6 +31,7 @@ export interface InfraConfig { export const publicConfigKeys = { sources: true, + logs: true, } as const; export type InfraPublicConfigKey = keyof { diff --git a/x-pack/plugins/infra/kibana.jsonc b/x-pack/plugins/infra/kibana.jsonc index 1243275a4a416..abf1d2f8766f2 100644 --- a/x-pack/plugins/infra/kibana.jsonc +++ b/x-pack/plugins/infra/kibana.jsonc @@ -14,6 +14,7 @@ "charts", "data", "dataViews", + "discover", "embeddable", "features", "lens", diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap index 9994945cd3290..5ee10d2d3381e 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap @@ -34,6 +34,7 @@ Array [ "groupBy": Array [ "host.hostname", ], + "hideTitle": true, "source": Object { "id": "default", }, diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx index 20f134633e04b..d73aec96da4d1 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx @@ -6,6 +6,8 @@ */ import React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { coreMock as mockCoreMock } from '@kbn/core/public/mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; @@ -17,6 +19,8 @@ import { import { AlertDetailsAppSection } from './alert_details_app_section'; import { ExpressionChart } from './expression_chart'; +const mockedChartStartContract = chartPluginMock.createStartContract(); + jest.mock('@kbn/observability-alert-details', () => ({ AlertAnnotation: () => {}, AlertActiveTimeRangeAnnotation: () => {}, @@ -32,7 +36,10 @@ jest.mock('./expression_chart', () => ({ jest.mock('../../../hooks/use_kibana', () => ({ useKibanaContextForPlugin: () => ({ - services: mockCoreMock.createStart(), + services: { + ...mockCoreMock.createStart(), + charts: mockedChartStartContract, + }, }), })); @@ -46,6 +53,8 @@ jest.mock('../../../containers/metrics_source/source', () => ({ describe('AlertDetailsAppSection', () => { const queryClient = new QueryClient(); + const mockedSetAlertSummaryFields = jest.fn(); + const ruleLink = 'ruleLink'; const renderComponent = () => { return render( @@ -53,16 +62,39 @@ describe('AlertDetailsAppSection', () => { ); }; - it('should render rule data', async () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render rule and alert data', async () => { const result = renderComponent(); expect((await result.findByTestId('metricThresholdAppSection')).children.length).toBe(3); + expect(result.getByTestId('threshold-2000-2500')).toBeTruthy(); + }); + + it('should render rule link', async () => { + renderComponent(); + + expect(mockedSetAlertSummaryFields).toBeCalledTimes(1); + expect(mockedSetAlertSummaryFields).toBeCalledWith([ + { + label: 'Rule', + value: ( + + Monitoring hosts + + ), + }, + ]); }); it('should render annotations', async () => { diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx index 06602c9638865..466d032b5c01f 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx @@ -5,17 +5,31 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useEffect, useMemo } from 'react'; import moment from 'moment'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, useEuiTheme } from '@elastic/eui'; -import { TopAlert } from '@kbn/observability-plugin/public'; -import { ALERT_END, ALERT_START } from '@kbn/rule-data-utils'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; +import { AlertSummaryField, TopAlert } from '@kbn/observability-plugin/public'; +import { ALERT_END, ALERT_START, ALERT_EVALUATION_VALUES } from '@kbn/rule-data-utils'; import { Rule } from '@kbn/alerting-plugin/common'; import { AlertAnnotation, getPaddedAlertTimeRange, AlertActiveTimeRangeAnnotation, } from '@kbn/observability-alert-details'; +import { metricValueFormatter } from '../../../../common/alerting/metrics/metric_value_formatter'; +import { TIME_LABELS } from '../../common/criterion_preview_chart/criterion_preview_chart'; +import { Threshold } from '../../common/components/threshold'; import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; import { generateUniqueKey } from '../lib/generate_unique_key'; import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; @@ -37,12 +51,19 @@ const ALERT_START_ANNOTATION_ID = 'alert_start_annotation'; const ALERT_TIME_RANGE_ANNOTATION_ID = 'alert_time_range_annotation'; interface AppSectionProps { - rule: MetricThresholdRule; alert: MetricThresholdAlert; + rule: MetricThresholdRule; + ruleLink: string; + setAlertSummaryFields: React.Dispatch>; } -export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { - const { uiSettings } = useKibanaContextForPlugin().services; +export function AlertDetailsAppSection({ + alert, + rule, + ruleLink, + setAlertSummaryFields, +}: AppSectionProps) { + const { uiSettings, charts } = useKibanaContextForPlugin().services; const { source, createDerivedIndexPattern } = useSourceContext(); const { euiTheme } = useEuiTheme(); @@ -50,6 +71,10 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { () => createDerivedIndexPattern(), [createDerivedIndexPattern] ); + const chartProps = { + theme: charts.theme.useChartsTheme(), + baseTheme: charts.theme.useChartsBaseTheme(), + }; const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; const annotations = [ @@ -68,22 +93,76 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { key={ALERT_TIME_RANGE_ANNOTATION_ID} />, ]; + useEffect(() => { + setAlertSummaryFields([ + { + label: i18n.translate('xpack.infra.metrics.alertDetailsAppSection.summaryField.rule', { + defaultMessage: 'Rule', + }), + value: ( + + {rule.name} + + ), + }, + ]); + }, [alert, rule, ruleLink, setAlertSummaryFields]); return !!rule.params.criteria ? ( - {rule.params.criteria.map((criterion) => ( + {rule.params.criteria.map((criterion, index) => ( - + +

    + {criterion.aggType.toUpperCase()}{' '} + {'metric' in criterion ? criterion.metric : undefined} +

    +
    + + + + + + + + metricValueFormatter(d, 'metric' in criterion ? criterion.metric : undefined) + } + title={i18n.translate( + 'xpack.infra.metrics.alertDetailsAppSection.thresholdTitle', + { + defaultMessage: 'Threshold breached', + } + )} + comparator={criterion.comparator} + /> + + + + +
    ))} diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx index f2bb22485fe9c..745a1f0169788 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx @@ -22,6 +22,9 @@ jest.mock('../../../hooks/use_kibana', () => ({ useKibanaContextForPlugin: () => ({ services: { ...mockStartServices, + charts: { + activeCursor: jest.fn(), + }, }, }), })); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx index 8dd7762feb6b9..41a313adda8fa 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { ReactElement } from 'react'; +import React, { ReactElement, useRef } from 'react'; import { Axis, Chart, @@ -17,6 +17,7 @@ import { } from '@elastic/charts'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useActiveCursor } from '@kbn/charts-plugin/public'; import { DataViewBase } from '@kbn/es-query'; import { first, last } from 'lodash'; @@ -48,25 +49,27 @@ import { CUSTOM_EQUATION } from '../i18n_strings'; interface Props { expression: MetricExpression; derivedIndexPattern: DataViewBase; - source?: MetricsSourceConfiguration; + annotations?: Array>; + chartType?: MetricsExplorerChartType; filterQuery?: string; groupBy?: string | string[]; - chartType?: MetricsExplorerChartType; + hideTitle?: boolean; + source?: MetricsSourceConfiguration; timeRange?: TimeRange; - annotations?: Array>; } export const ExpressionChart: React.FC = ({ expression, derivedIndexPattern, - source, + annotations, + chartType = MetricsExplorerChartType.bar, filterQuery, groupBy, - chartType = MetricsExplorerChartType.bar, + hideTitle = false, + source, timeRange, - annotations, }) => { - const { uiSettings } = useKibanaContextForPlugin().services; + const { uiSettings, charts } = useKibanaContextForPlugin().services; const { isLoading, data } = useMetricsExplorerChartData( expression, @@ -77,6 +80,11 @@ export const ExpressionChart: React.FC = ({ timeRange ); + const chartRef = useRef(null); + const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { + isDateHistogram: true, + }); + if (isLoading) { return ; } @@ -141,7 +149,7 @@ export const ExpressionChart: React.FC = ({ return ( <> - + = ({ tickFormat={createFormatterForMetric(metric)} domain={domain} /> - + -
    - {series.id !== 'ALL' ? ( - - - - ) : ( - - - - )} -
    + {!hideTitle && ( +
    + {series.id !== 'ALL' ? ( + + + + ) : ( + + + + )} +
    + )} ); }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx index ae8bf2ad972e6..3a59108e7e4d8 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -226,6 +226,7 @@ export const ExpressionRow: React.FC = (props) => { ', - threshold: [0.1], - timeSize: 1, + aggType: Aggregators.AVERAGE, + comparator: Comparator.GT, + threshold: [2000], + timeSize: 15, timeUnit: 'm', metric: 'system.cpu.user.pct', }, + { + aggType: Aggregators.MAX, + comparator: Comparator.GT, + threshold: [4], + timeSize: 15, + timeUnit: 'm', + metric: 'system.cpu.user.pct', + warningComparator: Comparator.GT, + warningThreshold: [2.2], + }, ], sourceId: 'default', alertOnNoData: true, alertOnGroupDisappear: true, }, + 'kibana.alert.evaluation.values': [2500, 5], 'kibana.alert.rule.category': 'Metric threshold', 'kibana.alert.rule.consumer': 'alerts', 'kibana.alert.rule.execution.uuid': '62dd07ef-ead9-4b1f-a415-7c83d03925f7', diff --git a/x-pack/plugins/infra/public/apps/discover_app.tsx b/x-pack/plugins/infra/public/apps/discover_app.tsx new file mode 100644 index 0000000000000..807b64845cdc7 --- /dev/null +++ b/x-pack/plugins/infra/public/apps/discover_app.tsx @@ -0,0 +1,105 @@ +/* + * 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 { interpret } from 'xstate'; +import type { DiscoverStart } from '@kbn/discover-plugin/public'; +import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; +import type { InfraClientStartDeps, InfraClientStartExports } from '../types'; +import type { LogViewColumnConfiguration, ResolvedLogView } from '../../common/log_views'; +import { + createLogViewStateMachine, + DEFAULT_LOG_VIEW, + initializeFromUrl, +} from '../observability_logs/log_view_state'; +import { MESSAGE_FIELD, TIMESTAMP_FIELD } from '../../common/constants'; + +export const renderApp = ( + core: CoreStart, + plugins: InfraClientStartDeps, + pluginStart: InfraClientStartExports, + params: AppMountParameters +) => { + const { discover } = plugins; + const { logViews } = pluginStart; + + const machine = createLogViewStateMachine({ + initialContext: { logViewReference: DEFAULT_LOG_VIEW }, + logViews: logViews.client, + initializeFromUrl: createInitializeFromUrl(core, params), + }); + + const service = interpret(machine) + .onTransition((state) => { + if ( + state.matches('checkingStatus') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') + ) { + return redirectToDiscover(discover, state.context.resolvedLogView); + } else if ( + state.matches('loadingFailed') || + state.matches('resolutionFailed') || + state.matches('checkingStatusFailed') + ) { + return redirectToDiscover(discover); + } + }) + .start(); + + return () => { + // Stop machine interpreter after navigation + service.stop(); + }; +}; + +const redirectToDiscover = (discover: DiscoverStart, resolvedLogView?: ResolvedLogView) => { + const navigationOptions = { replace: true }; + + if (!resolvedLogView) { + return discover.locator?.navigate({}, navigationOptions); + } + + const columns = parseColumns(resolvedLogView.columns); + const dataViewSpec = resolvedLogView.dataViewReference.toSpec(); + + return discover.locator?.navigate( + { + columns, + dataViewId: dataViewSpec.id, + dataViewSpec, + }, + navigationOptions + ); +}; + +/** + * Helpers + */ + +const parseColumns = (columns: ResolvedLogView['columns']) => { + return columns.map(getColumnValue).filter(Boolean) as string[]; +}; + +const getColumnValue = (column: LogViewColumnConfiguration) => { + if ('messageColumn' in column) return MESSAGE_FIELD; + if ('timestampColumn' in column) return TIMESTAMP_FIELD; + if ('fieldColumn' in column) return column.fieldColumn.field; + + return null; +}; + +const createInitializeFromUrl = (core: CoreStart, params: AppMountParameters) => { + const toastsService = core.notifications.toasts; + + const urlStateStorage = createKbnUrlStateStorage({ + history: params.history, + useHash: false, + useHashQuery: false, + }); + + return initializeFromUrl({ toastsService, urlStateStorage }); +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/constants.ts b/x-pack/plugins/infra/public/common/visualizations/constants.ts new file mode 100644 index 0000000000000..3ea98953c3c4c --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/constants.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 { + cpu, + diskIORead, + diskIOWrite, + load, + memory, + memoryAvailable, + rx, + tx, + hostCount, +} from './lens/formulas/host'; +import { LineChart, MetricChart } from './lens/visualization_types'; + +export const hostLensFormulas = { + cpu, + diskIORead, + diskIOWrite, + hostCount, + load, + memory, + memoryAvailable, + rx, + tx, +}; + +export const visualizationTypes = { + lineChart: LineChart, + metricChart: MetricChart, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/index.ts b/x-pack/plugins/infra/public/common/visualizations/index.ts index 9f933baa0edc6..cba62658e2e8a 100644 --- a/x-pack/plugins/infra/public/common/visualizations/index.ts +++ b/x-pack/plugins/infra/public/common/visualizations/index.ts @@ -5,19 +5,18 @@ * 2.0. */ -import { CPU, Load, Memory, MemoryAvailable, RX, TX, DiskIORead, DiskIOWrite } from './lens/hosts'; +export type { + HostsLensFormulas, + LineChartOptions, + LensChartConfig, + LensLineChartConfig, + MetricChartOptions, + HostsLensMetricChartFormulas, + HostsLensLineChartFormulas, + LensOptions, + LensAttributes, +} from './types'; -export { buildLensAttributes } from './lens/lens_visualization'; +export { hostLensFormulas, visualizationTypes } from './constants'; -export const hostMetricsLensAttributes = { - cpu: CPU, - load: Load, - memory: Memory, - memoryAvailable: MemoryAvailable, - rx: RX, - tx: TX, - diskIORead: DiskIORead, - diskIOWrite: DiskIOWrite, -}; - -export type HostLensAttributesTypes = keyof typeof hostMetricsLensAttributes; +export { buildLensAttributes } from './lens/build_lens_attributes'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/lens_visualization.ts b/x-pack/plugins/infra/public/common/visualizations/lens/build_lens_attributes.ts similarity index 69% rename from x-pack/plugins/infra/public/common/visualizations/lens/lens_visualization.ts rename to x-pack/plugins/infra/public/common/visualizations/lens/build_lens_attributes.ts index 46f33718d405c..6c5adb7bce203 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/lens_visualization.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/build_lens_attributes.ts @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { LensAttributes } from '../../../types'; -import type { ILensVisualization } from './types'; +import type { LensAttributes, TVisualization, VisualizationAttributes } from '../types'; -export const buildLensAttributes = (visualization: ILensVisualization): LensAttributes => { +export const buildLensAttributes = >( + visualization: T +): LensAttributes => { return { title: visualization.getTitle(), visualizationType: visualization.getVisualizationType(), @@ -18,7 +19,8 @@ export const buildLensAttributes = (visualization: ILensVisualization): LensAttr layers: visualization.getLayers(), }, }, - filters: [], + internalReferences: visualization.getReferences(), + filters: visualization.getFilters(), query: { language: 'kuery', query: '' }, visualization: visualization.getVisualizationState(), adHocDataViews: visualization.getAdhocDataView(), diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu.ts new file mode 100644 index 0000000000000..f4d60c15a5317 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu.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 type { LensChartConfig, LensLineChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const cpuLineChart: LensLineChartConfig = { + extraVisualizationState: { + yLeftExtent: { + mode: 'custom', + lowerBound: 0, + upperBound: 1, + }, + }, +}; + +export const cpu: LensChartConfig = { + title: 'CPU Usage', + formula: { + formula: + '(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)', + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, + }, + getFilters, + + lineChartConfig: cpuLineChart, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/diskio_read.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/diskio_read.ts new file mode 100644 index 0000000000000..344cff8a82f4a --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/diskio_read.ts @@ -0,0 +1,23 @@ +/* + * 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 { LensChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const diskIORead: LensChartConfig = { + title: 'Disk Read IOPS', + formula: { + formula: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes: *')", + format: { + id: 'bytes', + params: { + decimals: 1, + }, + }, + }, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/diskio_write.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/diskio_write.ts new file mode 100644 index 0000000000000..f0d1d2cd718b2 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/diskio_write.ts @@ -0,0 +1,23 @@ +/* + * 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 { LensChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const diskIOWrite: LensChartConfig = { + title: 'Disk Write IOPS', + formula: { + formula: "counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes: *')", + format: { + id: 'bytes', + params: { + decimals: 1, + }, + }, + }, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts new file mode 100644 index 0000000000000..4f0d230176368 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts @@ -0,0 +1,23 @@ +/* + * 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 { LensChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const hostCount: LensChartConfig = { + title: 'Hosts', + formula: { + formula: 'unique_count(host.name)', + format: { + id: 'number', + params: { + decimals: 0, + }, + }, + }, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts new file mode 100644 index 0000000000000..8b15f4308cebc --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/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. + */ + +export { cpu } from './cpu'; +export { diskIORead } from './diskio_read'; +export { diskIOWrite } from './diskio_write'; +export { hostCount } from './host_count'; +export { load } from './load'; +export { memory } from './memory'; +export { memoryAvailable } from './memory_available'; +export { rx } from './rx'; +export { tx } from './tx'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load.ts new file mode 100644 index 0000000000000..c4e349fe01aaf --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load.ts @@ -0,0 +1,76 @@ +/* + * 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 { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types'; +import type { LensChartConfig, LensLineChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +const REFERENCE_LAYER = 'referenceLayer'; + +export const loadLineChart: LensLineChartConfig = { + extraLayers: { + [REFERENCE_LAYER]: { + linkToLayers: [], + columnOrder: ['referenceColumn'], + columns: { + referenceColumn: { + label: 'Reference', + dataType: 'number', + operationType: 'static_value', + isStaticValue: true, + isBucketed: false, + scale: 'ratio', + params: { + value: 1, + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, + }, + references: [], + customLabel: true, + } as ReferenceBasedIndexPatternColumn, + }, + sampling: 1, + incompleteColumns: {}, + }, + }, + extraVisualizationState: { + layers: [ + { + layerId: REFERENCE_LAYER, + layerType: 'referenceLine', + accessors: ['referenceColumn'], + yConfig: [ + { + forAccessor: 'referenceColumn', + axisMode: 'left', + color: '#6092c0', + }, + ], + }, + ], + }, + extraReference: REFERENCE_LAYER, +}; + +export const load: LensChartConfig = { + title: 'Normalized Load', + formula: { + formula: 'average(system.load.1) / max(system.load.cores)', + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, + }, + getFilters, + lineChartConfig: loadLineChart, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory.ts new file mode 100644 index 0000000000000..413b9aa99001f --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory.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 type { LensChartConfig, LensLineChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +const memoryLineChart: LensLineChartConfig = { + extraVisualizationState: { + yLeftExtent: { + mode: 'custom', + lowerBound: 0, + upperBound: 1, + }, + }, +}; + +export const memory: LensChartConfig = { + title: 'Memory', + formula: { + formula: 'average(system.memory.actual.used.pct)', + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, + }, + lineChartConfig: memoryLineChart, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_available.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_available.ts new file mode 100644 index 0000000000000..69cea10f423ef --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_available.ts @@ -0,0 +1,23 @@ +/* + * 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 { LensChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const memoryAvailable: LensChartConfig = { + title: 'Memory Available', + formula: { + formula: 'max(system.memory.total) - average(system.memory.actual.used.bytes)', + format: { + id: 'bytes', + params: { + decimals: 1, + }, + }, + }, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts new file mode 100644 index 0000000000000..b396dffb979e6 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LensChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const rx: LensChartConfig = { + title: 'Network Inbound (RX)', + formula: { + formula: + "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)", + format: { + id: 'bits', + params: { + decimals: 1, + }, + }, + }, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts new file mode 100644 index 0000000000000..f9f97a8ed9112 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LensChartConfig } from '../../../types'; +import { getFilters } from './utils'; + +export const tx: LensChartConfig = { + title: 'Network Outbound (TX)', + formula: { + formula: + "average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)", + format: { + id: 'bits', + params: { + decimals: 1, + }, + }, + }, + getFilters, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/utils.ts b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/utils.ts new file mode 100644 index 0000000000000..c0b16ca705b13 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/utils.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 { DataViewBase } from '@kbn/es-query'; + +export const getFilters = ({ id }: Pick) => [ + { + meta: { + index: id, + }, + query: { + exists: { + field: 'host.name', + }, + }, + }, +]; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts deleted file mode 100644 index 00ed6c799dda0..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/cpu.ts +++ /dev/null @@ -1,97 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { LensOptions } from '../../../../types'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class CPU implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'CPU Usage'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_cpu_usage', - { - formula: 'average(system.cpu.total.norm.pct)', - format: { - id: 'percent', - params: { - decimals: 0, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_cpu_usage'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - yLeftExtent: { - mode: 'custom', - lowerBound: 0, - upperBound: 1, - }, - }); - }; - - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/diskio_read.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/diskio_read.ts deleted file mode 100644 index 768ae9a640075..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/diskio_read.ts +++ /dev/null @@ -1,93 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import type { LensOptions } from '../../../../types'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class DiskIORead implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Disk Read IOPS'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_diskio_read', - { - formula: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes >= 0')", - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_diskio_read'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - }); - }; - - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/diskio_write.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/diskio_write.ts deleted file mode 100644 index ae6bd12fd026c..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/diskio_write.ts +++ /dev/null @@ -1,94 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import type { LensOptions } from '../../../../types'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class DiskIOWrite implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Disk Write IOPS'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_diskio_write', - { - formula: - "counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes>= 0')", - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_diskio_write'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - }); - }; - - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/index.ts deleted file mode 100644 index 1332b4ed2aadc..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/index.ts +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { CPU } from './cpu'; -export { Load } from './load'; -export { Memory } from './memory'; -export { MemoryAvailable } from './memory_available'; -export { RX } from './rx'; -export { TX } from './tx'; -export { DiskIORead } from './diskio_read'; -export { DiskIOWrite } from './diskio_write'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts deleted file mode 100644 index 59b6167c5dc1d..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/load.ts +++ /dev/null @@ -1,144 +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 { - PersistedIndexPatternLayer, - FormulaPublicApi, - XYState, - FormBasedLayer, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import type { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types'; -import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; -import { - DEFAULT_AD_HOC_DATA_VIEW_ID, - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { LensOptions } from '../../../../types'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; -const REFERENCE_LAYER = 'referenceLayer'; - -export class Load implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Normalized Load'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_cpu_cores_usage', - { - formula: 'average(system.load.1) / max(system.load.cores)', - format: { - id: 'percent', - params: { - decimals: 0, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { - [DEFAULT_LAYER_ID]: dataLayer, - referenceLayer: { - linkToLayers: [], - columnOrder: ['referenceColumn'], - columns: { - referenceColumn: { - label: 'Reference', - dataType: 'number', - operationType: 'static_value', - isStaticValue: true, - isBucketed: false, - scale: 'ratio', - params: { - value: 1, - format: { - id: 'percent', - params: { - decimals: 0, - }, - }, - }, - references: [], - customLabel: true, - } as ReferenceBasedIndexPatternColumn, - }, - sampling: 1, - incompleteColumns: {}, - }, - }; - }; - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_cpu_cores_usage'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - { - layerId: REFERENCE_LAYER, - layerType: 'referenceLine', - accessors: ['referenceColumn'], - yConfig: [ - { - forAccessor: 'referenceColumn', - axisMode: 'left', - color: '#6092c0', - }, - ], - }, - ], - }); - }; - - getReferences = (): SavedObjectReference[] => [ - ...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID), - { - type: 'index-pattern', - id: this.dataView.id ?? DEFAULT_AD_HOC_DATA_VIEW_ID, - name: `indexpattern-datasource-layer-${REFERENCE_LAYER}`, - }, - ]; - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts deleted file mode 100644 index 01bcd45b6e283..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory.ts +++ /dev/null @@ -1,97 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { LensOptions } from '../../../../types'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class Memory implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Disk Writes IOPS'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_memory_usage', - { - formula: 'average(system.memory.actual.used.bytes) / max(system.memory.total)', - format: { - id: 'percent', - params: { - decimals: 0, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_memory_usage'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - yLeftExtent: { - mode: 'custom', - lowerBound: 0, - upperBound: 1, - }, - }); - }; - - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory_available.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory_available.ts deleted file mode 100644 index 69818f407180e..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/memory_available.ts +++ /dev/null @@ -1,91 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { LensOptions } from '../../../../types'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class MemoryAvailable implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Memory Available'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_memory_available', - { - formula: 'max(system.memory.total) - average(system.memory.actual.used.bytes)', - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_memory_available'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - }); - }; - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts deleted file mode 100644 index ff5e19b40dc85..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/rx.ts +++ /dev/null @@ -1,92 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { LensOptions } from '../../../../types'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class RX implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Network Inbound (RX)'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_network_in_bytes', - { - formula: - "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)", - format: { - id: 'bits', - params: { - decimals: 1, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_network_in_bytes'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - }); - }; - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts b/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts deleted file mode 100644 index 8923060763d79..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/hosts/tx.ts +++ /dev/null @@ -1,92 +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 { - FormBasedLayer, - FormulaPublicApi, - PersistedIndexPatternLayer, - XYState, -} from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { - DEFAULT_LAYER_ID, - getAdhocDataView, - getBreakdownColumn, - getDefaultReferences, - getHistogramColumn, - getXYVisualizationState, -} from '../utils'; -import type { LensOptions } from '../../../../types'; -import type { ILensVisualization } from '../types'; - -const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; -const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; - -export class TX implements ILensVisualization { - constructor( - private dataView: DataView, - private options: LensOptions, - private formula: FormulaPublicApi - ) {} - - getTitle(): string { - return 'Network Outbound (TX)'; - } - - getVisualizationType(): string { - return 'lnsXY'; - } - - getLayers = (): Record> => { - const baseLayer: PersistedIndexPatternLayer = { - columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], - columns: { - ...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize), - ...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'), - }, - }; - - const dataLayer = this.formula.insertOrReplaceFormulaColumn( - 'y_network_out_bytes', - { - formula: - "average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)", - format: { - id: 'bits', - params: { - decimals: 1, - }, - }, - }, - baseLayer, - this.dataView - ); - - if (!dataLayer) { - throw new Error('Error generating the data layer for the chart'); - } - - return { [DEFAULT_LAYER_ID]: dataLayer }; - }; - getVisualizationState = (): XYState => { - return getXYVisualizationState({ - layers: [ - { - layerId: DEFAULT_LAYER_ID, - seriesType: 'line', - accessors: ['y_network_out_bytes'], - yConfig: [], - layerType: 'data', - xAccessor: HISTOGRAM_COLUMN_NAME, - splitAccessor: BREAKDOWN_COLUMN_NAME, - }, - ], - }); - }; - getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID); - getAdhocDataView = () => getAdhocDataView(this.dataView); -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/types.ts b/x-pack/plugins/infra/public/common/visualizations/lens/types.ts deleted file mode 100644 index a4e2779fad52c..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/types.ts +++ /dev/null @@ -1,19 +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 { SavedObjectReference } from '@kbn/core-saved-objects-common'; -import type { DataViewSpec } from '@kbn/data-views-plugin/common'; -import type { FormBasedLayer, XYState } from '@kbn/lens-plugin/public'; - -export interface ILensVisualization { - getTitle(): string; - getVisualizationType(): string; - getLayers(): Record>; - getVisualizationState(): XYState; - getReferences(): SavedObjectReference[]; - getAdhocDataView(): Record; -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts b/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts index 8a4f1936c77d3..88b78e5a8096d 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts +++ b/x-pack/plugins/infra/public/common/visualizations/lens/utils.ts @@ -8,33 +8,45 @@ import { DateHistogramIndexPatternColumn, PersistedIndexPatternLayer, TermsIndexPatternColumn, - XYState, } from '@kbn/lens-plugin/public'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; export const DEFAULT_LAYER_ID = 'layer1'; export const DEFAULT_AD_HOC_DATA_VIEW_ID = 'infra_lens_ad_hoc_default'; +const DEFAULT_BREAKDOWN_SIZE = 10; -export const getHistogramColumn = (columnName: string, sourceField: string) => { +export const getHistogramColumn = ({ + columnName, + overrides, +}: { + columnName: string; + overrides?: Partial>; +}) => { return { [columnName]: { dataType: 'date', isBucketed: true, label: '@timestamp', operationType: 'date_histogram', - params: { interval: 'auto' }, scale: 'interval', - sourceField, + sourceField: '@timestamp', + ...overrides, + params: { interval: 'auto', ...overrides?.params }, } as DateHistogramIndexPatternColumn, }; }; -export const getBreakdownColumn = ( - columnName: string, - sourceField: string, - breakdownSize: number -): PersistedIndexPatternLayer['columns'] => { +export const getBreakdownColumn = ({ + columnName, + overrides, +}: { + columnName: string; + overrides?: Partial> & { + breakdownSize?: number; + }; +}): PersistedIndexPatternLayer['columns'] => { + const { breakdownSize = DEFAULT_BREAKDOWN_SIZE, sourceField } = overrides ?? {}; return { [columnName]: { label: `Top ${breakdownSize} values of ${sourceField}`, @@ -64,47 +76,6 @@ export const getBreakdownColumn = ( }; }; -export const getXYVisualizationState = ( - custom: Omit, 'layers'> & { layers: XYState['layers'] } -): XYState => ({ - legend: { - isVisible: false, - position: 'right', - showSingleSeries: false, - }, - valueLabels: 'show', - fittingFunction: 'Zero', - curveType: 'LINEAR', - yLeftScale: 'linear', - axisTitlesVisibilitySettings: { - x: false, - yLeft: false, - yRight: true, - }, - tickLabelsVisibilitySettings: { - x: true, - yLeft: true, - yRight: true, - }, - labelsOrientation: { - x: 0, - yLeft: 0, - yRight: 0, - }, - gridlinesVisibilitySettings: { - x: true, - yLeft: true, - yRight: true, - }, - preferredSeriesType: 'line', - valuesInLegend: false, - emphasizeFitting: true, - yTitle: '', - xTitle: '', - hideEndzones: true, - ...custom, -}); - export const getDefaultReferences = ( dataView: DataView, dataLayerId: string diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/index.ts new file mode 100644 index 0000000000000..4abfeb3a60c45 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/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 { LineChart } from './line_chart'; +export { MetricChart } from './metric_chart'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/line_chart.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/line_chart.ts new file mode 100644 index 0000000000000..68035fc821e80 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/line_chart.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 type { + FormBasedPersistedState, + FormulaPublicApi, + PersistedIndexPatternLayer, + XYState, +} from '@kbn/lens-plugin/public'; +import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; +import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; +import { Filter } from '@kbn/es-query'; +import { + DEFAULT_LAYER_ID, + getAdhocDataView, + getBreakdownColumn, + getDefaultReferences, + getHistogramColumn, +} from '../utils'; +import type { LensChartConfig, VisualizationAttributes, LineChartOptions } from '../../types'; + +const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown'; +const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; +const ACCESSOR = 'formula_accessor'; + +export class LineChart implements VisualizationAttributes { + constructor( + private chartConfig: LensChartConfig, + private dataView: DataView, + private formulaAPI: FormulaPublicApi, + private options?: LineChartOptions + ) {} + + getVisualizationType(): string { + return 'lnsXY'; + } + + getLayers(): FormBasedPersistedState['layers'] { + const baseLayer: PersistedIndexPatternLayer = { + columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME], + columns: { + ...getBreakdownColumn({ + columnName: BREAKDOWN_COLUMN_NAME, + overrides: { + sourceField: 'host.name', + breakdownSize: this.options?.breakdownSize, + }, + }), + ...getHistogramColumn({ + columnName: HISTOGRAM_COLUMN_NAME, + overrides: { + sourceField: this.dataView.timeFieldName, + }, + }), + }, + }; + + const dataLayer = this.formulaAPI.insertOrReplaceFormulaColumn( + ACCESSOR, + this.chartConfig.formula, + baseLayer, + this.dataView + ); + + if (!dataLayer) { + throw new Error('Error generating the data layer for the chart'); + } + + return { [DEFAULT_LAYER_ID]: dataLayer, ...this.chartConfig.lineChartConfig?.extraLayers }; + } + + getVisualizationState(): XYState { + const extraVisualizationState = this.chartConfig.lineChartConfig?.extraVisualizationState; + + return getXYVisualizationState({ + ...extraVisualizationState, + layers: [ + { + layerId: DEFAULT_LAYER_ID, + seriesType: 'line', + accessors: [ACCESSOR], + yConfig: [], + layerType: 'data', + xAccessor: HISTOGRAM_COLUMN_NAME, + splitAccessor: BREAKDOWN_COLUMN_NAME, + }, + ...(extraVisualizationState?.layers ? extraVisualizationState?.layers : []), + ], + }); + } + + getReferences(): SavedObjectReference[] { + const extraReference = this.chartConfig.lineChartConfig?.extraReference; + return [ + ...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID), + ...(extraReference ? getDefaultReferences(this.dataView, extraReference) : []), + ]; + } + + getAdhocDataView(): Record { + return getAdhocDataView(this.dataView); + } + + getTitle(): string { + return this.options?.title ?? this.chartConfig.title ?? ''; + } + + getFilters(): Filter[] { + return this.chartConfig.getFilters({ id: this.dataView.id ?? DEFAULT_LAYER_ID }); + } +} + +export const getXYVisualizationState = ( + custom: Omit, 'layers'> & { layers: XYState['layers'] } +): XYState => ({ + legend: { + isVisible: false, + position: 'right', + showSingleSeries: false, + }, + valueLabels: 'show', + fittingFunction: 'Zero', + curveType: 'LINEAR', + yLeftScale: 'linear', + axisTitlesVisibilitySettings: { + x: false, + yLeft: false, + yRight: true, + }, + tickLabelsVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + labelsOrientation: { + x: 0, + yLeft: 0, + yRight: 0, + }, + gridlinesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + preferredSeriesType: 'line', + valuesInLegend: false, + emphasizeFitting: true, + hideEndzones: true, + ...custom, +}); diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts new file mode 100644 index 0000000000000..823d882e02f8a --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/lens/visualization_types/metric_chart.ts @@ -0,0 +1,139 @@ +/* + * 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 { + FormBasedPersistedState, + FormulaPublicApi, + MetricVisualizationState, + PersistedIndexPatternLayer, +} from '@kbn/lens-plugin/public'; +import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; +import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; +import type { Filter } from '@kbn/es-query'; +import { + DEFAULT_LAYER_ID, + getAdhocDataView, + getDefaultReferences, + getHistogramColumn, +} from '../utils'; + +import type { VisualizationAttributes, LensChartConfig, MetricChartOptions } from '../../types'; + +const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; +const TRENDLINE_LAYER_ID = 'trendline_layer'; +const TRENDLINE_ACCESSOR = 'metric_trendline_formula_accessor'; +const ACCESSOR = 'metric_formula_accessor'; + +export class MetricChart implements VisualizationAttributes { + constructor( + private chartConfig: LensChartConfig, + private dataView: DataView, + private formulaAPI: FormulaPublicApi, + private options?: MetricChartOptions + ) {} + + getVisualizationType(): string { + return 'lnsMetric'; + } + + getTrendLineLayer(baseLayer: PersistedIndexPatternLayer): FormBasedPersistedState['layers'] { + const trendLineLayer = this.formulaAPI.insertOrReplaceFormulaColumn( + TRENDLINE_ACCESSOR, + this.chartConfig.formula, + baseLayer, + this.dataView + ); + + if (!trendLineLayer) { + throw new Error('Error generating the data layer for the chart'); + } + + return { + [TRENDLINE_LAYER_ID]: { + linkToLayers: [DEFAULT_LAYER_ID], + ...trendLineLayer, + }, + }; + } + + getLayers(): FormBasedPersistedState['layers'] { + const { showTrendLine = true } = this.options ?? {}; + const baseLayer: PersistedIndexPatternLayer = { + columnOrder: [HISTOGRAM_COLUMN_NAME], + columns: getHistogramColumn({ + columnName: HISTOGRAM_COLUMN_NAME, + overrides: { + sourceField: this.dataView.timeFieldName, + params: { + interval: 'auto', + includeEmptyRows: true, + }, + }, + }), + sampling: 1, + }; + + const baseLayerDetails = this.formulaAPI.insertOrReplaceFormulaColumn( + ACCESSOR, + { + ...this.chartConfig.formula, + label: this.options?.title ?? this.chartConfig.title, + }, + { columnOrder: [], columns: {} }, + this.dataView + ); + + if (!baseLayerDetails) { + throw new Error('Error generating the data layer for the chart'); + } + + return { + [DEFAULT_LAYER_ID]: baseLayerDetails, + ...(showTrendLine ? this.getTrendLineLayer(baseLayer) : {}), + }; + } + + getVisualizationState(): MetricVisualizationState { + const { subtitle, backgroundColor, showTrendLine = true } = this.options ?? {}; + return { + layerId: DEFAULT_LAYER_ID, + layerType: 'data', + metricAccessor: ACCESSOR, + color: backgroundColor, + subtitle, + showBar: false, + ...(showTrendLine + ? { + trendlineLayerId: TRENDLINE_LAYER_ID, + trendlineLayerType: 'metricTrendline', + trendlineMetricAccessor: TRENDLINE_ACCESSOR, + trendlineTimeAccessor: HISTOGRAM_COLUMN_NAME, + } + : {}), + }; + } + + getReferences(): SavedObjectReference[] { + const { showTrendLine = true } = this.options ?? {}; + return [ + ...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID), + ...(showTrendLine ? getDefaultReferences(this.dataView, TRENDLINE_LAYER_ID) : []), + ]; + } + + getAdhocDataView(): Record { + return getAdhocDataView(this.dataView); + } + + getTitle(): string { + return this.options?.showTitle ? this.options?.title ?? this.chartConfig.title : ''; + } + + getFilters(): Filter[] { + return this.chartConfig.getFilters({ id: this.dataView.id ?? DEFAULT_LAYER_ID }); + } +} diff --git a/x-pack/plugins/infra/public/common/visualizations/types.ts b/x-pack/plugins/infra/public/common/visualizations/types.ts new file mode 100644 index 0000000000000..76f644525bd14 --- /dev/null +++ b/x-pack/plugins/infra/public/common/visualizations/types.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; +import type { DataViewSpec } from '@kbn/data-views-plugin/common'; +import { DataViewBase, Filter } from '@kbn/es-query'; +import { + FormBasedPersistedState, + FormulaPublicApi, + MetricVisualizationState, + TypedLensByValueInput, + XYState, +} from '@kbn/lens-plugin/public'; +import { hostLensFormulas, visualizationTypes } from './constants'; + +export type LensAttributes = TypedLensByValueInput['attributes']; + +export interface LensOptions { + title: string; +} +export interface LineChartOptions extends LensOptions { + breakdownSize?: number; +} +export interface MetricChartOptions extends LensOptions { + subtitle?: string; + showTitle?: boolean; + showTrendLine?: boolean; + backgroundColor?: string; +} + +export interface LensLineChartConfig { + extraVisualizationState?: Partial & { layers: XYState['layers'] }>; + extraLayers?: FormBasedPersistedState['layers']; + extraReference?: string; +} +export interface LensChartConfig { + title: string; + formula: Formula; + lineChartConfig?: LensLineChartConfig; + getFilters: ({ id }: Pick) => Filter[]; +} + +export type TVisualization = XYState | MetricVisualizationState; +export interface VisualizationAttributes { + getTitle(): string; + getVisualizationType(): string; + getLayers(): FormBasedPersistedState['layers']; + getVisualizationState(): T; + getReferences(): SavedObjectReference[]; + getFilters(): Filter[]; + getAdhocDataView(): Record; +} + +export type Formula = Parameters[1]; + +export type VisualizationTypes = keyof typeof visualizationTypes; +export type HostsLensFormulas = keyof typeof hostLensFormulas; +export type HostsLensMetricChartFormulas = Exclude; +export type HostsLensLineChartFormulas = Exclude; diff --git a/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx b/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx index 2ee792022c1ef..33e5b71c56a3f 100644 --- a/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useState } from 'react'; +import useToggle from 'react-use/lib/useToggle'; import { EuiButtonEmpty, @@ -26,121 +27,100 @@ import { SavedView } from '../../containers/saved_view/saved_view'; interface Props { views: Array>; loading: boolean; - defaultViewId: string; sourceIsLoading: boolean; - close(): void; - makeDefault(id: string): void; + onClose(): void; + onMakeDefaultView(id: string): void; setView(viewState: ViewState): void; - deleteView(id: string): void; + onDeleteView(id: string): void; } interface DeleteConfimationProps { isDisabled?: boolean; - confirmedAction(): void; + onConfirm(): void; } -const DeleteConfimation = (props: DeleteConfimationProps) => { - const [confirmVisible, setConfirmVisible] = useState(false); - const showConfirm = useCallback(() => setConfirmVisible(true), []); - const hideConfirm = useCallback(() => setConfirmVisible(false), []); +const DeleteConfimation = ({ isDisabled, onConfirm }: DeleteConfimationProps) => { + const [isConfirmVisible, toggleVisibility] = useToggle(false); - return ( - <> - {confirmVisible && ( - - - - - - - - - )} - {!confirmVisible && ( - + + + + + - )} - + +
    + ) : ( + ); }; export function SavedViewManageViewsFlyout({ - close, + onClose, views, - defaultViewId, setView, - makeDefault, - deleteView, + onMakeDefaultView, + onDeleteView, loading, sourceIsLoading, }: Props) { const [inProgressView, setInProgressView] = useState(null); - const renderName = useCallback( - (name: string, item: SavedView) => ( - { - setView(item); - close(); - }} - > - {name} - - ), - [setView, close] - ); - - const renderDeleteAction = useCallback( - (item: SavedView) => { - if (item.id === '0') { - return <>; - } - return ( - { - deleteView(item.id); - }} - /> - ); - }, - [deleteView] + const renderName = (name: string, item: SavedView) => ( + { + setView(item); + onClose(); + }} + > + {name} + ); - const renderMakeDefaultAction = useCallback( - (item: SavedView) => { - const isDefault = item.id === defaultViewId; - return ( - <> - { - setInProgressView(item.id); - makeDefault(item.id); - }} - /> - - ); - }, - [makeDefault, defaultViewId, sourceIsLoading, inProgressView] - ); + const renderDeleteAction = (item: SavedView) => { + return ( + { + onDeleteView(item.id); + }} + /> + ); + }; + + const renderMakeDefaultAction = (item: SavedView) => { + return ( + { + setInProgressView(item.id); + onMakeDefaultView(item.id); + }} + /> + ); + }; const columns = [ { @@ -156,11 +136,10 @@ export function SavedViewManageViewsFlyout({ }), actions: [ { - available: () => true, render: renderMakeDefaultAction, }, { - available: (item: SavedView) => true, + available: (item: SavedView) => item.id !== '0', render: renderDeleteAction, }, ], @@ -169,7 +148,7 @@ export function SavedViewManageViewsFlyout({ return ( - +

    @@ -180,7 +159,6 @@ export function SavedViewManageViewsFlyout({

    - ({ sorting={true} /> - - + diff --git a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx index 58c0a60a8827c..1610b1b63fd82 100644 --- a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx @@ -9,11 +9,12 @@ import React, { useCallback, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiPopover, EuiListGroup, EuiListGroupItem } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { SavedViewCreateModal } from './create_modal'; -import { SavedViewUpdateModal } from './update_modal'; +import { FormattedMessage } from '@kbn/i18n-react'; import { SavedViewManageViewsFlyout } from './manage_views_flyout'; import { useSavedViewContext } from '../../containers/saved_view/saved_view'; import { SavedViewListModal } from './view_list_modal'; +import { useBoolean } from '../../hooks/use_boolean'; +import { UpsertViewModal } from './upsert_modal'; interface Props { viewState: ViewState; @@ -28,7 +29,6 @@ export function SavedViewsToolbarControls(props: Props) { updateView, deletedId, deleteView, - defaultViewId, makeDefault, sourceIsLoading, find, @@ -39,48 +39,40 @@ export function SavedViewsToolbarControls(props: Props) { currentView, setCurrentView, } = useSavedViewContext(); - const [modalOpen, setModalOpen] = useState(false); - const [viewListModalOpen, setViewListModalOpen] = useState(false); + + const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false); + + const [isManageFlyoutOpen, { on: openManageFlyout, off: closeManageFlyout }] = useBoolean(false); + const [isUpdateModalOpen, { on: openUpdateModal, off: closeUpdateModal }] = useBoolean(false); + const [isLoadModalOpen, { on: openLoadModal, off: closeLoadModal }] = useBoolean(false); + const [isCreateModalOpen, { on: openCreateModal, off: closeCreateModal }] = useBoolean(false); + const [isInvalid, setIsInvalid] = useState(false); - const [isSavedViewMenuOpen, setIsSavedViewMenuOpen] = useState(false); - const [createModalOpen, setCreateModalOpen] = useState(false); - const [updateModalOpen, setUpdateModalOpen] = useState(false); - const hideSavedViewMenu = useCallback(() => { - setIsSavedViewMenuOpen(false); - }, [setIsSavedViewMenuOpen]); - const openViewListModal = useCallback(() => { - hideSavedViewMenu(); + + const goToManageViews = () => { + closePopover(); + find(); + openManageFlyout(); + }; + + const goToLoadView = () => { + closePopover(); find(); - setViewListModalOpen(true); - }, [setViewListModalOpen, find, hideSavedViewMenu]); - const closeViewListModal = useCallback(() => { - setViewListModalOpen(false); - }, [setViewListModalOpen]); - const openSaveModal = useCallback(() => { - hideSavedViewMenu(); + openLoadModal(); + }; + + const goToCreateView = () => { + closePopover(); setIsInvalid(false); - setCreateModalOpen(true); - }, [hideSavedViewMenu]); - const openUpdateModal = useCallback(() => { - hideSavedViewMenu(); + openCreateModal(); + }; + + const goToUpdateView = () => { + closePopover(); setIsInvalid(false); - setUpdateModalOpen(true); - }, [hideSavedViewMenu]); - const closeModal = useCallback(() => setModalOpen(false), []); - const closeCreateModal = useCallback(() => setCreateModalOpen(false), []); - const closeUpdateModal = useCallback(() => setUpdateModalOpen(false), []); - const loadViews = useCallback(() => { - hideSavedViewMenu(); - find(); - setModalOpen(true); - }, [find, hideSavedViewMenu]); - const showSavedViewMenu = useCallback(() => { - if (isSavedViewMenuOpen) { - setIsSavedViewMenuOpen(false); - return; - } - setIsSavedViewMenuOpen(true); - }, [setIsSavedViewMenuOpen, isSavedViewMenuOpen]); + openUpdateModal(); + }; + const save = useCallback( (name: string, hasTime: boolean = false) => { const currentState = { @@ -146,7 +138,7 @@ export function SavedViewsToolbarControls(props: Props) { data-test-subj="savedViews-popover" button={ (props: Props) { })} } - isOpen={isSavedViewMenuOpen} - closePopover={hideSavedViewMenu} + isOpen={isPopoverOpen} + closePopover={closePopover} anchorPosition="leftCenter" > - - - - - {createModalOpen && ( - + {isCreateModalOpen && ( + + } + /> )} - - {updateModalOpen && ( - + } /> )} - - {viewListModalOpen && ( + {isLoadModalOpen && ( currentView={currentView} views={views} - close={closeViewListModal} + onClose={closeLoadModal} setView={setCurrentView} /> )} - - {modalOpen && ( + {isManageFlyoutOpen && ( sourceIsLoading={sourceIsLoading} loading={loading} views={views} - defaultViewId={defaultViewId} - makeDefault={makeDefault} - deleteView={deleteView} - close={closeModal} + onMakeDefaultView={makeDefault} + onDeleteView={deleteView} + onClose={closeManageFlyout} setView={setCurrentView} /> )} diff --git a/x-pack/plugins/infra/public/components/saved_views/update_modal.tsx b/x-pack/plugins/infra/public/components/saved_views/update_modal.tsx deleted file mode 100644 index 43ad4eef185a8..0000000000000 --- a/x-pack/plugins/infra/public/components/saved_views/update_modal.tsx +++ /dev/null @@ -1,111 +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, { useCallback, useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiButtonEmpty, - EuiButton, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiFieldText, - EuiSpacer, - EuiSwitch, - EuiText, -} from '@elastic/eui'; - -interface Props { - isInvalid: boolean; - close(): void; - save(name: string, shouldIncludeTime: boolean): void; - currentView: ViewState; -} - -export function SavedViewUpdateModal({ - close, - save, - isInvalid, - currentView, -}: Props) { - const [viewName, setViewName] = useState(currentView.name); - const [includeTime, setIncludeTime] = useState(false); - const onCheckChange = useCallback((e) => setIncludeTime(e.target.checked), []); - const textChange = useCallback((e) => setViewName(e.target.value), []); - - const saveView = useCallback(() => { - save(viewName, includeTime); - }, [includeTime, save, viewName]); - - return ( - - - - - - - - - - - - } - checked={includeTime} - onChange={onCheckChange} - /> - - - - - - - - - - - - - - - - ); -} diff --git a/x-pack/plugins/infra/public/components/saved_views/create_modal.tsx b/x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx similarity index 64% rename from x-pack/plugins/infra/public/components/saved_views/create_modal.tsx rename to x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx index 7b14ee2e3fed1..fa2fc3777ca90 100644 --- a/x-pack/plugins/infra/public/components/saved_views/create_modal.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { @@ -21,43 +21,54 @@ import { EuiSwitch, EuiText, } from '@elastic/eui'; +import { EuiSwitchEvent } from '@elastic/eui'; interface Props { isInvalid: boolean; - close(): void; - save(name: string, shouldIncludeTime: boolean): void; + onClose(): void; + onSave(name: string, shouldIncludeTime: boolean): void; + initialName?: string; + initialIncludeTime?: boolean; + title: React.ReactNode; } -export const SavedViewCreateModal = ({ close, save, isInvalid }: Props) => { - const [viewName, setViewName] = useState(''); - const [includeTime, setIncludeTime] = useState(false); - const onCheckChange = useCallback((e) => setIncludeTime(e.target.checked), []); - const textChange = useCallback((e) => setViewName(e.target.value), []); +export const UpsertViewModal = ({ + onClose, + onSave, + isInvalid, + initialName = '', + initialIncludeTime = false, + title, +}: Props) => { + const [viewName, setViewName] = useState(initialName); + const [includeTime, setIncludeTime] = useState(initialIncludeTime); - const saveView = useCallback(() => { - save(viewName, includeTime); - }, [includeTime, save, viewName]); + const handleNameChange: React.ChangeEventHandler = (e) => { + setViewName(e.target.value); + }; + + const handleTimeCheckChange = (e: EuiSwitchEvent) => { + setIncludeTime(e.target.checked); + }; + + const saveView = () => { + onSave(viewName, includeTime); + }; return ( - + - - - + {title} - { /> } checked={includeTime} - onChange={onCheckChange} + onChange={handleTimeCheckChange} /> - + - - + { diff --git a/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx b/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx index 1c01d504de14d..43ca2776b284d 100644 --- a/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useState, useMemo } from 'react'; -import { EuiButtonEmpty, EuiModalFooter, EuiButton } from '@elastic/eui'; +import { EuiButtonEmpty, EuiModalFooter, EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiModal, EuiModalHeader, EuiModalHeaderTitle, EuiModalBody } from '@elastic/eui'; import { EuiSelectable } from '@elastic/eui'; @@ -17,13 +17,13 @@ import { SavedView } from '../../containers/saved_view/saved_view'; interface Props { views: Array>; - close(): void; + onClose(): void; setView(viewState: ViewState): void; currentView?: ViewState; } export function SavedViewListModal({ - close, + onClose, views, setView, currentView, @@ -36,18 +36,18 @@ export function SavedViewListModal { if (!options) { - close(); + onClose(); return; } const selected = options.find((o) => o.checked); if (!selected) { - close(); + onClose(); return; } setView(views.find((v) => v.id === selected.key)!); - close(); - }, [options, views, setView, close]); + onClose(); + }, [options, views, setView, onClose]); const defaultOptions = useMemo(() => { return views.map((v) => ({ @@ -58,7 +58,7 @@ export function SavedViewListModal + {(list, search) => ( <> {search} -
    - {list} -
    + + {list} )}
    - + - + diff --git a/x-pack/plugins/infra/public/hooks/use_boolean.ts b/x-pack/plugins/infra/public/hooks/use_boolean.ts new file mode 100644 index 0000000000000..321dddf627eda --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_boolean.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 { useMemo } from 'react'; +import useToggle from 'react-use/lib/useToggle'; + +export type VoidHandler = () => void; + +export type DispatchWithOptionalAction = (_arg?: Type | unknown) => void; + +export interface UseBooleanHandlers { + on: VoidHandler; + off: VoidHandler; + toggle: DispatchWithOptionalAction; +} + +export type UseBooleanResult = [boolean, UseBooleanHandlers]; + +export const useBoolean = (initialValue: boolean = false): UseBooleanResult => { + const [value, toggle] = useToggle(initialValue); + + const handlers = useMemo( + () => ({ + toggle, + on: () => toggle(true), + off: () => toggle(false), + }), + [toggle] + ); + + return [value, handlers]; +}; diff --git a/x-pack/plugins/infra/public/hooks/use_intersection_once.ts b/x-pack/plugins/infra/public/hooks/use_intersection_once.ts new file mode 100644 index 0000000000000..8894e9fec3176 --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_intersection_once.ts @@ -0,0 +1,25 @@ +/* + * 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 { RefObject, useEffect, useState } from 'react'; +import useIntersection from 'react-use/lib/useIntersection'; + +export const useIntersectedOnce = ( + ref: RefObject, + options: IntersectionObserverInit +) => { + const [intersectedOnce, setIntersectedOnce] = useState(false); + const intersection = useIntersection(ref, options); + + useEffect(() => { + if (!intersectedOnce && (intersection?.intersectionRatio ?? 0) > 0) { + setIntersectedOnce(true); + } + }, [intersectedOnce, intersection?.intersectionRatio]); + + return { intersectedOnce, intersection }; +}; diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts index d4eb0e810cc1e..b58b25ec35b28 100644 --- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts +++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts @@ -48,7 +48,11 @@ describe('useHostTable hook', () => { it('should return the basic lens attributes', async () => { const { result, waitForNextUpdate } = renderHook(() => useLensAttributes({ + visualizationType: 'lineChart', type: 'load', + options: { + title: 'Injected Normalized Load', + }, dataView: mockDataView, }) ); @@ -57,12 +61,12 @@ describe('useHostTable hook', () => { const { state, title } = result.current.attributes ?? {}; const { datasourceStates, filters } = state ?? {}; - expect(title).toBe('Normalized Load'); + expect(title).toBe('Injected Normalized Load'); expect(datasourceStates).toEqual({ formBased: { layers: { layer1: { - columnOrder: ['hosts_aggs_breakdown', 'x_date_histogram', 'y_cpu_cores_usage'], + columnOrder: ['hosts_aggs_breakdown', 'x_date_histogram', 'formula_accessor'], columns: { hosts_aggs_breakdown: { dataType: 'string', @@ -100,7 +104,7 @@ describe('useHostTable hook', () => { scale: 'interval', sourceField: '@timestamp', }, - y_cpu_cores_usage: { + formula_accessor: { customLabel: false, dataType: 'number', filter: undefined, @@ -154,19 +158,36 @@ describe('useHostTable hook', () => { }, }, }); - expect(filters).toEqual([]); + expect(filters).toEqual([ + { + meta: { + index: 'mock-id', + }, + query: { + exists: { + field: 'host.name', + }, + }, + }, + ]); }); - it('should return attributes with injected values', async () => { + it('should return extra actions', async () => { const { result, waitForNextUpdate } = renderHook(() => useLensAttributes({ + visualizationType: 'lineChart', type: 'load', dataView: mockDataView, }) ); await waitForNextUpdate(); - const injectedData = { + const extraActions = result.current.getExtraActions({ + timeRange: { + from: 'now-15m', + to: 'now', + mode: 'relative', + }, query: { language: 'kuery', query: '{term: { host.name: "a"}}', @@ -186,17 +207,8 @@ describe('useHostTable hook', () => { query: { range: { 'system.load.cores': { gte: 0 } } }, }, ], - title: 'Injected CPU Cores', - }; - - const injectedAttributes = result.current.injectData(injectedData); - - const { state, title } = injectedAttributes ?? {}; - const { filters, query } = state ?? {}; + }); - expect(title).toEqual(injectedData.title); - expect(query).toEqual(injectedData.query); - expect(filters).toHaveLength(1); - expect(filters).toContain(injectedData.filters[0]); + expect(extraActions.openInLens).not.toBeNull(); }); }); diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts index c3b005f9ad8ad..6250d20750e29 100644 --- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts +++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts @@ -12,25 +12,45 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import { i18n } from '@kbn/i18n'; import useAsync from 'react-use/lib/useAsync'; -import { InfraClientSetupDeps, LensAttributes, LensOptions } from '../types'; +import { InfraClientSetupDeps } from '../types'; import { buildLensAttributes, - HostLensAttributesTypes, - hostMetricsLensAttributes, + HostsLensFormulas, + HostsLensMetricChartFormulas, + HostsLensLineChartFormulas, + LineChartOptions, + MetricChartOptions, + LensAttributes, + hostLensFormulas, + visualizationTypes, } from '../common/visualizations'; -interface UseLensAttributesParams { - type: HostLensAttributesTypes; +type Options = LineChartOptions | MetricChartOptions; +interface UseLensAttributesBaseParams { dataView: DataView | undefined; - options?: LensOptions; + type: T; + options?: O; } +interface UseLensAttributesLineChartParams + extends UseLensAttributesBaseParams { + visualizationType: 'lineChart'; +} + +interface UseLensAttributesMetricChartParams + extends UseLensAttributesBaseParams { + visualizationType: 'metricChart'; +} + +type UseLensAttributesParams = + | UseLensAttributesLineChartParams + | UseLensAttributesMetricChartParams; + export const useLensAttributes = ({ type, dataView, - options = { - breakdownSize: 10, - }, + options, + visualizationType, }: UseLensAttributesParams) => { const { services: { lens }, @@ -39,31 +59,27 @@ export const useLensAttributes = ({ const { value, error } = useAsync(lens.stateHelperApi, [lens]); const { formula: formulaAPI } = value ?? {}; - const attributes: LensAttributes | null = useMemo(() => { + const attributes = useMemo(() => { if (!dataView || !formulaAPI) { return null; } - const VisualizationClass = hostMetricsLensAttributes[type]; + const lensChartConfig = hostLensFormulas[type]; + const VisualizationType = visualizationTypes[visualizationType]; + const visualizationAttributes = buildLensAttributes( - new VisualizationClass(dataView, options, formulaAPI) + new VisualizationType(lensChartConfig, dataView, formulaAPI, options) ); return visualizationAttributes; - }, [dataView, formulaAPI, options, type]); + }, [dataView, formulaAPI, options, type, visualizationType]); - const injectData = (data: { - filters: Filter[]; - query: Query; - title?: string; - }): LensAttributes | null => { + const injectFilters = (data: { filters: Filter[]; query: Query }): LensAttributes | null => { if (!attributes) { return null; } - return { ...attributes, - ...(!!data.title ? { title: data.title } : {}), state: { ...attributes.state, query: data.query, @@ -72,7 +88,15 @@ export const useLensAttributes = ({ }; }; - const getExtraActions = (currentAttributes: LensAttributes | null, timeRange: TimeRange) => { + const getExtraActions = ({ + timeRange, + filters, + query, + }: { + timeRange: TimeRange; + filters: Filter[]; + query: Query; + }) => { return { openInLens: { id: 'openInLens', @@ -93,12 +117,13 @@ export const useLensAttributes = ({ return true; }, async execute(_context: ActionExecutionContext): Promise { - if (currentAttributes) { + const injectedAttributes = injectFilters({ filters, query }); + if (injectedAttributes) { navigateToPrefilledEditor( { id: '', timeRange, - attributes: currentAttributes, + attributes: injectedAttributes, }, { openInNewTab: true, @@ -111,5 +136,5 @@ export const useLensAttributes = ({ }; }; - return { attributes, injectData, getExtraActions, error }; + return { attributes, getExtraActions, error }; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/chart_loader.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/chart_loader.tsx new file mode 100644 index 0000000000000..bbddb338ef73f --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/chart_loader.tsx @@ -0,0 +1,58 @@ +/* + * 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, EuiProgress, EuiFlexItem, EuiLoadingChart, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; + +export const ChartLoader = ({ + children, + loading, + style, + loadedOnce = false, + hasTitle = false, +}: { + style?: React.CSSProperties; + children: React.ReactNode; + loadedOnce: boolean; + loading: boolean; + hasTitle?: boolean; +}) => { + const { euiTheme } = useEuiTheme(); + return ( + + {loading && ( + + )} + {loading && !loadedOnce ? ( + + + + + + ) : ( + children + )} + + ); +}; + +const LoaderContainer = euiStyled.div` + position: relative; + border-radius: ${({ theme }) => theme.eui.euiSizeS}; + overflow: hidden; + height: 100%; +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.tsx new file mode 100644 index 0000000000000..34e536aaf37d2 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.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, { useEffect, useState, useRef } from 'react'; + +import { Action } from '@kbn/ui-actions-plugin/public'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { BrushTriggerEvent } from '@kbn/charts-plugin/public'; +import { Filter, Query, TimeRange } from '@kbn/es-query'; +import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; +import { useIntersectedOnce } from '../../../../../hooks/use_intersection_once'; +import { LensAttributes } from '../../../../../common/visualizations'; +import { ChartLoader } from './chart_loader'; + +export interface LensWrapperProps { + id: string; + attributes: LensAttributes | null; + dateRange: TimeRange; + query: Query; + filters: Filter[]; + extraActions: Action[]; + lastReloadRequestTime?: number; + style?: React.CSSProperties; + loading?: boolean; + hasTitle?: boolean; + onBrushEnd?: (data: BrushTriggerEvent['data']) => void; + onLoad?: () => void; +} + +export const LensWrapper = ({ + attributes, + dateRange, + filters, + id, + query, + extraActions, + style, + onBrushEnd, + lastReloadRequestTime, + loading = false, + hasTitle = false, +}: LensWrapperProps) => { + const intersectionRef = useRef(null); + const [loadedOnce, setLoadedOnce] = useState(false); + + const [state, setState] = useState({ + attributes, + lastReloadRequestTime, + query, + filters, + dateRange, + }); + + const { + services: { lens }, + } = useKibanaContextForPlugin(); + const { intersectedOnce, intersection } = useIntersectedOnce(intersectionRef, { + threshold: 1, + }); + + const EmbeddableComponent = lens.EmbeddableComponent; + + useEffect(() => { + if ((intersection?.intersectionRatio ?? 0) === 1) { + setState({ + attributes, + lastReloadRequestTime, + query, + filters, + dateRange, + }); + } + }, [ + attributes, + dateRange, + filters, + intersection?.intersectionRatio, + lastReloadRequestTime, + query, + ]); + + const isReady = state.attributes && intersectedOnce; + + return ( +
    + + {state.attributes && ( + { + if (!loadedOnce) { + setLoadedOnce(true); + } + }} + /> + )} + +
    + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx new file mode 100644 index 0000000000000..a98de9c773c52 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useEffect, useRef } from 'react'; +import { Chart, Metric, type MetricWNumber, type MetricWTrend } from '@elastic/charts'; +import { EuiPanel, EuiToolTip } from '@elastic/eui'; +import styled from 'styled-components'; +import { ChartLoader } from './chart_loader'; + +export interface Props extends Pick { + id: string; + loading: boolean; + value: number; + toolTip: string; + ['data-test-subj']?: string; +} + +const MIN_HEIGHT = 150; + +export const MetricChartWrapper = ({ + color, + extra, + id, + loading, + value, + subtitle, + title, + toolTip, + ...props +}: Props) => { + const loadedOnce = useRef(false); + + useEffect(() => { + if (!loadedOnce.current && !loading) { + loadedOnce.current = true; + } + return () => { + loadedOnce.current = false; + }; + }, [loading]); + + const metricsData: MetricWNumber = { + title, + subtitle, + color, + extra, + value, + valueFormatter: (d: number) => d.toString(), + }; + + return ( + + + + + + + + + + ); +}; + +const KPIChartStyled = styled(Chart)` + .echMetric { + border-radius: ${(p) => p.theme.eui.euiBorderRadius}; + pointer-events: none; + } +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/add_metadata_filter_button.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/add_metadata_filter_button.tsx index 51277427b6352..acefcbea5e304 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/add_metadata_filter_button.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/add_metadata_filter_button.tsx @@ -37,6 +37,7 @@ export const AddMetadataFilterButton = ({ item }: AddMetadataFilterButtonProps) query: { filterManager: filterManagerService }, }, notifications: { toasts: toastsService }, + telemetry, }, } = useKibanaContextForPlugin(); @@ -53,6 +54,9 @@ export const AddMetadataFilterButton = ({ item }: AddMetadataFilterButtonProps) negate: false, }); if (newFilter) { + telemetry.reportHostFlyoutFilterAdded({ + field_name: item.name, + }); filterManagerService.addFilters(newFilter); toastsService.addSuccess({ title: filterAddedToastTitle, @@ -84,7 +88,12 @@ export const AddMetadataFilterButton = ({ item }: AddMetadataFilterButtonProps) defaultMessage: 'Filter', } )} - onClick={() => filterManagerService.removeFilter(existingFilter)} + onClick={() => { + telemetry.reportHostFlyoutFilterRemoved({ + field_name: existingFilter.meta.key!, + }); + filterManagerService.removeFilter(existingFilter); + }} /> diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/metadata.test.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/metadata.test.tsx index 1c6320c142d7a..46392fa8609d1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/metadata.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/metadata/metadata.test.tsx @@ -32,42 +32,12 @@ const metadataProps: TabProps = { name: 'host-1', cloudProvider: 'gcp', }, - rx: { - name: 'rx', - value: 0, - max: 0, - avg: 0, - }, - tx: { - name: 'tx', - value: 0, - max: 0, - avg: 0, - }, - memory: { - name: 'memory', - value: 0.5445920331099282, - max: 0.5445920331099282, - avg: 0.5445920331099282, - }, - cpu: { - name: 'cpu', - value: 0.2000718443867342, - max: 0.2000718443867342, - avg: 0.2000718443867342, - }, - diskLatency: { - name: 'diskLatency', - value: null, - max: 0, - avg: 0, - }, - memoryTotal: { - name: 'memoryTotal', - value: 16777216, - max: 16777216, - avg: 16777216, - }, + rx: 0, + tx: 0, + memory: 0.5445920331099282, + cpu: 0.2000718443867342, + diskLatency: 0, + memoryTotal: 16777216, }, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_container.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_container.tsx index 9bae6b9ae1df0..d42944857af34 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_container.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_container.tsx @@ -10,12 +10,13 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { InfraLoadingPanel } from '../../../../components/loading'; import { useMetricsDataViewContext } from '../hooks/use_data_view'; -import { UnifiedSearchBar } from './unified_search_bar'; +import { UnifiedSearchBar } from './search_bar/unified_search_bar'; import { HostsTable } from './hosts_table'; -import { HostsViewProvider } from '../hooks/use_hosts_view'; -import { KPICharts } from './kpi_charts/kpi_charts'; +import { KPIGrid } from './kpis/kpi_grid'; import { Tabs } from './tabs/tabs'; import { AlertsQueryProvider } from '../hooks/use_alerts_query'; +import { HostsViewProvider } from '../hooks/use_hosts_view'; +import { HostsTableProvider } from '../hooks/use_hosts_table'; export const HostContainer = () => { const { dataView, loading, hasError } = useMetricsDataViewContext(); @@ -38,19 +39,21 @@ export const HostContainer = () => { - - - - - - - - - - - - - + + + + + + + + + + + + + + + ); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx index 798147bc1da0a..535afe8befff5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx @@ -5,93 +5,78 @@ * 2.0. */ -import React, { useCallback } from 'react'; -import { EuiInMemoryTable } from '@elastic/eui'; +import React from 'react'; +import { EuiBasicTable } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { isEqual } from 'lodash'; import { NoData } from '../../../../components/empty_states'; -import { InfraLoadingPanel } from '../../../../components/loading'; -import { useHostsTable } from '../hooks/use_hosts_table'; -import { useTableProperties } from '../hooks/use_table_properties_url_state'; +import { HostNodeRow, useHostsTableContext } from '../hooks/use_hosts_table'; import { useHostsViewContext } from '../hooks/use_hosts_view'; import { useUnifiedSearchContext } from '../hooks/use_unified_search'; import { Flyout } from './host_details_flyout/flyout'; +import { DEFAULT_PAGE_SIZE } from '../constants'; -export const HostsTable = () => { - const { hostNodes, loading } = useHostsViewContext(); - const { onSubmit, searchCriteria } = useUnifiedSearchContext(); - const [properties, setProperties] = useTableProperties(); - - const { columns, items, isFlyoutOpen, closeFlyout, clickedItem } = useHostsTable(hostNodes, { - time: searchCriteria.dateRange, - }); - - const noData = items.length === 0; - - const onTableChange = useCallback( - ({ page = {}, sort = {} }) => { - const { index: pageIndex, size: pageSize } = page; - const { field, direction } = sort; - - const sorting = field && direction ? { field, direction } : true; - const pagination = pageIndex >= 0 && pageSize !== 0 ? { pageIndex, pageSize } : true; - - if (!isEqual(properties.sorting, sorting)) { - setProperties({ sorting }); - } - if (!isEqual(properties.pagination, pagination)) { - setProperties({ pagination }); - } - }, - [setProperties, properties.pagination, properties.sorting] - ); +const PAGE_SIZE_OPTIONS = [5, 10, 20]; - if (loading) { - return ( - - ); - } +export const HostsTable = () => { + const { loading } = useHostsViewContext(); + const { onSubmit } = useUnifiedSearchContext(); - if (noData) { - return ( - onSubmit()} - testString="noMetricsDataPrompt" - /> - ); - } + const { + columns, + items, + currentPage, + isFlyoutOpen, + closeFlyout, + clickedItem, + onTableChange, + pagination, + sorting, + } = useHostsTableContext(); return ( <> - onSubmit()} + testString="noMetricsDataPrompt" + /> + ) + } /> {isFlyoutOpen && clickedItem && } diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/hosts_tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/hosts_tile.tsx deleted file mode 100644 index b6973327101f0..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/hosts_tile.tsx +++ /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 React from 'react'; - -import { useHostsViewContext } from '../../hooks/use_hosts_view'; -import { type ChartBaseProps, KPIChart } from './kpi_chart'; - -export const HostsTile = ({ type, ...props }: ChartBaseProps) => { - const { hostNodes, loading } = useHostsViewContext(); - - return ( - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx deleted file mode 100644 index dc2a4d7d72c86..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_chart.tsx +++ /dev/null @@ -1,126 +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, { useMemo } from 'react'; -import { - Chart, - Metric, - MetricTrendShape, - type MetricWNumber, - type MetricWTrend, -} from '@elastic/charts'; - -import { EuiPanel } from '@elastic/eui'; -import styled from 'styled-components'; -import { EuiLoadingChart } from '@elastic/eui'; -import { EuiFlexGroup } from '@elastic/eui'; -import { EuiFlexItem } from '@elastic/eui'; -import { EuiToolTip } from '@elastic/eui'; -import type { SnapshotNode, SnapshotNodeMetric } from '../../../../../../common/http_api'; -import { createInventoryMetricFormatter } from '../../../inventory_view/lib/create_inventory_metric_formatter'; -import type { SnapshotMetricType } from '../../../../../../common/inventory_models/types'; - -type MetricType = keyof Pick; - -type AcceptedType = SnapshotMetricType | 'hostsCount'; - -export interface ChartBaseProps - extends Pick< - MetricWTrend, - 'title' | 'color' | 'extra' | 'subtitle' | 'trendA11yDescription' | 'trendA11yTitle' - > { - type: AcceptedType; - toolTip: string; - metricType: MetricType; - ['data-test-subj']?: string; -} - -interface Props extends ChartBaseProps { - id: string; - nodes: SnapshotNode[]; - loading: boolean; - overrideValue?: number; -} - -const MIN_HEIGHT = 150; - -export const KPIChart = ({ - color, - extra, - id, - loading, - metricType, - nodes, - overrideValue, - subtitle, - title, - toolTip, - trendA11yDescription, - trendA11yTitle, - type, - ...props -}: Props) => { - const metrics = useMemo(() => (nodes ?? [])[0]?.metrics ?? [], [nodes]); - const metricsTimeseries = useMemo( - () => (metrics ?? []).find((m) => m.name === type)?.timeseries, - [metrics, type] - ); - - const metricsValue = useMemo(() => { - if (overrideValue) { - return overrideValue; - } - return (metrics ?? []).find((m) => m.name === type)?.[metricType] ?? 0; - }, [metricType, metrics, overrideValue, type]); - - const metricsData: MetricWNumber = { - title, - subtitle, - color, - extra, - value: metricsValue, - valueFormatter: (d: number) => - type === 'hostsCount' ? d.toString() : createInventoryMetricFormatter({ type })(d), - ...(!!metricsTimeseries - ? { - trend: metricsTimeseries.rows.map((row) => ({ x: row.timestamp, y: row.metric_0 ?? 0 })), - trendShape: MetricTrendShape.Area, - trendA11yTitle, - trendA11yDescription, - } - : {}), - }; - - return ( - - {loading ? ( - - - - - - ) : ( - - - - - - )} - - ); -}; - -const KPIChartStyled = styled(Chart)` - .echMetric { - border-radius: ${(p) => p.theme.eui.euiBorderRadius}; - pointer-events: none; - } -`; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_charts.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_charts.tsx deleted file mode 100644 index af2cbb18fdcbb..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/kpi_charts.tsx +++ /dev/null @@ -1,154 +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 { EuiFlexGroup } from '@elastic/eui'; -import { EuiFlexItem } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import { Tile } from './tile'; -import { HostsTile } from './hosts_tile'; - -export const KPICharts = () => { - return ( - - - - - - - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx deleted file mode 100644 index 3f396ebcd7bf8..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpi_charts/tile.tsx +++ /dev/null @@ -1,32 +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 type { SnapshotMetricType } from '../../../../../../common/inventory_models/types'; - -import { useSnapshot } from '../../../inventory_view/hooks/use_snaphot'; -import { useHostsViewContext } from '../../hooks/use_hosts_view'; -import { type ChartBaseProps, KPIChart } from './kpi_chart'; - -interface Props extends Omit { - type: SnapshotMetricType; -} -export const Tile = ({ type, ...props }: Props) => { - const { baseRequest } = useHostsViewContext(); - - const { nodes, loading } = useSnapshot( - { - ...baseRequest, - metrics: [{ type }], - groupBy: null, - includeTimeseries: true, - dropPartialBuckets: false, - }, - { abortable: true } - ); - - return ; -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx new file mode 100644 index 0000000000000..14a617682bf25 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/hosts_tile.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useHostCountContext } from '../../hooks/use_host_count'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; + +import { type Props, MetricChartWrapper } from '../chart/metric_chart_wrapper'; + +const HOSTS_CHART: Omit = { + id: `metric-hostCount`, + color: '#6DCCB1', + title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.title', { + defaultMessage: 'Hosts', + }), + toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip', { + defaultMessage: 'The number of hosts returned by your current search criteria.', + }), + ['data-test-subj']: 'hostsView-metricsTrend-hosts', +}; + +export const HostsTile = () => { + const { data: hostCountData, isRequestRunning: hostCountLoading } = useHostCountContext(); + const { searchCriteria } = useUnifiedSearchContext(); + + const getSubtitle = () => { + return searchCriteria.limit < (hostCountData?.count.value ?? 0) + ? i18n.translate('xpack.infra.hostsViewPage.metricTrend.subtitle.hostCount.limit', { + defaultMessage: 'Limited to {limit}', + values: { + limit: searchCriteria.limit, + }, + }) + : undefined; + }; + + return ( + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx new file mode 100644 index 0000000000000..c3f751d26befb --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx @@ -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 React from 'react'; + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { KPIChartProps, Tile } from './tile'; +import { HostCountProvider } from '../../hooks/use_host_count'; +import { HostsTile } from './hosts_tile'; + +const KPI_CHARTS: Array> = [ + { + type: 'cpu', + trendLine: true, + backgroundColor: '#F1D86F', + title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.title', { + defaultMessage: 'CPU usage', + }), + toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.tooltip', { + defaultMessage: + 'Average of percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. Includes both time spent on user space and kernel space. 100% means all CPUs of the host are busy.', + }), + }, + { + type: 'memory', + trendLine: true, + backgroundColor: '#A987D1', + title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.title', { + defaultMessage: 'Memory usage', + }), + toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.tooltip', { + defaultMessage: + "Average of percentage of main memory usage excluding page cache. This includes resident memory for all processes plus memory used by the kernel structures and code apart the page cache. A high level indicates a situation of memory saturation for a host. 100% means the main memory is entirely filled with memory that can't be reclaimed, except by swapping out.", + }), + }, + { + type: 'rx', + trendLine: true, + backgroundColor: '#79AAD9', + title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.title', { + defaultMessage: 'Network inbound (RX)', + }), + toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.tooltip', { + defaultMessage: + 'Number of bytes which have been received per second on the public interfaces of the hosts.', + }), + }, + { + type: 'tx', + trendLine: true, + backgroundColor: '#F5A35C', + title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.title', { + defaultMessage: 'Network outbound (TX)', + }), + toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.tooltip', { + defaultMessage: + 'Number of bytes which have been received per second on the public interfaces of the hosts.', + }), + }, +]; + +export const KPIGrid = () => { + return ( + + + + + + {KPI_CHARTS.map(({ ...chartProp }) => ( + + + + ))} + + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx new file mode 100644 index 0000000000000..89eebeefd240e --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx @@ -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 React, { useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { BrushTriggerEvent } from '@kbn/charts-plugin/public'; +import { + EuiIcon, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiI18n, + EuiToolTip, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { useLensAttributes } from '../../../../../hooks/use_lens_attributes'; +import { useMetricsDataViewContext } from '../../hooks/use_data_view'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; +import { HostsLensMetricChartFormulas } from '../../../../../common/visualizations'; +import { useHostsViewContext } from '../../hooks/use_hosts_view'; +import { LensWrapper } from '../chart/lens_wrapper'; +import { createHostsFilter } from '../../utils'; +import { useHostCountContext } from '../../hooks/use_host_count'; +import { useAfterLoadedState } from '../../hooks/use_after_loaded_state'; + +export interface KPIChartProps { + title: string; + subtitle?: string; + trendLine?: boolean; + backgroundColor: string; + type: HostsLensMetricChartFormulas; + toolTip: string; +} + +const MIN_HEIGHT = 150; + +export const Tile = ({ + title, + type, + backgroundColor, + toolTip, + trendLine = false, +}: KPIChartProps) => { + const { searchCriteria, onSubmit } = useUnifiedSearchContext(); + const { dataView } = useMetricsDataViewContext(); + const { requestTs, hostNodes, loading: hostsLoading } = useHostsViewContext(); + const { data: hostCountData, isRequestRunning: hostCountLoading } = useHostCountContext(); + + const getSubtitle = () => { + return searchCriteria.limit < (hostCountData?.count.value ?? 0) + ? i18n.translate('xpack.infra.hostsViewPage.metricTrend.subtitle.average.limit', { + defaultMessage: 'Average (of {limit} hosts)', + values: { + limit: searchCriteria.limit, + }, + }) + : i18n.translate('xpack.infra.hostsViewPage.metricTrend.subtitle.average', { + defaultMessage: 'Average', + }); + }; + + const { attributes, getExtraActions, error } = useLensAttributes({ + type, + dataView, + options: { + title, + subtitle: getSubtitle(), + backgroundColor, + showTrendLine: trendLine, + showTitle: false, + }, + visualizationType: 'metricChart', + }); + + const hostsFilterQuery = useMemo(() => { + return createHostsFilter( + hostNodes.map((p) => p.name), + dataView + ); + }, [hostNodes, dataView]); + + const filters = useMemo( + () => [...searchCriteria.filters, ...searchCriteria.panelFilters, ...[hostsFilterQuery]], + [hostsFilterQuery, searchCriteria.filters, searchCriteria.panelFilters] + ); + + const extraActionOptions = getExtraActions({ + timeRange: searchCriteria.dateRange, + filters, + query: searchCriteria.query, + }); + + const handleBrushEnd = ({ range }: BrushTriggerEvent['data']) => { + const [min, max] = range; + onSubmit({ + dateRange: { + from: new Date(min).toISOString(), + to: new Date(max).toISOString(), + mode: 'absolute', + }, + }); + }; + + const loading = hostsLoading || !attributes || hostCountLoading; + const { afterLoadedState } = useAfterLoadedState(loading, { + attributes, + lastReloadRequestTime: requestTs, + ...searchCriteria, + filters, + }); + + return ( + + {error ? ( + + + + + + + + + + + ) : ( + + + + )} + + ); +}; + +const EuiPanelStyled = styled(EuiPanel)` + .echMetric { + border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; + pointer-events: none; + } +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/lazy_controls_renderer.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/lazy_controls_renderer.tsx deleted file mode 100644 index 32a110733ea84..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/lazy_controls_renderer.tsx +++ /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 { LazyControlGroupRenderer } from '@kbn/controls-plugin/public'; -import { EuiLoadingSpinner, EuiErrorBoundary } from '@elastic/eui'; -import React from 'react'; - -export const LazyControlsRenderer = ( - props: React.ComponentProps -) => ( - - }> - - - -); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx similarity index 69% rename from x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx rename to x-pack/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx index dfe591db4b2af..e2bd7d0c74dae 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/controls_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx @@ -6,18 +6,22 @@ */ import React, { useCallback, useEffect, useRef } from 'react'; -import { ControlGroupContainer, type ControlGroupInput } from '@kbn/controls-plugin/public'; +import { + ControlGroupAPI, + ControlGroupRenderer, + type ControlGroupInput, +} from '@kbn/controls-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { compareFilters, COMPARE_ALL_OPTIONS, Filter, Query, TimeRange } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; -import { Subscription } from 'rxjs'; -import { LazyControlsRenderer } from './lazy_controls_renderer'; -import { useControlPanels } from '../hooks/use_control_panels_url_state'; +import { skipWhile, Subscription } from 'rxjs'; +import { useControlPanels } from '../../hooks/use_control_panels_url_state'; interface Props { dataView: DataView | undefined; timeRange: TimeRange; filters: Filter[]; + selectedOptions: Filter[]; query: Query; onFiltersChange: (filters: Filter[]) => void; } @@ -26,6 +30,7 @@ export const ControlsContent: React.FC = ({ dataView, filters, query, + selectedOptions, timeRange, onFiltersChange, }) => { @@ -50,16 +55,23 @@ export const ControlsContent: React.FC = ({ }, [controlPanels, dataView?.id, filters, query, timeRange]); const loadCompleteHandler = useCallback( - (controlGroup: ControlGroupContainer) => { - inputSubscription.current = controlGroup.onFiltersPublished$.subscribe((newFilters) => { - onFiltersChange(newFilters); - }); + (controlGroup: ControlGroupAPI) => { + if (!controlGroup) return; + inputSubscription.current = controlGroup.onFiltersPublished$ + .pipe( + skipWhile((newFilters) => + compareFilters(selectedOptions, newFilters, COMPARE_ALL_OPTIONS) + ) + ) + .subscribe((newFilters) => { + onFiltersChange(newFilters); + }); filterSubscription.current = controlGroup .getInput$() .subscribe(({ panels }) => setControlPanels(panels)); }, - [onFiltersChange, setControlPanels] + [onFiltersChange, setControlPanels, selectedOptions] ); useEffect(() => { @@ -70,9 +82,9 @@ export const ControlsContent: React.FC = ({ }, []); return ( - void; +} + +export const LimitOptions = ({ limit, onChange }: Props) => { + return ( + + + + + + + + + + + + + + + onChange(value)} + /> + + + ); +}; + +const buildId = (option: number) => `hostLimit_${option}`; +const options: EuiButtonGroupOptionProps[] = HOST_LIMIT_OPTIONS.map((option) => ({ + id: buildId(option), + label: `${option}`, + value: option, + 'data-test-subj': `hostsViewLimitSelection${option}button`, +})); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/search_bar/unified_search_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/search_bar/unified_search_bar.tsx new file mode 100644 index 0000000000000..ef515cc018839 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/search_bar/unified_search_bar.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, { useMemo } from 'react'; +import { compareFilters, COMPARE_ALL_OPTIONS, type Filter } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGrid, + useEuiTheme, + EuiHorizontalRule, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { METRICS_APP_DATA_TEST_SUBJ } from '../../../../../apps/metrics_app'; +import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; +import { ControlsContent } from './controls_content'; +import { useMetricsDataViewContext } from '../../hooks/use_data_view'; +import { HostsSearchPayload } from '../../hooks/use_unified_search_url_state'; +import { LimitOptions } from './limit_options'; +import { HostLimitOptions } from '../../types'; + +export const UnifiedSearchBar = () => { + const { + services: { unifiedSearch, application }, + } = useKibanaContextForPlugin(); + const { dataView } = useMetricsDataViewContext(); + const { searchCriteria, onSubmit } = useUnifiedSearchContext(); + + const { SearchBar } = unifiedSearch.ui; + + const onLimitChange = (limit: number) => { + onSubmit({ limit }); + }; + + const onPanelFiltersChange = (panelFilters: Filter[]) => { + if (!compareFilters(searchCriteria.panelFilters, panelFilters, COMPARE_ALL_OPTIONS)) { + onSubmit({ panelFilters }); + } + }; + + const handleRefresh = (payload: HostsSearchPayload, isUpdate?: boolean) => { + // This makes sure `onQueryChange` is only called when the submit button is clicked + if (isUpdate === false) { + onSubmit(payload); + } + }; + + return ( + + + + 0.5)', + })} + onQuerySubmit={handleRefresh} + showSaveQuery={Boolean(application?.capabilities?.visualize?.saveQuery)} + showDatePicker + showFilterBar + showQueryInput + showQueryMenu + useDefaultBehaviors + /> + + + + + + + + + + + + + + + ); +}; + +const StickyContainer = (props: { children: React.ReactNode }) => { + const { euiTheme } = useEuiTheme(); + + const top = useMemo(() => { + const wrapper = document.querySelector(`[data-test-subj="${METRICS_APP_DATA_TEST_SUBJ}"]`); + if (!wrapper) { + return `calc(${euiTheme.size.xxxl} * 2)`; + } + + return `${wrapper.getBoundingClientRect().top}px`; + }, [euiTheme]); + + return ( + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx index 0fad370960f22..6813dee1caa10 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx @@ -9,7 +9,6 @@ import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { InfraLoadingPanel } from '../../../../../../components/loading'; -import { SnapshotNode } from '../../../../../../../common/http_api'; import { LogStream } from '../../../../../../components/log_stream'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; @@ -24,10 +23,13 @@ export const LogsTabContent = () => { const { from, to } = useMemo(() => getDateRangeAsTimestamp(), [getDateRangeAsTimestamp]); const { hostNodes, loading } = useHostsViewContext(); - const hostsFilterQuery = useMemo(() => createHostsFilter(hostNodes), [hostNodes]); + const hostsFilterQuery = useMemo( + () => createHostsFilter(hostNodes.map((p) => p.name)), + [hostNodes] + ); const logsLinkToStreamQuery = useMemo(() => { - const hostsFilterQueryParam = createHostsFilterQueryParam(hostNodes); + const hostsFilterQueryParam = createHostsFilterQueryParam(hostNodes.map((p) => p.name)); if (filterQuery.query && hostsFilterQueryParam) { return `${filterQuery.query} and ${hostsFilterQueryParam}`; @@ -80,12 +82,12 @@ export const LogsTabContent = () => { ); }; -const createHostsFilterQueryParam = (hostNodes: SnapshotNode[]): string => { +const createHostsFilterQueryParam = (hostNodes: string[]): string => { if (!hostNodes.length) { return ''; } - const joinedHosts = hostNodes.map((p) => p.name).join(' or '); + const joinedHosts = hostNodes.join(' or '); const hostsQueryParam = `host.name:(${joinedHosts})`; return hostsQueryParam; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx index 9563dbaa6169e..f81228957107a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx @@ -4,57 +4,81 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import React, { useMemo } from 'react'; import { Action } from '@kbn/ui-actions-plugin/public'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; import { BrushTriggerEvent } from '@kbn/charts-plugin/public'; -import { EuiIcon, EuiPanel } from '@elastic/eui'; -import { EuiFlexGroup } from '@elastic/eui'; -import { EuiFlexItem } from '@elastic/eui'; -import { EuiText } from '@elastic/eui'; -import { EuiI18n } from '@elastic/eui'; -import { InfraClientSetupDeps } from '../../../../../../types'; +import { + EuiIcon, + EuiPanel, + EuiI18n, + EuiFlexGroup, + EuiFlexItem, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; import { useLensAttributes } from '../../../../../../hooks/use_lens_attributes'; import { useMetricsDataViewContext } from '../../../hooks/use_data_view'; import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; -import { HostLensAttributesTypes } from '../../../../../../common/visualizations'; +import { HostsLensLineChartFormulas } from '../../../../../../common/visualizations'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; +import { createHostsFilter } from '../../../utils'; +import { useHostsTableContext } from '../../../hooks/use_hosts_table'; +import { LensWrapper } from '../../chart/lens_wrapper'; +import { useAfterLoadedState } from '../../../hooks/use_after_loaded_state'; export interface MetricChartProps { title: string; - type: HostLensAttributesTypes; + type: HostsLensLineChartFormulas; breakdownSize: number; + render?: boolean; } const MIN_HEIGHT = 300; export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => { + const { euiTheme } = useEuiTheme(); const { searchCriteria, onSubmit } = useUnifiedSearchContext(); const { dataView } = useMetricsDataViewContext(); - const { baseRequest } = useHostsViewContext(); - const { - services: { lens }, - } = useKibana(); + const { requestTs, loading } = useHostsViewContext(); + const { currentPage } = useHostsTableContext(); - const EmbeddableComponent = lens.EmbeddableComponent; + // prevents updates on requestTs and serchCriteria states from relaoding the chart + // we want it to reload only once the table has finished loading + const { afterLoadedState } = useAfterLoadedState(loading, { + lastReloadRequestTime: requestTs, + ...searchCriteria, + }); - const { injectData, getExtraActions, error } = useLensAttributes({ + const { attributes, getExtraActions, error } = useLensAttributes({ type, dataView, options: { + title, breakdownSize, }, + visualizationType: 'lineChart', }); - const injectedLensAttributes = injectData({ - filters: [...searchCriteria.filters, ...searchCriteria.panelFilters], - query: searchCriteria.query, - title, + const hostsFilterQuery = useMemo(() => { + return createHostsFilter( + currentPage.map((p) => p.name), + dataView + ); + }, [currentPage, dataView]); + + const filters = [ + ...afterLoadedState.filters, + ...afterLoadedState.panelFilters, + ...[hostsFilterQuery], + ]; + const extraActionOptions = getExtraActions({ + timeRange: afterLoadedState.dateRange, + filters, + query: afterLoadedState.query, }); - const extraActionOptions = getExtraActions(injectedLensAttributes, searchCriteria.dateRange); - const extraAction: Action[] = [extraActionOptions.openInLens]; + const extraActions: Action[] = [extraActionOptions.openInLens]; const handleBrushEnd = ({ range }: BrushTriggerEvent['data']) => { const [min, max] = range; @@ -73,12 +97,15 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => hasShadow={false} hasBorder paddingSize={error ? 'm' : 'none'} - style={{ minHeight: MIN_HEIGHT }} + css={css` + min-height: calc(${MIN_HEIGHT} + ${euiTheme.size.l}); + position: 'relative'; + `} data-test-subj={`hostsView-metricChart-${type}`} > {error ? (
    ) : ( - injectedLensAttributes && ( - - ) + )} ); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx index e307dde0d09e5..7f3dac7a3af16 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiFlexGrid, EuiFlexItem, EuiFlexGroup, EuiText, EuiI18n } from '@elastic/eui'; +import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { MetricChart, MetricChartProps } from './metric_chart'; @@ -64,32 +64,12 @@ const CHARTS_IN_ORDER: Array & { fullRo export const MetricsGrid = React.memo(() => { return ( - - - - - - {DEFAULT_BREAKDOWN_SIZE}, - attribute: name, - }} - /> - - - - - - - {CHARTS_IN_ORDER.map(({ fullRow, ...chartProp }) => ( - - - - ))} - - - + + {CHARTS_IN_ORDER.map(({ fullRow, ...chartProp }) => ( + + + + ))} + ); }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx deleted file mode 100644 index 168f825a9d2d1..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx +++ /dev/null @@ -1,98 +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, { useMemo } from 'react'; -import { compareFilters, COMPARE_ALL_OPTIONS, type Filter } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; -import { EuiFlexGrid, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { EuiHorizontalRule } from '@elastic/eui'; -import { METRICS_APP_DATA_TEST_SUBJ } from '../../../../apps/metrics_app'; -import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; -import { useUnifiedSearchContext } from '../hooks/use_unified_search'; -import { ControlsContent } from './controls_content'; -import { useMetricsDataViewContext } from '../hooks/use_data_view'; -import { HostsSearchPayload } from '../hooks/use_unified_search_url_state'; - -export const UnifiedSearchBar = () => { - const { - services: { unifiedSearch, application }, - } = useKibanaContextForPlugin(); - const { dataView } = useMetricsDataViewContext(); - const { searchCriteria, onSubmit } = useUnifiedSearchContext(); - - const { SearchBar } = unifiedSearch.ui; - - const onPanelFiltersChange = (panelFilters: Filter[]) => { - if (!compareFilters(searchCriteria.panelFilters, panelFilters, COMPARE_ALL_OPTIONS)) { - onSubmit({ panelFilters }); - } - }; - - const handleRefresh = (payload: HostsSearchPayload, isUpdate?: boolean) => { - // This makes sure `onQueryChange` is only called when the submit button is clicked - if (isUpdate === false) { - onSubmit(payload); - } - }; - - return ( - - 0.5)', - })} - onQuerySubmit={handleRefresh} - showSaveQuery={Boolean(application?.capabilities?.visualize?.saveQuery)} - showDatePicker - showFilterBar - showQueryInput - showQueryMenu - useDefaultBehaviors - /> - - - - ); -}; - -const StickyContainer = (props: { children: React.ReactNode }) => { - const { euiTheme } = useEuiTheme(); - - const top = useMemo(() => { - const wrapper = document.querySelector(`[data-test-subj="${METRICS_APP_DATA_TEST_SUBJ}"]`); - if (!wrapper) { - return `calc(${euiTheme.size.xxxl} * 2)`; - } - - return `${wrapper.getBoundingClientRect().top}px`; - }, [euiTheme]); - - return ( - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts index 98aa8a145e3a0..69cfc446d0095 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts @@ -7,12 +7,17 @@ import { i18n } from '@kbn/i18n'; import { ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils'; -import { AlertStatusFilter } from './types'; +import { AlertStatusFilter, HostLimitOptions } from './types'; export const ALERT_STATUS_ALL = 'all'; export const TIMESTAMP_FIELD = '@timestamp'; export const DATA_VIEW_PREFIX = 'infra_metrics'; +export const DEFAULT_HOST_LIMIT: HostLimitOptions = 100; +export const DEFAULT_PAGE_SIZE = 10; +export const LOCAL_STORAGE_HOST_LIMIT_KEY = 'hostsView:hostLimitSelection'; +export const LOCAL_STORAGE_PAGE_SIZE_KEY = 'hostsView:pageSizeSelection'; + export const ALL_ALERTS: AlertStatusFilter = { status: ALERT_STATUS_ALL, label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.showAll', { @@ -52,3 +57,5 @@ export const ALERT_STATUS_QUERY = { [ACTIVE_ALERTS.status]: ACTIVE_ALERTS.query, [RECOVERED_ALERTS.status]: RECOVERED_ALERTS.query, }; + +export const HOST_LIMIT_OPTIONS = [10, 20, 50, 100, 500] as const; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_after_loaded_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_after_loaded_state.ts new file mode 100644 index 0000000000000..8c9a84d4402f8 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_after_loaded_state.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 { useState, useEffect, useRef } from 'react'; + +export const useAfterLoadedState = (loading: boolean, state: T) => { + const ref = useRef(undefined); + const [internalState, setInternalState] = useState(state); + + if (!ref.current || loading !== ref.current) { + ref.current = loading; + } + + useEffect(() => { + if (!loading) { + setInternalState(state); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ref.current]); + + return { afterLoadedState: internalState }; +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts index 9877d61643721..200bff521d86a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts @@ -9,7 +9,7 @@ import createContainer from 'constate'; import { getTime } from '@kbn/data-plugin/common'; import { ALERT_TIME_RANGE } from '@kbn/rule-data-utils'; import { BoolQuery, buildEsQuery, Filter } from '@kbn/es-query'; -import { SnapshotNode } from '../../../../../common/http_api'; +import { InfraAssetMetricsItem } from '../../../../../common/http_api'; import { useUnifiedSearchContext } from './use_unified_search'; import { HostsState } from './use_unified_search_url_state'; import { useHostsViewContext } from './use_hosts_view'; @@ -63,13 +63,13 @@ const createAlertsEsQuery = ({ status, }: { dateRange: HostsState['dateRange']; - hostNodes: SnapshotNode[]; + hostNodes: InfraAssetMetricsItem[]; status?: AlertStatus; }): AlertsEsQuery => { const alertStatusFilter = createAlertStatusFilter(status); const dateFilter = createDateFilter(dateRange); - const hostsFilter = createHostsFilter(hostNodes); + const hostsFilter = createHostsFilter(hostNodes.map((p) => p.name)); const filters = [alertStatusFilter, dateFilter, hostsFilter].filter(Boolean) as Filter[]; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_data_view.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_data_view.ts index 94e3a963075be..83fedf4292937 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_data_view.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_data_view.ts @@ -72,6 +72,7 @@ export const useDataView = ({ metricAlias }: { metricAlias: string }) => { }, [hasError, notifications, metricAlias]); return { + metricAlias, dataView, loading, hasError, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts new file mode 100644 index 0000000000000..5575c46e621f1 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts @@ -0,0 +1,129 @@ +/* + * 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 * as rt from 'io-ts'; +import { ES_SEARCH_STRATEGY, IKibanaSearchResponse } from '@kbn/data-plugin/common'; +import { useCallback, useEffect } from 'react'; +import { catchError, map, Observable, of, startWith } from 'rxjs'; +import createContainer from 'constate'; +import type { QueryDslQueryContainer, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { useDataSearch, useLatestPartialDataSearchResponse } from '../../../../utils/data_search'; +import { useMetricsDataViewContext } from './use_data_view'; +import { useUnifiedSearchContext } from './use_unified_search'; + +export const useHostCount = () => { + const { dataView, metricAlias } = useMetricsDataViewContext(); + const { buildQuery, getParsedDateRange } = useUnifiedSearchContext(); + + const { search: fetchHostCount, requests$ } = useDataSearch({ + getRequest: useCallback(() => { + const query = buildQuery(); + const dateRange = getParsedDateRange(); + + const filters: QueryDslQueryContainer = { + bool: { + ...query.bool, + filter: [ + ...query.bool.filter, + { + exists: { + field: 'host.name', + }, + }, + { + range: { + [dataView?.timeFieldName ?? '@timestamp']: { + gte: dateRange.from, + lte: dateRange.to, + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }; + + return { + request: { + params: { + allow_no_indices: true, + ignore_unavailable: true, + index: metricAlias, + size: 0, + track_total_hits: false, + body: { + query: filters, + aggs: { + count: { + cardinality: { + field: 'host.name', + }, + }, + }, + }, + }, + }, + options: { strategy: ES_SEARCH_STRATEGY }, + }; + }, [buildQuery, dataView, getParsedDateRange, metricAlias]), + parseResponses: normalizeDataSearchResponse, + }); + + const { isRequestRunning, isResponsePartial, latestResponseData, latestResponseErrors } = + useLatestPartialDataSearchResponse(requests$); + + useEffect(() => { + fetchHostCount(); + }, [fetchHostCount]); + + return { + errors: latestResponseErrors, + isRequestRunning, + isResponsePartial, + data: latestResponseData ?? null, + }; +}; + +export const HostCount = createContainer(useHostCount); +export const [HostCountProvider, useHostCountContext] = HostCount; + +const INITIAL_STATE = { + data: null, + errors: [], + isPartial: true, + isRunning: true, + loaded: 0, + total: undefined, +}; +const normalizeDataSearchResponse = ( + response$: Observable>>> +) => + response$.pipe( + map((response) => ({ + data: decodeOrThrow(HostCountResponseRT)(response.rawResponse.aggregations), + errors: [], + isPartial: response.isPartial ?? false, + isRunning: response.isRunning ?? false, + loaded: response.loaded, + total: response.total, + })), + startWith(INITIAL_STATE), + catchError((error) => + of({ + ...INITIAL_STATE, + errors: [error.message ?? error], + isRunning: false, + }) + ) + ); + +const HostCountResponseRT = rt.type({ + count: rt.type({ + value: rt.number, + }), +}); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_open_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_open_url_state.ts index 635fe556e85e5..663ecf3a92643 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_open_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_flyout_open_url_state.ts @@ -25,19 +25,29 @@ export const GET_DEFAULT_TABLE_PROPERTIES = { const HOST_TABLE_PROPERTIES_URL_STATE_KEY = 'hostFlyoutOpen'; type Action = rt.TypeOf; -type SetNewHostFlyoutOpen = (newProps: Action) => void; - -export const useHostFlyoutOpen = (): [HostFlyoutOpen, SetNewHostFlyoutOpen] => { - const [urlState, setUrlState] = useUrlState({ - defaultState: GET_DEFAULT_TABLE_PROPERTIES, +type SetNewHostFlyoutOpen = (newProp: Action) => void; +type SetNewHostFlyoutClose = () => void; + +export const useHostFlyoutOpen = (): [ + HostFlyoutOpen, + SetNewHostFlyoutOpen, + SetNewHostFlyoutClose +] => { + const [urlState, setUrlState] = useUrlState({ + defaultState: '', decodeUrlState, encodeUrlState, urlStateKey: HOST_TABLE_PROPERTIES_URL_STATE_KEY, }); - const setHostFlyoutOpen = (newProps: Action) => setUrlState({ ...urlState, ...newProps }); + const setHostFlyoutOpen = (newProps: Action) => + typeof urlState !== 'string' + ? setUrlState({ ...urlState, ...newProps }) + : setUrlState({ ...GET_DEFAULT_TABLE_PROPERTIES, ...newProps }); + + const setFlyoutClosed = () => setUrlState(''); - return [urlState, setHostFlyoutOpen]; + return [urlState as HostFlyoutOpen, setHostFlyoutOpen, setFlyoutClosed]; }; const FlyoutTabIdRT = rt.union([rt.literal('metadata'), rt.literal('processes')]); @@ -74,9 +84,12 @@ const HostFlyoutOpenRT = rt.type({ metadataSearch: SearchFilterRT, }); +const HostFlyoutUrlRT = rt.union([HostFlyoutOpenRT, rt.string]); + +type HostFlyoutUrl = rt.TypeOf; type HostFlyoutOpen = rt.TypeOf; -const encodeUrlState = HostFlyoutOpenRT.encode; +const encodeUrlState = HostFlyoutUrlRT.encode; const decodeUrlState = (value: unknown) => { - return pipe(HostFlyoutOpenRT.decode(value), fold(constant(undefined), identity)); + return pipe(HostFlyoutUrlRT.decode(value), fold(constant('undefined'), identity)); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts index 4ae8823adaf2e..5619a788b19a7 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts @@ -7,69 +7,96 @@ import { useHostsTable } from './use_hosts_table'; import { renderHook } from '@testing-library/react-hooks'; -import { SnapshotNode } from '../../../../../common/http_api'; +import { InfraAssetMetricsItem } from '../../../../../common/http_api'; +import * as useUnifiedSearchHooks from './use_unified_search'; +import * as useHostsViewHooks from './use_hosts_view'; -describe('useHostTable hook', () => { - it('it should map the nodes returned from the snapshot api to a format matching eui table items', () => { - const nodes: SnapshotNode[] = [ +jest.mock('./use_unified_search'); +jest.mock('./use_hosts_view'); + +const mockUseUnifiedSearchContext = + useUnifiedSearchHooks.useUnifiedSearchContext as jest.MockedFunction< + typeof useUnifiedSearchHooks.useUnifiedSearchContext + >; +const mockUseHostsViewContext = useHostsViewHooks.useHostsViewContext as jest.MockedFunction< + typeof useHostsViewHooks.useHostsViewContext +>; + +const mockHostNode: InfraAssetMetricsItem[] = [ + { + metrics: [ { - metrics: [ - { - name: 'rx', - avg: 252456.92916666667, - }, - { - name: 'tx', - avg: 252758.425, - }, - { - name: 'memory', - avg: 0.94525, - }, - { - name: 'cpu', - value: 0.6353277777777777, - }, - { - name: 'memoryTotal', - avg: 34359.738368, - }, - ], - path: [{ value: 'host-0', label: 'host-0', os: null, cloudProvider: 'aws' }], - name: 'host-0', + name: 'rx', + value: 252456.92916666667, }, { - metrics: [ - { - name: 'rx', - avg: 95.86339715321859, - }, - { - name: 'tx', - avg: 110.38566859563191, - }, - { - name: 'memory', - avg: 0.5400000214576721, - }, - { - name: 'cpu', - value: 0.8647805555555556, - }, - { - name: 'memoryTotal', - avg: 9.194304, - }, - ], - path: [ - { value: 'host-1', label: 'host-1' }, - { value: 'host-1', label: 'host-1', ip: '243.86.94.22', os: 'macOS' }, - ], - name: 'host-1', + name: 'tx', + value: 252758.425, }, - ]; + { + name: 'memory', + value: 0.94525, + }, + { + name: 'cpu', + value: 0.6353277777777777, + }, + { + name: 'memoryTotal', + value: 34359.738368, + }, + ], + metadata: [ + { name: 'host.os.name', value: null }, + { name: 'cloud.provider', value: 'aws' }, + ], + name: 'host-0', + }, + { + metrics: [ + { + name: 'rx', + value: 95.86339715321859, + }, + { + name: 'tx', + value: 110.38566859563191, + }, + { + name: 'memory', + value: 0.5400000214576721, + }, + { + name: 'cpu', + value: 0.8647805555555556, + }, + { + name: 'memoryTotal', + value: 9.194304, + }, + ], + metadata: [ + { name: 'host.os.name', value: 'macOS' }, + { name: 'host.ip', value: '243.86.94.22' }, + ], + name: 'host-1', + }, +]; + +describe('useHostTable hook', () => { + beforeAll(() => { + mockUseUnifiedSearchContext.mockReturnValue({ + searchCriteria: { + dateRange: { from: 'now-15m', to: 'now' }, + }, + } as ReturnType); - const items = [ + mockUseHostsViewContext.mockReturnValue({ + hostNodes: mockHostNode, + } as ReturnType); + }); + it('it should map the nodes returned from the snapshot api to a format matching eui table items', () => { + const expected = [ { name: 'host-0', os: '-', @@ -79,27 +106,11 @@ describe('useHostTable hook', () => { cloudProvider: 'aws', name: 'host-0', }, - rx: { - name: 'rx', - avg: 252456.92916666667, - }, - tx: { - name: 'tx', - avg: 252758.425, - }, - memory: { - name: 'memory', - avg: 0.94525, - }, - cpu: { - name: 'cpu', - value: 0.6353277777777777, - }, - memoryTotal: { - name: 'memoryTotal', - - avg: 34359.738368, - }, + rx: 252456.92916666667, + tx: 252758.425, + memory: 0.94525, + cpu: 0.6353277777777777, + memoryTotal: 34359.738368, }, { name: 'host-1', @@ -110,32 +121,16 @@ describe('useHostTable hook', () => { cloudProvider: null, name: 'host-1', }, - rx: { - name: 'rx', - avg: 95.86339715321859, - }, - tx: { - name: 'tx', - avg: 110.38566859563191, - }, - memory: { - name: 'memory', - avg: 0.5400000214576721, - }, - cpu: { - name: 'cpu', - value: 0.8647805555555556, - }, - memoryTotal: { - name: 'memoryTotal', - avg: 9.194304, - }, + rx: 95.86339715321859, + tx: 110.38566859563191, + memory: 0.5400000214576721, + cpu: 0.8647805555555556, + memoryTotal: 9.194304, }, ]; - const time = { from: 'now-15m', to: 'now', interval: '>=1m' }; - const { result } = renderHook(() => useHostsTable(nodes, { time })); + const { result } = renderHook(() => useHostsTable()); - expect(result.current.items).toStrictEqual(items); + expect(result.current.items).toStrictEqual(expected); }); }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index 0b7021b97f84c..7350f402c57ec 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -8,64 +8,107 @@ import React, { useCallback, useMemo } from 'react'; import { EuiBasicTableColumn, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { TimeRange } from '@kbn/es-query'; - +import createContainer from 'constate'; +import { isEqual } from 'lodash'; +import { CriteriaWithPagination } from '@elastic/eui'; +import { isNumber } from 'lodash/fp'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; import { HostsTableEntryTitle } from '../components/hosts_table_entry_title'; -import type { - SnapshotNode, - SnapshotNodeMetric, - SnapshotMetricInput, +import { + InfraAssetMetadataType, + InfraAssetMetricsItem, + InfraAssetMetricType, } from '../../../../../common/http_api'; import { useHostFlyoutOpen } from './use_host_flyout_open_url_state'; +import { Sorting, useHostsTableProperties } from './use_hosts_table_url_state'; +import { useHostsViewContext } from './use_hosts_view'; +import { useUnifiedSearchContext } from './use_unified_search'; /** * Columns and items types */ export type CloudProvider = 'gcp' | 'aws' | 'azure' | 'unknownProvider'; +type HostMetrics = Record; -type HostMetric = 'cpu' | 'diskLatency' | 'rx' | 'tx' | 'memory' | 'memoryTotal'; - -type HostMetrics = Record; - -export interface HostNodeRow extends HostMetrics { +interface HostMetadata { os?: string | null; ip?: string | null; servicesOnHost?: number | null; title: { name: string; cloudProvider?: CloudProvider | null }; - name: string; id: string; } - -interface HostTableParams { - time: TimeRange; -} +export type HostNodeRow = HostMetadata & + HostMetrics & { + name: string; + }; /** * Helper functions */ -const formatMetric = (type: SnapshotMetricInput['type'], value: number | undefined | null) => { +const formatMetric = (type: InfraAssetMetricType, value: number | undefined | null) => { return value || value === 0 ? createInventoryMetricFormatter({ type })(value) : 'N/A'; }; -const buildItemsList = (nodes: SnapshotNode[]) => { - return nodes.map(({ metrics, path, name }) => ({ - id: `${name}-${path.at(-1)?.os ?? '-'}`, - name, - os: path.at(-1)?.os ?? '-', - ip: path.at(-1)?.ip ?? '', - title: { +const buildItemsList = (nodes: InfraAssetMetricsItem[]): HostNodeRow[] => { + return nodes.map(({ metrics, metadata, name }) => { + const metadataKeyValue = metadata.reduce( + (acc, curr) => ({ + ...acc, + [curr.name]: curr.value, + }), + {} as Record + ); + + return { name, - cloudProvider: path.at(-1)?.cloudProvider ?? null, - }, - ...metrics.reduce((data, metric) => { - data[metric.name as HostMetric] = metric; - return data; - }, {} as HostMetrics), - })) as HostNodeRow[]; + id: `${name}-${metadataKeyValue['host.os.name'] ?? '-'}`, + title: { + name, + cloudProvider: (metadataKeyValue['cloud.provider'] as CloudProvider) ?? null, + }, + os: metadataKeyValue['host.os.name'] ?? '-', + ip: metadataKeyValue['host.ip'] ?? '', + ...metrics.reduce( + (acc, curr) => ({ + ...acc, + [curr.name]: curr.value ?? 0, + }), + {} as HostMetrics + ), + }; + }); +}; + +const isTitleColumn = (cell: any): cell is HostNodeRow['title'] => { + return typeof cell === 'object' && cell && 'name' in cell; +}; + +const sortValues = (aValue: any, bValue: any, { direction }: Sorting) => { + if (typeof aValue === 'string' && typeof bValue === 'string') { + return direction === 'desc' ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue); + } + + if (isNumber(aValue) && isNumber(bValue)) { + return direction === 'desc' ? bValue - aValue : aValue - bValue; + } + + return 1; }; +const sortTableData = + ({ direction, field }: Sorting) => + (a: HostNodeRow, b: HostNodeRow) => { + const aValue = a[field as keyof HostNodeRow]; + const bValue = b[field as keyof HostNodeRow]; + + if (isTitleColumn(aValue) && isTitleColumn(bValue)) { + return sortValues(aValue.name, bValue.name, { direction, field }); + } + + return sortValues(aValue, bValue, { direction, field }); + }; + /** * Columns translations */ @@ -120,14 +163,17 @@ const toggleDialogActionLabel = i18n.translate( /** * Build a table columns and items starting from the snapshot nodes. */ -export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) => { +export const useHostsTable = () => { + const { hostNodes } = useHostsViewContext(); + const { searchCriteria } = useUnifiedSearchContext(); + const [{ pagination, sorting }, setProperties] = useHostsTableProperties(); const { services: { telemetry }, } = useKibanaContextForPlugin(); - const [hostFlyoutOpen, setHostFlyoutOpen] = useHostFlyoutOpen(); + const [hostFlyoutOpen, setHostFlyoutOpen, setFlyoutClosed] = useHostFlyoutOpen(); - const closeFlyout = () => setHostFlyoutOpen({ clickedItemId: '' }); + const closeFlyout = () => setFlyoutClosed(); const reportHostEntryClick = useCallback( ({ name, cloudProvider }: HostNodeRow['title']) => { @@ -139,12 +185,38 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) [telemetry] ); - const items = useMemo(() => buildItemsList(nodes), [nodes]); + const onTableChange = useCallback( + ({ page, sort }: CriteriaWithPagination) => { + const { index: pageIndex, size: pageSize } = page; + const { field, direction } = sort ?? {}; + + const currentSorting = { field: field as keyof HostNodeRow, direction }; + const currentPagination = { pageIndex, pageSize }; + + if (!isEqual(sorting, currentSorting)) { + setProperties({ sorting: currentSorting }); + } else if (!isEqual(pagination, currentPagination)) { + setProperties({ pagination: currentPagination }); + } + }, + [setProperties, pagination, sorting] + ); + + const items = useMemo(() => buildItemsList(hostNodes), [hostNodes]); const clickedItem = useMemo( () => items.find(({ id }) => id === hostFlyoutOpen.clickedItemId), [hostFlyoutOpen.clickedItemId, items] ); + const currentPage = useMemo(() => { + const { pageSize = 0, pageIndex = 0 } = pagination; + + const endIndex = (pageIndex + 1) * pageSize; + const startIndex = pageIndex * pageSize; + + return items.sort(sortTableData(sorting)).slice(startIndex, endIndex); + }, [items, pagination, sorting]); + const columns: Array> = useMemo( () => [ { @@ -166,7 +238,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) clickedItemId: id, }); if (id === hostFlyoutOpen.clickedItemId) { - setHostFlyoutOpen({ clickedItemId: '' }); + setFlyoutClosed(); } else { setHostFlyoutOpen({ clickedItemId: id }); } @@ -183,7 +255,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) render: (title: HostNodeRow['title']) => ( reportHostEntryClick(title)} /> ), @@ -197,7 +269,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) }, { name: averageCpuUsageLabel, - field: 'cpu.avg', + field: 'cpu', sortable: true, 'data-test-subj': 'hostsView-tableRow-cpuUsage', render: (avg: number) => formatMetric('cpu', avg), @@ -205,7 +277,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) }, { name: diskLatencyLabel, - field: 'diskLatency.avg', + field: 'diskLatency', sortable: true, 'data-test-subj': 'hostsView-tableRow-diskLatency', render: (avg: number) => formatMetric('diskLatency', avg), @@ -213,7 +285,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) }, { name: averageRXLabel, - field: 'rx.avg', + field: 'rx', sortable: true, 'data-test-subj': 'hostsView-tableRow-rx', render: (avg: number) => formatMetric('rx', avg), @@ -221,7 +293,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) }, { name: averageTXLabel, - field: 'tx.avg', + field: 'tx', sortable: true, 'data-test-subj': 'hostsView-tableRow-tx', render: (avg: number) => formatMetric('tx', avg), @@ -229,7 +301,7 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) }, { name: averageTotalMemoryLabel, - field: 'memoryTotal.avg', + field: 'memoryTotal', sortable: true, 'data-test-subj': 'hostsView-tableRow-memoryTotal', render: (avg: number) => formatMetric('memoryTotal', avg), @@ -237,21 +309,34 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams) }, { name: averageMemoryUsageLabel, - field: 'memory.avg', + field: 'memory', sortable: true, 'data-test-subj': 'hostsView-tableRow-memory', render: (avg: number) => formatMetric('memory', avg), align: 'right', }, ], - [hostFlyoutOpen.clickedItemId, reportHostEntryClick, setHostFlyoutOpen, time] + [ + hostFlyoutOpen.clickedItemId, + reportHostEntryClick, + searchCriteria.dateRange, + setFlyoutClosed, + setHostFlyoutOpen, + ] ); return { columns, - items, clickedItem, - isFlyoutOpen: !!hostFlyoutOpen.clickedItemId, + currentPage, closeFlyout, + items, + isFlyoutOpen: !!hostFlyoutOpen.clickedItemId, + onTableChange, + pagination, + sorting, }; }; + +export const HostsTable = createContainer(useHostsTable); +export const [HostsTableProvider, useHostsTableContext] = HostsTable; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts new file mode 100644 index 0000000000000..b4889d62f5878 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.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. + */ + +import * as rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { constant, identity } from 'fp-ts/lib/function'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import deepEqual from 'fast-deep-equal'; +import { useReducer } from 'react'; +import { useUrlState } from '../../../../utils/use_url_state'; +import { DEFAULT_PAGE_SIZE, LOCAL_STORAGE_PAGE_SIZE_KEY } from '../constants'; + +export const GET_DEFAULT_TABLE_PROPERTIES: TableProperties = { + sorting: { + direction: 'asc', + field: 'name', + }, + pagination: { + pageIndex: 0, + pageSize: DEFAULT_PAGE_SIZE, + }, +}; + +const HOST_TABLE_PROPERTIES_URL_STATE_KEY = 'tableProperties'; + +const reducer = (prevState: TableProperties, params: Payload) => { + const payload = Object.fromEntries(Object.entries(params).filter(([_, v]) => !!v)); + + return { + ...prevState, + ...payload, + }; +}; + +export const useHostsTableProperties = (): [TableProperties, TablePropertiesUpdater] => { + const [localStoragePageSize, setLocalStoragePageSize] = useLocalStorage( + LOCAL_STORAGE_PAGE_SIZE_KEY, + DEFAULT_PAGE_SIZE + ); + + const [urlState, setUrlState] = useUrlState({ + defaultState: { + ...GET_DEFAULT_TABLE_PROPERTIES, + pagination: { + ...GET_DEFAULT_TABLE_PROPERTIES.pagination, + pageSize: localStoragePageSize, + }, + }, + + decodeUrlState, + encodeUrlState, + urlStateKey: HOST_TABLE_PROPERTIES_URL_STATE_KEY, + }); + + const [properties, setProperties] = useReducer(reducer, urlState); + if (!deepEqual(properties, urlState)) { + setUrlState(properties); + if (localStoragePageSize !== properties.pagination.pageSize) { + setLocalStoragePageSize(properties.pagination.pageSize); + } + } + + return [properties, setProperties]; +}; + +const PaginationRT = rt.partial({ pageIndex: rt.number, pageSize: rt.number }); +const SortingRT = rt.intersection([ + rt.type({ + field: rt.string, + }), + rt.partial({ direction: rt.union([rt.literal('asc'), rt.literal('desc')]) }), +]); + +const TableStateRT = rt.type({ + pagination: PaginationRT, + sorting: SortingRT, +}); + +export type TableState = rt.TypeOf; +export type Payload = Partial; +export type TablePropertiesUpdater = (params: Payload) => void; + +export type Sorting = rt.TypeOf; +type TableProperties = rt.TypeOf; + +const encodeUrlState = TableStateRT.encode; +const decodeUrlState = (value: unknown) => { + return pipe(TableStateRT.decode(value), fold(constant(undefined), identity)); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts index d0df961dc7ef9..f84acf5931ea1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts @@ -12,16 +12,21 @@ * 2.0. */ -import { useMemo } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import createContainer from 'constate'; import { BoolQuery } from '@kbn/es-query'; -import { SnapshotMetricType } from '../../../../../common/inventory_models/types'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { useSourceContext } from '../../../../containers/metrics_source'; -import { useSnapshot, type UseSnapshotRequest } from '../../inventory_view/hooks/use_snaphot'; import { useUnifiedSearchContext } from './use_unified_search'; -import { StringDateRangeTimestamp } from './use_unified_search_url_state'; +import { + GetInfraMetricsRequestBodyPayload, + GetInfraMetricsResponsePayload, + InfraAssetMetricType, +} from '../../../../../common/http_api'; +import { StringDateRange } from './use_unified_search_url_state'; -const HOST_TABLE_METRICS: Array<{ type: SnapshotMetricType }> = [ +const HOST_TABLE_METRICS: Array<{ type: InfraAssetMetricType }> = [ { type: 'rx' }, { type: 'tx' }, { type: 'memory' }, @@ -30,40 +35,52 @@ const HOST_TABLE_METRICS: Array<{ type: SnapshotMetricType }> = [ { type: 'memoryTotal' }, ]; +const BASE_INFRA_METRICS_PATH = '/api/metrics/infra'; + export const useHostsView = () => { const { sourceId } = useSourceContext(); - const { buildQuery, getDateRangeAsTimestamp } = useUnifiedSearchContext(); + const { + services: { http }, + } = useKibanaContextForPlugin(); + const { buildQuery, getParsedDateRange, searchCriteria } = useUnifiedSearchContext(); + const abortCtrlRef = useRef(new AbortController()); const baseRequest = useMemo( () => - createSnapshotRequest({ - dateRange: getDateRangeAsTimestamp(), + createInfraMetricsRequest({ + dateRange: getParsedDateRange(), esQuery: buildQuery(), sourceId, + limit: searchCriteria.limit, }), - [buildQuery, getDateRangeAsTimestamp, sourceId] + [buildQuery, getParsedDateRange, sourceId, searchCriteria.limit] ); - // Snapshot endpoint internally uses the indices stored in source.configuration.metricAlias. - // For the Unified Search, we create a data view, which for now will be built off of source.configuration.metricAlias too - // if we introduce data view selection, we'll have to change this hook and the endpoint to accept a new parameter for the indices - const { - loading, - error, - nodes: hostNodes, - } = useSnapshot( - { - ...baseRequest, - metrics: HOST_TABLE_METRICS, + const [state, refetch] = useAsyncFn( + () => { + abortCtrlRef.current.abort(); + abortCtrlRef.current = new AbortController(); + + return http.post(`${BASE_INFRA_METRICS_PATH}`, { + signal: abortCtrlRef.current.signal, + body: JSON.stringify(baseRequest), + }); }, - { abortable: true } + [baseRequest, http], + { loading: true } ); + useEffect(() => { + refetch(); + }, [refetch]); + + const { value, error, loading } = state; + return { - baseRequest, + requestTs: baseRequest.requestTs, loading, error, - hostNodes, + hostNodes: value?.nodes ?? [], }; }; @@ -73,30 +90,26 @@ export const [HostsViewProvider, useHostsViewContext] = HostsView; /** * Helpers */ -const createSnapshotRequest = ({ + +const createInfraMetricsRequest = ({ esQuery, sourceId, dateRange, + limit, }: { esQuery: { bool: BoolQuery }; sourceId: string; - dateRange: StringDateRangeTimestamp; -}): UseSnapshotRequest => ({ - filterQuery: JSON.stringify(esQuery), - metrics: [], - groupBy: [], - nodeType: 'host', - sourceId, - currentTime: dateRange.to, - includeTimeseries: false, - sendRequestImmediately: true, - timerange: { - interval: '1m', + dateRange: StringDateRange; + limit: number; +}): GetInfraMetricsRequestBodyPayload & { requestTs: number } => ({ + type: 'host', + query: esQuery, + range: { from: dateRange.from, to: dateRange.to, - ignoreLookback: true, }, - // The user might want to click on the submit button without changing the filters - // This makes sure all child components will re-render. + metrics: HOST_TABLE_METRICS, + limit, + sourceId, requestTs: Date.now(), }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_table_properties_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_table_properties_url_state.ts deleted file mode 100644 index 980fdf19a684c..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_table_properties_url_state.ts +++ /dev/null @@ -1,62 +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 * as rt from 'io-ts'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState } from '../../../../utils/use_url_state'; - -export const GET_DEFAULT_TABLE_PROPERTIES = { - sorting: true, - pagination: true, -}; -const HOST_TABLE_PROPERTIES_URL_STATE_KEY = 'tableProperties'; - -type Action = rt.TypeOf; -type PropertiesUpdater = (newProps: Action) => void; - -export const useTableProperties = (): [TableProperties, PropertiesUpdater] => { - const [urlState, setUrlState] = useUrlState({ - defaultState: GET_DEFAULT_TABLE_PROPERTIES, - decodeUrlState, - encodeUrlState, - urlStateKey: HOST_TABLE_PROPERTIES_URL_STATE_KEY, - }); - - const setProperties = (newProps: Action) => setUrlState({ ...urlState, ...newProps }); - - return [urlState, setProperties]; -}; - -const PaginationRT = rt.union([ - rt.boolean, - rt.partial({ pageIndex: rt.number, pageSize: rt.number }), -]); -const SortingRT = rt.union([rt.boolean, rt.type({ field: rt.string, direction: rt.any })]); - -const SetSortingRT = rt.partial({ - sorting: SortingRT, -}); - -const SetPaginationRT = rt.partial({ - pagination: PaginationRT, -}); - -const ActionRT = rt.intersection([SetSortingRT, SetPaginationRT]); - -const TablePropertiesRT = rt.type({ - pagination: PaginationRT, - sorting: SortingRT, -}); - -type TableProperties = rt.TypeOf; - -const encodeUrlState = TablePropertiesRT.encode; -const decodeUrlState = (value: unknown) => { - return pipe(TablePropertiesRT.decode(value), fold(constant(undefined), identity)); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index 950bd9cf3c94e..e242f58054c6c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -23,14 +23,14 @@ import { } from './use_unified_search_url_state'; const buildQuerySubmittedPayload = ( - hostState: HostsState & { dateRangeTimestamp: StringDateRangeTimestamp } + hostState: HostsState & { parsedDateRange: StringDateRangeTimestamp } ) => { - const { panelFilters, filters, dateRangeTimestamp, query: queryObj } = hostState; + const { panelFilters, filters, parsedDateRange, query: queryObj } = hostState; return { control_filters: panelFilters.map((filter) => JSON.stringify(filter)), filters: filters.map((filter) => JSON.stringify(filter)), - interval: telemetryTimeRangeFormatter(dateRangeTimestamp.to - dateRangeTimestamp.from), + interval: telemetryTimeRangeFormatter(parsedDateRange.to - parsedDateRange.from), query: queryObj.query, }; }; @@ -41,8 +41,8 @@ const getDefaultTimestamps = () => { const now = Date.now(); return { - from: now - DEFAULT_FROM_IN_MILLISECONDS, - to: now, + from: new Date(now - DEFAULT_FROM_IN_MILLISECONDS).toISOString(), + to: new Date(now).toISOString(), }; }; @@ -63,16 +63,25 @@ export const useUnifiedSearch = () => { const onSubmit = (params?: HostsSearchPayload) => setSearch(params ?? {}); - const getDateRangeAsTimestamp = useCallback(() => { + const getParsedDateRange = useCallback(() => { const defaults = getDefaultTimestamps(); - const from = DateMath.parse(searchCriteria.dateRange.from)?.valueOf() ?? defaults.from; + const from = DateMath.parse(searchCriteria.dateRange.from)?.toISOString() ?? defaults.from; const to = - DateMath.parse(searchCriteria.dateRange.to, { roundUp: true })?.valueOf() ?? defaults.to; + DateMath.parse(searchCriteria.dateRange.to, { roundUp: true })?.toISOString() ?? defaults.to; return { from, to }; }, [searchCriteria.dateRange]); + const getDateRangeAsTimestamp = useCallback(() => { + const parsedDate = getParsedDateRange(); + + const from = new Date(parsedDate.from).getTime(); + const to = new Date(parsedDate.to).getTime(); + + return { from, to }; + }, [getParsedDateRange]); + const buildQuery = useCallback(() => { return buildEsQuery(dataView, searchCriteria.query, [ ...searchCriteria.filters, @@ -116,15 +125,16 @@ export const useUnifiedSearch = () => { // Track telemetry event on query/filter/date changes useEffect(() => { - const dateRangeTimestamp = getDateRangeAsTimestamp(); + const parsedDateRange = getDateRangeAsTimestamp(); telemetry.reportHostsViewQuerySubmitted( - buildQuerySubmittedPayload({ ...searchCriteria, dateRangeTimestamp }) + buildQuerySubmittedPayload({ ...searchCriteria, parsedDateRange }) ); }, [getDateRangeAsTimestamp, searchCriteria, telemetry]); return { buildQuery, onSubmit, + getParsedDateRange, getDateRangeAsTimestamp, searchCriteria, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts index 861f3c26472e8..bae9f2ed3f713 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts @@ -13,11 +13,13 @@ import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import { enumeration } from '@kbn/securitysolution-io-ts-types'; import { FilterStateStore } from '@kbn/es-query'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; import { useUrlState } from '../../../../utils/use_url_state'; import { useKibanaTimefilterTime, useSyncKibanaTimeFilterTime, } from '../../../../hooks/use_kibana_timefilter_time'; +import { DEFAULT_HOST_LIMIT, LOCAL_STORAGE_HOST_LIMIT_KEY } from '../constants'; const DEFAULT_QUERY = { language: 'kuery', @@ -32,6 +34,7 @@ const INITIAL_HOSTS_STATE: HostsState = { filters: [], panelFilters: [], dateRange: INITIAL_DATE_RANGE, + limit: DEFAULT_HOST_LIMIT, }; const reducer = (prevState: HostsState, params: HostsSearchPayload) => { @@ -45,9 +48,17 @@ const reducer = (prevState: HostsState, params: HostsSearchPayload) => { export const useHostsUrlState = (): [HostsState, HostsStateUpdater] => { const [getTime] = useKibanaTimefilterTime(INITIAL_DATE_RANGE); + const [localStorageHostLimit, setLocalStorageHostLimit] = useLocalStorage( + LOCAL_STORAGE_HOST_LIMIT_KEY, + INITIAL_HOSTS_STATE.limit + ); const [urlState, setUrlState] = useUrlState({ - defaultState: { ...INITIAL_HOSTS_STATE, dateRange: getTime() }, + defaultState: { + ...INITIAL_HOSTS_STATE, + dateRange: getTime(), + limit: localStorageHostLimit ?? INITIAL_HOSTS_STATE.limit, + }, decodeUrlState, encodeUrlState, urlStateKey: '_a', @@ -57,6 +68,9 @@ export const useHostsUrlState = (): [HostsState, HostsStateUpdater] => { const [search, setSearch] = useReducer(reducer, urlState); if (!deepEqual(search, urlState)) { setUrlState(search); + if (localStorageHostLimit !== search.limit) { + setLocalStorageHostLimit(search.limit); + } } useSyncKibanaTimeFilterTime(INITIAL_DATE_RANGE, urlState.dateRange, (dateRange) => @@ -110,6 +124,7 @@ const HostsStateRT = rt.type({ panelFilters: HostsFiltersRT, query: HostsQueryStateRT, dateRange: StringDateRangeRT, + limit: rt.number, }); export type HostsState = rt.TypeOf; @@ -118,6 +133,7 @@ export type HostsSearchPayload = Partial; export type HostsStateUpdater = (params: HostsSearchPayload) => void; +export type StringDateRange = rt.TypeOf; export interface StringDateRangeTimestamp { from: number; to: number; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts index 6b948fb0da6c9..080b47f54d4da 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/types.ts @@ -7,7 +7,7 @@ import { Filter } from '@kbn/es-query'; import { ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils'; -import { ALERT_STATUS_ALL } from './constants'; +import { ALERT_STATUS_ALL, HOST_LIMIT_OPTIONS } from './constants'; export type AlertStatus = | typeof ALERT_STATUS_ACTIVE @@ -19,3 +19,5 @@ export interface AlertStatusFilter { query?: Filter['query']; label: string; } + +export type HostLimitOptions = typeof HOST_LIMIT_OPTIONS[number]; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/utils.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/utils.ts index a04fdfa46b279..5da9d36b0f587 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/utils.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/utils.ts @@ -5,16 +5,23 @@ * 2.0. */ -import { Filter } from '@kbn/es-query'; -import { SnapshotNode } from '../../../../common/http_api'; +import { DataViewBase, Filter } from '@kbn/es-query'; -export const createHostsFilter = (hostNodes: SnapshotNode[]): Filter => { +export const createHostsFilter = (hostNames: string[], dataView?: DataViewBase): Filter => { return { query: { terms: { - 'host.name': hostNodes.map((p) => p.name), + 'host.name': hostNames, }, }, - meta: {}, + meta: dataView + ? { + value: hostNames.join(), + type: 'phrases', + params: hostNames, + index: dataView.id, + key: 'host.name', + } + : {}, }; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx index 6fca2e5ced98a..373a6a563fee9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx @@ -181,7 +181,7 @@ export const Layout = React.memo( - {!hostsLinkClickedRef.current && ( + {!hostsLinkClickedRef.current && nodeType === 'host' && ( { + // mount callback should not use setup dependencies, get start dependencies instead + const [coreStart, plugins, pluginStart] = await core.getStartServices(); + + const { renderApp } = await import('./apps/discover_app'); + + return renderApp(coreStart, plugins, pluginStart, params); }, - ], - category: DEFAULT_APP_CATEGORIES.observability, - mount: async (params: AppMountParameters) => { - // mount callback should not use setup dependencies, get start dependencies instead - const [coreStart, pluginsStart, pluginStart] = await core.getStartServices(); - const { renderApp } = await import('./apps/logs_app'); + }); + } - return renderApp(coreStart, pluginsStart, pluginStart, params); - }, - }); + if (this.config.logs.app_target === 'logs-ui') { + core.application.register({ + id: 'logs', + title: i18n.translate('xpack.infra.logs.pluginTitle', { + defaultMessage: 'Logs', + }), + euiIconType: 'logoObservability', + order: 8100, + appRoute: '/app/logs', + // !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/logs/page_content.tsx + deepLinks: [ + { + id: 'stream', + title: i18n.translate('xpack.infra.logs.index.streamTabTitle', { + defaultMessage: 'Stream', + }), + path: '/stream', + }, + { + id: 'anomalies', + title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { + defaultMessage: 'Anomalies', + }), + path: '/anomalies', + }, + { + id: 'log-categories', + title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { + defaultMessage: 'Categories', + }), + path: '/log-categories', + }, + { + id: 'settings', + title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', { + defaultMessage: 'Settings', + }), + path: '/settings', + }, + ], + category: DEFAULT_APP_CATEGORIES.observability, + mount: async (params: AppMountParameters) => { + // mount callback should not use setup dependencies, get start dependencies instead + const [coreStart, plugins, pluginStart] = await core.getStartServices(); + + const { renderApp } = await import('./apps/logs_app'); + return renderApp(coreStart, plugins, pluginStart, params); + }, + }); + } // !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/metrics/index.tsx const infraDeepLinks = [ diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_client.mock.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_client.mock.ts index 83e8fc2420440..298424f5c9db6 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_client.mock.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_client.mock.ts @@ -10,4 +10,6 @@ import { ITelemetryClient } from './types'; export const createTelemetryClientMock = (): jest.Mocked => ({ reportHostEntryClicked: jest.fn(), reportHostsViewQuerySubmitted: jest.fn(), + reportHostFlyoutFilterRemoved: jest.fn(), + reportHostFlyoutFilterAdded: jest.fn(), }); diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts index 66ee3220c2935..f53a6b298de1d 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_client.ts @@ -8,6 +8,7 @@ import { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import { HostEntryClickedParams, + HostFlyoutFilterActionParams, HostsViewQuerySubmittedParams, InfraTelemetryEventTypes, ITelemetryClient, @@ -30,6 +31,22 @@ export class TelemetryClient implements ITelemetryClient { }); }; + public reportHostFlyoutFilterRemoved = ({ + field_name: fieldName, + }: HostFlyoutFilterActionParams) => { + this.analytics.reportEvent(InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_REMOVED, { + field_name: fieldName, + }); + }; + + public reportHostFlyoutFilterAdded = ({ + field_name: fieldName, + }: HostFlyoutFilterActionParams) => { + this.analytics.reportEvent(InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_ADDED, { + field_name: fieldName, + }); + }; + public reportHostsViewQuerySubmitted = (params: HostsViewQuerySubmittedParams) => { this.analytics.reportEvent(InfraTelemetryEventTypes.HOSTS_VIEW_QUERY_SUBMITTED, params); }; diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts index ee7545022d9ee..597c9c56eacfd 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts @@ -66,4 +66,34 @@ const hostsEntryClickedEvent: InfraTelemetryEvent = { }, }; -export const infraTelemetryEvents = [hostsViewQuerySubmittedEvent, hostsEntryClickedEvent]; +const hostFlyoutRemoveFilter: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_REMOVED, + schema: { + field_name: { + type: 'keyword', + _meta: { + description: 'Removed filter field name for the selected host.', + optional: false, + }, + }, + }, +}; +const hostFlyoutAddFilter: InfraTelemetryEvent = { + eventType: InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_ADDED, + schema: { + field_name: { + type: 'keyword', + _meta: { + description: 'Added filter field name for the selected host.', + optional: false, + }, + }, + }, +}; + +export const infraTelemetryEvents = [ + hostsViewQuerySubmittedEvent, + hostsEntryClickedEvent, + hostFlyoutRemoveFilter, + hostFlyoutAddFilter, +]; diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts index d3516fc84600b..6fdd2105ec2df 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts @@ -49,6 +49,8 @@ describe('TelemetryService', () => { const telemetry = service.start(); expect(telemetry).toHaveProperty('reportHostEntryClicked'); + expect(telemetry).toHaveProperty('reportHostFlyoutFilterRemoved'); + expect(telemetry).toHaveProperty('reportHostFlyoutFilterAdded'); expect(telemetry).toHaveProperty('reportHostsViewQuerySubmitted'); }); }); @@ -74,7 +76,7 @@ describe('TelemetryService', () => { ); }); - it('should report hosts entry click with cloud provider equal to "unknow" if not exist', async () => { + it('should report hosts entry click with cloud provider equal to "unknown" if not exist', async () => { const setupParams = getSetupParams(); service.setup(setupParams); const telemetry = service.start(); @@ -119,4 +121,44 @@ describe('TelemetryService', () => { ); }); }); + + describe('#reportHostFlyoutFilterRemoved', () => { + it('should report Host Flyout Filter Removed click with field name', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + + telemetry.reportHostFlyoutFilterRemoved({ + field_name: 'agent.version', + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_REMOVED, + { + field_name: 'agent.version', + } + ); + }); + }); + + describe('#reportHostFlyoutFilterAdded', () => { + it('should report Host Flyout Filter Added click with field name', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + + telemetry.reportHostFlyoutFilterAdded({ + field_name: 'agent.version', + }); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_ADDED, + { + field_name: 'agent.version', + } + ); + }); + }); }); diff --git a/x-pack/plugins/infra/public/services/telemetry/types.ts b/x-pack/plugins/infra/public/services/telemetry/types.ts index a24f64e4c5f4a..f0f64ff00c918 100644 --- a/x-pack/plugins/infra/public/services/telemetry/types.ts +++ b/x-pack/plugins/infra/public/services/telemetry/types.ts @@ -15,6 +15,8 @@ export interface TelemetryServiceSetupParams { export enum InfraTelemetryEventTypes { HOSTS_VIEW_QUERY_SUBMITTED = 'Hosts View Query Submitted', HOSTS_ENTRY_CLICKED = 'Host Entry Clicked', + HOST_FLYOUT_FILTER_REMOVED = 'Host Flyout Filter Removed', + HOST_FLYOUT_FILTER_ADDED = 'Host Flyout Filter Added', } export interface HostsViewQuerySubmittedParams { @@ -29,10 +31,19 @@ export interface HostEntryClickedParams { cloud_provider?: string | null; } -export type InfraTelemetryEventParams = HostsViewQuerySubmittedParams | HostEntryClickedParams; +export interface HostFlyoutFilterActionParams { + field_name: string; +} + +export type InfraTelemetryEventParams = + | HostsViewQuerySubmittedParams + | HostEntryClickedParams + | HostFlyoutFilterActionParams; export interface ITelemetryClient { reportHostEntryClicked(params: HostEntryClickedParams): void; + reportHostFlyoutFilterRemoved(params: HostFlyoutFilterActionParams): void; + reportHostFlyoutFilterAdded(params: HostFlyoutFilterActionParams): void; reportHostsViewQuerySubmitted(params: HostsViewQuerySubmittedParams): void; } @@ -41,6 +52,14 @@ export type InfraTelemetryEvent = eventType: InfraTelemetryEventTypes.HOSTS_VIEW_QUERY_SUBMITTED; schema: RootSchema; } + | { + eventType: InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_ADDED; + schema: RootSchema; + } + | { + eventType: InfraTelemetryEventTypes.HOST_FLYOUT_FILTER_REMOVED; + schema: RootSchema; + } | { eventType: InfraTelemetryEventTypes.HOSTS_ENTRY_CLICKED; schema: RootSchema; diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index 0b961e0b8ed36..9d5cc8abbfbe1 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -34,9 +34,10 @@ import type { // import type { OsqueryPluginStart } from '../../osquery/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { type TypedLensByValueInput, LensPublicStart } from '@kbn/lens-plugin/public'; +import { LensPublicStart } from '@kbn/lens-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { CasesUiStart } from '@kbn/cases-plugin/public'; +import { DiscoverStart } from '@kbn/discover-plugin/public'; import type { UnwrapPromise } from '../common/utility_types'; import type { SourceProviderProps, @@ -79,6 +80,7 @@ export interface InfraClientStartDeps { charts: ChartsPluginStart; data: DataPublicPluginStart; dataViews: DataViewsPublicPluginStart; + discover: DiscoverStart; embeddable?: EmbeddableStart; lens: LensPublicStart; ml: MlPluginStart; @@ -112,12 +114,6 @@ export interface InfraHttpError extends IHttpFetchError { }; } -export type LensAttributes = TypedLensByValueInput['attributes']; - -export interface LensOptions { - breakdownSize: number; -} - export interface ExecutionTimeRange { gte: number; lte: number; diff --git a/x-pack/plugins/infra/server/infra_server.ts b/x-pack/plugins/infra/server/infra_server.ts index 18f6c943f234b..3b6ea0333f236 100644 --- a/x-pack/plugins/infra/server/infra_server.ts +++ b/x-pack/plugins/infra/server/infra_server.ts @@ -8,6 +8,7 @@ import { InfraBackendLibs } from './lib/infra_types'; import { initGetHostsAnomaliesRoute, initGetK8sAnomaliesRoute } from './routes/infra_ml'; import { initInventoryMetaRoute } from './routes/inventory_metadata'; +import { initInventoryViewRoutes } from './routes/inventory_views'; import { initIpToHostName } from './routes/ip_to_hostname'; import { initGetLogAlertsChartPreviewDataRoute } from './routes/log_alerts'; import { @@ -35,6 +36,7 @@ import { initNodeDetailsRoute } from './routes/node_details'; import { initOverviewRoute } from './routes/overview'; import { initProcessListRoute } from './routes/process_list'; import { initSnapshotRoute } from './routes/snapshot'; +import { initInfraMetricsRoute } from './routes/infra'; export const initInfraServer = (libs: InfraBackendLibs) => { initIpToHostName(libs); @@ -60,7 +62,9 @@ export const initInfraServer = (libs: InfraBackendLibs) => { initMetricsAPIRoute(libs); initMetadataRoute(libs); initInventoryMetaRoute(libs); + initInventoryViewRoutes(libs); initGetLogAlertsChartPreviewDataRoute(libs); initProcessListRoute(libs); initOverviewRoute(libs); + initInfraMetricsRoute(libs); }; diff --git a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts index 9a7c9d2764486..6bc48083d252b 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts @@ -138,7 +138,7 @@ export const buildNoDataAlertReason: (alertResult: { timeUnit: string; }) => string = ({ group, metric, timeSize, timeUnit }) => i18n.translate('xpack.infra.metrics.alerting.threshold.noDataAlertReason', { - defaultMessage: '{metric} reported no data in the last {interval} for {group}', + defaultMessage: '{metric} reported no data in the last {interval}{group}', values: { metric, interval: `${timeSize}${timeUnit}`, diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts index de3ef3cf68d0c..0860da5c7a184 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -99,6 +99,9 @@ const createMockStaticConfiguration = (sources: any) => ({ inventory: { compositeSize: 2000, }, + logs: { + app_target: 'logs-ui', + }, sources, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index bd033b5285b3e..d0eb8603ee69c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ALERT_REASON, ALERT_ACTION_GROUP } from '@kbn/rule-data-utils'; +import { ALERT_REASON, ALERT_ACTION_GROUP, ALERT_EVALUATION_VALUES } from '@kbn/rule-data-utils'; import { first, get } from 'lodash'; import { ActionGroup, @@ -65,8 +65,7 @@ type InventoryMetricThresholdAlertFactory = ( reason: string, actionGroup: InventoryThrehsoldActionGroup, additionalContext?: AdditionalContext | null, - threshold?: number | undefined, - value?: number | undefined + evaluationValues?: Array ) => InventoryMetricThresholdAlert; export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => @@ -109,13 +108,15 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = id, reason, actionGroup, - additionalContext + additionalContext, + evaluationValues ) => alertWithLifecycle({ id, fields: { [ALERT_REASON]: reason, [ALERT_ACTION_GROUP]: actionGroup, + [ALERT_EVALUATION_VALUES]: evaluationValues, ...flattenAdditionalContext(additionalContext), }, }); @@ -243,7 +244,18 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = new Set([...(additionalContext.tags ?? []), ...ruleTags]) ); - const alert = alertFactory(group, reason, actionGroupId, additionalContext); + const evaluationValues = results.reduce((acc: Array, result) => { + acc.push(result[group].currentValue); + return acc; + }, []); + + const alert = alertFactory( + group, + reason, + actionGroupId, + additionalContext, + evaluationValues + ); const indexedStartedDate = getAlertStartedDate(group) ?? startedAt.toISOString(); const alertUuid = getAlertUuid(group); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index a24a02b2df5e8..fb8121d224187 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -34,6 +34,7 @@ import { import { Evaluation } from './lib/evaluate_rule'; import type { LogMeta, Logger } from '@kbn/logging'; import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; +import { InfraConfig } from '../../../../common/plugin_config_types'; jest.mock('./lib/evaluate_rule', () => ({ evaluateRule: jest.fn() })); @@ -69,9 +70,11 @@ const logger = { get: () => logger, } as unknown as Logger; +const STARTED_AT_MOCK_DATE = new Date(); + const mockOptions = { executionId: '', - startedAt: new Date(), + startedAt: STARTED_AT_MOCK_DATE, previousStartedAt: null, state: { wrapped: initialRuleState, @@ -127,7 +130,8 @@ const setEvaluationResults = (response: Array>) => { jest.requireMock('./lib/evaluate_rule').evaluateRule.mockImplementation(() => response); }; -describe('The metric threshold alert type', () => { +// FAILING: https://github.com/elastic/kibana/issues/155534 +describe.skip('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { afterAll(() => clearInstances()); const instanceID = '*'; @@ -1325,7 +1329,9 @@ describe('The metric threshold alert type', () => { }, ]); await execute(true); - expect(mostRecentAction(instanceID)).toBeNoDataAction(); + const recentAction = mostRecentAction(instanceID); + expect(recentAction.action.reason).toEqual('test.metric.3 reported no data in the last 1m'); + expect(recentAction).toBeNoDataAction(); }); test('does not send a No Data alert when not configured to do so', async () => { setEvaluationResults([ @@ -1349,6 +1355,68 @@ describe('The metric threshold alert type', () => { }); }); + describe('alerts with NO_DATA where one condtion is an aggregation and the other is a document count', () => { + afterAll(() => clearInstances()); + const instanceID = '*'; + const execute = (alertOnNoData: boolean, sourceId: string = 'default') => + executor({ + ...mockOptions, + services, + params: { + sourceId, + criteria: [ + { + ...baseNonCountCriterion, + comparator: Comparator.GT, + threshold: [1], + metric: 'test.metric.3', + }, + { + ...baseCountCriterion, + comparator: Comparator.GT, + threshold: [30], + }, + ], + alertOnNoData, + }, + }); + test('sends a No Data alert when configured to do so', async () => { + setEvaluationResults([ + { + '*': { + ...baseNonCountCriterion, + comparator: Comparator.LT, + threshold: [1], + metric: 'test.metric.3', + currentValue: null, + timestamp: STARTED_AT_MOCK_DATE.toISOString(), + shouldFire: false, + shouldWarn: false, + isNoData: true, + bucketKey: { groupBy0: '*' }, + }, + }, + {}, + ]); + await execute(true); + const recentAction = mostRecentAction(instanceID); + expect(recentAction.action).toEqual({ + alertDetailsUrl: 'http://localhost:5601/app/observability/alerts/mock-alert-uuid', + alertState: 'NO DATA', + group: '*', + groupByKeys: undefined, + metric: { condition0: 'test.metric.3', condition1: 'count' }, + reason: 'test.metric.3 reported no data in the last 1m', + threshold: { condition0: ['1'], condition1: [30] }, + timestamp: STARTED_AT_MOCK_DATE.toISOString(), + value: { condition0: '[NO DATA]', condition1: 0 }, + viewInAppUrl: 'http://localhost:5601/app/metrics/explorer', + tags: [], + }); + expect(recentAction).toBeNoDataAction(); + }); + }); + describe('querying a groupBy alert that starts reporting no data, and then later reports data', () => { afterAll(() => clearInstances()); const instanceID = '*'; @@ -1818,7 +1886,7 @@ describe('The metric threshold alert type', () => { }); }); -const createMockStaticConfiguration = (sources: any) => ({ +const createMockStaticConfiguration = (sources: any): InfraConfig => ({ alerting: { inventory_threshold: { group_by_page_size: 100, @@ -1830,6 +1898,9 @@ const createMockStaticConfiguration = (sources: any) => ({ inventory: { compositeSize: 2000, }, + logs: { + app_target: 'logs-ui', + }, sources, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 28a32a8c46174..46d2766777d21 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ALERT_ACTION_GROUP, ALERT_REASON } from '@kbn/rule-data-utils'; +import { ALERT_ACTION_GROUP, ALERT_EVALUATION_VALUES, ALERT_REASON } from '@kbn/rule-data-utils'; import { isEqual } from 'lodash'; import { ActionGroupIdsOf, @@ -51,8 +51,8 @@ export type MetricThresholdRuleTypeState = RuleTypeState & { groupBy?: string | string[]; filterQuery?: string; }; -export type MetricThresholdAlertState = AlertState; // no specific instace state used -export type MetricThresholdAlertContext = AlertContext; // no specific instace state used +export type MetricThresholdAlertState = AlertState; // no specific instance state used +export type MetricThresholdAlertContext = AlertContext; // no specific instance state used export const FIRED_ACTIONS_ID = 'metrics.threshold.fired'; export const WARNING_ACTIONS_ID = 'metrics.threshold.warning'; @@ -79,8 +79,7 @@ type MetricThresholdAlertFactory = ( reason: string, actionGroup: MetricThresholdActionGroup, additionalContext?: AdditionalContext | null, - threshold?: number | undefined, - value?: number | undefined + evaluationValues?: Array ) => MetricThresholdAlert; export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => @@ -117,13 +116,15 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => id, reason, actionGroup, - additionalContext + additionalContext, + evaluationValues ) => alertWithLifecycle({ id, fields: { [ALERT_REASON]: reason, [ALERT_ACTION_GROUP]: actionGroup, + [ALERT_EVALUATION_VALUES]: evaluationValues, ...flattenAdditionalContext(additionalContext), }, }); @@ -268,7 +269,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => // check to see if a No Data state has occurred if (nextState === AlertStates.NO_DATA) { reason = alertResults - .filter((result) => result[group].isNoData) + .filter((result) => result[group]?.isNoData) .map((result) => buildNoDataAlertReason({ ...result[group], group })) .join('\n'); } @@ -295,7 +296,18 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => new Set([...(additionalContext.tags ?? []), ...options.rule.tags]) ); - const alert = alertFactory(`${group}`, reason, actionGroupId, additionalContext); + const evaluationValues = alertResults.reduce((acc: Array, result) => { + acc.push(result[group].currentValue); + return acc; + }, []); + + const alert = alertFactory( + `${group}`, + reason, + actionGroupId, + additionalContext, + evaluationValues + ); const alertUuid = getAlertUuid(group); scheduledActionsCount++; @@ -304,17 +316,30 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => alertState: stateToAlertMessage[nextState], group, groupByKeys: groupByKeysObjectMapping[group], - metric: mapToConditionsLookup(criteria, (c) => c.metric), + metric: mapToConditionsLookup(criteria, (c) => { + if (c.aggType === 'count') { + return 'count'; + } + return c.metric; + }), reason, - threshold: mapToConditionsLookup( - alertResults, - (result) => formatAlertResult(result[group]).threshold - ), + threshold: mapToConditionsLookup(alertResults, (result, index) => { + const evaluation = result[group]; + if (!evaluation) { + return criteria[index].threshold; + } + return formatAlertResult(evaluation).threshold; + }), timestamp, - value: mapToConditionsLookup( - alertResults, - (result) => formatAlertResult(result[group]).currentValue - ), + value: mapToConditionsLookup(alertResults, (result, index) => { + const evaluation = result[group]; + if (!evaluation && criteria[index].aggType === 'count') { + return 0; + } else if (!evaluation) { + return null; + } + return formatAlertResult(evaluation).currentValue; + }), viewInAppUrl: getViewInMetricsAppUrl(libs.basePath, spaceId), ...additionalContext, }); @@ -342,7 +367,12 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => alertState: stateToAlertMessage[AlertStates.OK], group: recoveredAlertId, groupByKeys: groupByKeysObjectForRecovered[recoveredAlertId], - metric: mapToConditionsLookup(criteria, (c) => c.metric), + metric: mapToConditionsLookup(criteria, (c) => { + if (criteria.aggType === 'count') { + return 'count'; + } + return c.metric; + }), timestamp: startedAt.toISOString(), threshold: mapToConditionsLookup(criteria, (c) => c.threshold), viewInAppUrl: getViewInMetricsAppUrl(libs.basePath, spaceId), diff --git a/x-pack/plugins/infra/server/lib/sources/sources.test.ts b/x-pack/plugins/infra/server/lib/sources/sources.test.ts index 22e6d4912a402..a8d78d1ba09f1 100644 --- a/x-pack/plugins/infra/server/lib/sources/sources.test.ts +++ b/x-pack/plugins/infra/server/lib/sources/sources.test.ts @@ -6,6 +6,7 @@ */ import { SavedObject } from '@kbn/core/server'; +import { InfraConfig } from '../../types'; import { infraSourceConfigurationSavedObjectName } from './saved_object_type'; import { InfraSources } from './sources'; @@ -108,7 +109,7 @@ describe('the InfraSources lib', () => { }); }); -const createMockStaticConfiguration = (sources: any) => ({ +const createMockStaticConfiguration = (sources: any): InfraConfig => ({ alerting: { inventory_threshold: { group_by_page_size: 10000, @@ -117,10 +118,12 @@ const createMockStaticConfiguration = (sources: any) => ({ group_by_page_size: 10000, }, }, - enabled: true, inventory: { compositeSize: 2000, }, + logs: { + app_target: 'logs-ui', + }, sources, }); diff --git a/x-pack/plugins/infra/server/mocks.ts b/x-pack/plugins/infra/server/mocks.ts index 5b587a1fe80d5..5a97f4a7d9a52 100644 --- a/x-pack/plugins/infra/server/mocks.ts +++ b/x-pack/plugins/infra/server/mocks.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { createInventoryViewsServiceStartMock } from './services/inventory_views/inventory_views_service.mock'; import { createLogViewsServiceSetupMock, createLogViewsServiceStartMock, @@ -23,6 +24,7 @@ const createInfraSetupMock = () => { const createInfraStartMock = () => { const infraStartMock: jest.Mocked = { getMetricIndices: jest.fn(), + inventoryViews: createInventoryViewsServiceStartMock(), logViews: createLogViewsServiceStartMock(), }; return infraStartMock; diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 0fb0aad42d0cc..2c114bb75d6e5 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -20,8 +20,6 @@ import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; import { defaultLogViewsStaticConfig } from '../common/log_views'; import { publicConfigKeys } from '../common/plugin_config_types'; -import { inventoryViewSavedObjectType } from '../common/saved_objects/inventory_view'; -import { metricsExplorerViewSavedObjectType } from '../common/saved_objects/metrics_explorer_view'; import { configDeprecations, getInfraDeprecationsFactory } from './deprecations'; import { LOGS_FEATURE, METRICS_FEATURE } from './features'; import { initInfraServer } from './infra_server'; @@ -43,7 +41,12 @@ import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types'; import { makeGetMetricIndices } from './lib/metrics/make_get_metric_indices'; import { infraSourceConfigurationSavedObjectType, InfraSources } from './lib/sources'; import { InfraSourceStatus } from './lib/source_status'; -import { logViewSavedObjectType } from './saved_objects'; +import { + inventoryViewSavedObjectType, + logViewSavedObjectType, + metricsExplorerViewSavedObjectType, +} from './saved_objects'; +import { InventoryViewsService } from './services/inventory_views'; import { LogEntriesService } from './services/log_entries'; import { LogViewsService } from './services/log_views'; import { RulesService } from './services/rules'; @@ -58,6 +61,11 @@ import { UsageCollector } from './usage/usage_collector'; export const config: PluginConfigDescriptor = { schema: schema.object({ + logs: schema.object({ + app_target: schema.oneOf([schema.literal('logs-ui'), schema.literal('discover')], { + defaultValue: 'logs-ui', + }), + }), alerting: schema.object({ inventory_threshold: schema.object({ group_by_page_size: schema.number({ defaultValue: 5_000 }), @@ -112,6 +120,7 @@ export class InfraServerPlugin private logsRules: RulesService; private metricsRules: RulesService; + private inventoryViews: InventoryViewsService; private logViews: LogViewsService; constructor(context: PluginInitializerContext) { @@ -129,6 +138,7 @@ export class InfraServerPlugin this.logger.get('metricsRules') ); + this.inventoryViews = new InventoryViewsService(this.logger.get('inventoryViews')); this.logViews = new LogViewsService(this.logger.get('logViews')); } @@ -143,6 +153,7 @@ export class InfraServerPlugin sources, } ); + const inventoryViews = this.inventoryViews.setup(); const logViews = this.logViews.setup(); // register saved object types @@ -224,11 +235,17 @@ export class InfraServerPlugin return { defineInternalSourceConfiguration: sources.defineInternalSourceConfiguration.bind(sources), + inventoryViews, logViews, } as InfraPluginSetup; } start(core: CoreStart, plugins: InfraServerPluginStartDeps) { + const inventoryViews = this.inventoryViews.start({ + infraSources: this.libs.sources, + savedObjects: core.savedObjects, + }); + const logViews = this.logViews.start({ infraSources: this.libs.sources, savedObjects: core.savedObjects, @@ -242,6 +259,7 @@ export class InfraServerPlugin }); return { + inventoryViews, logViews, getMetricIndices: makeGetMetricIndices(this.libs.sources), }; diff --git a/x-pack/plugins/infra/server/routes/infra/README.md b/x-pack/plugins/infra/server/routes/infra/README.md new file mode 100644 index 0000000000000..50a65ce9c89ba --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/README.md @@ -0,0 +1,115 @@ +# Infra Hosts API + +This API returns a list of hosts and their metrics. + +**POST /api/metrics/infra** +parameters: + +- type: asset type. 'host' is the only one supported now +- metrics: list of metrics to be calculated and returned for each host +- sourceId: sourceId to retrieve configuration such as index-pattern used to query the results +- limit: max number of hosts - max 500 +- timeRange: time range object containing start and end attributes - passed in timestamp +- (optional) query: filter + +The response includes: + +- hosts: array of metrics and metadata +- metrics: object containing name of the metric and value +- metadata: object containing name of the metadata and value + +## Examples + +Request + +```bash +curl --location -u elastic:changeme 'http://0.0.0.0:5601/ftw/api/metrics/infra' \ +--header 'kbn-xsrf: xxxx' \ +--header 'Content-Type: application/json' \ +--data '{ + "type": 'host', + "limit": 100, + "metrics": [ + { + "type": "rx" + }, + { + "type": "tx" + }, + { + "type": "memory" + }, + { + "type": "cpu" + }, + { + "type": "diskLatency" + }, + { + "type": "memoryTotal" + } + ], + "query": { + "bool": { + "must": [], + "filter": [], + "should": [], + "must_not": [] + } + }, + "range": { + "from": "2023-04-18T11:15:31.407Z", + "to": "2023-04-18T11:30:31.407Z" + }, + "sourceId": "default" +}' +``` + +Response + +```json +{ + "type": "host", + "nodes":[ + { + "metadata":[ + { + "name":"host.os.name", + "value":null + }, + { + "name":"cloud.provider", + "value":null + } + ], + "metrics":[ + { + "name":"cpu", + "value":0.13271302652800487 + }, + { + "name":"diskLatency", + "value":0 + }, + { + "name":"memory", + "value":0.542838307852529 + }, + { + "name":"memoryTotal", + "value":66640704.099216014 + }, + { + "name":"rx", + "value":3959.4930095127706 + }, + { + "name":"tx", + "value":100.26926542816672 + } + ], + "name":"host-0" + } + ] +} +``` diff --git a/x-pack/plugins/infra/server/routes/infra/index.ts b/x-pack/plugins/infra/server/routes/infra/index.ts new file mode 100644 index 0000000000000..d98e8034e6207 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/index.ts @@ -0,0 +1,62 @@ +/* + * 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 Boom from '@hapi/boom'; +import { createRouteValidationFunction } from '@kbn/io-ts-utils'; + +import { + GetInfraMetricsRequestBodyPayloadRT, + GetInfraMetricsRequestBodyPayload, + GetInfraMetricsResponsePayloadRT, +} from '../../../common/http_api/infra'; +import { InfraBackendLibs } from '../../lib/infra_types'; +import { getHosts } from './lib/host/get_hosts'; + +export const initInfraMetricsRoute = (libs: InfraBackendLibs) => { + const validateBody = createRouteValidationFunction(GetInfraMetricsRequestBodyPayloadRT); + + const { framework } = libs; + + framework.registerRoute( + { + method: 'post', + path: '/api/metrics/infra', + validate: { + body: validateBody, + }, + }, + async (_, request, response) => { + const [{ savedObjects }, { data }] = await libs.getStartServices(); + const params: GetInfraMetricsRequestBodyPayload = request.body; + + try { + const searchClient = data.search.asScoped(request); + const soClient = savedObjects.getScopedClient(request); + const source = await libs.sources.getSourceConfiguration(soClient, params.sourceId); + + const hosts = await getHosts({ searchClient, sourceConfig: source.configuration, params }); + return response.ok({ + body: GetInfraMetricsResponsePayloadRT.encode(hosts), + }); + } catch (err) { + if (Boom.isBoom(err)) { + return response.customError({ + statusCode: err.output.statusCode, + body: { message: err.output.payload.message }, + }); + } + + return response.customError({ + statusCode: err.statusCode ?? 500, + body: { + message: err.message ?? 'An unexpected error occurred', + }, + }); + } + } + ); +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/constants.ts b/x-pack/plugins/infra/server/routes/infra/lib/constants.ts new file mode 100644 index 0000000000000..f39eaafdd039e --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/constants.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 { estypes } from '@elastic/elasticsearch'; + +export const BUCKET_KEY = 'host.name'; +export const METADATA_AGGREGATION_NAME = 'metadata'; +export const FILTER_AGGREGATION_SUB_AGG_NAME = 'result'; +export const INVENTORY_MODEL_NODE_TYPE = 'host'; + +export const MAX_SIZE = 500; + +export const METADATA_AGGREGATION: Record = { + [METADATA_AGGREGATION_NAME]: { + top_metrics: { + metrics: [ + { + field: 'host.os.name', + }, + { + field: 'cloud.provider', + }, + { + field: 'host.ip', + }, + ], + size: 1, + sort: { + '@timestamp': 'desc', + }, + }, + }, +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/helpers/query.ts b/x-pack/plugins/infra/server/routes/infra/lib/helpers/query.ts new file mode 100644 index 0000000000000..30a65333987fb --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/helpers/query.ts @@ -0,0 +1,100 @@ +/* + * 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 { estypes } from '@elastic/elasticsearch'; +import { ISearchClient } from '@kbn/data-plugin/common'; +import { ESSearchRequest } from '@kbn/es-types'; +import { catchError, map, Observable } from 'rxjs'; +import { findInventoryModel } from '../../../../../common/inventory_models'; +import { + GetInfraMetricsRequestBodyPayload, + InfraAssetMetricType, +} from '../../../../../common/http_api/infra'; +import { INVENTORY_MODEL_NODE_TYPE } from '../constants'; + +export const createFilters = ({ + params, + extraFilter, + hostNamesShortList = [], +}: { + params: GetInfraMetricsRequestBodyPayload; + hostNamesShortList?: string[]; + extraFilter?: estypes.QueryDslQueryContainer; +}) => { + const extrafilterClause = extraFilter?.bool?.filter; + const extraFilterList = !!extrafilterClause + ? Array.isArray(extrafilterClause) + ? extrafilterClause + : [extrafilterClause] + : []; + + const hostNamesFilter = + hostNamesShortList.length > 0 + ? [ + { + terms: { + 'host.name': hostNamesShortList, + }, + }, + ] + : []; + + return [ + ...hostNamesFilter, + ...extraFilterList, + { + range: { + '@timestamp': { + gte: new Date(params.range.from).getTime(), + lte: new Date(params.range.to).getTime(), + format: 'epoch_millis', + }, + }, + }, + { + exists: { + field: 'host.name', + }, + }, + ]; +}; + +export const runQuery = ( + serchClient: ISearchClient, + queryRequest: ESSearchRequest, + decoder: (aggregation: Record | undefined) => T | undefined +): Observable => { + return serchClient + .search({ + params: queryRequest, + }) + .pipe( + map((res) => decoder(res.rawResponse.aggregations)), + catchError((err) => { + const error = { + message: err.message, + statusCode: err.statusCode, + attributes: err.errBody?.error, + }; + + throw error; + }) + ); +}; + +export const getInventoryModelAggregations = ( + metrics: InfraAssetMetricType[] +): Record => { + const inventoryModel = findInventoryModel(INVENTORY_MODEL_NODE_TYPE); + return metrics.reduce( + (acc, metric) => ({ + ...acc, + ...inventoryModel.metrics.snapshot?.[metric], + }), + {} + ); +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/host/get_all_hosts.ts b/x-pack/plugins/infra/server/routes/infra/lib/host/get_all_hosts.ts new file mode 100644 index 0000000000000..e63f0d78b367d --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/host/get_all_hosts.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { estypes } from '@elastic/elasticsearch'; +import { lastValueFrom } from 'rxjs'; +import { ESSearchRequest } from '@kbn/es-types'; +import { InfraStaticSourceConfiguration } from '../../../../lib/sources'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { GetInfraMetricsRequestBodyPayload } from '../../../../../common/http_api/infra'; +import { BUCKET_KEY, MAX_SIZE, METADATA_AGGREGATION } from '../constants'; +import { + GetHostsArgs, + HostsMetricsSearchAggregationResponse, + HostsMetricsSearchAggregationResponseRT, +} from '../types'; +import { createFilters, getInventoryModelAggregations, runQuery } from '../helpers/query'; + +export const getAllHosts = async ( + { searchClient, sourceConfig, params }: GetHostsArgs, + hostNamesShortList: string[] = [] +): Promise => { + const query = createQuery(params, sourceConfig, hostNamesShortList); + return lastValueFrom( + runQuery(searchClient, query, decodeOrThrow(HostsMetricsSearchAggregationResponseRT)) + ); +}; + +const createQuery = ( + params: GetInfraMetricsRequestBodyPayload, + sourceConfig: InfraStaticSourceConfiguration, + hostNamesShortList: string[] +): ESSearchRequest => { + const metricAggregations = getInventoryModelAggregations(params.metrics.map((p) => p.type)); + + return { + allow_no_indices: true, + ignore_unavailable: true, + index: sourceConfig.metricAlias, + body: { + size: 0, + query: { + bool: { + filter: createFilters({ + params, + hostNamesShortList, + }), + }, + }, + aggs: createAggregations(params, metricAggregations), + }, + }; +}; + +const createAggregations = ( + { limit }: GetInfraMetricsRequestBodyPayload, + metricAggregations: Record +): Record => { + return { + nodes: { + terms: { + field: BUCKET_KEY, + size: limit ?? MAX_SIZE, + order: { + _key: 'asc', + }, + }, + aggs: { + ...metricAggregations, + ...METADATA_AGGREGATION, + }, + }, + }; +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/host/get_filtered_hosts.ts b/x-pack/plugins/infra/server/routes/infra/lib/host/get_filtered_hosts.ts new file mode 100644 index 0000000000000..95b8753487cf7 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/host/get_filtered_hosts.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 { ESSearchRequest } from '@kbn/es-types'; +import { lastValueFrom } from 'rxjs'; + +import { InfraStaticSourceConfiguration } from '../../../../lib/sources'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { GetInfraMetricsRequestBodyPayload } from '../../../../../common/http_api/infra'; +import { + FilteredHostsSearchAggregationResponseRT, + FilteredHostsSearchAggregationResponse, + GetHostsArgs, +} from '../types'; +import { BUCKET_KEY, MAX_SIZE } from '../constants'; +import { assertQueryStructure } from '../utils'; +import { createFilters, runQuery } from '../helpers/query'; + +export const getFilteredHosts = async ({ + searchClient, + sourceConfig, + params, +}: GetHostsArgs): Promise => { + const query = createQuery(params, sourceConfig); + return lastValueFrom( + runQuery(searchClient, query, decodeOrThrow(FilteredHostsSearchAggregationResponseRT)) + ); +}; + +const createQuery = ( + params: GetInfraMetricsRequestBodyPayload, + sourceConfig: InfraStaticSourceConfiguration +): ESSearchRequest => { + assertQueryStructure(params.query); + + return { + allow_no_indices: true, + ignore_unavailable: true, + index: sourceConfig.metricAlias, + body: { + size: 0, + query: { + bool: { + ...params.query.bool, + filter: createFilters({ params, extraFilter: params.query }), + }, + }, + aggs: { + nodes: { + terms: { + size: params.limit ?? MAX_SIZE, + field: BUCKET_KEY, + order: { + _key: 'asc', + }, + }, + }, + }, + }, + }; +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts b/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts new file mode 100644 index 0000000000000..6d44224661750 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts @@ -0,0 +1,35 @@ +/* + * 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 { GetInfraMetricsResponsePayload } from '../../../../../common/http_api/infra'; +import { getFilteredHosts } from './get_filtered_hosts'; +import { mapToApiResponse } from '../mapper'; +import { hasFilters } from '../utils'; +import { GetHostsArgs } from '../types'; +import { getAllHosts } from './get_all_hosts'; + +export const getHosts = async (args: GetHostsArgs): Promise => { + const runFilterQuery = hasFilters(args.params.query); + // filter first to prevent filter clauses from impacting the metrics aggregations. + const hostNamesShortList = runFilterQuery ? await getFilteredHostNames(args) : []; + if (runFilterQuery && hostNamesShortList.length === 0) { + return { + type: 'host', + nodes: [], + }; + } + + const result = await getAllHosts(args, hostNamesShortList); + return mapToApiResponse(args.params, result?.nodes.buckets); +}; + +const getFilteredHostNames = async (args: GetHostsArgs) => { + const filteredHosts = await getFilteredHosts(args); + + const { nodes } = filteredHosts ?? {}; + return nodes?.buckets.map((p) => p.key) ?? []; +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/mapper.test.ts b/x-pack/plugins/infra/server/routes/infra/lib/mapper.test.ts new file mode 100644 index 0000000000000..67cd31dc090de --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/mapper.test.ts @@ -0,0 +1,113 @@ +/* + * 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 { mapToApiResponse } from './mapper'; +import { GetInfraMetricsRequestBodyPayload } from '../../../../common/http_api/infra'; + +const metricsApiRequest: GetInfraMetricsRequestBodyPayload = { + type: 'host', + limit: 20, + metrics: [ + { + type: 'cpu', + }, + { + type: 'diskLatency', + }, + { + type: 'memory', + }, + { + type: 'memoryTotal', + }, + { + type: 'rx', + }, + { + type: 'tx', + }, + ], + range: { + from: '2023-04-18T11:15:31.407Z', + to: '2023-04-18T11:15:31.407Z', + }, + query: { bool: [{ must_not: [], filter: [], should: [], must: [] }] }, + sourceId: 'id', +}; + +describe('mapper', () => { + test('should map the aggregation object to the expected response object', () => { + const hosts = mapToApiResponse(metricsApiRequest, [ + { + key: 'host-0', + doc_count: 155, + diskLatency: { + doc_count: 0, + result: { + value: null, + }, + }, + memory: { + value: 0.542838307852529, + }, + tx: { + doc_count: 155, + result: { + value: 100.26926542816672, + }, + }, + rx: { + doc_count: 155, + result: { + value: 3959.4930095127706, + }, + }, + memoryTotal: { + value: 66640704.099216014, + }, + cpu: { + doc_count: 155, + result: { + value: 0.13271302652800487, + }, + }, + metadata: { + top: [ + { + sort: ['2023-04-04T06:35:13.793Z'], + metrics: { + 'host.os.name': null, + 'cloud.provider': '', + }, + }, + ], + }, + }, + ]); + + expect(hosts).toEqual({ + type: 'host', + nodes: [ + { + metadata: [ + { name: 'host.os.name', value: null }, + { name: 'cloud.provider', value: null }, + ], + metrics: [ + { name: 'cpu', value: 0.13271302652800487 }, + { name: 'diskLatency', value: 0 }, + { name: 'memory', value: 0.542838307852529 }, + { name: 'memoryTotal', value: 66640704.099216014 }, + { name: 'rx', value: 3959.4930095127706 }, + { name: 'tx', value: 100.26926542816672 }, + ], + name: 'host-0', + }, + ], + }); + }); +}); diff --git a/x-pack/plugins/infra/server/routes/infra/lib/mapper.ts b/x-pack/plugins/infra/server/routes/infra/lib/mapper.ts new file mode 100644 index 0000000000000..89d2e71b364f9 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/mapper.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. + */ + +import { BasicMetricValueRT, TopMetricsTypeRT } from '../../../lib/metrics/types'; +import { + GetInfraMetricsRequestBodyPayload, + GetInfraMetricsResponsePayload, + InfraAssetMetadata, + InfraAssetMetrics, +} from '../../../../common/http_api/infra'; + +import { + FilteredMetricsTypeRT, + HostsMetricsSearchBucket, + HostsMetricsSearchValue, + HostsMetricsSearchValueRT, +} from './types'; +import { METADATA_AGGREGATION_NAME } from './constants'; + +export const mapToApiResponse = ( + params: GetInfraMetricsRequestBodyPayload, + buckets?: HostsMetricsSearchBucket[] | undefined +): GetInfraMetricsResponsePayload => { + if (!buckets) { + return { + type: params.type, + nodes: [], + }; + } + + const hosts = buckets.map((bucket) => { + const metrics = convertMetricBucket(params, bucket); + const metadata = convertMetadataBucket(bucket); + + return { name: bucket.key as string, metrics, metadata }; + }); + + return { + type: params.type, + nodes: hosts, + }; +}; + +const normalizeValue = (value: string | number | null) => { + if (typeof value === 'string') { + return value?.trim().length === 0 ? null : value; + } + + return value; +}; + +const convertMetadataBucket = (bucket: HostsMetricsSearchBucket): InfraAssetMetadata[] => { + const metadataAggregation = bucket[METADATA_AGGREGATION_NAME]; + return TopMetricsTypeRT.is(metadataAggregation) + ? metadataAggregation.top + .flatMap((top) => Object.entries(top.metrics)) + .map( + ([key, value]) => + ({ + name: key, + value: normalizeValue(value), + } as InfraAssetMetadata) + ) + : []; +}; + +const convertMetricBucket = ( + params: GetInfraMetricsRequestBodyPayload, + bucket: HostsMetricsSearchBucket +): InfraAssetMetrics[] => { + return params.metrics.map((returnedMetric) => { + const metricBucket = bucket[returnedMetric.type]; + return { + name: returnedMetric.type, + value: HostsMetricsSearchValueRT.is(metricBucket) ? getMetricValue(metricBucket) ?? 0 : null, + } as InfraAssetMetrics; + }); +}; + +export const getMetricValue = (valueObject: HostsMetricsSearchValue) => { + if (FilteredMetricsTypeRT.is(valueObject)) { + return valueObject.result.value; + } + + if (BasicMetricValueRT.is(valueObject)) { + return valueObject.value; + } + + return valueObject; +}; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/types.ts b/x-pack/plugins/infra/server/routes/infra/lib/types.ts new file mode 100644 index 0000000000000..d9800112e7dfe --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/types.ts @@ -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 { estypes } from '@elastic/elasticsearch'; +import { ISearchClient } from '@kbn/data-plugin/common'; +import * as rt from 'io-ts'; +import { InfraStaticSourceConfiguration } from '../../../../common/source_configuration/source_configuration'; + +import { GetInfraMetricsRequestBodyPayload } from '../../../../common/http_api/infra'; +import { BasicMetricValueRT, TopMetricsTypeRT } from '../../../lib/metrics/types'; + +export const FilteredMetricsTypeRT = rt.type({ + doc_count: rt.number, + result: BasicMetricValueRT, +}); + +export const HostsMetricsSearchValueRT = rt.union([ + BasicMetricValueRT, + FilteredMetricsTypeRT, + TopMetricsTypeRT, +]); + +export const HostsMetricsSearchBucketRT = rt.record( + rt.union([rt.string, rt.undefined]), + rt.union([ + rt.string, + rt.number, + HostsMetricsSearchValueRT, + rt.record(rt.string, rt.string), + rt.type({ doc_count: rt.number }), + ]) +); + +export const HostsNameBucketRT = rt.type({ + key: rt.string, + doc_count: rt.number, +}); + +export const HostsMetricsSearchAggregationResponseRT = rt.union([ + rt.type({ + nodes: rt.intersection([ + rt.partial({ + sum_other_doc_count: rt.number, + doc_count_error_upper_bound: rt.number, + }), + rt.type({ buckets: rt.array(HostsMetricsSearchBucketRT) }), + ]), + }), + rt.undefined, +]); + +export const FilteredHostsSearchAggregationResponseRT = rt.union([ + rt.type({ + nodes: rt.intersection([ + rt.partial({ + sum_other_doc_count: rt.number, + doc_count_error_upper_bound: rt.number, + }), + rt.type({ + buckets: rt.array(HostsNameBucketRT), + }), + ]), + }), + rt.undefined, +]); + +export interface HostsMetricsAggregationQueryConfig { + fieldName: string; + aggregation: estypes.AggregationsAggregationContainer; + runtimeField?: estypes.MappingRuntimeFields; +} + +export interface GetHostsArgs { + searchClient: ISearchClient; + sourceConfig: InfraStaticSourceConfiguration; + params: GetInfraMetricsRequestBodyPayload; +} + +export type HostsMetricsSearchValue = rt.TypeOf; +export type HostsMetricsSearchBucket = rt.TypeOf; + +export type FilteredHostsSearchAggregationResponse = rt.TypeOf< + typeof FilteredHostsSearchAggregationResponseRT +>; + +export type HostsMetricsSearchAggregationResponse = rt.TypeOf< + typeof HostsMetricsSearchAggregationResponseRT +>; diff --git a/x-pack/plugins/infra/server/routes/infra/lib/utils.test.ts b/x-pack/plugins/infra/server/routes/infra/lib/utils.test.ts new file mode 100644 index 0000000000000..504585db478cd --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/utils.test.ts @@ -0,0 +1,61 @@ +/* + * 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 { assertQueryStructure, hasFilters } from './utils'; + +const query = { bool: { must_not: [], filter: [], should: [], must: [] } }; + +describe('utils', () => { + describe('assertQueryStructure', () => { + test('should successfully parse a partial query object', () => { + const partialQuery = { + ...query, + bool: { + filter: [], + }, + }; + + expect(() => assertQueryStructure(partialQuery)).not.toThrow(); + }); + + test('should successfully parse query object', () => { + expect(() => assertQueryStructure(query)).not.toThrow(); + }); + + test('should fail to parse query object', () => { + const anyObject = { test: [{ a: 1 }] }; + expect(() => assertQueryStructure(anyObject)).toThrow(); + }); + + test('should fail to parse query object without any filter clause', () => { + const anyObject = { bool: {} }; + expect(() => assertQueryStructure(anyObject)).toThrow(); + }); + }); + describe('hasFilters', () => { + test('should return true if there is any filter', () => { + const result = hasFilters({ + ...query, + bool: { + filter: [ + { + term: { + 'host.name': 'host', + }, + }, + ], + }, + }); + expect(result).toEqual(true); + }); + + test('should return false when there is not filter', () => { + const result = hasFilters(query); + expect(result).toEqual(false); + }); + }); +}); diff --git a/x-pack/plugins/infra/server/routes/infra/lib/utils.ts b/x-pack/plugins/infra/server/routes/infra/lib/utils.ts new file mode 100644 index 0000000000000..7ea5a3d0b4fee --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/utils.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { estypes } from '@elastic/elasticsearch'; +import Boom from '@hapi/boom'; + +type FilterClauses = keyof estypes.QueryDslBoolQuery; +const validClauses: FilterClauses[] = ['must', 'filter', 'must_not', 'should']; + +interface BoolQuery { + bool: estypes.QueryDslBoolQuery; +} + +const isValidFilter = (query: any): query is BoolQuery => { + const boolClause = (query as estypes.QueryDslQueryContainer).bool; + + if (!boolClause || Object.keys(boolClause).length === 0) { + return false; + } + + return [boolClause.filter, boolClause.must, boolClause.must_not, boolClause.should] + .filter(Boolean) + .every((clause) => Array.isArray(clause) || clause === undefined); +}; + +export const assertQueryStructure: (query: any) => asserts query is BoolQuery = (query) => { + if (!isValidFilter(query)) { + throw Boom.badRequest('Invalid query'); + } +}; + +export const hasFilters = (query?: any) => { + if (!query) { + return false; + } + + assertQueryStructure(query); + + // ignores minimum_should_match + return Object.entries(query.bool) + .filter(([key, _]) => validClauses.includes(key as FilterClauses)) + .some(([_, filter]) => { + return Array.isArray(filter) ? filter.length > 0 : !!filter; + }); +}; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/README.md b/x-pack/plugins/infra/server/routes/inventory_views/README.md new file mode 100644 index 0000000000000..8a09aedef1b75 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/README.md @@ -0,0 +1,350 @@ +# Inventory Views CRUD api + +## Find all: `GET /api/infra/inventory_views` + +Retrieves all inventory views in a reduced version. + +### Request + +- **Method**: GET +- **Path**: /api/infra/inventory_views +- **Query params**: + - `sourceId` _(optional)_: Specify a source id related to the inventory views. Default value: `default`. + +### Response + +```json +GET /api/infra/inventory_views + +Status code: 200 + +{ + "data": [ + { + "id": "static", + "attributes": { + "name": "Default view", + "isDefault": false, + "isStatic": true + } + }, + { + "id": "927ad6a0-da0c-11ed-9487-41e9b90f96b9", + "version": "WzQwMiwxXQ==", + "updatedAt": 1681398305034, + "attributes": { + "name": "Ad-hoc", + "isDefault": true, + "isStatic": false + } + }, + { + "id": "c301ef20-da0c-11ed-aac0-77131228e6f1", + "version": "WzQxMCwxXQ==", + "updatedAt": 1681398386450, + "attributes": { + "name": "Custom", + "isDefault": false, + "isStatic": false + } + } + ] +} +``` + +## Get one: `GET /api/infra/inventory_views/{inventoryViewId}` + +Retrieves a single inventory view by ID + +### Request + +- **Method**: GET +- **Path**: /api/infra/inventory_views/{inventoryViewId} +- **Query params**: + - `sourceId` _(optional)_: Specify a source id related to the inventory view. Default value: `default`. + +### Response + +```json +GET /api/infra/inventory_views/927ad6a0-da0c-11ed-9487-41e9b90f96b9 + +Status code: 200 + +{ + "data": { + "id": "927ad6a0-da0c-11ed-9487-41e9b90f96b9", + "version": "WzQwMiwxXQ==", + "updatedAt": 1681398305034, + "attributes": { + "name": "Ad-hoc", + "isDefault": true, + "isStatic": false, + "metric": { + "type": "cpu" + }, + "sort": { + "by": "name", + "direction": "desc" + }, + "groupBy": [], + "nodeType": "host", + "view": "map", + "customOptions": [], + "customMetrics": [], + "boundsOverride": { + "max": 1, + "min": 0 + }, + "autoBounds": true, + "accountId": "", + "region": "", + "autoReload": false, + "filterQuery": { + "expression": "", + "kind": "kuery" + }, + "legend": { + "palette": "cool", + "reverseColors": false, + "steps": 10 + }, + "timelineOpen": false + } + } +} +``` + +```json +GET /api/infra/inventory_views/random-id + +Status code: 404 + +{ + "statusCode": 404, + "error": "Not Found", + "message": "Saved object [inventory-view/random-id] not found" +} +``` + +## Create one: `POST /api/infra/inventory_views` + +Creates a new inventory view. + +### Request + +- **Method**: POST +- **Path**: /api/infra/inventory_views +- **Request body**: + ```json + { + "attributes": { + "name": "View name", + "metric": { + "type": "cpu" + }, + "sort": { + "by": "name", + "direction": "desc" + }, + //... + } + } + ``` + +### Response + +```json +POST /api/infra/inventory_views + +Status code: 201 + +{ + "data": { + "id": "927ad6a0-da0c-11ed-9487-41e9b90f96b9", + "version": "WzQwMiwxXQ==", + "updatedAt": 1681398305034, + "attributes": { + "name": "View name", + "isDefault": false, + "isStatic": false, + "metric": { + "type": "cpu" + }, + "sort": { + "by": "name", + "direction": "desc" + }, + "groupBy": [], + "nodeType": "host", + "view": "map", + "customOptions": [], + "customMetrics": [], + "boundsOverride": { + "max": 1, + "min": 0 + }, + "autoBounds": true, + "accountId": "", + "region": "", + "autoReload": false, + "filterQuery": { + "expression": "", + "kind": "kuery" + }, + "legend": { + "palette": "cool", + "reverseColors": false, + "steps": 10 + }, + "timelineOpen": false + } + } +} +``` + +Send in the payload a `name` attribute already held by another view: +```json +POST /api/infra/inventory_views + +Status code: 409 + +{ + "statusCode": 409, + "error": "Conflict", + "message": "A view with that name already exists." +} +``` + +## Update one: `PUT /api/infra/inventory_views/{inventoryViewId}` + +Updates an inventory view. + +Any attribute can be updated except for `isDefault` and `isStatic`, which are derived by the source configuration preference set by the user. + +### Request + +- **Method**: PUT +- **Path**: /api/infra/inventory_views/{inventoryViewId} +- **Query params**: + - `sourceId` _(optional)_: Specify a source id related to the inventory view. Default value: `default`. +- **Request body**: + ```json + { + "attributes": { + "name": "View name", + "metric": { + "type": "cpu" + }, + "sort": { + "by": "name", + "direction": "desc" + }, + //... + } + } + ``` + +### Response + +```json +PUT /api/infra/inventory_views/927ad6a0-da0c-11ed-9487-41e9b90f96b9 + +Status code: 200 + +{ + "data": { + "id": "927ad6a0-da0c-11ed-9487-41e9b90f96b9", + "version": "WzQwMiwxXQ==", + "updatedAt": 1681398305034, + "attributes": { + "name": "View name", + "isDefault": false, + "isStatic": false, + "metric": { + "type": "cpu" + }, + "sort": { + "by": "name", + "direction": "desc" + }, + "groupBy": [], + "nodeType": "host", + "view": "map", + "customOptions": [], + "customMetrics": [], + "boundsOverride": { + "max": 1, + "min": 0 + }, + "autoBounds": true, + "accountId": "", + "region": "", + "autoReload": false, + "filterQuery": { + "expression": "", + "kind": "kuery" + }, + "legend": { + "palette": "cool", + "reverseColors": false, + "steps": 10 + }, + "timelineOpen": false + } + } +} +``` + +```json +PUT /api/infra/inventory_views/random-id + +Status code: 404 + +{ + "statusCode": 404, + "error": "Not Found", + "message": "Saved object [inventory-view/random-id] not found" +} +``` + +Send in the payload a `name` attribute already held by another view: +```json +PUT /api/infra/inventory_views/927ad6a0-da0c-11ed-9487-41e9b90f96b9 + +Status code: 409 + +{ + "statusCode": 409, + "error": "Conflict", + "message": "A view with that name already exists." +} +``` + +## Delete one: `DELETE /api/infra/inventory_views/{inventoryViewId}` + +Deletes an inventory view. + +### Request + +- **Method**: DELETE +- **Path**: /api/infra/inventory_views/{inventoryViewId} + +### Response + +```json +DELETE /api/infra/inventory_views/927ad6a0-da0c-11ed-9487-41e9b90f96b9 + +Status code: 204 No content +``` + +```json +DELETE /api/infra/inventory_views/random-id + +Status code: 404 + +{ + "statusCode": 404, + "error": "Not Found", + "message": "Saved object [inventory-view/random-id] not found" +} +``` diff --git a/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts b/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts new file mode 100644 index 0000000000000..8f3d52db7a6dd --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts @@ -0,0 +1,58 @@ +/* + * 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 { isBoom } from '@hapi/boom'; +import { createValidationFunction } from '../../../common/runtime_types'; +import { + createInventoryViewRequestPayloadRT, + inventoryViewResponsePayloadRT, + INVENTORY_VIEW_URL, +} from '../../../common/http_api/latest'; +import type { InfraBackendLibs } from '../../lib/infra_types'; + +export const initCreateInventoryViewRoute = ({ + framework, + getStartServices, +}: Pick) => { + framework.registerRoute( + { + method: 'post', + path: INVENTORY_VIEW_URL, + validate: { + body: createValidationFunction(createInventoryViewRequestPayloadRT), + }, + }, + async (_requestContext, request, response) => { + const { body } = request; + const { inventoryViews } = (await getStartServices())[2]; + const inventoryViewsClient = inventoryViews.getScopedClient(request); + + try { + const inventoryView = await inventoryViewsClient.create(body.attributes); + + return response.custom({ + statusCode: 201, + body: inventoryViewResponsePayloadRT.encode({ data: inventoryView }), + }); + } catch (error) { + if (isBoom(error)) { + return response.customError({ + statusCode: error.output.statusCode, + body: { message: error.output.payload.message }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + } + ); +}; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/delete_inventory_view.ts b/x-pack/plugins/infra/server/routes/inventory_views/delete_inventory_view.ts new file mode 100644 index 0000000000000..83ad61fc46c52 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/delete_inventory_view.ts @@ -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 { isBoom } from '@hapi/boom'; +import { createValidationFunction } from '../../../common/runtime_types'; +import { + inventoryViewRequestParamsRT, + INVENTORY_VIEW_URL_ENTITY, +} from '../../../common/http_api/latest'; +import type { InfraBackendLibs } from '../../lib/infra_types'; + +export const initDeleteInventoryViewRoute = ({ + framework, + getStartServices, +}: Pick) => { + framework.registerRoute( + { + method: 'delete', + path: INVENTORY_VIEW_URL_ENTITY, + validate: { + params: createValidationFunction(inventoryViewRequestParamsRT), + }, + }, + async (_requestContext, request, response) => { + const { params } = request; + const { inventoryViews } = (await getStartServices())[2]; + const inventoryViewsClient = inventoryViews.getScopedClient(request); + + try { + await inventoryViewsClient.delete(params.inventoryViewId); + + return response.noContent(); + } catch (error) { + if (isBoom(error)) { + return response.customError({ + statusCode: error.output.statusCode, + body: { message: error.output.payload.message }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + } + ); +}; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/find_inventory_view.ts b/x-pack/plugins/infra/server/routes/inventory_views/find_inventory_view.ts new file mode 100644 index 0000000000000..abdfc2f8749e4 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/find_inventory_view.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createValidationFunction } from '../../../common/runtime_types'; +import { + findInventoryViewResponsePayloadRT, + inventoryViewRequestQueryRT, + INVENTORY_VIEW_URL, +} from '../../../common/http_api/latest'; +import type { InfraBackendLibs } from '../../lib/infra_types'; + +export const initFindInventoryViewRoute = ({ + framework, + getStartServices, +}: Pick) => { + framework.registerRoute( + { + method: 'get', + path: INVENTORY_VIEW_URL, + validate: { + query: createValidationFunction(inventoryViewRequestQueryRT), + }, + }, + async (_requestContext, request, response) => { + const { query } = request; + const { inventoryViews } = (await getStartServices())[2]; + const inventoryViewsClient = inventoryViews.getScopedClient(request); + + try { + const inventoryViewsList = await inventoryViewsClient.find(query); + + return response.ok({ + body: findInventoryViewResponsePayloadRT.encode({ data: inventoryViewsList }), + }); + } catch (error) { + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + } + ); +}; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/get_inventory_view.ts b/x-pack/plugins/infra/server/routes/inventory_views/get_inventory_view.ts new file mode 100644 index 0000000000000..1a5f5adec136d --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/get_inventory_view.ts @@ -0,0 +1,59 @@ +/* + * 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 { isBoom } from '@hapi/boom'; +import { createValidationFunction } from '../../../common/runtime_types'; +import { + inventoryViewResponsePayloadRT, + inventoryViewRequestQueryRT, + INVENTORY_VIEW_URL_ENTITY, + getInventoryViewRequestParamsRT, +} from '../../../common/http_api/latest'; +import type { InfraBackendLibs } from '../../lib/infra_types'; + +export const initGetInventoryViewRoute = ({ + framework, + getStartServices, +}: Pick) => { + framework.registerRoute( + { + method: 'get', + path: INVENTORY_VIEW_URL_ENTITY, + validate: { + params: createValidationFunction(getInventoryViewRequestParamsRT), + query: createValidationFunction(inventoryViewRequestQueryRT), + }, + }, + async (_requestContext, request, response) => { + const { params, query } = request; + const { inventoryViews } = (await getStartServices())[2]; + const inventoryViewsClient = inventoryViews.getScopedClient(request); + + try { + const inventoryView = await inventoryViewsClient.get(params.inventoryViewId, query); + + return response.ok({ + body: inventoryViewResponsePayloadRT.encode({ data: inventoryView }), + }); + } catch (error) { + if (isBoom(error)) { + return response.customError({ + statusCode: error.output.statusCode, + body: { message: error.output.payload.message }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + } + ); +}; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/index.ts b/x-pack/plugins/infra/server/routes/inventory_views/index.ts new file mode 100644 index 0000000000000..55cee58a8a464 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { InfraBackendLibs } from '../../lib/infra_types'; +import { initCreateInventoryViewRoute } from './create_inventory_view'; +import { initDeleteInventoryViewRoute } from './delete_inventory_view'; +import { initFindInventoryViewRoute } from './find_inventory_view'; +import { initGetInventoryViewRoute } from './get_inventory_view'; +import { initUpdateInventoryViewRoute } from './update_inventory_view'; + +export const initInventoryViewRoutes = ( + dependencies: Pick +) => { + initCreateInventoryViewRoute(dependencies); + initDeleteInventoryViewRoute(dependencies); + initFindInventoryViewRoute(dependencies); + initGetInventoryViewRoute(dependencies); + initUpdateInventoryViewRoute(dependencies); +}; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/update_inventory_view.ts b/x-pack/plugins/infra/server/routes/inventory_views/update_inventory_view.ts new file mode 100644 index 0000000000000..d2b583437d177 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/inventory_views/update_inventory_view.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 { isBoom } from '@hapi/boom'; +import { createValidationFunction } from '../../../common/runtime_types'; +import { + inventoryViewRequestParamsRT, + inventoryViewRequestQueryRT, + inventoryViewResponsePayloadRT, + INVENTORY_VIEW_URL_ENTITY, + updateInventoryViewRequestPayloadRT, +} from '../../../common/http_api/latest'; +import type { InfraBackendLibs } from '../../lib/infra_types'; + +export const initUpdateInventoryViewRoute = ({ + framework, + getStartServices, +}: Pick) => { + framework.registerRoute( + { + method: 'put', + path: INVENTORY_VIEW_URL_ENTITY, + validate: { + params: createValidationFunction(inventoryViewRequestParamsRT), + query: createValidationFunction(inventoryViewRequestQueryRT), + body: createValidationFunction(updateInventoryViewRequestPayloadRT), + }, + }, + async (_requestContext, request, response) => { + const { body, params, query } = request; + const { inventoryViews } = (await getStartServices())[2]; + const inventoryViewsClient = inventoryViews.getScopedClient(request); + + try { + const inventoryView = await inventoryViewsClient.update( + params.inventoryViewId, + body.attributes, + query + ); + + return response.ok({ + body: inventoryViewResponsePayloadRT.encode({ data: inventoryView }), + }); + } catch (error) { + if (isBoom(error)) { + return response.customError({ + statusCode: error.output.statusCode, + body: { message: error.output.payload.message }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + } + ); +}; diff --git a/x-pack/plugins/infra/server/saved_objects/index.ts b/x-pack/plugins/infra/server/saved_objects/index.ts index bd7ecac5179a1..cf6906fc733f7 100644 --- a/x-pack/plugins/infra/server/saved_objects/index.ts +++ b/x-pack/plugins/infra/server/saved_objects/index.ts @@ -5,4 +5,6 @@ * 2.0. */ +export * from './inventory_view'; export * from './log_view'; +export * from './metrics_explorer_view'; diff --git a/x-pack/plugins/infra/server/saved_objects/inventory_view/index.ts b/x-pack/plugins/infra/server/saved_objects/inventory_view/index.ts new file mode 100644 index 0000000000000..458d3fa65c6a0 --- /dev/null +++ b/x-pack/plugins/infra/server/saved_objects/inventory_view/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. + */ + +export { + inventoryViewSavedObjectName, + inventoryViewSavedObjectType, +} from './inventory_view_saved_object'; +export { inventoryViewSavedObjectRT } from './types'; diff --git a/x-pack/plugins/infra/server/saved_objects/inventory_view/inventory_view_saved_object.ts b/x-pack/plugins/infra/server/saved_objects/inventory_view/inventory_view_saved_object.ts new file mode 100644 index 0000000000000..f9c4c4d354024 --- /dev/null +++ b/x-pack/plugins/infra/server/saved_objects/inventory_view/inventory_view_saved_object.ts @@ -0,0 +1,39 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import type { SavedObject, SavedObjectsType } from '@kbn/core/server'; +import { inventoryViewSavedObjectRT } from './types'; + +export const inventoryViewSavedObjectName = 'inventory-view'; + +const getInventoryViewTitle = (savedObject: SavedObject) => + pipe( + inventoryViewSavedObjectRT.decode(savedObject), + fold( + () => `Inventory view [id=${savedObject.id}]`, + ({ attributes: { name } }) => name + ) + ); + +export const inventoryViewSavedObjectType: SavedObjectsType = { + name: inventoryViewSavedObjectName, + hidden: false, + namespaceType: 'single', + management: { + defaultSearchField: 'name', + displayName: 'inventory view', + getTitle: getInventoryViewTitle, + icon: 'metricsApp', + importableAndExportable: true, + }, + mappings: { + dynamic: false, + properties: {}, + }, +}; diff --git a/x-pack/plugins/infra/server/saved_objects/inventory_view/types.ts b/x-pack/plugins/infra/server/saved_objects/inventory_view/types.ts new file mode 100644 index 0000000000000..45e738f3920f1 --- /dev/null +++ b/x-pack/plugins/infra/server/saved_objects/inventory_view/types.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 { isoToEpochRt, nonEmptyStringRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; + +export const inventoryViewSavedObjectAttributesRT = rt.intersection([ + rt.strict({ + name: nonEmptyStringRt, + }), + rt.UnknownRecord, +]); + +export const inventoryViewSavedObjectRT = rt.intersection([ + rt.type({ + id: rt.string, + attributes: inventoryViewSavedObjectAttributesRT, + }), + rt.partial({ + version: rt.string, + updated_at: isoToEpochRt, + }), +]); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/index.ts b/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/index.ts similarity index 56% rename from x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/index.ts rename to x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/index.ts index 8379f425733cb..6f3f926319cf2 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/index.ts +++ b/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/index.ts @@ -5,5 +5,8 @@ * 2.0. */ -export { EndpointAgentAndIsolationStatus } from './endpoint_agent_and_isolation_status'; -export type { EndpointAgentAndIsolationStatusProps } from './endpoint_agent_and_isolation_status'; +export { + metricsExplorerViewSavedObjectName, + metricsExplorerViewSavedObjectType, +} from './metrics_explorer_view_saved_object'; +export { metricsExplorerViewSavedObjectRT } from './types'; diff --git a/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/metrics_explorer_view_saved_object.ts b/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/metrics_explorer_view_saved_object.ts new file mode 100644 index 0000000000000..ce47aa93951b6 --- /dev/null +++ b/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/metrics_explorer_view_saved_object.ts @@ -0,0 +1,39 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import type { SavedObject, SavedObjectsType } from '@kbn/core/server'; +import { metricsExplorerViewSavedObjectRT } from './types'; + +export const metricsExplorerViewSavedObjectName = 'metrics-explorer-view'; + +const getMetricsExplorerViewTitle = (savedObject: SavedObject) => + pipe( + metricsExplorerViewSavedObjectRT.decode(savedObject), + fold( + () => `Metrics explorer view [id=${savedObject.id}]`, + ({ attributes: { name } }) => name + ) + ); + +export const metricsExplorerViewSavedObjectType: SavedObjectsType = { + name: metricsExplorerViewSavedObjectName, + hidden: false, + namespaceType: 'single', + management: { + defaultSearchField: 'name', + displayName: 'metrics explorer view', + getTitle: getMetricsExplorerViewTitle, + icon: 'metricsApp', + importableAndExportable: true, + }, + mappings: { + dynamic: false, + properties: {}, + }, +}; diff --git a/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/types.ts b/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/types.ts new file mode 100644 index 0000000000000..1168b2003994e --- /dev/null +++ b/x-pack/plugins/infra/server/saved_objects/metrics_explorer_view/types.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 { isoToEpochRt } from '@kbn/io-ts-utils'; +import * as rt from 'io-ts'; +import { metricsExplorerViewAttributesRT } from '../../../common/metrics_explorer_views'; + +export const metricsExplorerViewSavedObjectRT = rt.intersection([ + rt.type({ + id: rt.string, + attributes: metricsExplorerViewAttributesRT, + }), + rt.partial({ + version: rt.string, + updated_at: isoToEpochRt, + }), +]); diff --git a/x-pack/plugins/infra/server/services/inventory_views/index.ts b/x-pack/plugins/infra/server/services/inventory_views/index.ts new file mode 100644 index 0000000000000..1df6b8cd44814 --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/index.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 { InventoryViewsService } from './inventory_views_service'; +export { InventoryViewsClient } from './inventory_views_client'; +export type { + InventoryViewsServiceSetup, + InventoryViewsServiceStart, + InventoryViewsServiceStartDeps, +} from './types'; diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.ts new file mode 100644 index 0000000000000..9d832f8502104 --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.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 type { IInventoryViewsClient } from './types'; + +export const createInventoryViewsClientMock = (): jest.Mocked => ({ + delete: jest.fn(), + find: jest.fn(), + get: jest.fn(), + create: jest.fn(), + update: jest.fn(), +}); diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.test.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.test.ts new file mode 100644 index 0000000000000..5d5b253045de4 --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.test.ts @@ -0,0 +1,255 @@ +/* + * 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 { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { InventoryViewAttributes } from '../../../common/inventory_views'; + +import { InfraSource } from '../../lib/sources'; +import { createInfraSourcesMock } from '../../lib/sources/mocks'; +import { inventoryViewSavedObjectName } from '../../saved_objects/inventory_view'; +import { InventoryViewsClient } from './inventory_views_client'; +import { createInventoryViewMock } from '../../../common/inventory_views/inventory_view.mock'; +import { + CreateInventoryViewAttributesRequestPayload, + UpdateInventoryViewAttributesRequestPayload, +} from '../../../common/http_api/latest'; + +describe('InventoryViewsClient class', () => { + const mockFindInventoryList = (savedObjectsClient: jest.Mocked) => { + const inventoryViewListMock = [ + createInventoryViewMock('0', { + isDefault: true, + } as InventoryViewAttributes), + createInventoryViewMock('default_id', { + name: 'Default view 2', + isStatic: false, + } as InventoryViewAttributes), + createInventoryViewMock('custom_id', { + name: 'Custom', + isStatic: false, + } as InventoryViewAttributes), + ]; + + savedObjectsClient.find.mockResolvedValue({ + total: 2, + saved_objects: inventoryViewListMock.slice(1).map((view) => ({ + ...view, + type: inventoryViewSavedObjectName, + score: 0, + references: [], + })), + per_page: 1000, + page: 1, + }); + + return inventoryViewListMock; + }; + + describe('.find', () => { + it('resolves the list of existing inventory views', async () => { + const { inventoryViewsClient, infraSources, savedObjectsClient } = + createInventoryViewsClient(); + + infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); + + const inventoryViewListMock = mockFindInventoryList(savedObjectsClient); + + const inventoryViewList = await inventoryViewsClient.find({}); + + expect(savedObjectsClient.find).toHaveBeenCalled(); + expect(inventoryViewList).toEqual(inventoryViewListMock); + }); + + it('always resolves at least the static inventory view', async () => { + const { inventoryViewsClient, infraSources, savedObjectsClient } = + createInventoryViewsClient(); + + const inventoryViewListMock = [ + createInventoryViewMock('0', { + isDefault: true, + } as InventoryViewAttributes), + ]; + + infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); + + savedObjectsClient.find.mockResolvedValue({ + total: 2, + saved_objects: [], + per_page: 1000, + page: 1, + }); + + const inventoryViewList = await inventoryViewsClient.find({}); + + expect(savedObjectsClient.find).toHaveBeenCalled(); + expect(inventoryViewList).toEqual(inventoryViewListMock); + }); + }); + + it('.get resolves the an inventory view by id', async () => { + const { inventoryViewsClient, infraSources, savedObjectsClient } = createInventoryViewsClient(); + + const inventoryViewMock = createInventoryViewMock('custom_id', { + name: 'Custom', + isDefault: false, + isStatic: false, + } as InventoryViewAttributes); + + infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); + + savedObjectsClient.get.mockResolvedValue({ + ...inventoryViewMock, + type: inventoryViewSavedObjectName, + references: [], + }); + + const inventoryView = await inventoryViewsClient.get('custom_id', {}); + + expect(savedObjectsClient.get).toHaveBeenCalled(); + expect(inventoryView).toEqual(inventoryViewMock); + }); + + describe('.create', () => { + it('generate a new inventory view', async () => { + const { inventoryViewsClient, savedObjectsClient } = createInventoryViewsClient(); + + const inventoryViewMock = createInventoryViewMock('new_id', { + name: 'New view', + isStatic: false, + } as InventoryViewAttributes); + + mockFindInventoryList(savedObjectsClient); + + savedObjectsClient.create.mockResolvedValue({ + ...inventoryViewMock, + type: inventoryViewSavedObjectName, + references: [], + }); + + const inventoryView = await inventoryViewsClient.create({ + name: 'New view', + } as CreateInventoryViewAttributesRequestPayload); + + expect(savedObjectsClient.create).toHaveBeenCalled(); + expect(inventoryView).toEqual(inventoryViewMock); + }); + + it('throws an error when a conflicting name is given', async () => { + const { inventoryViewsClient, savedObjectsClient } = createInventoryViewsClient(); + + mockFindInventoryList(savedObjectsClient); + + await expect( + async () => + await inventoryViewsClient.create({ + name: 'Custom', + } as CreateInventoryViewAttributesRequestPayload) + ).rejects.toThrow('A view with that name already exists.'); + }); + }); + + describe('.update', () => { + it('update an existing inventory view by id', async () => { + const { inventoryViewsClient, infraSources, savedObjectsClient } = + createInventoryViewsClient(); + + const inventoryViews = mockFindInventoryList(savedObjectsClient); + + const inventoryViewMock = { + ...inventoryViews[1], + attributes: { + ...inventoryViews[1].attributes, + name: 'New name', + }, + }; + + infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); + + savedObjectsClient.update.mockResolvedValue({ + ...inventoryViewMock, + type: inventoryViewSavedObjectName, + references: [], + }); + + const inventoryView = await inventoryViewsClient.update( + 'default_id', + { + name: 'New name', + } as UpdateInventoryViewAttributesRequestPayload, + {} + ); + + expect(savedObjectsClient.update).toHaveBeenCalled(); + expect(inventoryView).toEqual(inventoryViewMock); + }); + + it('throws an error when a conflicting name is given', async () => { + const { inventoryViewsClient, savedObjectsClient } = createInventoryViewsClient(); + + mockFindInventoryList(savedObjectsClient); + + await expect( + async () => + await inventoryViewsClient.update( + 'default_id', + { + name: 'Custom', + } as UpdateInventoryViewAttributesRequestPayload, + {} + ) + ).rejects.toThrow('A view with that name already exists.'); + }); + }); + + it('.delete removes an inventory view by id', async () => { + const { inventoryViewsClient, savedObjectsClient } = createInventoryViewsClient(); + + savedObjectsClient.delete.mockResolvedValue({}); + + const inventoryView = await inventoryViewsClient.delete('custom_id'); + + expect(savedObjectsClient.delete).toHaveBeenCalled(); + expect(inventoryView).toEqual({}); + }); +}); + +const createInventoryViewsClient = () => { + const logger = loggerMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const infraSources = createInfraSourcesMock(); + + const inventoryViewsClient = new InventoryViewsClient(logger, savedObjectsClient, infraSources); + + return { + infraSources, + inventoryViewsClient, + savedObjectsClient, + }; +}; + +const basicTestSourceConfiguration: InfraSource = { + id: 'ID', + origin: 'stored', + configuration: { + name: 'NAME', + description: 'DESCRIPTION', + logIndices: { + type: 'index_pattern', + indexPatternId: 'INDEX_PATTERN_ID', + }, + logColumns: [], + fields: { + message: [], + }, + metricAlias: 'METRIC_ALIAS', + inventoryDefaultView: '0', + metricsExplorerDefaultView: 'METRICS_EXPLORER_DEFAULT_VIEW', + anomalyThreshold: 0, + }, +}; diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts new file mode 100644 index 0000000000000..55a8df1024a6e --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts @@ -0,0 +1,199 @@ +/* + * 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 { + Logger, + SavedObject, + SavedObjectsClientContract, + SavedObjectsUpdateResponse, +} from '@kbn/core/server'; +import Boom from '@hapi/boom'; +import { + staticInventoryViewAttributes, + staticInventoryViewId, +} from '../../../common/inventory_views'; +import type { + CreateInventoryViewAttributesRequestPayload, + InventoryViewRequestQuery, +} from '../../../common/http_api/latest'; +import type { InventoryView, InventoryViewAttributes } from '../../../common/inventory_views'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import type { IInfraSources } from '../../lib/sources'; +import { inventoryViewSavedObjectName } from '../../saved_objects/inventory_view'; +import { inventoryViewSavedObjectRT } from '../../saved_objects/inventory_view/types'; +import type { IInventoryViewsClient } from './types'; + +export class InventoryViewsClient implements IInventoryViewsClient { + constructor( + private readonly logger: Logger, + private readonly savedObjectsClient: SavedObjectsClientContract, + private readonly infraSources: IInfraSources + ) {} + + static STATIC_VIEW_ID = '0'; + + public async find(query: InventoryViewRequestQuery): Promise { + this.logger.debug('Trying to load inventory views ...'); + + const sourceId = query.sourceId ?? 'default'; + + const [sourceConfiguration, inventoryViewSavedObject] = await Promise.all([ + this.infraSources.getSourceConfiguration(this.savedObjectsClient, sourceId), + this.savedObjectsClient.find({ + type: inventoryViewSavedObjectName, + perPage: 1000, // Fetch 1 page by default with a max of 1000 results + }), + ]); + + const defaultView = InventoryViewsClient.createStaticView( + sourceConfiguration.configuration.inventoryDefaultView + ); + const views = inventoryViewSavedObject.saved_objects.map((savedObject) => + this.mapSavedObjectToInventoryView( + savedObject, + sourceConfiguration.configuration.inventoryDefaultView + ) + ); + + const inventoryViews = [defaultView, ...views]; + + const sortedInventoryViews = this.moveDefaultViewOnTop(inventoryViews); + + return sortedInventoryViews; + } + + public async get( + inventoryViewId: string, + query: InventoryViewRequestQuery + ): Promise { + this.logger.debug(`Trying to load inventory view with id ${inventoryViewId} ...`); + + const sourceId = query.sourceId ?? 'default'; + + // Handle the case where the requested resource is the static inventory view + if (inventoryViewId === InventoryViewsClient.STATIC_VIEW_ID) { + const sourceConfiguration = await this.infraSources.getSourceConfiguration( + this.savedObjectsClient, + sourceId + ); + + return InventoryViewsClient.createStaticView( + sourceConfiguration.configuration.inventoryDefaultView + ); + } + + const [sourceConfiguration, inventoryViewSavedObject] = await Promise.all([ + this.infraSources.getSourceConfiguration(this.savedObjectsClient, sourceId), + this.savedObjectsClient.get(inventoryViewSavedObjectName, inventoryViewId), + ]); + + return this.mapSavedObjectToInventoryView( + inventoryViewSavedObject, + sourceConfiguration.configuration.inventoryDefaultView + ); + } + + public async create( + attributes: CreateInventoryViewAttributesRequestPayload + ): Promise { + this.logger.debug(`Trying to create inventory view ...`); + + // Validate there is not a view with the same name + await this.assertNameConflict(attributes.name); + + const inventoryViewSavedObject = await this.savedObjectsClient.create( + inventoryViewSavedObjectName, + attributes + ); + + return this.mapSavedObjectToInventoryView(inventoryViewSavedObject); + } + + public async update( + inventoryViewId: string, + attributes: CreateInventoryViewAttributesRequestPayload, + query: InventoryViewRequestQuery + ): Promise { + this.logger.debug(`Trying to update inventory view with id "${inventoryViewId}"...`); + + // Validate there is not a view with the same name + await this.assertNameConflict(attributes.name, [inventoryViewId]); + + const sourceId = query.sourceId ?? 'default'; + + const [sourceConfiguration, inventoryViewSavedObject] = await Promise.all([ + this.infraSources.getSourceConfiguration(this.savedObjectsClient, sourceId), + this.savedObjectsClient.update(inventoryViewSavedObjectName, inventoryViewId, attributes), + ]); + + return this.mapSavedObjectToInventoryView( + inventoryViewSavedObject, + sourceConfiguration.configuration.inventoryDefaultView + ); + } + + public delete(inventoryViewId: string): Promise<{}> { + this.logger.debug(`Trying to delete inventory view with id ${inventoryViewId} ...`); + + return this.savedObjectsClient.delete(inventoryViewSavedObjectName, inventoryViewId); + } + + private mapSavedObjectToInventoryView( + savedObject: SavedObject | SavedObjectsUpdateResponse, + defaultViewId?: string + ) { + const inventoryViewSavedObject = decodeOrThrow(inventoryViewSavedObjectRT)(savedObject); + + return { + id: inventoryViewSavedObject.id, + version: inventoryViewSavedObject.version, + updatedAt: inventoryViewSavedObject.updated_at, + attributes: { + ...inventoryViewSavedObject.attributes, + isDefault: inventoryViewSavedObject.id === defaultViewId, + isStatic: false, + }, + }; + } + + private moveDefaultViewOnTop(views: InventoryView[]) { + const defaultViewPosition = views.findIndex((view) => view.attributes.isDefault); + + if (defaultViewPosition !== -1) { + const element = views.splice(defaultViewPosition, 1)[0]; + views.unshift(element); + } + + return views; + } + + /** + * We want to control conflicting names on the views + */ + private async assertNameConflict(name: string, whitelist: string[] = []) { + const results = await this.savedObjectsClient.find({ + type: inventoryViewSavedObjectName, + perPage: 1000, + }); + + const hasConflict = [InventoryViewsClient.createStaticView(), ...results.saved_objects].some( + (obj) => !whitelist.includes(obj.id) && obj.attributes.name === name + ); + + if (hasConflict) { + throw Boom.conflict('A view with that name already exists.'); + } + } + + private static createStaticView = (defaultViewId?: string): InventoryView => ({ + id: staticInventoryViewId, + attributes: { + ...staticInventoryViewAttributes, + isDefault: defaultViewId === InventoryViewsClient.STATIC_VIEW_ID, + }, + }); +} diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_service.mock.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_service.mock.ts new file mode 100644 index 0000000000000..cb3e85643303c --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_service.mock.ts @@ -0,0 +1,18 @@ +/* + * 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 { createInventoryViewsClientMock } from './inventory_views_client.mock'; +import type { InventoryViewsServiceSetup, InventoryViewsServiceStart } from './types'; + +export const createInventoryViewsServiceSetupMock = + (): jest.Mocked => {}; + +export const createInventoryViewsServiceStartMock = + (): jest.Mocked => ({ + getClient: jest.fn((_savedObjectsClient: any) => createInventoryViewsClientMock()), + getScopedClient: jest.fn((_request: any) => createInventoryViewsClientMock()), + }); diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_service.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_service.ts new file mode 100644 index 0000000000000..3f51f0e65b29c --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_service.ts @@ -0,0 +1,39 @@ +/* + * 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 { KibanaRequest, Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { InventoryViewsClient } from './inventory_views_client'; +import type { + InventoryViewsServiceSetup, + InventoryViewsServiceStart, + InventoryViewsServiceStartDeps, +} from './types'; + +export class InventoryViewsService { + constructor(private readonly logger: Logger) {} + + public setup(): InventoryViewsServiceSetup {} + + public start({ + infraSources, + savedObjects, + }: InventoryViewsServiceStartDeps): InventoryViewsServiceStart { + const { logger } = this; + + return { + getClient(savedObjectsClient: SavedObjectsClientContract) { + return new InventoryViewsClient(logger, savedObjectsClient, infraSources); + }, + + getScopedClient(request: KibanaRequest) { + const savedObjectsClient = savedObjects.getScopedClient(request); + + return this.getClient(savedObjectsClient); + }, + }; + } +} diff --git a/x-pack/plugins/infra/server/services/inventory_views/types.ts b/x-pack/plugins/infra/server/services/inventory_views/types.ts new file mode 100644 index 0000000000000..3e023b77af6c2 --- /dev/null +++ b/x-pack/plugins/infra/server/services/inventory_views/types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + KibanaRequest, + SavedObjectsClientContract, + SavedObjectsServiceStart, +} from '@kbn/core/server'; +import type { + CreateInventoryViewAttributesRequestPayload, + InventoryViewRequestQuery, + UpdateInventoryViewAttributesRequestPayload, +} from '../../../common/http_api/latest'; +import type { InventoryView } from '../../../common/inventory_views'; +import type { InfraSources } from '../../lib/sources'; + +export interface InventoryViewsServiceStartDeps { + infraSources: InfraSources; + savedObjects: SavedObjectsServiceStart; +} + +export type InventoryViewsServiceSetup = void; + +export interface InventoryViewsServiceStart { + getClient(savedObjectsClient: SavedObjectsClientContract): IInventoryViewsClient; + getScopedClient(request: KibanaRequest): IInventoryViewsClient; +} + +export interface IInventoryViewsClient { + delete(inventoryViewId: string): Promise<{}>; + find(query: InventoryViewRequestQuery): Promise; + get(inventoryViewId: string, query: InventoryViewRequestQuery): Promise; + create( + inventoryViewAttributes: CreateInventoryViewAttributesRequestPayload + ): Promise; + update( + inventoryViewId: string, + inventoryViewAttributes: UpdateInventoryViewAttributesRequestPayload, + query: InventoryViewRequestQuery + ): Promise; +} diff --git a/x-pack/plugins/infra/server/types.ts b/x-pack/plugins/infra/server/types.ts index 108575c0f8324..c415103d2256d 100644 --- a/x-pack/plugins/infra/server/types.ts +++ b/x-pack/plugins/infra/server/types.ts @@ -14,6 +14,7 @@ import type { SearchRequestHandlerContext } from '@kbn/data-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import type { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration'; import { InfraServerPluginStartDeps } from './lib/adapters/framework'; +import { InventoryViewsServiceStart } from './services/inventory_views'; import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views/types'; export type { InfraConfig } from '../common/plugin_config_types'; @@ -30,6 +31,7 @@ export interface InfraPluginSetup { } export interface InfraPluginStart { + inventoryViews: InventoryViewsServiceStart; logViews: LogViewsServiceStart; getMetricIndices: ( savedObjectsClient: SavedObjectsClientContract, diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index 6d7327a7d9731..76cbce409ce2f 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -59,9 +59,10 @@ "@kbn/shared-ux-prompt-not-found", "@kbn/shared-ux-router", "@kbn/shared-ux-link-redirect-app", + "@kbn/discover-plugin", "@kbn/observability-alert-details", "@kbn/observability-shared-plugin", - "@kbn/ui-theme" + "@kbn/ui-theme", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/attachment.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/attachment.test.tsx new file mode 100644 index 0000000000000..b4cfbf3046db0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/attachment.test.tsx @@ -0,0 +1,122 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setup, SetupResult, getProcessorValue, setupEnvironment } from './processor.helpers'; + +const ATTACHMENT_TYPE = 'attachment'; + +describe('Processor: Attachment', () => { + let onUpdate: jest.Mock; + let testBed: SetupResult; + const { httpSetup } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers({ legacyFakeTimers: true }); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(async () => { + onUpdate = jest.fn(); + + await act(async () => { + testBed = await setup(httpSetup, { + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + + const { component, actions } = testBed; + + component.update(); + + // Open flyout to add new processor + actions.addProcessor(); + // Add type (the other fields are not visible until a type is selected) + await actions.addProcessorType(ATTACHMENT_TYPE); + }); + + test('prevents form submission if required fields are not provided', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Click submit button with only the type defined + await saveNewProcessor(); + + // Expect form error as "field" is a required parameter + expect(form.getErrorsMessages()).toEqual([ + 'A field value is required.', // "Field" input + ]); + }); + + test('saves with default parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value + form.setInputValue('fieldNameField.input', 'test_attachment_processor'); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, ATTACHMENT_TYPE); + + expect(processors[0][ATTACHMENT_TYPE]).toEqual({ + field: 'test_attachment_processor', + }); + }); + + test('saves with optional parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + find, + component, + } = testBed; + + // Add required fields + form.setInputValue('fieldNameField.input', 'test_attachment_processor'); + + // Add optional fields + form.setInputValue('targetField.input', 'test_target'); + form.setInputValue('indexedCharsField.input', '123456'); + form.setInputValue('indexedCharsFieldField.input', 'indexed_chars_field'); + form.toggleEuiSwitch('removeBinaryField.input'); + form.setInputValue('resourceNameField.input', 'resource_name_field'); + + // Add "networkDirectionField" value (required) + await act(async () => { + find('propertiesField').simulate('change', [{ label: 'content' }]); + }); + component.update(); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, ATTACHMENT_TYPE); + + expect(processors[0][ATTACHMENT_TYPE]).toEqual({ + field: 'test_attachment_processor', + target_field: 'test_target', + properties: ['content'], + indexed_chars: '123456', + indexed_chars_field: 'indexed_chars_field', + remove_binary: true, + resource_name: 'resource_name_field', + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/geo_grid.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/geo_grid.test.tsx new file mode 100644 index 0000000000000..93e4860eb7cc2 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/geo_grid.test.tsx @@ -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 { act } from 'react-dom/test-utils'; +import { setup, SetupResult, getProcessorValue, setupEnvironment } from './processor.helpers'; + +const GEO_GRID_TYPE = 'geo_grid'; + +describe('Processor: GeoGrid', () => { + let onUpdate: jest.Mock; + let testBed: SetupResult; + const { httpSetup } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers({ legacyFakeTimers: true }); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(async () => { + onUpdate = jest.fn(); + + await act(async () => { + testBed = await setup(httpSetup, { + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + + const { component, actions } = testBed; + + component.update(); + + // Open flyout to add new processor + actions.addProcessor(); + // Add type (the other fields are not visible until a type is selected) + await actions.addProcessorType(GEO_GRID_TYPE); + }); + + test('prevents form submission if required fields are not provided', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Click submit button with only the type defined + await saveNewProcessor(); + + // Expect form error as "field" is a required parameter + expect(form.getErrorsMessages()).toEqual([ + 'A field value is required.', // "Field" input + 'A tile type value is required.', // "Tile type" input + ]); + }); + + test('saves with default parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value + form.setInputValue('fieldNameField.input', 'test_geo_grid_processor'); + + // Add "tile tyle" field + form.setSelectValue('tileTypeField', 'geohex'); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, GEO_GRID_TYPE); + + expect(processors[0][GEO_GRID_TYPE]).toEqual( + expect.objectContaining({ + field: 'test_geo_grid_processor', + tile_type: 'geohex', + }) + ); + }); + + test('saves with optional parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add required fields + form.setInputValue('fieldNameField.input', 'test_geo_grid_processor'); + form.setSelectValue('tileTypeField', 'geohex'); + + // Add optional fields + form.setInputValue('targetField.input', 'test_target'); + form.setSelectValue('targetFormatField', 'WKT'); + form.setInputValue('parentField.input', 'parent_field'); + form.setInputValue('childrenField.input', 'children_field'); + form.setInputValue('nonChildrenField.input', 'nonchildren_field'); + form.setInputValue('precisionField.input', 'precision_field'); + + // Save the field + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, GEO_GRID_TYPE); + + expect(processors[0][GEO_GRID_TYPE]).toEqual({ + field: 'test_geo_grid_processor', + tile_type: 'geohex', + target_field: 'test_target', + target_format: 'WKT', + parent_field: 'parent_field', + children_field: 'children_field', + non_children_field: 'nonchildren_field', + precision_field: 'precision_field', + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx index 970aaf83b5ae8..125d8758ca0d0 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx @@ -190,4 +190,15 @@ type TestSubject = | 'droppableList.input-2' | 'prefixField.input' | 'suffixField.input' + | 'indexedCharsField.input' + | 'indexedCharsFieldField.input' + | 'removeBinaryField.input' + | 'resourceNameField.input' + | 'propertiesField' + | 'tileTypeField' + | 'targetFormatField' + | 'parentField.input' + | 'childrenField.input' + | 'nonChildrenField.input' + | 'precisionField.input' | 'patternDefinitionsField'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/attachment.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/attachment.tsx new file mode 100644 index 0000000000000..57f26bdaf204f --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/attachment.tsx @@ -0,0 +1,202 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCode, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { + ComboBoxField, + FIELD_TYPES, + UseField, + ToggleField, + Field, +} from '../../../../../../shared_imports'; + +import { FieldNameField } from './common_fields/field_name_field'; +import { TargetField } from './common_fields/target_field'; +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldsConfig, to, from } from './shared'; + +const propertyValues: string[] = [ + 'content', + 'title', + 'author', + 'keywords', + 'date', + 'content_type', + 'content_length', + 'language', +]; + +const fieldsConfig: FieldsConfig = { + /* Optional field configs */ + indexed_chars: { + type: FIELD_TYPES.NUMBER, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.indexedCharsFieldLabel', + { + defaultMessage: 'Indexed chars (optional)', + } + ), + helpText: ( + {'100000'} }} + /> + ), + }, + indexed_chars_field: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.indexedCharsFieldFieldLabel', + { + defaultMessage: 'Indexed chars field (optional)', + } + ), + helpText: ( + {'null'} }} + /> + ), + }, + properties: { + type: FIELD_TYPES.COMBO_BOX, + deserializer: to.arrayOfStrings, + serializer: from.optionalArrayOfStrings, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.attachment.propertiesFieldLabel', { + defaultMessage: 'Properties (optional)', + }), + helpText: ( + {'all'} }} + /> + ), + }, + remove_binary: { + type: FIELD_TYPES.TOGGLE, + defaultValue: false, + deserializer: to.booleanOrUndef, + serializer: from.undefinedIfValue(false), + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.removeBinaryFieldLabel', + { + defaultMessage: 'Remove binary', + } + ), + helpText: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.removeBinaryFieldHelpText', + { + defaultMessage: 'If enabled, the binary field will be removed from the document.', + } + ), + }, + resource_name: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.attachment.resourceNameFieldLabel', + { + defaultMessage: 'Resource name (optional)', + } + ), + helpText: ( + + ), + }, +}; + +export const Attachment: FunctionComponent = () => { + return ( + <> + + + + } + /> + + + {'attachment'} }} + /> + } + /> + + + + + + + + + + + + + + + + + ({ label })), + }} + path="fields.properties" + /> + + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geogrid.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geogrid.tsx new file mode 100644 index 0000000000000..01fc6829101dc --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/geogrid.tsx @@ -0,0 +1,246 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiSpacer, EuiCode, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { + fieldValidators, + FIELD_TYPES, + UseField, + Field, + SelectField, +} from '../../../../../../shared_imports'; + +import { FieldNameField } from './common_fields/field_name_field'; +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldsConfig, from } from './shared'; +import { TargetField } from './common_fields/target_field'; + +const fieldsConfig: FieldsConfig = { + tile_type: { + type: FIELD_TYPES.TEXT, + defaultValue: '', + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.tileTypeFieldLabel', { + defaultMessage: 'Tile type', + }), + helpText: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.tileTypeFieldHelpText', { + defaultMessage: 'The type of tile from field.', + }), + validations: [ + { + validator: fieldValidators.emptyField( + i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.tileTypeRequiredError', { + defaultMessage: 'A tile type value is required.', + }) + ), + }, + ], + }, + /* Optional field config */ + parent_field: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.parentFieldLabel', { + defaultMessage: 'Parent field (optional)', + }), + helpText: ( + + ), + }, + children_field: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.childrenFieldLabel', { + defaultMessage: 'Children field (optional)', + }), + helpText: ( + + ), + }, + non_children_field: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.nonchildrenFieldLabel', { + defaultMessage: 'Non children field (optional)', + }), + helpText: ( + + ), + }, + precision_field: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.precisionFieldLabel', { + defaultMessage: 'Precision field (optional)', + }), + helpText: ( + + ), + }, + target_format: { + type: FIELD_TYPES.TEXT, + serializer: from.emptyStringToUndefined, + defaultValue: '', + label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoGrid.targetFormatFieldLabel', { + defaultMessage: 'Target format (optional)', + }), + helpText: ( + {'Geo-JSON'}, + }} + /> + ), + }, +}; + +export const GeoGrid: FunctionComponent = () => { + return ( + <> + + + {'tile_type'} }} + /> + } + /> + + + {'field'} }} + /> + } + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts index 27f19e6c6cf9a..210d113bd2aba 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts @@ -8,6 +8,7 @@ // please try to keep this list sorted by module name (e.g. './bar' before './foo') export { Append } from './append'; +export { Attachment } from './attachment'; export { Bytes } from './bytes'; export { Circle } from './circle'; export { CommunityId } from './community_id'; @@ -22,6 +23,7 @@ export { Enrich } from './enrich'; export { Fail } from './fail'; export { Fingerprint } from './fingerprint'; export { Foreach } from './foreach'; +export { GeoGrid } from './geogrid'; export { GeoIP } from './geoip'; export { Grok } from './grok'; export { Gsub } from './gsub'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx index 3218ca456f959..6d232ba70557d 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx @@ -14,6 +14,7 @@ import { LicenseType } from '../../../../../types'; import { Append, + Attachment, Bytes, Circle, CommunityId, @@ -28,6 +29,7 @@ import { Fail, Fingerprint, Foreach, + GeoGrid, GeoIP, Grok, Gsub, @@ -99,6 +101,23 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { }, }), }, + attachment: { + FieldsComponent: Attachment, + docLinkPath: '/attachment.html', + label: i18n.translate('xpack.ingestPipelines.processors.label.attachment', { + defaultMessage: 'Attachment', + }), + typeDescription: i18n.translate('xpack.ingestPipelines.processors.description.attachment', { + defaultMessage: 'Extract file attachments in common formats (such as PPT, XLS, and PDF).', + }), + getDefaultDescription: ({ field }) => + i18n.translate('xpack.ingestPipelines.processors.defaultDescription.attachment', { + defaultMessage: 'Extracts attachment from "{field}"', + values: { + field, + }, + }), + }, bytes: { FieldsComponent: Bytes, docLinkPath: '/bytes-processor.html', @@ -374,6 +393,24 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { }, }), }, + geo_grid: { + FieldsComponent: GeoGrid, + docLinkPath: '/ingest-geo-grid-processor.html', + label: i18n.translate('xpack.ingestPipelines.processors.label.geogrid', { + defaultMessage: 'GeoGrid', + }), + typeDescription: i18n.translate('xpack.ingestPipelines.processors.description.geogrid', { + defaultMessage: + 'Converts geo-grid definitions of grid tiles or cells to regular bounding boxes or polygons which describe their shape.', + }), + getDefaultDescription: ({ field }) => + i18n.translate('xpack.ingestPipelines.processors.defaultDescription.geogrid', { + defaultMessage: 'Adds geo-grid data to documents based on the value of "{field}"', + values: { + field, + }, + }), + }, geoip: { FieldsComponent: GeoIP, docLinkPath: '/geoip-processor.html', @@ -459,7 +496,6 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { }, inference: { FieldsComponent: Inference, - forLicenseAtLeast: 'platinum', docLinkPath: '/inference-processor.html', label: i18n.translate('xpack.ingestPipelines.processors.label.inference', { defaultMessage: 'Inference', diff --git a/x-pack/plugins/kubernetes_security/server/routes/agent_id.ts b/x-pack/plugins/kubernetes_security/server/routes/agent_id.ts index 2408d29d83228..399a9435d42e0 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/agent_id.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/agent_id.ts @@ -7,7 +7,6 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; import type { ElasticsearchClient } from '@kbn/core/server'; -import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants'; import { AGENT_ID_ROUTE } from '../../common/constants'; export const registerAgentIdRoute = (router: IRouter) => { @@ -34,10 +33,10 @@ export const registerAgentIdRoute = (router: IRouter) => { ); }; -export const getAgentId = async (client: ElasticsearchClient, query: string, index?: string) => { +export const getAgentId = async (client: ElasticsearchClient, query: string, index: string) => { const queryDSL = JSON.parse(query); const search = await client.search({ - index: [index || PROCESS_EVENTS_INDEX], + index: [index], body: { query: queryDSL, size: 1, diff --git a/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts b/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts index e56a17d89845f..43b53dcf61622 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts @@ -8,7 +8,6 @@ import type { SortCombinations } from '@elastic/elasticsearch/lib/api/typesWithB import { schema } from '@kbn/config-schema'; import type { ElasticsearchClient } from '@kbn/core/server'; import { IRouter } from '@kbn/core/server'; -import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants'; import { AGGREGATE_ROUTE, AGGREGATE_PAGE_SIZE, @@ -26,12 +25,12 @@ export const registerAggregateRoute = (router: IRouter) => { path: AGGREGATE_ROUTE, validate: { query: schema.object({ + index: schema.string(), query: schema.string(), countBy: schema.maybe(schema.string()), groupBy: schema.string(), page: schema.number(), perPage: schema.maybe(schema.number()), - index: schema.maybe(schema.string()), sortByCount: schema.maybe(schema.string()), }), }, @@ -43,11 +42,11 @@ export const registerAggregateRoute = (router: IRouter) => { try { const body = await doSearch( client, + index, query, groupBy, page, perPage, - index, countBy, sortByCount ); @@ -62,11 +61,11 @@ export const registerAggregateRoute = (router: IRouter) => { export const doSearch = async ( client: ElasticsearchClient, + index: string, query: string, groupBy: string, page: number, // zero based perPage = AGGREGATE_PAGE_SIZE, - index?: string, countBy?: string, sortByCount?: string ): Promise => { @@ -88,7 +87,7 @@ export const doSearch = async ( } const search = await client.search({ - index: [index || PROCESS_EVENTS_INDEX], + index: [index], body: { query: queryDSL, size: 0, diff --git a/x-pack/plugins/kubernetes_security/server/routes/count.ts b/x-pack/plugins/kubernetes_security/server/routes/count.ts index ecf28df4f7c2b..70a10d0c94eda 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/count.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/count.ts @@ -7,7 +7,6 @@ import { schema } from '@kbn/config-schema'; import type { ElasticsearchClient } from '@kbn/core/server'; import { IRouter } from '@kbn/core/server'; -import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants'; import { COUNT_ROUTE } from '../../common/constants'; export const registerCountRoute = (router: IRouter) => { @@ -16,9 +15,9 @@ export const registerCountRoute = (router: IRouter) => { path: COUNT_ROUTE, validate: { query: schema.object({ + index: schema.string(), query: schema.string(), field: schema.string(), - index: schema.maybe(schema.string()), }), }, }, @@ -27,7 +26,7 @@ export const registerCountRoute = (router: IRouter) => { const { query, field, index } = request.query; try { - const body = await doCount(client, query, field, index); + const body = await doCount(client, index, query, field); return response.ok({ body }); } catch (err) { @@ -39,14 +38,14 @@ export const registerCountRoute = (router: IRouter) => { export const doCount = async ( client: ElasticsearchClient, + index: string, query: string, - field: string, - index?: string + field: string ) => { const queryDSL = JSON.parse(query); const search = await client.search({ - index: [index || PROCESS_EVENTS_INDEX], + index: [index], body: { query: queryDSL, size: 0, diff --git a/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts b/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts index f3c22cf40e28c..ef21a47dd170c 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts @@ -7,7 +7,6 @@ import { schema } from '@kbn/config-schema'; import type { ElasticsearchClient } from '@kbn/core/server'; import { IRouter } from '@kbn/core/server'; -import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants'; import { MULTI_TERMS_AGGREGATE_ROUTE, AGGREGATE_PAGE_SIZE } from '../../common/constants'; import { MultiTermsAggregateGroupBy, @@ -20,6 +19,7 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => { path: MULTI_TERMS_AGGREGATE_ROUTE, validate: { query: schema.object({ + index: schema.string(), query: schema.string(), countBy: schema.maybe(schema.string()), groupBys: schema.arrayOf( @@ -31,7 +31,6 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => { ), page: schema.number(), perPage: schema.maybe(schema.number()), - index: schema.maybe(schema.string()), }), }, }, @@ -40,7 +39,7 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => { const { query, countBy, groupBys, page, perPage, index } = request.query; try { - const body = await doSearch(client, query, groupBys, page, perPage, index, countBy); + const body = await doSearch(client, index, query, groupBys, page, perPage, countBy); return response.ok({ body }); } catch (err) { @@ -52,11 +51,11 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => { export const doSearch = async ( client: ElasticsearchClient, + index: string, query: string, groupBys: MultiTermsAggregateGroupBy[], page: number, // zero based perPage = AGGREGATE_PAGE_SIZE, - index?: string, countBy?: string ): Promise => { const queryDSL = JSON.parse(query); @@ -72,7 +71,7 @@ export const doSearch = async ( : undefined; const search = await client.search({ - index: [index || PROCESS_EVENTS_INDEX], + index: [index], body: { query: queryDSL, size: 0, diff --git a/x-pack/plugins/lens/public/datasources/common/field_item.scss b/x-pack/plugins/lens/public/datasources/common/field_item.scss new file mode 100644 index 0000000000000..4e1949c19fc4a --- /dev/null +++ b/x-pack/plugins/lens/public/datasources/common/field_item.scss @@ -0,0 +1,4 @@ +.lnsFieldItem__fieldPanel { + min-width: 260px; + max-width: 300px; +} diff --git a/x-pack/plugins/lens/public/datasources/form_based/field_item.test.tsx b/x-pack/plugins/lens/public/datasources/common/field_item.test.tsx similarity index 92% rename from x-pack/plugins/lens/public/datasources/form_based/field_item.test.tsx rename to x-pack/plugins/lens/public/datasources/common/field_item.test.tsx index 982a66fc05876..ec1ac67ad0f45 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/field_item.test.tsx +++ b/x-pack/plugins/lens/public/datasources/common/field_item.test.tsx @@ -9,35 +9,31 @@ import React, { ReactElement } from 'react'; import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { EuiLoadingSpinner, EuiPopover } from '@elastic/eui'; -import { InnerFieldItem, FieldItemProps } from './field_item'; +import { InnerFieldItem, FieldItemIndexPatternFieldProps } from './field_item'; import { coreMock } from '@kbn/core/public/mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { IndexPattern } from '../../types'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { documentField } from './document_field'; +import { documentField } from '../form_based/document_field'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { loadFieldStats } from '@kbn/unified-field-list-plugin/public/services/field_stats'; -import { FieldIcon } from '@kbn/unified-field-list-plugin/public'; import { DOCUMENT_FIELD_NAME } from '../../../common/constants'; -import { FieldStats, FieldVisualizeButton } from '@kbn/unified-field-list-plugin/public'; +import { FieldIcon, FieldStats, FieldPopoverFooter } from '@kbn/unified-field-list-plugin/public'; jest.mock('@kbn/unified-field-list-plugin/public/services/field_stats', () => ({ loadFieldStats: jest.fn().mockResolvedValue({}), })); -const chartsThemeService = chartPluginMock.createSetupContract().theme; - const clickField = async (wrapper: ReactWrapper, field: string) => { await act(async () => { await wrapper - .find(`[data-test-subj="lnsFieldListPanelField-${field}"] button`) + .find(`[data-test-subj="lnsFieldListPanelField-${field}"] .kbnFieldButton__button`) .simulate('click'); }); }; @@ -47,6 +43,7 @@ const mockedServices = { dataViews: dataViewPluginMocks.createStartContract(), fieldFormats: fieldFormatsServiceMock.createStartContract(), charts: chartPluginMock.createSetupContract(), + uiActions: uiActionsPluginMock.createStartContract(), uiSettings: coreMock.createStart().uiSettings, share: { url: { @@ -64,7 +61,7 @@ const mockedServices = { }, }; -const InnerFieldItemWrapper: React.FC = (props) => { +const InnerFieldItemWrapper: React.FC = (props) => { return ( @@ -72,7 +69,7 @@ const InnerFieldItemWrapper: React.FC = (props) => { ); }; -async function getComponent(props: FieldItemProps) { +async function getComponent(props: FieldItemIndexPatternFieldProps) { const instance = await mountWithIntl(); // wait for lazy modules await new Promise((resolve) => setTimeout(resolve, 0)); @@ -80,8 +77,8 @@ async function getComponent(props: FieldItemProps) { return instance; } -describe('IndexPattern Field Item', () => { - let defaultProps: FieldItemProps; +describe('Lens Field Item', () => { + let defaultProps: FieldItemIndexPatternFieldProps; let indexPattern: IndexPattern; let dataView: DataView; @@ -146,13 +143,6 @@ describe('IndexPattern Field Item', () => { defaultProps = { indexPattern, - fieldFormats: { - ...fieldFormatsServiceMock.createStartContract(), - getDefaultInstance: jest.fn(() => ({ - convert: jest.fn((s: unknown) => JSON.stringify(s)), - })), - } as unknown as FieldFormatsStart, - core: coreMock.createStart(), highlight: '', dateRange: { fromDate: 'now-7d', @@ -168,17 +158,17 @@ describe('IndexPattern Field Item', () => { searchable: true, }, exists: true, - chartsThemeService, groupIndex: 0, itemIndex: 0, dropOntoWorkspace: () => {}, hasSuggestionForField: () => false, - uiActions: uiActionsPluginMock.createStartContract(), }; dataView = { ...indexPattern, - getFormatterForField: defaultProps.fieldFormats.getDefaultInstance, + getFormatterForField: jest.fn(() => ({ + convert: jest.fn((s: unknown) => JSON.stringify(s)), + })), } as unknown as DataView; (mockedServices.dataViews.get as jest.Mock).mockImplementation(() => { @@ -460,7 +450,7 @@ describe('IndexPattern Field Item', () => { expect(wrapper.find(EuiPopover).prop('isOpen')).toEqual(true); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); expect(wrapper.find(FieldStats).text()).toBe('Analysis is not available for this field.'); - expect(wrapper.find(FieldVisualizeButton).exists()).toBeFalsy(); + expect(wrapper.find(FieldPopoverFooter).exists()).toBeFalsy(); }); it('should request examples for geo fields and render Visualize button', async () => { @@ -485,7 +475,7 @@ describe('IndexPattern Field Item', () => { expect(wrapper.find(FieldStats).text()).toBe( 'Lens is unable to create visualizations with this field because it does not contain data. To create a visualization, drag and drop a different field.' ); - expect(wrapper.find(FieldVisualizeButton).exists()).toBeTruthy(); + expect(wrapper.find(FieldPopoverFooter).exists()).toBeTruthy(); }); it('should display Explore in discover button', async () => { diff --git a/x-pack/plugins/lens/public/datasources/form_based/field_item.tsx b/x-pack/plugins/lens/public/datasources/common/field_item.tsx similarity index 71% rename from x-pack/plugins/lens/public/datasources/form_based/field_item.tsx rename to x-pack/plugins/lens/public/datasources/common/field_item.tsx index 5774a803b5985..e53e00b9d881c 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/field_item.tsx +++ b/x-pack/plugins/lens/public/datasources/common/field_item.tsx @@ -8,52 +8,71 @@ import './field_item.scss'; import React, { useCallback, useState, useMemo } from 'react'; -import { EuiText, EuiButton, EuiPopoverFooter, EuiIconTip } from '@elastic/eui'; +import { EuiText, EuiButton, EuiPopoverFooter } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { Filter, Query } from '@kbn/es-query'; import { DataViewField, type DataView } from '@kbn/data-views-plugin/common'; -import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { AddFieldFilterHandler, FieldStats, FieldPopover, FieldPopoverHeader, - FieldPopoverVisualize, + FieldPopoverFooter, FieldItemButton, + type GetCustomFieldType, } from '@kbn/unified-field-list-plugin/public'; import { DragDrop } from '@kbn/dom-drag-drop'; import { generateFilters, getEsQueryConfig } from '@kbn/data-plugin/public'; +import { type DatatableColumn } from '@kbn/expressions-plugin/common'; import { DatasourceDataPanelProps } from '../../types'; import type { IndexPattern, IndexPatternField } from '../../types'; import type { LensAppServices } from '../../app_plugin/types'; import { APP_ID, DOCUMENT_FIELD_NAME } from '../../../common/constants'; import { combineQueryAndFilters } from '../../app_plugin/show_underlying_data'; +import { getFieldItemActions } from './get_field_item_actions'; -export interface FieldItemProps { - core: DatasourceDataPanelProps['core']; - fieldFormats: FieldFormatsStart; - field: IndexPatternField; - indexPattern: IndexPattern; +type LensFieldListItem = IndexPatternField | DatatableColumn | DataViewField; + +function isTextBasedColumnField(field: LensFieldListItem): field is DatatableColumn { + return !('type' in field) && Boolean(field?.meta.type); +} + +interface FieldItemBaseProps { highlight?: string; exists: boolean; - query: Query; - dateRange: DatasourceDataPanelProps['dateRange']; - chartsThemeService: ChartsPluginSetup['theme']; - filters: Filter[]; hideDetails?: boolean; itemIndex: number; groupIndex: number; dropOntoWorkspace: DatasourceDataPanelProps['dropOntoWorkspace']; + hasSuggestionForField: DatasourceDataPanelProps['hasSuggestionForField']; +} + +export interface FieldItemIndexPatternFieldProps extends FieldItemBaseProps { + field: IndexPatternField; + indexPattern: IndexPattern; + query: Query; + dateRange: DatasourceDataPanelProps['dateRange']; + filters: Filter[]; editField?: (name: string) => void; removeField?: (name: string) => void; - hasSuggestionForField: DatasourceDataPanelProps['hasSuggestionForField']; - uiActions: UiActionsStart; + getCustomFieldType?: never; } -export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { +export interface FieldItemDatatableColumnProps extends FieldItemBaseProps { + field: DatatableColumn; + indexPattern?: never; + query?: never; + dateRange?: never; + filters?: never; + editField?: never; + removeField?: never; + getCustomFieldType: GetCustomFieldType; +} + +export type FieldItemProps = FieldItemIndexPatternFieldProps | FieldItemDatatableColumnProps; + +export function InnerFieldItem(props: FieldItemProps) { const { field, indexPattern, @@ -66,9 +85,21 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { hasSuggestionForField, editField, removeField, + getCustomFieldType, } = props; - - const dataViewField = useMemo(() => new DataViewField(field), [field]); + const dataViewField = useMemo(() => { + // DatatableColumn type + if (isTextBasedColumnField(field)) { + return new DataViewField({ + name: field.name, + type: field.meta?.type ?? 'unknown', + searchable: true, + aggregatable: true, + }); + } + // IndexPatternField type + return new DataViewField(field); + }, [field]); const services = useKibana().services; const filterManager = services?.data?.query?.filterManager; const [infoIsOpen, setOpen] = useState(false); @@ -83,7 +114,7 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { const addFilterAndClose: AddFieldFilterHandler | undefined = useMemo( () => - filterManager + filterManager && indexPattern ? (clickedField, values, operation) => { closePopover(); const newFilters = generateFilters( @@ -121,52 +152,45 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { [removeField, closePopover] ); + const indexPatternId = indexPattern?.id; const value = useMemo( - () => ({ - field, - indexPatternId: indexPattern.id, - id: field.name, - humanData: { - label: field.displayName, - position: itemIndex + 1, - }, - }), - [field, indexPattern.id, itemIndex] + () => + isTextBasedColumnField(field) + ? { + field: field.name, + id: field.id, + humanData: { label: field.name }, + } + : { + field, + indexPatternId, + id: field.name, + humanData: { + label: field.displayName, + position: itemIndex + 1, + }, + }, + [field, indexPatternId, itemIndex] ); - const dropOntoWorkspaceAndClose = useCallback(() => { - closePopover(); - dropOntoWorkspace(value); - }, [dropOntoWorkspace, closePopover, value]); - - const onDragStart = useCallback(() => { - setOpen(false); - }, [setOpen]); - const order = useMemo(() => [0, groupIndex, itemIndex], [groupIndex, itemIndex]); - const lensInfoIcon = ( - - ); + const { buttonAddFieldToWorkspaceProps, onAddFieldToWorkspace } = getFieldItemActions({ + value, + hasSuggestionForField, + dropOntoWorkspace, + closeFieldPopover: closePopover, + }); + + const commonFieldItemButtonProps = { + isSelected: false, // multiple selections are allowed + isEmpty: !exists, + isActive: infoIsOpen, + fieldSearchHighlight: highlight, + onClick: togglePopover, + buttonAddFieldToWorkspaceProps, + onAddFieldToWorkspace, + }; return (
  • @@ -187,41 +211,26 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { order={order} value={value} dataTestSubj={`lnsFieldListPanelField-${field.name}`} - onDragStart={onDragStart} + onDragStart={closePopover} > - - isEmpty={!exists} - isActive={infoIsOpen} - infoIcon={lensInfoIcon} - field={field} - fieldSearchHighlight={highlight} - onClick={togglePopover} - /> + {isTextBasedColumnField(field) ? ( + + field={field} + getCustomFieldType={getCustomFieldType} + {...commonFieldItemButtonProps} + /> + ) : ( + + )} } renderHeader={() => { - const canAddToWorkspace = hasSuggestionForField(value); - const buttonTitle = canAddToWorkspace - ? i18n.translate('xpack.lens.indexPattern.moveToWorkspace', { - defaultMessage: 'Add {field} to workspace', - values: { - field: value.field.name, - }, - }) - : i18n.translate('xpack.lens.indexPattern.moveToWorkspaceNotAvailable', { - defaultMessage: - 'To visualize this field, please add it directly to the desired layer. Adding this field to the workspace is not supported based on your current configuration.', - }); - return (
  • ); -}; +} -export const FieldItem = React.memo(InnerFieldItem); +export const FieldItem = React.memo(InnerFieldItem) as typeof InnerFieldItem; function FieldItemPopoverContents( props: FieldItemProps & { @@ -252,10 +261,13 @@ function FieldItemPopoverContents( onAddFilter: AddFieldFilterHandler | undefined; } ) { - const { query, filters, indexPattern, dataViewField, dateRange, onAddFilter, uiActions } = props; + const { query, filters, indexPattern, dataViewField, dateRange, onAddFilter } = props; const services = useKibana().services; const exploreInDiscover = useMemo(() => { + if (!indexPattern) { + return null; + } const meta = { id: indexPattern.id, columns: [dataViewField.name], @@ -290,6 +302,10 @@ function FieldItemPopoverContents( }); }, [dataViewField.name, filters, indexPattern, query, services]); + if (!indexPattern) { + return null; + } + return ( <> {dataViewField.type === 'geo_point' || dataViewField.type === 'geo_shape' ? ( - indexPattern.spec } as unknown as DataView} originatingApp={APP_ID} - uiActions={uiActions} + uiActions={services.uiActions} buttonProps={{ 'data-test-subj': `lensVisualize-GeoField-${dataViewField.name}`, }} diff --git a/x-pack/plugins/lens/public/datasources/common/get_field_item_actions.tsx b/x-pack/plugins/lens/public/datasources/common/get_field_item_actions.tsx new file mode 100644 index 0000000000000..70f4d58fd6ce4 --- /dev/null +++ b/x-pack/plugins/lens/public/datasources/common/get_field_item_actions.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 { type DragDropIdentifier } from '@kbn/dom-drag-drop'; +import type { FieldItemButtonProps, FieldListItem } from '@kbn/unified-field-list-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { type DataViewField } from '@kbn/data-views-plugin/common'; + +interface GetFieldItemActionsParams { + value: DragDropIdentifier; + dropOntoWorkspace: (value: DragDropIdentifier) => void; + hasSuggestionForField: (value: DragDropIdentifier) => boolean; + closeFieldPopover?: () => void; +} + +interface GetFieldItemActionsResult { + buttonAddFieldToWorkspaceProps: FieldItemButtonProps['buttonAddFieldToWorkspaceProps']; + onAddFieldToWorkspace: FieldItemButtonProps['onAddFieldToWorkspace']; +} + +export function getFieldItemActions({ + value, + hasSuggestionForField, + dropOntoWorkspace, + closeFieldPopover, +}: GetFieldItemActionsParams): GetFieldItemActionsResult { + const canAddToWorkspace = hasSuggestionForField(value); + const addToWorkplaceButtonTitle = canAddToWorkspace + ? i18n.translate('xpack.lens.indexPattern.moveToWorkspace', { + defaultMessage: 'Add {field} to workspace', + values: { + field: value.humanData.label, + }, + }) + : i18n.translate('xpack.lens.indexPattern.moveToWorkspaceNotAvailable', { + defaultMessage: + 'To visualize this field, please add it directly to the desired layer. Adding this field to the workspace is not supported based on your current configuration.', + }); + + const dropOntoWorkspaceAndClose = () => { + closeFieldPopover?.(); + dropOntoWorkspace(value); + }; + + return { + buttonAddFieldToWorkspaceProps: { + isDisabled: !canAddToWorkspace, + 'aria-label': addToWorkplaceButtonTitle, + }, + onAddFieldToWorkspace: dropOntoWorkspaceAndClose, + }; +} diff --git a/x-pack/plugins/lens/public/datasources/form_based/datapanel.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/datapanel.test.tsx index 364e811740737..c32ac66051eae 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/datapanel.test.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/datapanel.test.tsx @@ -17,7 +17,7 @@ import { InnerFormBasedDataPanel, FormBasedDataPanel } from './datapanel'; import { FieldListGrouped } from '@kbn/unified-field-list-plugin/public'; import * as UseExistingFieldsApi from '@kbn/unified-field-list-plugin/public/hooks/use_existing_fields'; import * as ExistingFieldsServiceApi from '@kbn/unified-field-list-plugin/public/services/field_existing/load_field_existing'; -import { FieldItem } from './field_item'; +import { FieldItem } from '../common/field_item'; import { act } from 'react-dom/test-utils'; import { coreMock } from '@kbn/core/public/mocks'; import { FormBasedPrivateState } from './types'; @@ -37,6 +37,7 @@ import { DataViewsState } from '../../state_management'; import { DataView } from '@kbn/data-views-plugin/public'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; import { ReactWrapper } from 'enzyme'; +import { IndexPatternField } from '../../types'; const fieldsOne = [ { @@ -787,7 +788,11 @@ describe('FormBased Data Panel', () => { it('should list all supported fields in the pattern sorted alphabetically in groups', async () => { const wrapper = await mountAndWaitForLazyModules(); - expect(wrapper.find(FieldItem).first().prop('field').displayName).toEqual('Records'); + expect(wrapper.find(FieldItem).first().prop('field')).toEqual( + expect.objectContaining({ + displayName: 'Records', + }) + ); const availableAccordion = wrapper.find('[data-test-subj="lnsIndexPatternAvailableFields"]'); expect( availableAccordion.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name) @@ -803,7 +808,9 @@ describe('FormBased Data Panel', () => { emptyAccordion.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name) ).toEqual(['client', 'source', 'timestamp']); expect( - emptyAccordion.find(FieldItem).map((fieldItem) => fieldItem.prop('field').displayName) + emptyAccordion + .find(FieldItem) + .map((fieldItem) => (fieldItem.prop('field') as IndexPatternField).displayName) ).toEqual(['client', 'source', 'timestampLabel']); expect(emptyAccordion.find(FieldItem).at(1).prop('exists')).toEqual(false); }); @@ -872,7 +879,7 @@ describe('FormBased Data Panel', () => { wrapper .find('[data-test-subj="lnsIndexPatternEmptyFields"]') .find(FieldItem) - .map((fieldItem) => fieldItem.prop('field').displayName) + .map((fieldItem) => (fieldItem.prop('field') as IndexPatternField).displayName) ).toEqual(['amemory', 'bytes', 'client', 'source', 'timestampLabel']); }); @@ -974,7 +981,9 @@ describe('FormBased Data Panel', () => { wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click'); expect( - wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').displayName) + wrapper + .find(FieldItem) + .map((fieldItem) => (fieldItem.prop('field') as IndexPatternField).displayName) ).toEqual(['amemory', 'bytes']); }); @@ -1010,7 +1019,9 @@ describe('FormBased Data Panel', () => { .first() .simulate('click'); expect( - wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').displayName) + wrapper + .find(FieldItem) + .map((fieldItem) => (fieldItem.prop('field') as IndexPatternField).displayName) ).toEqual(['Records', 'amemory', 'bytes', 'client', 'source', 'timestampLabel']); }); diff --git a/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx b/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx index e1b561cb66509..b82dadcabcc88 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx @@ -37,7 +37,7 @@ import type { } from '../../types'; import type { FormBasedPrivateState } from './types'; import { IndexPatternServiceAPI } from '../../data_views_service/service'; -import { FieldItem } from './field_item'; +import { FieldItem } from '../common/field_item'; export type Props = Omit< DatasourceDataPanelProps, @@ -175,9 +175,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ core, data, dataViews, - fieldFormats, indexPatternFieldEditor, - charts, dropOntoWorkspace, hasSuggestionForField, uiActions, @@ -380,30 +378,22 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ hasSuggestionForField={hasSuggestionForField} editField={editField} removeField={removeField} - uiActions={uiActions} - core={core} - fieldFormats={fieldFormats} indexPattern={currentIndexPattern} highlight={fieldSearchHighlight} dateRange={dateRange} query={query} filters={filters} - chartsThemeService={charts.theme} /> ), [ - core, - fieldFormats, currentIndexPattern, dateRange, query, filters, - charts.theme, dropOntoWorkspace, hasSuggestionForField, editField, removeField, - uiActions, ] ); diff --git a/x-pack/plugins/lens/public/datasources/form_based/field_item.scss b/x-pack/plugins/lens/public/datasources/form_based/field_item.scss deleted file mode 100644 index 62ba6135aca52..0000000000000 --- a/x-pack/plugins/lens/public/datasources/form_based/field_item.scss +++ /dev/null @@ -1,26 +0,0 @@ -.kbnFieldButton { - .lnsFieldItem__infoIcon { - visibility: hidden; - opacity: 0; - } - - &:hover:not([class*='isActive']) { - .lnsFieldItem__infoIcon { - visibility: visible; - opacity: 1; - transition: opacity $euiAnimSpeedFast ease-in-out 1s; - } - } -} - -.kbnFieldButton.domDragDrop_ghost { - .lnsFieldItem__infoIcon { - visibility: hidden; - opacity: 0; - } -} - -.lnsFieldItem__fieldPanel { - min-width: 260px; - max-width: 300px; -} diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts index 0490f3c479297..0bdf9aa0bc795 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.test.ts @@ -29,10 +29,12 @@ import { getFieldByNameFactory } from '../pure_helpers'; import { generateId } from '../../../id_generator'; import { createMockedFullReference, createMockedManagedReference } from './mocks'; import { + CounterRateIndexPatternColumn, FiltersIndexPatternColumn, FormulaIndexPatternColumn, GenericIndexPatternColumn, MathIndexPatternColumn, + MaxIndexPatternColumn, MovingAverageIndexPatternColumn, OperationDefinition, } from './definitions'; @@ -1356,6 +1358,47 @@ describe('state_helpers', () => { }).columns.col1 ).toEqual(expect.objectContaining({ label: 'Average of bytes' })); }); + + it('should update default label when referenced column gets a field change', () => { + expect( + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'MyDefaultLabel', + dataType: 'number', + operationType: 'counter_rate', + isBucketed: false, + scale: 'ratio', + references: ['col2'], + timeScale: 's', + timeShift: '', + filter: undefined, + params: undefined, + } as CounterRateIndexPatternColumn, + col2: { + label: 'Max of bytes', + dataType: 'number', + operationType: 'max', + scale: 'ratio', + sourceField: indexPattern.fields[2].displayName, + } as MaxIndexPatternColumn, + }, + }, + indexPattern, + columnId: 'col2', + op: 'max', + field: indexPattern.fields[3], + visualizationGroups: [], + }).columns.col1 + ).toEqual( + expect.objectContaining({ + label: 'Counter rate of memory per second', + }) + ); + }); }); it('should execute adjustments for other columns', () => { diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts index efa523a5bd5da..e6ac80c439083 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/layer_helpers.ts @@ -836,12 +836,16 @@ export function replaceColumn({ { ...layer, columns: { ...layer.columns, [columnId]: newColumn } }, columnId ); - return adjustColumnReferencesForChangedColumn( - { - ...newLayer, - columnOrder: getColumnOrder(newLayer), - }, - columnId + + return updateDefaultLabels( + adjustColumnReferencesForChangedColumn( + { + ...newLayer, + columnOrder: getColumnOrder(newLayer), + }, + columnId + ), + indexPattern ); } else if (operationDefinition.input === 'managedReference') { // Just changing a param in a formula column should trigger diff --git a/x-pack/plugins/lens/public/datasources/text_based/datapanel.test.tsx b/x-pack/plugins/lens/public/datasources/text_based/datapanel.test.tsx index 9e9b76ceaf0ff..abc84813ce9a2 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/datapanel.test.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/datapanel.test.tsx @@ -18,7 +18,7 @@ import { Start as DataViewPublicStart, } from '@kbn/data-views-plugin/public/mocks'; import type { DatatableColumn } from '@kbn/expressions-plugin/public'; -import { EuiHighlight } from '@elastic/eui'; +import { EuiHighlight, EuiToken } from '@elastic/eui'; import { type TextBasedDataPanelProps, TextBasedDataPanel } from './datapanel'; @@ -274,4 +274,15 @@ describe('TextBased Query Languages Data Panel', () => { .map((item) => item.prop('children')) ).toEqual(['memory']); }); + + it('should render correct field type icons', async () => { + const wrapper = await mountAndWaitForLazyModules(); + + expect( + wrapper + .find('[data-test-subj="lnsTextBasedLanguagesAvailableFields"]') + .find(EuiToken) + .map((item) => item.prop('iconType')) + ).toEqual(['tokenNumber', 'tokenNumber', 'tokenDate']); + }); }); diff --git a/x-pack/plugins/lens/public/datasources/text_based/datapanel.tsx b/x-pack/plugins/lens/public/datasources/text_based/datapanel.tsx index 9bdc052cbae47..0f62b52d29068 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/datapanel.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/datapanel.tsx @@ -18,17 +18,17 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { FieldList, FieldListFilters, - FieldItemButton, - GetCustomFieldType, FieldListGrouped, FieldListGroupedProps, FieldsGroupNames, + GetCustomFieldType, useGroupedFields, } from '@kbn/unified-field-list-plugin/public'; -import { ChildDragDropProvider, DragDrop } from '@kbn/dom-drag-drop'; +import { ChildDragDropProvider } from '@kbn/dom-drag-drop'; import type { DatasourceDataPanelProps } from '../../types'; import type { TextBasedPrivateState } from './types'; import { getStateFromAggregateQuery } from './utils'; +import { FieldItem } from '../common/field_item'; const getCustomFieldType: GetCustomFieldType = (field) => field?.meta.type; @@ -52,6 +52,8 @@ export function TextBasedDataPanel({ expressions, dataViews, layerFields, + hasSuggestionForField, + dropOntoWorkspace, }: TextBasedDataPanelProps) { const prevQuery = usePrevious(query); const [dataHasLoaded, setDataHasLoaded] = useState(false); @@ -108,33 +110,26 @@ export function TextBasedDataPanel({ }); const renderFieldItem: FieldListGroupedProps['renderFieldItem'] = useCallback( - ({ field, itemIndex, fieldSearchHighlight }) => { + ({ field, groupIndex, itemIndex, fieldSearchHighlight, groupName }) => { if (!field) { return <>; } + return ( - - - isEmpty={false} - isActive={false} - field={field} - fieldSearchHighlight={fieldSearchHighlight} - getCustomFieldType={getCustomFieldType} - onClick={() => {}} - /> - + ); }, - [] + [hasSuggestionForField, dropOntoWorkspace] ); return ( diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index 1d20433893a61..c3da573af3433 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; import type { SavedObjectReference } from '@kbn/core/public'; -import { EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; +import { EuiFormRow } from '@elastic/eui'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { ExpressionsStart, DatatableColumnType } from '@kbn/expressions-plugin/public'; import type { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public'; @@ -42,6 +42,7 @@ import type { import { FieldSelect } from './field_select'; import type { Datasource, IndexPatternMap } from '../../types'; import { LayerPanel } from './layerpanel'; +import { DimensionTrigger } from '../../shared_components/dimension_trigger'; function getLayerReferenceName(layerId: string) { return `textBasedLanguages-datasource-layer-${layerId}`; @@ -377,16 +378,17 @@ export function getTextBasedDatasource({ } render( - {}} - data-test-subj="lns-dimensionTrigger-textBased" - > - {customLabel ?? + dataTestSubj="lns-dimensionTrigger-textBased" + label={ + customLabel ?? i18n.translate('xpack.lens.textBasedLanguages.missingField', { defaultMessage: 'Missing field', - })} - , + }) + } + />, domElement ); }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx index 430ce019aaa20..299f2b81fc5bb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx @@ -66,13 +66,12 @@ export function AddLayerButton({ position="bottom" > onAddLayerClick(supportedLayers[0].type)} iconType="layers" > @@ -89,13 +88,12 @@ export function AddLayerButton({ data-test-subj="lnsConfigPanel__addLayerPopover" button={ toggleLayersChoice(!showLayersChoice)} iconType="layers" > diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/dimension_button.tsx index ad3ae44a2b3e6..71f7a2928bc41 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/dimension_button.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiButtonIcon, EuiLink, EuiToolTip } from '@elastic/eui'; +import { EuiButtonIcon, EuiLink, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -28,7 +28,9 @@ export function DimensionButton({ accessorConfig, label, message, + ...otherProps // from Drag&Drop integration }: { + className?: string; group: VisualizationDimensionGroupConfig; children: React.ReactElement; onClick: (id: string) => void; @@ -38,32 +40,34 @@ export function DimensionButton({ message: UserMessage | undefined; }) { return ( - <> -
    - - onClick(accessorConfig.columnId)} - aria-label={triggerLinkA11yText(label)} - title={triggerLinkA11yText(label)} - color={ - message?.severity === 'error' - ? 'danger' - : message?.severity === 'warning' - ? 'warning' - : undefined - } +
    + + + - - {children} - - - -
    + onClick(accessorConfig.columnId)} + aria-label={triggerLinkA11yText(label)} + title={triggerLinkA11yText(label)} + color={ + message?.severity === 'error' + ? 'danger' + : message?.severity === 'warning' + ? 'warning' + : 'text' + } + > + + {children} + + +
    + + - +
    ); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index 2159ac29327b2..f0ef1508c5076 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -60,7 +60,7 @@ const DefaultEmptyButton = ({ columnId, group, onClick }: EmptyButtonProps) => { className="lnsLayerPanel__triggerText" color="text" size="s" - iconType="plusInCircleFilled" + iconType="plus" contentProps={{ className: 'lnsLayerPanel__triggerTextContent', }} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index ff0c03fdc8f73..1ff1cd513cee9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -54,7 +54,7 @@ &, .euiFormRow__fieldWrapper { & > * + * { - margin-top: $euiSize; + margin-top: $euiSizeS; } } } @@ -64,22 +64,39 @@ padding: $euiSizeS $euiSize; } +.lnsLayerPanel__dimensionRemove { + margin-right: $euiSizeS; + visibility: hidden; + opacity: 0; +} + .lnsLayerPanel__dimension { @include euiFontSizeS; border-radius: $euiBorderRadius; display: flex; align-items: center; overflow: hidden; - min-height: $euiSizeXXL; + min-height: $euiSizeXL; position: relative; // NativeRenderer is messing this up > div { flex-grow: 1; } + + &:hover, + &:focus { + .lnsLayerPanel__dimensionRemove { + visibility: visible; + opacity: 1; + transition: opacity $euiAnimSpeedFast ease-in-out; + } + } } .lnsLayerPanel__dimension--empty { + border: $euiBorderWidthThin dashed $euiBorderColor !important; + &:focus, &:focus-within { @include euiFocusRing; @@ -94,26 +111,31 @@ } } -.lnsLayerPanel__dimensionRemove { - margin-right: $euiSizeS; -} - .lnsLayerPanel__triggerText { width: 100%; - padding: $euiSizeS; - min-height: $euiSizeXXL - 2; + padding: $euiSizeXS $euiSizeS; word-break: break-word; font-weight: $euiFontWeightRegular; } -.domDragDrop-isReplacing { - .lnsLayerPanel__triggerText { - text-decoration: line-through; +.lnsLayerPanel__dimensionLink { + &:hover { + text-decoration: none; } } .lnsLayerPanel__triggerTextLabel { transition: background-color $euiAnimSpeedFast ease-in-out; + + &:hover { + text-decoration: underline; + } +} + +.domDragDrop-isReplacing { + .lnsLayerPanel__triggerText { + text-decoration: line-through; + } } .lnsLayerPanel__triggerTextContent { @@ -139,6 +161,7 @@ } .lnsLayerPanel__palette { + height: $euiSizeXS / 2; border-radius: 0 0 ($euiBorderRadius - 1px) ($euiBorderRadius - 1px); &::after { @@ -153,5 +176,6 @@ &:focus { @include passDownFocusRing('.lnsLayerPanel__triggerTextLabel'); background-color: transparent; + text-decoration-thickness: $euiBorderWidthThin !important; } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index eaec08cf3f0e5..18b5acd160aaf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -826,7 +826,8 @@ describe('LayerPanel', () => { const dragDropElement = instance .find('[data-test-subj="lnsGroup"] DragDrop') .first() - .find('.lnsLayerPanel__dimension'); + .find('.lnsLayerPanel__dimension') + .first(); dragDropElement.simulate('dragOver'); dragDropElement.simulate('drop'); @@ -906,7 +907,7 @@ describe('LayerPanel', () => { const updatedDragDropElement = instance .find('[data-test-subj="lnsGroupB"] DragDrop .domDragDrop') - .at(2); + .last(); updatedDragDropElement.simulate('dragOver'); updatedDragDropElement.simulate('drop'); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 43587e1690298..a547e2a022ec2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -571,46 +571,45 @@ export function LayerPanel( onDrop={onDrop} indexPatterns={dataViews.indexPatterns} > -
    - { - setActiveDimension({ - isNew: false, - activeGroup: group, - activeId: id, - }); - }} - onRemoveClick={(id: string) => { - props.onRemoveDimension({ columnId: id, layerId }); - removeButtonRef(id); - }} - message={messages[0]} - > - {layerDatasource ? ( - - ) : ( - <> - {activeVisualization?.renderDimensionTrigger?.({ - columnId, - label: columnLabelMap?.[columnId] ?? '', - hideTooltip, - })} - - )} - -
    + { + setActiveDimension({ + isNew: false, + activeGroup: group, + activeId: id, + }); + }} + onRemoveClick={(id: string) => { + props.onRemoveDimension({ columnId: id, layerId }); + removeButtonRef(id); + }} + message={messages[0]} + > + {layerDatasource ? ( + + ) : ( + <> + {activeVisualization?.renderDimensionTrigger?.({ + columnId, + label: columnLabelMap?.[columnId] ?? '', + hideTooltip, + })} + + )} + ); })} diff --git a/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx b/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx index b4cd1dcc0d828..d705c50017a8d 100644 --- a/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx +++ b/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx @@ -7,6 +7,7 @@ import { EuiText, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { EuiTextProps } from '@elastic/eui/src/components/text/text'; export const defaultDimensionTriggerTooltip = (

    @@ -20,13 +21,24 @@ export const defaultDimensionTriggerTooltip = (

    ); -export const DimensionTrigger = ({ id, label }: { label: string; id: string }) => { +export const DimensionTrigger = ({ + id, + label, + color, + dataTestSubj, +}: { + label: string; + id: string; + color?: EuiTextProps['color']; + dataTestSubj?: string; +}) => { return ( diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index ffba67d77336d..370c855ef0d2e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -167,6 +167,7 @@ describe('annotation group save action', () => { onTitleDuplicate: () => {}, }, dataViews, + goToAnnotationLibrary: () => {}, }; }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 3a8dab39e1a2f..bebee83801514 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -316,10 +316,6 @@ export const getXyVisualization = ({ ); }, - onLayerAction(layerId, actionId, state) { - return state; - }, - onIndexPatternChange(state, indexPatternId, layerId) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; diff --git a/x-pack/plugins/lens/server/saved_objects.ts b/x-pack/plugins/lens/server/saved_objects.ts index ed3451862b176..b91d138902140 100644 --- a/x-pack/plugins/lens/server/saved_objects.ts +++ b/x-pack/plugins/lens/server/saved_objects.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CoreSetup } from '@kbn/core/server'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; @@ -19,6 +20,7 @@ export function setupSavedObjects( ) { core.savedObjects.registerType({ name: 'lens', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', @@ -59,6 +61,7 @@ export function setupSavedObjects( core.savedObjects.registerType({ name: 'lens-ui-telemetry', + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'single', mappings: { diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index 95675fe96aee8..a0f2bf9c21fe4 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -11,6 +11,7 @@ import { addExceptionListItem, deleteExceptionListById, deleteExceptionListItemById, + duplicateExceptionList, exportExceptionList, fetchExceptionListById, fetchExceptionListItemById, @@ -728,4 +729,30 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(blob); }); }); + + describe('#duplicateExceptionList', () => { + beforeEach(() => { + httpMock.fetch.mockResolvedValue(getExceptionListSchemaMock()); + }); + + test('it invokes "duplicateExceptionList" with expected url and body values', async () => { + await duplicateExceptionList({ + http: httpMock, + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/_duplicate', { + method: 'POST', + query: { + include_expired_exceptions: false, + list_id: 'my_list', + namespace_type: 'single', + }, + signal: abortCtrl.signal, + }); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts index bf10fb57f1a5c..55c8ebde7cebd 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_api.test.ts @@ -12,6 +12,7 @@ import type { AddExceptionListItemProps, ApiCallByIdProps, ApiCallByListIdProps, + DuplicateExceptionListProps, UpdateExceptionListItemProps, } from '@kbn/securitysolution-io-ts-list-types'; import { coreMock } from '@kbn/core/public/mocks'; @@ -462,4 +463,61 @@ describe('useApi', () => { }); }); }); + + describe('duplicateExceptionList', () => { + test('it invokes "onSuccess" when duplication does not throw', async () => { + const onSuccessMock = jest.fn(); + const spyOnDuplicateExceptionList = jest + .spyOn(api, 'duplicateExceptionList') + .mockResolvedValue(getExceptionListSchemaMock()); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useApi(mockKibanaHttpService) + ); + await waitForNextUpdate(); + + await result.current.duplicateExceptionList({ + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + onError: jest.fn(), + onSuccess: onSuccessMock, + }); + + const expected: DuplicateExceptionListProps = { + http: mockKibanaHttpService, + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + signal: new AbortController().signal, + }; + + expect(spyOnDuplicateExceptionList).toHaveBeenCalledWith(expected); + expect(onSuccessMock).toHaveBeenCalled(); + }); + }); + + test('invokes "onError" callback if "duplicateExceptionList" fails', async () => { + const mockError = new Error('failed to duplicate item'); + jest.spyOn(api, 'duplicateExceptionList').mockRejectedValue(mockError); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useApi(mockKibanaHttpService) + ); + await waitForNextUpdate(); + + await result.current.duplicateExceptionList({ + includeExpiredExceptions: false, + listId: 'my_list', + namespaceType: 'single', + onError: onErrorMock, + onSuccess: jest.fn(), + }); + + expect(onErrorMock).toHaveBeenCalledWith(mockError); + }); + }); + }); }); diff --git a/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts b/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts new file mode 100644 index 0000000000000..2724215971aae --- /dev/null +++ b/x-pack/plugins/lists/server/routes/duplicate_exception_list_route.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. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import { + DuplicateExceptionListQuerySchemaDecoded, + duplicateExceptionListQuerySchema, + exceptionListSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { validate } from '@kbn/securitysolution-io-ts-utils'; +import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; + +import type { ListsPluginRouter } from '../types'; + +import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils'; + +export const duplicateExceptionsRoute = (router: ListsPluginRouter): void => { + router.post( + { + options: { + tags: ['access:lists-all'], + }, + path: `${EXCEPTION_LIST_URL}/_duplicate`, + validate: { + query: buildRouteValidation< + typeof duplicateExceptionListQuerySchema, + DuplicateExceptionListQuerySchemaDecoded + >(duplicateExceptionListQuerySchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const { + list_id: listId, + namespace_type: namespaceType, + include_expired_exceptions: includeExpiredExceptionsString, + } = request.query; + + const exceptionListsClient = await getExceptionListClient(context); + + // fetch list container + const listToDuplicate = await exceptionListsClient.getExceptionList({ + id: undefined, + listId, + namespaceType, + }); + + if (listToDuplicate == null) { + return siemResponse.error({ + body: `exception list id: "${listId}" does not exist`, + statusCode: 404, + }); + } + + // Defaults to including expired exceptions if query param is not present + const includeExpiredExceptions = + includeExpiredExceptionsString !== undefined + ? includeExpiredExceptionsString === 'true' + : true; + const duplicatedList = await exceptionListsClient.duplicateExceptionListAndItems({ + includeExpiredExceptions, + list: listToDuplicate, + namespaceType, + }); + + if (duplicatedList == null) { + return siemResponse.error({ + body: `unable to duplicate exception list with list_id: ${listId} - action not allowed`, + statusCode: 405, + }); + } + + const [validated, errors] = validate(duplicatedList, exceptionListSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts index 851ed971c9b09..6347d564981bf 100644 --- a/x-pack/plugins/lists/server/routes/index.ts +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -18,6 +18,7 @@ export * from './delete_exception_list_item_route'; export * from './delete_list_index_route'; export * from './delete_list_item_route'; export * from './delete_list_route'; +export * from './duplicate_exception_list_route'; export * from './export_exception_list_route'; export * from './export_list_item_route'; export * from './find_endpoint_list_item_route'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 4649a82a8e4a1..7fd87c72765bd 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -22,6 +22,7 @@ import { deleteListIndexRoute, deleteListItemRoute, deleteListRoute, + duplicateExceptionsRoute, exportExceptionsRoute, exportListItemRoute, findEndpointListItemRoute, @@ -87,6 +88,7 @@ export const initRoutes = (router: ListsPluginRouter, config: ConfigType): void updateExceptionListRoute(router); deleteExceptionListRoute(router); findExceptionListRoute(router); + duplicateExceptionsRoute(router); // exception list items createExceptionListItemRoute(router); diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/plugins/lists/server/saved_objects/exception_list.ts index 4d401da8d74d2..b475f1586eb1f 100644 --- a/x-pack/plugins/lists/server/saved_objects/exception_list.ts +++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectsType } from '@kbn/core/server'; import { exceptionListAgnosticSavedObjectType, @@ -182,6 +183,7 @@ const combinedMappings: SavedObjectsType['mappings'] = { export const exceptionListType: SavedObjectsType = { convertToMultiNamespaceTypeVersion: '8.0.0', hidden: false, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, mappings: combinedMappings, migrations, name: exceptionListSavedObjectType, @@ -190,6 +192,7 @@ export const exceptionListType: SavedObjectsType = { export const exceptionListAgnosticType: SavedObjectsType = { hidden: false, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, mappings: combinedMappings, migrations, name: exceptionListAgnosticSavedObjectType, diff --git a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts new file mode 100644 index 0000000000000..562c6ac674beb --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts @@ -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 { savedObjectsClientMock } from '@kbn/core/server/mocks'; + +import { + getDetectionsExceptionListSchemaMock, + getTrustedAppsListSchemaMock, +} from '../../../common/schemas/response/exception_list_schema.mock'; +import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock'; + +import { findExceptionListsItemPointInTimeFinder } from './find_exception_list_items_point_in_time_finder'; +import { duplicateExceptionListAndItems } from './duplicate_exception_list'; +import { getExceptionList } from './get_exception_list'; +import { createExceptionList } from './create_exception_list'; + +jest.mock('./get_exception_list'); +jest.mock('./create_exception_list'); +jest.mock('./bulk_create_exception_list_items'); +jest.mock('./find_exception_list_items_point_in_time_finder'); + +const mockCurrentTime = new Date('2023-02-01T10:20:30Z'); + +describe('duplicateExceptionListAndItems', () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(mockCurrentTime); + }); + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + test('should return null exception list is not of type "detection" or "rule_default"', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getTrustedAppsListSchemaMock()); + + const result = await duplicateExceptionListAndItems({ + includeExpiredExceptions: true, + list: getTrustedAppsListSchemaMock(), + namespaceType: 'single', + savedObjectsClient: savedObjectsClientMock.create(), + user: 'test-user', + }); + + expect(result).toBeNull(); + }); + + test('should duplicate a list with expired exceptions', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getDetectionsExceptionListSchemaMock()); + (createExceptionList as jest.Mock).mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + list_id: 'exception_list_id_dupe', + name: 'Test [Duplicate]', + }); + (findExceptionListsItemPointInTimeFinder as jest.Mock).mockImplementationOnce( + ({ executeFunctionOnStream }) => { + executeFunctionOnStream({ data: [getExceptionListItemSchemaMock()] }); + } + ); + + await duplicateExceptionListAndItems({ + includeExpiredExceptions: true, + list: getDetectionsExceptionListSchemaMock(), + namespaceType: 'single', + savedObjectsClient: savedObjectsClientMock.create(), + user: 'test-user', + }); + + expect(findExceptionListsItemPointInTimeFinder).toHaveBeenCalledWith({ + executeFunctionOnStream: expect.any(Function), + filter: [], + listId: ['exception_list_id'], + maxSize: 10000, + namespaceType: ['single'], + perPage: undefined, + savedObjectsClient: expect.any(Object), + sortField: undefined, + sortOrder: undefined, + }); + }); + + test('should duplicate a list without expired exceptions', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getDetectionsExceptionListSchemaMock()); + (createExceptionList as jest.Mock).mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + list_id: 'exception_list_id_dupe', + name: 'Test [Duplicate]', + }); + (findExceptionListsItemPointInTimeFinder as jest.Mock).mockImplementationOnce( + ({ executeFunctionOnStream }) => { + executeFunctionOnStream({ data: [getExceptionListItemSchemaMock()] }); + } + ); + + await duplicateExceptionListAndItems({ + includeExpiredExceptions: false, + list: getDetectionsExceptionListSchemaMock(), + namespaceType: 'single', + savedObjectsClient: savedObjectsClientMock.create(), + user: 'test-user', + }); + + expect(findExceptionListsItemPointInTimeFinder).toHaveBeenCalledWith({ + executeFunctionOnStream: expect.any(Function), + filter: [ + '(exception-list.attributes.expire_time > "2023-02-01T10:20:30.000Z" OR NOT exception-list.attributes.expire_time: *)', + ], + listId: ['exception_list_id'], + maxSize: 10000, + namespaceType: ['single'], + perPage: undefined, + savedObjectsClient: expect.any(Object), + sortField: undefined, + sortOrder: undefined, + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts index 4d4c8a07b455e..0bd17f8c39e2e 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.ts @@ -12,13 +12,12 @@ import { ExceptionListSchema, ExceptionListTypeEnum, FoundExceptionListItemSchema, - ListId, NamespaceType, } from '@kbn/securitysolution-io-ts-list-types'; +import { getSavedObjectType } from '@kbn/securitysolution-list-utils'; import { findExceptionListsItemPointInTimeFinder } from './find_exception_list_items_point_in_time_finder'; import { bulkCreateExceptionListItems } from './bulk_create_exception_list_items'; -import { getExceptionList } from './get_exception_list'; import { createExceptionList } from './create_exception_list'; const LISTS_ABLE_TO_DUPLICATE = [ @@ -26,48 +25,38 @@ const LISTS_ABLE_TO_DUPLICATE = [ ExceptionListTypeEnum.RULE_DEFAULT.toString(), ]; -interface CreateExceptionListOptions { - listId: ListId; +interface DuplicateExceptionListOptions { + list: ExceptionListSchema; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; user: string; + includeExpiredExceptions: boolean; } export const duplicateExceptionListAndItems = async ({ - listId, + includeExpiredExceptions, + list, savedObjectsClient, namespaceType, user, -}: CreateExceptionListOptions): Promise => { +}: DuplicateExceptionListOptions): Promise => { // Generate a new static listId const newListId = uuidv4(); - // fetch list container - const listToDuplicate = await getExceptionList({ - id: undefined, - listId, - namespaceType, - savedObjectsClient, - }); - - if (listToDuplicate == null) { - throw new Error(`Exception list to duplicat of list_id:${listId} not found.`); - } - - if (!LISTS_ABLE_TO_DUPLICATE.includes(listToDuplicate.type)) { - throw new Error(`Exception list of type:${listToDuplicate.type} cannot be duplicated.`); + if (!LISTS_ABLE_TO_DUPLICATE.includes(list.type)) { + return null; } const newlyCreatedList = await createExceptionList({ - description: listToDuplicate.description, - immutable: listToDuplicate.immutable, + description: list.description, + immutable: list.immutable, listId: newListId, - meta: listToDuplicate.meta, - name: listToDuplicate.name, - namespaceType: listToDuplicate.namespace_type, + meta: list.meta, + name: `${list.name} [Duplicate]`, + namespaceType: list.namespace_type, savedObjectsClient, - tags: listToDuplicate.tags, - type: listToDuplicate.type, + tags: list.tags, + type: list.type, user, version: 1, }); @@ -96,10 +85,16 @@ export const duplicateExceptionListAndItems = async ({ }); itemsToBeDuplicated = [...itemsToBeDuplicated, ...transformedItems]; }; + const savedObjectPrefix = getSavedObjectType({ namespaceType }); + const filter = includeExpiredExceptions + ? [] + : [ + `(${savedObjectPrefix}.attributes.expire_time > "${new Date().toISOString()}" OR NOT ${savedObjectPrefix}.attributes.expire_time: *)`, + ]; await findExceptionListsItemPointInTimeFinder({ executeFunctionOnStream, - filter: [], - listId: [listId], + filter, + listId: [list.list_id], maxSize: 10000, namespaceType: [namespaceType], perPage: undefined, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index db9c62ae5b377..484353d30e346 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -317,17 +317,20 @@ export class ExceptionListClient { /** * Create the Trusted Apps Agnostic list if it does not yet exist (`null` is returned if it does exist) - * @param options.listId the "list_id" of the exception list + * @param options.list the "list" to be duplicated * @param options.namespaceType saved object namespace (single | agnostic) + * @param options.includeExpiredExceptions include or exclude expired TTL exception items * @returns The exception list schema or null if it does not exist */ public duplicateExceptionListAndItems = async ({ - listId, + list, namespaceType, + includeExpiredExceptions, }: DuplicateExceptionListOptions): Promise => { const { savedObjectsClient, user } = this; return duplicateExceptionListAndItems({ - listId, + includeExpiredExceptions, + list, namespaceType, savedObjectsClient, user, @@ -1051,6 +1054,7 @@ export class ExceptionListClient { * @param options.listId the "list_id" of an exception list * @param options.id the "id" of an exception list * @param options.namespaceType saved object namespace (single | agnostic) + * @param options.includeExpiredExceptions include or exclude expired TTL exception items * @returns the ndjson of the list and items to export or null if none exists */ public exportExceptionListAndItems = async ({ diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index b93de1413e4db..14c75b84a1367 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -19,6 +19,7 @@ import type { EntriesArray, ExceptionListItemType, ExceptionListItemTypeOrUndefined, + ExceptionListSchema, ExceptionListType, ExceptionListTypeOrUndefined, ExpireTimeOrUndefined, @@ -295,10 +296,12 @@ export interface CreateEndpointListItemOptions { * {@link ExceptionListClient.duplicateExceptionListAndItems} */ export interface DuplicateExceptionListOptions { - /** The single list id to do the search against */ - listId: ListId; + /** The list to be duplicated */ + list: ExceptionListSchema; /** saved object namespace (single | agnostic) */ namespaceType: NamespaceType; + /** determines whether exception items with an expired TTL are included in duplication */ + includeExpiredExceptions: boolean; } /** diff --git a/x-pack/plugins/lists/tsconfig.json b/x-pack/plugins/lists/tsconfig.json index e6fb0a15e7586..b18887c254336 100644 --- a/x-pack/plugins/lists/tsconfig.json +++ b/x-pack/plugins/lists/tsconfig.json @@ -38,6 +38,7 @@ "@kbn/logging-mocks", "@kbn/utility-types", "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 6c8f719bf2886..8f5ad4869ebe7 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -188,10 +188,6 @@ export const GEOCENTROID_AGG_NAME = 'gridCentroid'; export const TOP_TERM_PERCENTAGE_SUFFIX = '__percentage'; export const DEFAULT_PERCENTILE = 50; -export const COUNT_PROP_LABEL = i18n.translate('xpack.maps.aggs.defaultCountLabel', { - defaultMessage: 'count', -}); - export const COUNT_PROP_NAME = 'doc_count'; export enum STYLE_TYPE { @@ -341,6 +337,11 @@ export enum WIZARD_ID { TMS_LAYER = 'tmsLayer', } +export enum MASK_OPERATOR { + ABOVE = 'ABOVE', + BELOW = 'BELOW', +} + // Maplibre does not provide any feedback when rendering is complete. // Workaround is hard-coded timeout period. export const RENDER_TIMEOUT = 1000; diff --git a/x-pack/plugins/maps/common/content_management/index.ts b/x-pack/plugins/maps/common/content_management/index.ts index daafc194a37fc..f389e3fea36e7 100644 --- a/x-pack/plugins/maps/common/content_management/index.ts +++ b/x-pack/plugins/maps/common/content_management/index.ts @@ -17,10 +17,10 @@ export type { MapGetOut, MapCreateIn, MapCreateOut, - CreateOptions, + MapCreateOptions, MapUpdateIn, MapUpdateOut, - UpdateOptions, + MapUpdateOptions, MapDeleteIn, MapDeleteOut, MapSearchIn, diff --git a/x-pack/plugins/maps/common/content_management/v1/cm_services.ts b/x-pack/plugins/maps/common/content_management/v1/cm_services.ts index 0e430b56ced98..5ea8008f53225 100644 --- a/x-pack/plugins/maps/common/content_management/v1/cm_services.ts +++ b/x-pack/plugins/maps/common/content_management/v1/cm_services.ts @@ -134,4 +134,11 @@ export const serviceDefinition: ServicesDefinition = { }, }, }, + mSearch: { + out: { + result: { + schema: mapSavedObjectSchema, + }, + }, + }, }; diff --git a/x-pack/plugins/maps/common/content_management/v1/index.ts b/x-pack/plugins/maps/common/content_management/v1/index.ts index 272e0e1eb5f2e..535a15e5a23a6 100644 --- a/x-pack/plugins/maps/common/content_management/v1/index.ts +++ b/x-pack/plugins/maps/common/content_management/v1/index.ts @@ -13,10 +13,10 @@ export type { MapGetOut, MapCreateIn, MapCreateOut, - CreateOptions, + MapCreateOptions, MapUpdateIn, MapUpdateOut, - UpdateOptions, + MapUpdateOptions, MapDeleteIn, MapDeleteOut, MapSearchIn, diff --git a/x-pack/plugins/maps/common/content_management/v1/types.ts b/x-pack/plugins/maps/common/content_management/v1/types.ts index d5a7351226c23..c19713e641659 100644 --- a/x-pack/plugins/maps/common/content_management/v1/types.ts +++ b/x-pack/plugins/maps/common/content_management/v1/types.ts @@ -6,24 +6,22 @@ */ import type { - GetIn, - GetResult, - CreateIn, - CreateResult, - SearchIn, - SearchResult, - UpdateIn, - UpdateResult, - DeleteIn, - DeleteResult, -} from '@kbn/content-management-plugin/common'; + ContentManagementCrudTypes, + SavedObjectCreateOptions, + SavedObjectUpdateOptions, +} from '@kbn/content-management-utils'; import { MapContentType } from '../types'; -interface Reference { - type: string; - id: string; - name: string; -} +export type MapCrudTypes = ContentManagementCrudTypes< + MapContentType, + MapAttributes, + Pick, + Pick, + { + /** Flag to indicate to only search the text on the "title" field */ + onlyTitle?: boolean; + } +>; /* eslint-disable-next-line @typescript-eslint/consistent-type-definitions */ export type MapAttributes = { @@ -34,77 +32,33 @@ export type MapAttributes = { uiStateJSON?: string; }; -export interface MapItem { - id: string; - type: string; - version?: string; - createdAt?: string; - updatedAt?: string; - error?: { - error: string; - message: string; - statusCode: number; - metadata?: Record; - }; - attributes: MapAttributes; - references: Reference[]; - namespaces?: string[]; - originId?: string; -} - -export type PartialMapItem = Omit & { - attributes: Partial; - references: Reference[] | undefined; -}; +export type MapItem = MapCrudTypes['Item']; +export type PartialMapItem = MapCrudTypes['PartialItem']; // ----------- GET -------------- -export type MapGetIn = GetIn; - -export type MapGetOut = GetResult< - MapItem, - { - outcome: 'exactMatch' | 'aliasMatch' | 'conflict'; - aliasTargetId?: string; - aliasPurpose?: 'savedObjectConversion' | 'savedObjectImport'; - } ->; +export type MapGetIn = MapCrudTypes['GetIn']; +export type MapGetOut = MapCrudTypes['GetOut']; // ----------- CREATE -------------- -export interface CreateOptions { - /** Array of referenced saved objects. */ - references?: Reference[]; -} - -export type MapCreateIn = CreateIn; - -export type MapCreateOut = CreateResult; +export type MapCreateIn = MapCrudTypes['CreateIn']; +export type MapCreateOut = MapCrudTypes['CreateOut']; +export type MapCreateOptions = MapCrudTypes['CreateOptions']; // ----------- UPDATE -------------- -export interface UpdateOptions { - /** Array of referenced saved objects. */ - references?: Reference[]; -} - -export type MapUpdateIn = UpdateIn; - -export type MapUpdateOut = UpdateResult; +export type MapUpdateIn = MapCrudTypes['UpdateIn']; +export type MapUpdateOut = MapCrudTypes['UpdateOut']; +export type MapUpdateOptions = MapCrudTypes['UpdateOptions']; // ----------- DELETE -------------- -export type MapDeleteIn = DeleteIn; - -export type MapDeleteOut = DeleteResult; +export type MapDeleteIn = MapCrudTypes['DeleteIn']; +export type MapDeleteOut = MapCrudTypes['DeleteOut']; // ----------- SEARCH -------------- -export interface MapSearchOptions { - /** Flag to indicate to only search the text on the "title" field */ - onlyTitle?: boolean; -} - -export type MapSearchIn = SearchIn; - -export type MapSearchOut = SearchResult; +export type MapSearchIn = MapCrudTypes['SearchIn']; +export type MapSearchOut = MapCrudTypes['SearchOut']; +export type MapSearchOptions = MapCrudTypes['SearchOptions']; diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index b862558d6a215..c76bc8f6a6e17 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -13,6 +13,7 @@ import { SortDirection } from '@kbn/data-plugin/common/search'; import { AGG_TYPE, GRID_RESOLUTION, + MASK_OPERATOR, RENDER_AS, SCALING_TYPES, MVT_FIELD_TYPE, @@ -49,6 +50,10 @@ export type AbstractESSourceDescriptor = AbstractSourceDescriptor & { type AbstractAggDescriptor = { type: AGG_TYPE; label?: string; + mask?: { + operator: MASK_OPERATOR; + value: number; + }; }; export type CountAggDescriptor = AbstractAggDescriptor & { diff --git a/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts b/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts index d881accb1e42f..6db0d32dbd551 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts @@ -9,14 +9,17 @@ import { DataView } from '@kbn/data-plugin/common'; import { IField } from '../field'; import { IESAggSource } from '../../sources/es_agg_source'; import { FIELD_ORIGIN } from '../../../../common/constants'; +import { AggDescriptor } from '../../../../common/descriptor_types'; export interface IESAggField extends IField { getValueAggDsl(indexPattern: DataView): unknown | null; getBucketCount(): number; + getMask(): AggDescriptor['mask'] | undefined; } export interface CountAggFieldParams { label?: string; source: IESAggSource; origin: FIELD_ORIGIN; + mask?: AggDescriptor['mask']; } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts index 03ade37c3dbec..140f605832fca 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts @@ -14,7 +14,7 @@ import { DataView } from '@kbn/data-plugin/common'; import { IESAggSource } from '../../sources/es_agg_source'; import { IVectorSource } from '../../sources/vector_source'; import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; -import { TileMetaFeature } from '../../../../common/descriptor_types'; +import { AggDescriptor, TileMetaFeature } from '../../../../common/descriptor_types'; import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_property'; import { ESAggTooltipProperty } from '../../tooltips/es_agg_tooltip_property'; import { IESAggField, CountAggFieldParams } from './agg_field_types'; @@ -25,11 +25,13 @@ export class CountAggField implements IESAggField { protected readonly _source: IESAggSource; private readonly _origin: FIELD_ORIGIN; protected readonly _label?: string; + protected readonly _mask?: AggDescriptor['mask']; - constructor({ label, source, origin }: CountAggFieldParams) { + constructor({ label, source, origin, mask }: CountAggFieldParams) { this._source = source; this._origin = origin; this._label = label; + this._mask = mask; } supportsFieldMetaFromEs(): boolean { @@ -131,4 +133,8 @@ export class CountAggField implements IESAggField { pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { return getAggRange(metaFeature, '_count'); } + + getMask() { + return this._mask; + } } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts b/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts index 7e43a2a63658c..9db4e481b9963 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts @@ -26,6 +26,7 @@ export function esAggFieldsFactory( label: aggDescriptor.label, source, origin, + mask: aggDescriptor.mask, }); } else if (aggDescriptor.type === AGG_TYPE.PERCENTILE) { aggField = new PercentileAggField({ @@ -40,6 +41,7 @@ export function esAggFieldsFactory( : DEFAULT_PERCENTILE, source, origin, + mask: aggDescriptor.mask, }); } else { aggField = new AggField({ @@ -51,6 +53,7 @@ export function esAggFieldsFactory( aggType: aggDescriptor.type, source, origin, + mask: aggDescriptor.mask, }); } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts index c6a19f448390a..568fad59e058b 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts @@ -105,4 +105,8 @@ export class TopTermPercentageField implements IESAggField { pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { return null; } + + getMask() { + return undefined; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts index 0421fc3b087b5..69988f0e8a6cb 100644 --- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; +import type { FilterSpecification, Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; import { AbstractLayer } from '../layer'; import { HeatmapStyle } from '../../styles/heatmap/heatmap_style'; import { LAYER_TYPE } from '../../../../common/constants'; @@ -21,6 +21,7 @@ import { DataRequestContext } from '../../../actions'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { IMvtVectorSource } from '../../sources/vector_source'; import { getAggsMeta } from '../../util/tile_meta_feature_utils'; +import { Mask } from '../vector_layer/mask'; export class HeatmapLayer extends AbstractLayer { private readonly _style: HeatmapStyle; @@ -186,6 +187,19 @@ export class HeatmapLayer extends AbstractLayer { this.syncVisibilityWithMb(mbMap, heatmapLayerId); mbMap.setPaintProperty(heatmapLayerId, 'heatmap-opacity', this.getAlpha()); + + // heatmap can implement mask with filter expression because + // feature-state support is not needed since heatmap layers do not support joins + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + const mask = new Mask({ + esAggField: metricField, + isGeometrySourceMvt: true, + ...maskDescriptor, + }); + mbMap.setFilter(heatmapLayerId, mask.getMatchUnmaskedExpression() as FilterSpecification); + } + mbMap.setLayerZoomRange(heatmapLayerId, this.getMinZoom(), this.getMaxZoom()); } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts index ee9fdaf410abb..200c8cad24a4c 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts @@ -13,7 +13,6 @@ import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style import { IDynamicStyleProperty } from '../../../styles/vector/properties/dynamic_style_property'; import { IStyleProperty } from '../../../styles/vector/properties/style_property'; import { - COUNT_PROP_LABEL, COUNT_PROP_NAME, GRID_RESOLUTION, LAYER_TYPE, @@ -67,7 +66,6 @@ function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle clusterSourceDescriptor.metrics = [ { type: AGG_TYPE.COUNT, - label: COUNT_PROP_LABEL, }, ...documentStyle.getDynamicPropertiesArray().map((dynamicProperty) => { return { @@ -267,9 +265,9 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay return [clonedDescriptor]; } - getSource(): IVectorSource { + getSource = () => { return this._isClustered ? this._clusterSource : this._documentSource; - } + }; getSourceForEditing() { // Layer is based on this._documentSource diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts new file mode 100644 index 0000000000000..0b1861fd73397 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts @@ -0,0 +1,193 @@ +/* + * 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 { MapGeoJSONFeature } from '@kbn/mapbox-gl'; +import type { IESAggSource } from '../../sources/es_agg_source'; +import type { IESAggField } from '../../fields/agg'; +import { FIELD_ORIGIN, MASK_OPERATOR, MB_LOOKUP_FUNCTION } from '../../../../common/constants'; + +export const BELOW = i18n.translate('xpack.maps.mask.belowLabel', { + defaultMessage: 'below', +}); + +export const ABOVE = i18n.translate('xpack.maps.mask.aboveLabel', { + defaultMessage: 'above', +}); + +export const BUCKETS = i18n.translate('xpack.maps.mask.genericBucketsName', { + defaultMessage: 'buckets', +}); + +const FEATURES = i18n.translate('xpack.maps.mask.genericFeaturesName', { + defaultMessage: 'features', +}); + +const VALUE = i18n.translate('xpack.maps.mask.genericAggLabel', { + defaultMessage: 'value', +}); + +const WHEN = i18n.translate('xpack.maps.mask.when', { + defaultMessage: 'when', +}); + +const WHEN_JOIN_METRIC = i18n.translate('xpack.maps.mask.whenJoinMetric', { + defaultMessage: '{whenLabel} join metric', + values: { + whenLabel: WHEN, + }, +}); + +function getOperatorLabel(operator: MASK_OPERATOR): string { + if (operator === MASK_OPERATOR.BELOW) { + return BELOW; + } + + if (operator === MASK_OPERATOR.ABOVE) { + return ABOVE; + } + + return operator as string; +} + +export function getMaskI18nValue(operator: MASK_OPERATOR, value: number): string { + return `${getOperatorLabel(operator)} ${value}`; +} + +export function getMaskI18nLabel({ + bucketsName, + isJoin, +}: { + bucketsName?: string; + isJoin: boolean; +}): string { + return i18n.translate('xpack.maps.mask.maskLabel', { + defaultMessage: 'Hide {hideNoun}', + values: { + hideNoun: isJoin ? FEATURES : bucketsName ? bucketsName : BUCKETS, + }, + }); +} + +export function getMaskI18nDescription({ + aggLabel, + bucketsName, + isJoin, +}: { + aggLabel?: string; + bucketsName?: string; + isJoin: boolean; +}): string { + return i18n.translate('xpack.maps.mask.maskDescription', { + defaultMessage: '{maskAdverb} {aggLabel} is ', + values: { + aggLabel: aggLabel ? aggLabel : VALUE, + maskAdverb: isJoin ? WHEN_JOIN_METRIC : WHEN, + }, + }); +} + +export class Mask { + private readonly _esAggField: IESAggField; + private readonly _isGeometrySourceMvt: boolean; + private readonly _operator: MASK_OPERATOR; + private readonly _value: number; + + constructor({ + esAggField, + isGeometrySourceMvt, + operator, + value, + }: { + esAggField: IESAggField; + isGeometrySourceMvt: boolean; + operator: MASK_OPERATOR; + value: number; + }) { + this._esAggField = esAggField; + this._isGeometrySourceMvt = isGeometrySourceMvt; + this._operator = operator; + this._value = value; + } + + private _isFeatureState() { + if (this._esAggField.getOrigin() === FIELD_ORIGIN.SOURCE) { + // source fields are stored in properties + return false; + } + + if (!this._isGeometrySourceMvt) { + // For geojson sources, join fields are stored in properties + return false; + } + + // For vector tile sources, it is not possible to add join fields to properties + // so join fields are stored in feature state + return true; + } + + /* + * Returns maplibre expression that matches masked features + */ + getMatchMaskedExpression() { + const comparisionOperator = this._operator === MASK_OPERATOR.BELOW ? '<' : '>'; + const lookup = this._isFeatureState() + ? MB_LOOKUP_FUNCTION.FEATURE_STATE + : MB_LOOKUP_FUNCTION.GET; + return [comparisionOperator, [lookup, this._esAggField.getMbFieldName()], this._value]; + } + + /* + * Returns maplibre expression that matches unmasked features + */ + getMatchUnmaskedExpression() { + const comparisionOperator = this._operator === MASK_OPERATOR.BELOW ? '>=' : '<='; + const lookup = this._isFeatureState() + ? MB_LOOKUP_FUNCTION.FEATURE_STATE + : MB_LOOKUP_FUNCTION.GET; + return [comparisionOperator, [lookup, this._esAggField.getMbFieldName()], this._value]; + } + + getEsAggField() { + return this._esAggField; + } + + getFieldOriginListLabel() { + const source = this._esAggField.getSource(); + const isJoin = this._esAggField.getOrigin() === FIELD_ORIGIN.JOIN; + const maskLabel = getMaskI18nLabel({ + bucketsName: + 'getBucketsName' in (source as IESAggSource) + ? (source as IESAggSource).getBucketsName() + : undefined, + isJoin, + }); + const adverb = isJoin ? WHEN_JOIN_METRIC : WHEN; + + return `${maskLabel} ${adverb}`; + } + + getOperator() { + return this._operator; + } + + getValue() { + return this._value; + } + + isFeatureMasked(feature: MapGeoJSONFeature) { + const featureValue = this._isFeatureState() + ? feature?.state[this._esAggField.getMbFieldName()] + : feature?.properties[this._esAggField.getMbFieldName()]; + if (typeof featureValue !== 'number') { + return false; + } + return this._operator === MASK_OPERATOR.BELOW + ? featureValue < this._value + : featureValue > this._value; + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx index a976837ee2881..82bb15c19ffca 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx @@ -25,6 +25,7 @@ import { } from '../../../../../common/descriptor_types'; import { LAYER_TYPE, SOURCE_TYPES } from '../../../../../common/constants'; import { MvtVectorLayer } from './mvt_vector_layer'; +import { ITermJoinSource } from '../../../sources/term_join_source'; const defaultConfig = { urlTemplate: 'https://example.com/{x}/{y}/{z}.pbf', @@ -176,6 +177,9 @@ describe('isLayerLoading', () => { getSourceDataRequestId: () => { return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; }, + getRightJoinSource: () => { + return {} as unknown as ITermJoinSource; + }, } as unknown as InnerJoin, ], layerDescriptor: { @@ -212,6 +216,9 @@ describe('isLayerLoading', () => { getSourceDataRequestId: () => { return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; }, + getRightJoinSource: () => { + return {} as unknown as ITermJoinSource; + }, } as unknown as InnerJoin, ], layerDescriptor: { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index dbee11b617dec..366c9cde6eee6 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -58,12 +58,14 @@ import { ITooltipProperty } from '../../tooltips/tooltip_property'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { IESSource } from '../../sources/es_source'; import { ITermJoinSource } from '../../sources/term_join_source'; +import type { IESAggSource } from '../../sources/es_agg_source'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { getJoinAggKey } from '../../../../common/get_agg_key'; import { syncBoundsData } from './bounds_data'; import { JoinState } from './types'; import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; import { PropertiesMap } from '../../../../common/elasticsearch_util'; +import { Mask } from './mask'; const SUPPORTS_FEATURE_EDITING_REQUEST_ID = 'SUPPORTS_FEATURE_EDITING_REQUEST_ID'; @@ -106,6 +108,7 @@ export interface IVectorLayer extends ILayer { getLeftJoinFields(): Promise; addFeature(geometry: Geometry | Position[]): Promise; deleteFeature(featureId: string): Promise; + getMasks(): Mask[]; } export const noResultsIcon = ; @@ -120,6 +123,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { protected readonly _style: VectorStyle; private readonly _joins: InnerJoin[]; protected readonly _descriptor: VectorLayerDescriptor; + private readonly _masks: Mask[]; static createDescriptor( options: Partial, @@ -163,6 +167,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { customIcons, chartsPaletteServiceGetColor ); + this._masks = this._createMasks(); } async cloneDescriptor(): Promise { @@ -692,6 +697,69 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { return undefined; } + _createMasks() { + const masks: Mask[] = []; + const source = this.getSource(); + if ('getMetricFields' in (source as IESAggSource)) { + const metricFields = (source as IESAggSource).getMetricFields(); + metricFields.forEach((metricField) => { + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + masks.push( + new Mask({ + esAggField: metricField, + isGeometrySourceMvt: source.isMvt(), + ...maskDescriptor, + }) + ); + } + }); + } + + this.getValidJoins().forEach((join) => { + const rightSource = join.getRightJoinSource(); + if ('getMetricFields' in (rightSource as unknown as IESAggSource)) { + const metricFields = (rightSource as unknown as IESAggSource).getMetricFields(); + metricFields.forEach((metricField) => { + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + masks.push( + new Mask({ + esAggField: metricField, + isGeometrySourceMvt: source.isMvt(), + ...maskDescriptor, + }) + ); + } + }); + } + }); + + return masks; + } + + getMasks() { + return this._masks; + } + + // feature-state is not supported in filter expressions + // https://github.com/mapbox/mapbox-gl-js/issues/8487 + // therefore, masking must be accomplished via setting opacity paint property (hack) + _getAlphaExpression() { + const maskCaseExpressions: unknown[] = []; + this.getMasks().forEach((mask) => { + // case expressions require 2 parts + // 1) condition expression + maskCaseExpressions.push(mask.getMatchMaskedExpression()); + // 2) output. 0 opacity styling "hides" feature + maskCaseExpressions.push(0); + }); + + return maskCaseExpressions.length + ? ['case', ...maskCaseExpressions, this.getAlpha()] + : this.getAlpha(); + } + _setMbPointsProperties( mbMap: MbMap, mvtSourceLayer?: string, @@ -759,13 +827,13 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { if (this.getCurrentStyle().arePointsSymbolizedAsCircles()) { this.getCurrentStyle().setMBPaintPropertiesForPoints({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, pointLayerId: markerLayerId, }); } else { this.getCurrentStyle().setMBSymbolPropertiesForPoints({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, symbolLayerId: markerLayerId, }); @@ -811,7 +879,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } this.getCurrentStyle().setMBPaintProperties({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, fillLayerId, lineLayerId, @@ -865,7 +933,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } this.getCurrentStyle().setMBPropertiesForLabelText({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, textLayerId: labelLayerId, }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts index fa7f329beb97a..dda3026ddd4ef 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts @@ -11,11 +11,13 @@ import { DataView } from '@kbn/data-plugin/common'; import type { IESAggSource } from './types'; import { AbstractESSource } from '../es_source'; import { esAggFieldsFactory, IESAggField } from '../../fields/agg'; -import { AGG_TYPE, COUNT_PROP_LABEL, FIELD_ORIGIN } from '../../../../common/constants'; +import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; import { getSourceAggKey } from '../../../../common/get_agg_key'; import { AbstractESAggSourceDescriptor, AggDescriptor } from '../../../../common/descriptor_types'; import { IField } from '../../fields/field'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; +import { getAggDisplayName } from './get_agg_display_name'; +import { BUCKETS } from '../../layers/vector_layer/mask'; export const DEFAULT_METRIC = { type: AGG_TYPE.COUNT }; @@ -46,6 +48,10 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE } } + getBucketsName() { + return BUCKETS; + } + getFieldByName(fieldName: string): IField | null { return this.getMetricFieldForName(fieldName); } @@ -83,14 +89,14 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE async getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise { switch (aggType) { case AGG_TYPE.COUNT: - return COUNT_PROP_LABEL; + return getAggDisplayName(aggType); case AGG_TYPE.TERMS: return i18n.translate('xpack.maps.source.esAggSource.topTermLabel', { - defaultMessage: `Top {fieldLabel}`, + defaultMessage: `top {fieldLabel}`, values: { fieldLabel }, }); default: - return `${aggType} ${fieldLabel}`; + return `${getAggDisplayName(aggType)} ${fieldLabel}`; } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts new file mode 100644 index 0000000000000..516f6448fb629 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { AGG_TYPE } from '../../../../common/constants'; + +export function getAggDisplayName(aggType: AGG_TYPE): string { + switch (aggType) { + case AGG_TYPE.AVG: + return i18n.translate('xpack.maps.aggType.averageLabel', { + defaultMessage: 'average', + }); + case AGG_TYPE.COUNT: + return i18n.translate('xpack.maps.aggType.countLabel', { + defaultMessage: 'count', + }); + case AGG_TYPE.MAX: + return i18n.translate('xpack.maps.aggType.maximumLabel', { + defaultMessage: 'max', + }); + case AGG_TYPE.MIN: + return i18n.translate('xpack.maps.aggType.minimumLabel', { + defaultMessage: 'min', + }); + case AGG_TYPE.PERCENTILE: + return i18n.translate('xpack.maps.aggType.percentileLabel', { + defaultMessage: 'percentile', + }); + case AGG_TYPE.SUM: + return i18n.translate('xpack.maps.aggType.sumLabel', { + defaultMessage: 'sum', + }); + case AGG_TYPE.TERMS: + return i18n.translate('xpack.maps.aggType.topTermLabel', { + defaultMessage: 'top term', + }); + case AGG_TYPE.UNIQUE_COUNT: + return i18n.translate('xpack.maps.aggType.cardinalityTermLabel', { + defaultMessage: 'unique count', + }); + default: + return aggType; + } +} diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts index 033d937aa9391..80bc74bd4ceb4 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts @@ -7,3 +7,4 @@ export type { IESAggSource } from './types'; export { AbstractESAggSource, DEFAULT_METRIC } from './es_agg_source'; +export { getAggDisplayName } from './get_agg_display_name'; diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts index d9cb6fcd95a10..697ae8a1a606d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts @@ -13,6 +13,12 @@ import { IESAggField } from '../../fields/agg'; export interface IESAggSource extends IESSource { getAggKey(aggType: AGG_TYPE, fieldName: string): string; getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise; + + /* + * Returns human readable name describing buckets, like "clusters" or "grids" + */ + getBucketsName(): string; + getMetricFields(): IESAggField[]; getMetricFieldForName(fieldName: string): IESAggField | null; getValueAggsDsl(indexPattern: DataView): { [key: string]: unknown }; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap index 79b8c4ffc9808..fe50d54ca5535 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap @@ -19,7 +19,9 @@ exports[`source editor geo_grid_source should not allow editing multiple metrics /> { async function onChange(...sourceChanges: OnSourceChangeArgs[]) { sourceEditorArgs.onChange(...sourceChanges); @@ -129,6 +147,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo } return ( ({ })); const defaultProps = { + bucketsName: 'clusters', currentLayerType: LAYER_TYPE.GEOJSON_VECTOR, geoFieldName: 'myLocation', indexPatternId: 'foobar', diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx index 3268992c7f2b6..d69a97d09dd47 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx @@ -6,7 +6,6 @@ */ import React, { Fragment, Component } from 'react'; - import { v4 as uuidv4 } from 'uuid'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiPanel, EuiSpacer, EuiComboBoxOptionOption, EuiTitle } from '@elastic/eui'; @@ -25,6 +24,7 @@ import { clustersTitle, heatmapTitle } from './es_geo_grid_source'; import { isMvt } from './is_mvt'; interface Props { + bucketsName: string; currentLayerType?: string; geoFieldName: string; indexPatternId: string; @@ -148,6 +148,8 @@ export class UpdateSourceEditor extends Component { { ); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx similarity index 74% rename from x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js rename to x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx index 856504c51865e..c36cc3d49089a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx @@ -6,17 +6,31 @@ */ import React, { Component, Fragment } from 'react'; - -import { getDataViewNotFoundMessage } from '../../../../common/i18n_getters'; -import { MetricsEditor } from '../../../components/metrics_editor'; -import { getIndexPatternService } from '../../../kibana_services'; +import type { DataViewField } from '@kbn/data-plugin/common'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { indexPatterns } from '@kbn/data-plugin/public'; +import { MetricsEditor } from '../../../components/metrics_editor'; +import { getIndexPatternService } from '../../../kibana_services'; +import type { AggDescriptor } from '../../../../common/descriptor_types'; +import type { OnSourceChangeArgs } from '../source'; + +interface Props { + bucketsName: string; + indexPatternId: string; + metrics: AggDescriptor[]; + onChange: (...args: OnSourceChangeArgs[]) => void; +} + +interface State { + fields: DataViewField[]; +} + +export class UpdateSourceEditor extends Component { + private _isMounted: boolean = false; -export class UpdateSourceEditor extends Component { state = { - fields: null, + fields: [], }; componentDidMount() { @@ -33,11 +47,6 @@ export class UpdateSourceEditor extends Component { try { indexPattern = await getIndexPatternService().get(this.props.indexPatternId); } catch (err) { - if (this._isMounted) { - this.setState({ - loadError: getDataViewNotFoundMessage(this.props.indexPatternId), - }); - } return; } @@ -50,7 +59,7 @@ export class UpdateSourceEditor extends Component { }); } - _onMetricsChange = (metrics) => { + _onMetricsChange = (metrics: AggDescriptor[]) => { this.props.onChange({ propName: 'metrics', value: metrics }); }; @@ -69,6 +78,8 @@ export class UpdateSourceEditor extends Component { { }); const metrics = source.getMetricFields(); expect(metrics[0].getName()).toEqual('__kbnjoin__count__1234'); - expect(await metrics[0].getLabel()).toEqual('Count of foobar'); + expect(await metrics[0].getLabel()).toEqual('count of foobar'); }); it('should override name and label of sum metric', async () => { @@ -51,7 +51,7 @@ describe('getMetricFields', () => { expect(metrics[0].getName()).toEqual('__kbnjoin__sum_of_myFieldGettingSummed__1234'); expect(await metrics[0].getLabel()).toEqual('my custom label'); expect(metrics[1].getName()).toEqual('__kbnjoin__count__1234'); - expect(await metrics[1].getLabel()).toEqual('Count of foobar'); + expect(await metrics[1].getLabel()).toEqual('count of foobar'); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts index 5540454702114..4c7793e1b01cb 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts @@ -31,6 +31,7 @@ import { import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { isValidStringConfig } from '../../util/valid_string_config'; import { ITermJoinSource } from '../term_join_source'; +import type { IESAggSource } from '../es_agg_source'; import { IField } from '../../fields/field'; import { mergeExecutionContext } from '../execution_context_utils'; @@ -52,7 +53,7 @@ export function extractPropertiesMap(rawEsData: any, countPropertyName: string): return propertiesMap; } -export class ESTermSource extends AbstractESAggSource implements ITermJoinSource { +export class ESTermSource extends AbstractESAggSource implements ITermJoinSource, IESAggSource { static type = SOURCE_TYPES.ES_TERM_SOURCE; static createDescriptor(descriptor: Partial): ESTermSourceDescriptor { @@ -115,7 +116,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource } return aggType === AGG_TYPE.COUNT ? i18n.translate('xpack.maps.source.esJoin.countLabel', { - defaultMessage: `Count of {indexPatternLabel}`, + defaultMessage: `count of {indexPatternLabel}`, values: { indexPatternLabel }, }) : super.getAggLabel(aggType, fieldLabel); diff --git a/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx b/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx index 390e48408d747..027e4dc29c58c 100644 --- a/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx +++ b/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx @@ -5,13 +5,15 @@ * 2.0. */ -import React, { Component } from 'react'; +import React, { Component, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; import { ColorGradient } from './color_gradient'; import { RangedStyleLegendRow } from '../../../components/ranged_style_legend_row'; import { HEATMAP_COLOR_RAMP_LABEL } from '../heatmap_constants'; -import { IField } from '../../../../fields/field'; +import type { IField } from '../../../../fields/field'; +import type { IESAggField } from '../../../../fields/agg'; +import { MaskLegend } from '../../../vector/components/legend/mask_legend'; interface Props { colorRampName: string; @@ -47,7 +49,7 @@ export class HeatmapLegend extends Component { } render() { - return ( + const metricLegend = ( } minLabel={i18n.translate('xpack.maps.heatmapLegend.coldLabel', { @@ -61,5 +63,28 @@ export class HeatmapLegend extends Component { invert={false} /> ); + + let maskLegend: ReactNode | undefined; + if ('getMask' in (this.props.field as IESAggField)) { + const mask = (this.props.field as IESAggField).getMask(); + if (mask) { + maskLegend = ( + + ); + } + } + + return maskLegend ? ( + <> + {maskLegend} + {metricLegend} + + ) : ( + metricLegend + ); } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx new file mode 100644 index 0000000000000..4ffb7ba5a1834 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx @@ -0,0 +1,95 @@ +/* + * 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, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiText } from '@elastic/eui'; +import { FIELD_ORIGIN, MASK_OPERATOR } from '../../../../../../common/constants'; +import type { IESAggField } from '../../../../fields/agg'; +import type { IESAggSource } from '../../../../sources/es_agg_source'; +import { + getMaskI18nDescription, + getMaskI18nLabel, + getMaskI18nValue, +} from '../../../../layers/vector_layer/mask'; + +interface Props { + esAggField: IESAggField; + onlyShowLabelAndValue?: boolean; + operator: MASK_OPERATOR; + value: number; +} + +interface State { + aggLabel?: string; +} + +export class MaskLegend extends Component { + private _isMounted = false; + + state: State = {}; + + componentDidMount() { + this._isMounted = true; + this._loadAggLabel(); + } + + componentWillUnmount() { + this._isMounted = false; + } + + componentDidUpdate() { + this._loadAggLabel(); + } + + _loadAggLabel = async () => { + const aggLabel = await this.props.esAggField.getLabel(); + if (this._isMounted && aggLabel !== this.state.aggLabel) { + this.setState({ aggLabel }); + } + }; + + _getBucketsName() { + const source = this.props.esAggField.getSource(); + return 'getBucketsName' in (source as IESAggSource) + ? (source as IESAggSource).getBucketsName() + : undefined; + } + + _getPrefix() { + if (this.props.onlyShowLabelAndValue) { + return i18n.translate('xpack.maps.maskLegend.is', { + defaultMessage: '{aggLabel} is', + values: { + aggLabel: this.state.aggLabel, + }, + }); + } + + const isJoin = this.props.esAggField.getOrigin() === FIELD_ORIGIN.JOIN; + const maskLabel = getMaskI18nLabel({ + bucketsName: this._getBucketsName(), + isJoin, + }); + const maskDescription = getMaskI18nDescription({ + aggLabel: this.state.aggLabel, + isJoin, + }); + return `${maskLabel} ${maskDescription}`; + } + + render() { + return ( + + + {`${this._getPrefix()} `} + {getMaskI18nValue(this.props.operator, this.props.value)} + + + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx index 2d282a4b530cb..60bcd05c9f738 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx @@ -6,17 +6,30 @@ */ import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { FIELD_ORIGIN } from '../../../../../../common/constants'; +import { Mask } from '../../../../layers/vector_layer/mask'; import { IStyleProperty } from '../../properties/style_property'; +import { MaskLegend } from './mask_legend'; interface Props { isLinesOnly: boolean; isPointsOnly: boolean; + masks: Mask[]; styles: Array>; symbolId?: string; svg?: string; } -export function VectorStyleLegend({ isLinesOnly, isPointsOnly, styles, symbolId, svg }: Props) { +export function VectorStyleLegend({ + isLinesOnly, + isPointsOnly, + masks, + styles, + symbolId, + svg, +}: Props) { const legendRows = []; for (let i = 0; i < styles.length; i++) { @@ -34,5 +47,55 @@ export function VectorStyleLegend({ isLinesOnly, isPointsOnly, styles, symbolId, ); } - return <>{legendRows}; + function renderMasksByFieldOrigin(fieldOrigin: FIELD_ORIGIN) { + const masksByFieldOrigin = masks.filter( + (mask) => mask.getEsAggField().getOrigin() === fieldOrigin + ); + if (masksByFieldOrigin.length === 0) { + return null; + } + + if (masksByFieldOrigin.length === 1) { + const mask = masksByFieldOrigin[0]; + return ( + + ); + } + + return ( + <> + + {masksByFieldOrigin[0].getFieldOriginListLabel()} + +
      + {masksByFieldOrigin.map((mask) => ( +
    • + +
    • + ))} +
    + + ); + } + + return ( + <> + {renderMasksByFieldOrigin(FIELD_ORIGIN.SOURCE)} + {renderMasksByFieldOrigin(FIELD_ORIGIN.JOIN)} + {legendRows} + + ); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx index 6daa8cf84afaa..f1a55b571e27d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx @@ -51,7 +51,7 @@ export class DynamicColorProperty extends DynamicStyleProperty { - syncCircleColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncCircleColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'circle-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); } - syncFillColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncFillColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'fill-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); } @@ -28,17 +28,17 @@ export class StaticColorProperty extends StaticStyleProperty mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', this._options.color); } - syncLineColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncLineColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'line-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); } - syncCircleStrokeWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncCircleStrokeWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'circle-stroke-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'circle-stroke-opacity', alpha); } - syncLabelColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncLabelColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'text-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 3b64d0960628c..31c06728d8112 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -130,7 +130,7 @@ export interface IVectorStyle extends IStyle { fillLayerId, lineLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; fillLayerId: string; lineLayerId: string; @@ -140,7 +140,7 @@ export interface IVectorStyle extends IStyle { mbMap, pointLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; pointLayerId: string; }) => void; @@ -149,7 +149,7 @@ export interface IVectorStyle extends IStyle { mbMap, textLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; textLayerId: string; }) => void; @@ -158,7 +158,7 @@ export interface IVectorStyle extends IStyle { symbolLayerId, alpha, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; symbolLayerId: string; }) => void; @@ -730,6 +730,7 @@ export class VectorStyle implements IVectorStyle { return ( void; + onClose: () => void; +} + +interface State { + operator: MASK_OPERATOR; + value: number | string; +} + +export class MaskEditor extends Component { + constructor(props: Props) { + super(props); + this.state = { + operator: + this.props.metric.mask !== undefined + ? this.props.metric.mask.operator + : MASK_OPERATOR.BELOW, + value: this.props.metric.mask !== undefined ? this.props.metric.mask.value : '', + }; + } + + _onSet = () => { + if (this._isValueInValid()) { + return; + } + + this.props.onChange({ + ...this.props.metric, + mask: { + operator: this.state.operator, + value: this.state.value as number, + }, + }); + this.props.onClose(); + }; + + _onClear = () => { + const newMetric = { + ...this.props.metric, + }; + delete newMetric.mask; + this.props.onChange(newMetric); + this.props.onClose(); + }; + + _onOperatorChange = (e: ChangeEvent) => { + this.setState({ + operator: e.target.value as MASK_OPERATOR, + }); + }; + + _onValueChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + value: isNaN(sanitizedValue) ? evt.target.value : sanitizedValue, + }); + }; + + _hasChanges() { + return ( + this.props.metric.mask === undefined || + this.props.metric.mask.operator !== this.state.operator || + this.props.metric.mask.value !== this.state.value + ); + } + + _isValueInValid() { + return typeof this.state.value === 'string'; + } + + _renderForm() { + return ( + + + + + + + + + + + + + ); + } + + _renderFooter() { + return ( + + + + + {panelStrings.close} + + + + + {panelStrings.clear} + + + + + {panelStrings.apply} + + + + + ); + } + + render() { + return ( + <> + {this._renderForm()} + + {this._renderFooter()} + + ); + } +} diff --git a/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx b/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx new file mode 100644 index 0000000000000..e05fdc6cdf2d5 --- /dev/null +++ b/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx @@ -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 React, { Component } from 'react'; +import { EuiExpression, EuiPopover } from '@elastic/eui'; +import { DataViewField } from '@kbn/data-views-plugin/public'; +import { AGG_TYPE } from '../../../../common/constants'; +import { AggDescriptor, FieldedAggDescriptor } from '../../../../common/descriptor_types'; +import { MaskEditor } from './mask_editor'; +import { getAggDisplayName } from '../../../classes/sources/es_agg_source'; +import { + getMaskI18nDescription, + getMaskI18nValue, +} from '../../../classes/layers/vector_layer/mask'; + +interface Props { + fields: DataViewField[]; + isJoin: boolean; + metric: AggDescriptor; + onChange: (metric: AggDescriptor) => void; +} + +interface State { + isPopoverOpen: boolean; +} + +export class MaskExpression extends Component { + state: State = { + isPopoverOpen: false, + }; + + _togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + + _closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + _getMaskExpressionValue() { + return this.props.metric.mask === undefined + ? '...' + : getMaskI18nValue(this.props.metric.mask.operator, this.props.metric.mask.value); + } + + _getAggLabel() { + const aggDisplayName = getAggDisplayName(this.props.metric.type); + if (this.props.metric.type === AGG_TYPE.COUNT || this.props.metric.field === undefined) { + return aggDisplayName; + } + + const targetField = this.props.fields.find( + (field) => field.name === (this.props.metric as FieldedAggDescriptor).field + ); + const fieldDisplayName = targetField?.displayName + ? targetField?.displayName + : this.props.metric.field; + return `${aggDisplayName} ${fieldDisplayName}`; + } + + render() { + // masks only supported for numerical metrics + if (this.props.metric.type === AGG_TYPE.TERMS) { + return null; + } + + return ( + + } + isOpen={this.state.isPopoverOpen} + closePopover={this._closePopover} + panelPaddingSize="s" + anchorPosition="downCenter" + repositionOnScroll={true} + > + + + ); + } +} diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx index 26cf6d5313821..5042cb4ffa9c7 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx @@ -18,6 +18,8 @@ import { AggDescriptor } from '../../../common/descriptor_types'; import { AGG_TYPE, DEFAULT_PERCENTILE } from '../../../common/constants'; import { getTermsFields } from '../../index_pattern_util'; import { ValidatedNumberInput } from '../validated_number_input'; +import { getMaskI18nLabel } from '../../classes/layers/vector_layer/mask'; +import { MaskExpression } from './mask_expression'; function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { if (!fields) { @@ -43,6 +45,8 @@ function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { } interface Props { + bucketsName?: string; + isJoin: boolean; metric: AggDescriptor; fields: DataViewField[]; onChange: (metric: AggDescriptor) => void; @@ -52,7 +56,9 @@ interface Props { } export function MetricEditor({ + bucketsName, fields, + isJoin, metricsFilter, metric, onChange, @@ -64,6 +70,8 @@ export function MetricEditor({ return; } + // Intentionally not adding mask. + // Changing aggregation likely changes value range so keeping old mask does not seem relevent const descriptor = { type: metricAggregationType, label: metric.label, @@ -93,6 +101,8 @@ export function MetricEditor({ if (!fieldName || metric.type === AGG_TYPE.COUNT) { return; } + // Intentionally not adding mask. + // Changing field likely changes value range so keeping old mask does not seem relevent onChange({ label: metric.label, type: metric.type, @@ -223,6 +233,11 @@ export function MetricEditor({ {fieldSelect} {percentileSelect} {labelInput} + + + + + {removeButton} ); diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx index e8dd63fe934e5..88c9b83bc54a4 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx @@ -9,54 +9,39 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui'; import { AGG_TYPE } from '../../../common/constants'; +import { getAggDisplayName } from '../../classes/sources/es_agg_source'; const AGG_OPTIONS = [ { - label: i18n.translate('xpack.maps.metricSelect.averageDropDownOptionLabel', { - defaultMessage: 'Average', - }), + label: getAggDisplayName(AGG_TYPE.AVG), value: AGG_TYPE.AVG, }, { - label: i18n.translate('xpack.maps.metricSelect.countDropDownOptionLabel', { - defaultMessage: 'Count', - }), + label: getAggDisplayName(AGG_TYPE.COUNT), value: AGG_TYPE.COUNT, }, { - label: i18n.translate('xpack.maps.metricSelect.maxDropDownOptionLabel', { - defaultMessage: 'Max', - }), + label: getAggDisplayName(AGG_TYPE.MAX), value: AGG_TYPE.MAX, }, { - label: i18n.translate('xpack.maps.metricSelect.minDropDownOptionLabel', { - defaultMessage: 'Min', - }), + label: getAggDisplayName(AGG_TYPE.MIN), value: AGG_TYPE.MIN, }, { - label: i18n.translate('xpack.maps.metricSelect.percentileDropDownOptionLabel', { - defaultMessage: 'Percentile', - }), + label: getAggDisplayName(AGG_TYPE.PERCENTILE), value: AGG_TYPE.PERCENTILE, }, { - label: i18n.translate('xpack.maps.metricSelect.sumDropDownOptionLabel', { - defaultMessage: 'Sum', - }), + label: getAggDisplayName(AGG_TYPE.SUM), value: AGG_TYPE.SUM, }, { - label: i18n.translate('xpack.maps.metricSelect.termsDropDownOptionLabel', { - defaultMessage: 'Top term', - }), + label: getAggDisplayName(AGG_TYPE.TERMS), value: AGG_TYPE.TERMS, }, { - label: i18n.translate('xpack.maps.metricSelect.cardinalityDropDownOptionLabel', { - defaultMessage: 'Unique count', - }), + label: getAggDisplayName(AGG_TYPE.UNIQUE_COUNT), value: AGG_TYPE.UNIQUE_COUNT, }, ]; diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx index 66fed40936b79..5aaf1369efe81 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx @@ -20,6 +20,7 @@ const defaultProps = { fields: [], onChange: () => {}, allowMultipleMetrics: true, + isJoin: false, }; test('should render metrics editor', () => { diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx index b38e20b40d990..a18608b9631c2 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx @@ -22,6 +22,8 @@ export function isMetricValid(aggDescriptor: AggDescriptor) { interface Props { allowMultipleMetrics: boolean; + bucketsName?: string; + isJoin: boolean; metrics: AggDescriptor[]; fields: DataViewField[]; onChange: (metrics: AggDescriptor[]) => void; @@ -81,6 +83,8 @@ export class MetricsEditor extends Component { return (
    - - } - layout="horizontal" - title="" + + -
    + `; diff --git a/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx b/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx index d7e99bb115b9b..543764aa9d7c2 100644 --- a/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx +++ b/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/layer_wizard_select.tsx @@ -15,6 +15,7 @@ import { EuiLoadingContent, EuiFacetGroup, EuiFacetButton, + EuiPanel, EuiToolTip, EuiSpacer, } from '@elastic/eui'; @@ -136,9 +137,9 @@ export class LayerWizardSelect extends Component { render() { if (!this.state.hasLoadedWizards) { return ( -
    - } layout="horizontal" /> -
    + + + ); } diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.tsx.snap index 1b6dff1c3396f..912bfa1c2e9d9 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.tsx.snap @@ -46,6 +46,7 @@ exports[`Should render default props 1`] = ` { metrics={this.props.metrics} onChange={this.props.onChange} allowMultipleMetrics={true} + isJoin={true} /> ); }; diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap index cb496311b3d1c..46ddc7f58eb78 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap @@ -76,11 +76,7 @@ exports[`Should render edit form row when attribution not provided 1`] = ` onClick={[Function]} size="xs" > - + Clear
    diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx index bdab63e1029e7..38ac195904592 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { EuiButtonEmpty, EuiLink, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { Attribution } from '../../../../common/descriptor_types'; import { ILayer } from '../../../classes/layers/layer'; import { AttributionPopover } from './attribution_popover'; +import { panelStrings } from '../../panel_strings'; interface Props { layer: ILayer; @@ -65,9 +65,7 @@ export function AttributionFormRow(props: Props) { defaultMessage: 'Edit attribution', } )} - popoverButtonLabel={i18n.translate('xpack.maps.attribution.editBtnLabel', { - defaultMessage: 'Edit', - })} + popoverButtonLabel={panelStrings.edit} label={layerDescriptor.attribution.label} url={layerDescriptor.attribution.url} /> @@ -83,10 +81,7 @@ export function AttributionFormRow(props: Props) { defaultMessage: 'Clear attribution', })} > - + {panelStrings.clear} diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx index 0371b68c85a3b..530b3cce20f00 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { Attribution } from '../../../../common/descriptor_types'; +import { panelStrings } from '../../panel_strings'; interface Props { onChange: (attribution: Attribution) => void; @@ -128,7 +129,7 @@ export class AttributionPopover extends Component { onClick={this._onApply} size="s" > - + {panelStrings.apply} diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx index 8d399f19a765c..02b9048e93b86 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx @@ -35,7 +35,7 @@ export function StyleSettings({ layer, updateStyleDescriptor, updateCustomIcons
    diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index da4765ca094ec..71858ecb02459 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -283,11 +283,10 @@ export class MbMap extends Component { } _initResizerChecker() { + this.state.mbMap?.resize(); // ensure map is sized for container prior to monitoring this._checker = new ResizeChecker(this._containerRef!); this._checker.on('resize', () => { - if (this.state.mbMap) { - this.state.mbMap.resize(); - } + this.state.mbMap?.resize(); }); } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx index edd27d2d1edb1..9074fbd8fbeaf 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx @@ -70,6 +70,9 @@ const mockLayer = { }, } as unknown as IVectorSource; }, + getMasks: () => { + return []; + }, } as unknown as IVectorLayer; const mockMbMapHandlers: { [key: string]: (event?: MapMouseEvent) => void } = {}; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx index 1c14d11eb1f96..75e41464fd0f8 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx @@ -184,6 +184,15 @@ export class TooltipControl extends Component { continue; } + // masking must use paint property "opacity" to hide features in order to support feature state + // therefore, there is no way to remove masked features with queryRenderedFeatures + // masked features must be removed via manual filtering + const masks = layer.getMasks(); + const maskHiddingFeature = masks.find((mask) => mask.isFeatureMasked(mbFeature)); + if (maskHiddingFeature) { + continue; + } + const featureId = layer.getFeatureId(mbFeature); if (featureId === undefined) { continue; diff --git a/x-pack/plugins/maps/public/connected_components/panel_strings.ts b/x-pack/plugins/maps/public/connected_components/panel_strings.ts index f7f7278138e1e..f4eb5e871a3fe 100644 --- a/x-pack/plugins/maps/public/connected_components/panel_strings.ts +++ b/x-pack/plugins/maps/public/connected_components/panel_strings.ts @@ -8,12 +8,21 @@ import { i18n } from '@kbn/i18n'; export const panelStrings = { + apply: i18n.translate('xpack.maps.panel.applyLabel', { + defaultMessage: 'Apply', + }), + clear: i18n.translate('xpack.maps.panel.clearLabel', { + defaultMessage: 'Clear', + }), close: i18n.translate('xpack.maps.panel.closeLabel', { defaultMessage: 'Close', }), discardChanges: i18n.translate('xpack.maps.panel.discardChangesLabel', { defaultMessage: 'Discard changes', }), + edit: i18n.translate('xpack.maps.panel.editLabel', { + defaultMessage: 'Edit', + }), keepChanges: i18n.translate('xpack.maps.panel.keepChangesLabel', { defaultMessage: 'Keep changes', }), diff --git a/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx b/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx index 0d75eae9597a2..8af50ec762286 100644 --- a/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx +++ b/x-pack/plugins/maps/public/connected_components/timeslider/timeslider.tsx @@ -11,18 +11,15 @@ import { Observable, Subscription } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { - ControlGroupContainer, type ControlGroupInput, - type controlGroupInputBuilder, - LazyControlGroupRenderer, + type ControlGroupInputBuilder, + type AwaitingControlGroupAPI, + ControlGroupRenderer, } from '@kbn/controls-plugin/public'; -import { withSuspense } from '@kbn/presentation-util-plugin/public'; import { first } from 'rxjs/operators'; import type { TimeRange } from '@kbn/es-query'; import { Timeslice } from '../../../common/descriptor_types'; -const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); - export interface Props { setTimeslice: (timeslice?: Timeslice) => void; timeRange: TimeRange; @@ -44,7 +41,7 @@ export class Timeslider extends Component { _getCreationOptions = async ( initialInput: Partial, - builder: typeof controlGroupInputBuilder + builder: ControlGroupInputBuilder ) => { builder.addTimeSliderControl(initialInput); return { @@ -56,8 +53,8 @@ export class Timeslider extends Component { }; }; - _onLoadComplete = (controlGroup: ControlGroupContainer) => { - if (!this._isMounted) { + _onLoadComplete = (controlGroup: AwaitingControlGroupAPI) => { + if (!this._isMounted || !controlGroup) { return; } @@ -92,7 +89,7 @@ export class Timeslider extends Component { return (
    diff --git a/x-pack/plugins/maps/public/content_management/maps_client.ts b/x-pack/plugins/maps/public/content_management/maps_client.ts index 932765899da22..065d44fdc0681 100644 --- a/x-pack/plugins/maps/public/content_management/maps_client.ts +++ b/x-pack/plugins/maps/public/content_management/maps_client.ts @@ -19,18 +19,19 @@ import type { MapSearchOut, MapSearchOptions, } from '../../common/content_management'; +import { CONTENT_ID as contentTypeId } from '../../common/content_management'; import { getContentManagement } from '../kibana_services'; const get = async (id: string) => { return getContentManagement().client.get({ - contentTypeId: 'map', + contentTypeId, id, }); }; const create = async ({ data, options }: Omit) => { const res = await getContentManagement().client.create({ - contentTypeId: 'map', + contentTypeId, data, options, }); @@ -39,7 +40,7 @@ const create = async ({ data, options }: Omit) => const update = async ({ id, data, options }: Omit) => { const res = await getContentManagement().client.update({ - contentTypeId: 'map', + contentTypeId, id, data, options, @@ -49,14 +50,14 @@ const update = async ({ id, data, options }: Omit) const deleteMap = async (id: string) => { await getContentManagement().client.delete({ - contentTypeId: 'map', + contentTypeId, id, }); }; const search = async (query: SearchQuery = {}, options?: MapSearchOptions) => { return getContentManagement().client.search({ - contentTypeId: 'map', + contentTypeId, query, options, }); diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx index 01eba4ee7905e..c4bddf5a71541 100644 --- a/x-pack/plugins/maps/public/render_app.tsx +++ b/x-pack/plugins/maps/public/render_app.tsx @@ -86,8 +86,7 @@ export async function renderApp( mapEmbeddableInput = { savedObjectId: routeProps.match.params.savedMapId, } as MapByReferenceInput; - } - if (valueInput) { + } else if (valueInput) { mapEmbeddableInput = valueInput as MapByValueInput; } diff --git a/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx b/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx index 26cb872006dee..f988c5b11ef98 100644 --- a/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx @@ -5,12 +5,10 @@ * 2.0. */ -import React, { Component } from 'react'; -import { i18n } from '@kbn/i18n'; +import React, { useState, useEffect } from 'react'; import { Redirect } from 'react-router-dom'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { ScopedHistory } from '@kbn/core/public'; -import { getToasts } from '../../kibana_services'; import { MapsListView } from './maps_list_view'; import { APP_ID } from '../../../common/constants'; import { mapsClient } from '../../content_management'; @@ -20,49 +18,39 @@ interface Props { stateTransfer: EmbeddableStateTransfer; } -export class LoadListAndRender extends Component { - _isMounted: boolean = false; - state = { - mapsLoaded: false, - hasSavedMaps: null, - }; - - componentDidMount() { - this._isMounted = true; - this.props.stateTransfer.clearEditorState(APP_ID); - this._loadMapsList(); - } - - componentWillUnmount() { - this._isMounted = false; +export function LoadListAndRender(props: Props) { + const [mapsLoaded, setMapsLoaded] = useState(false); + const [hasSavedMaps, setHasSavedMaps] = useState(true); + + useEffect(() => { + props.stateTransfer.clearEditorState(APP_ID); + + let ignore = false; + mapsClient + .search({ limit: 1 }) + .then((results) => { + if (!ignore) { + setHasSavedMaps(results.hits.length > 0); + setMapsLoaded(true); + } + }) + .catch((err) => { + if (!ignore) { + setMapsLoaded(true); + setHasSavedMaps(false); + } + }); + return () => { + ignore = true; + }; + // only run on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (!mapsLoaded) { + // do not render loading state to avoid UI flash when listing page is displayed + return null; } - async _loadMapsList() { - try { - const results = await mapsClient.search({ limit: 1 }); - if (this._isMounted) { - this.setState({ mapsLoaded: true, hasSavedMaps: !!results.hits.length }); - } - } catch (err) { - if (this._isMounted) { - this.setState({ mapsLoaded: true, hasSavedMaps: false }); - getToasts().addDanger({ - title: i18n.translate('xpack.maps.mapListing.errorAttemptingToLoadSavedMaps', { - defaultMessage: `Unable to load maps`, - }), - text: `${err}`, - }); - } - } - } - - render() { - const { mapsLoaded, hasSavedMaps } = this.state; - - if (mapsLoaded) { - return hasSavedMaps ? : ; - } else { - return null; - } - } + return hasSavedMaps ? : ; } diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index 95063f728a8fc..d98444057097d 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, memo } from 'react'; +import React, { useCallback, memo, useEffect } from 'react'; import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; @@ -72,8 +72,14 @@ function MapsListViewComp({ history }: Props) { const listingLimit = getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = getUiSettings().get(SAVED_OBJECTS_PER_PAGE_SETTING); - getCoreChrome().docTitle.change(APP_NAME); - getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); + // TLDR; render should be side effect free + // + // setBreadcrumbs fires observables which cause state changes in ScreenReaderRouteAnnouncements. + // wrap chrome updates in useEffect to avoid potentially causing state changes in other component during render phase. + useEffect(() => { + getCoreChrome().docTitle.change(APP_NAME); + getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); + }, []); const findMaps = useCallback( async ( diff --git a/x-pack/plugins/maps/server/content_management/maps_storage.ts b/x-pack/plugins/maps/server/content_management/maps_storage.ts index 266ad81007345..3063980a561f3 100644 --- a/x-pack/plugins/maps/server/content_management/maps_storage.ts +++ b/x-pack/plugins/maps/server/content_management/maps_storage.ts @@ -6,11 +6,16 @@ */ import Boom from '@hapi/boom'; import type { SearchQuery } from '@kbn/content-management-plugin/common'; -import type { ContentStorage, StorageContext } from '@kbn/content-management-plugin/server'; +import type { + ContentStorage, + StorageContext, + MSearchConfig, +} from '@kbn/content-management-plugin/server'; import type { SavedObject, SavedObjectReference, SavedObjectsFindOptions, + SavedObjectsFindResult, } from '@kbn/core-saved-objects-api-server'; import { CONTENT_ID } from '../../common/content_management'; @@ -23,10 +28,10 @@ import type { MapGetOut, MapCreateIn, MapCreateOut, - CreateOptions, + MapCreateOptions, MapUpdateIn, MapUpdateOut, - UpdateOptions, + MapUpdateOptions, MapDeleteOut, MapSearchOptions, MapSearchOut, @@ -86,7 +91,9 @@ function savedObjectToMapItem( const SO_TYPE: MapContentType = 'map'; -export class MapsStorage implements ContentStorage { +export class MapsStorage + implements ContentStorage> +{ constructor() {} async get(ctx: StorageContext, id: string): Promise { @@ -133,7 +140,7 @@ export class MapsStorage implements ContentStorage { async create( ctx: StorageContext, data: MapCreateIn['data'], - options: CreateOptions + options: MapCreateOptions ): Promise { const { utils: { getTransforms }, @@ -150,8 +157,8 @@ export class MapsStorage implements ContentStorage { } const { value: optionsToLatest, error: optionsError } = transforms.create.in.options.up< - CreateOptions, - CreateOptions + MapCreateOptions, + MapCreateOptions >(options); if (optionsError) { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); @@ -184,7 +191,7 @@ export class MapsStorage implements ContentStorage { ctx: StorageContext, id: string, data: MapUpdateIn['data'], - options: UpdateOptions + options: MapUpdateOptions ): Promise { const { utils: { getTransforms }, @@ -201,8 +208,8 @@ export class MapsStorage implements ContentStorage { } const { value: optionsToLatest, error: optionsError } = transforms.update.in.options.up< - CreateOptions, - CreateOptions + MapCreateOptions, + MapCreateOptions >(options); if (optionsError) { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); @@ -306,4 +313,29 @@ export class MapsStorage implements ContentStorage { return value; } + + // Configure `mSearch` to opt-in maps into the multi content type search API + mSearch = { + savedObjectType: SO_TYPE, + toItemResult: ( + ctx: StorageContext, + savedObject: SavedObjectsFindResult + ): MapItem => { + const { + utils: { getTransforms }, + } = ctx; + const transforms = getTransforms(cmServicesDefinition); + + // Validate DB response and DOWN transform to the request version + const { value, error: resultError } = transforms.mSearch.out.result.down( + savedObjectToMapItem(savedObject, false) + ); + + if (resultError) { + throw Boom.badRequest(`Invalid response. ${resultError.message}`); + } + + return value; + }, + }; } diff --git a/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts b/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts index a8500968af3cf..af9dc73dbc646 100644 --- a/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts +++ b/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts @@ -8,8 +8,9 @@ import { mapValues } from 'lodash'; import type { CoreSetup, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; import type { SavedObjectMigrationMap } from '@kbn/core/server'; -import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; +import type { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common'; import { mergeSavedObjectMigrationMaps } from '@kbn/core/server'; +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { APP_ICON, getFullPath } from '../../common/constants'; import { CONTENT_ID } from '../../common/content_management'; import { migrateDataPersistedState } from '../../common/migrations/migrate_data_persisted_state'; @@ -24,6 +25,7 @@ export function setupSavedObjects( ) { core.savedObjects.registerType({ name: CONTENT_ID, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 9e501ba66559c..cdbebc4ad74b3 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -67,6 +67,8 @@ "@kbn/core-saved-objects-api-server", "@kbn/object-versioning", "@kbn/field-types", + "@kbn/content-management-utils", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/ml/common/constants/search.ts b/x-pack/plugins/ml/common/constants/search.ts index 9985b502b085c..8ff9b022c274f 100644 --- a/x-pack/plugins/ml/common/constants/search.ts +++ b/x-pack/plugins/ml/common/constants/search.ts @@ -14,8 +14,3 @@ export const SEARCH_QUERY_LANGUAGE = { } as const; export type SearchQueryLanguage = typeof SEARCH_QUERY_LANGUAGE[keyof typeof SEARCH_QUERY_LANGUAGE]; - -export interface ErrorMessage { - query: string; - message: string; -} diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index 8d419f120a564..b540c4d3751f1 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -16,7 +16,6 @@ export { export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; export { composeValidators, patternValidator } from './util/validators'; export { isRuntimeMappings, isRuntimeField } from './util/runtime_field_utils'; -export { extractErrorMessage } from './util/errors'; export type { RuntimeMappings } from './types/fields'; export { getDefaultCapabilities as getDefaultMlCapabilities } from './types/capabilities'; export { DATAFEED_STATE, JOB_STATE } from './constants/states'; diff --git a/x-pack/plugins/ml/common/types/data_frame_analytics.ts b/x-pack/plugins/ml/common/types/data_frame_analytics.ts index 26bdd29ac3090..cba66124bab4b 100644 --- a/x-pack/plugins/ml/common/types/data_frame_analytics.ts +++ b/x-pack/plugins/ml/common/types/data_frame_analytics.ts @@ -9,7 +9,7 @@ import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { EsErrorBody } from '../util/errors'; +import { EsErrorBody } from '@kbn/ml-error-utils'; import { ANALYSIS_CONFIG_TYPE } from '../constants/data_frame_analytics'; import type { UrlConfig } from './custom_urls'; diff --git a/x-pack/plugins/ml/common/types/job_service.ts b/x-pack/plugins/ml/common/types/job_service.ts index a3e1571070ffd..cf029111e0577 100644 --- a/x-pack/plugins/ml/common/types/job_service.ts +++ b/x-pack/plugins/ml/common/types/job_service.ts @@ -5,10 +5,10 @@ * 2.0. */ +import type { ErrorType } from '@kbn/ml-error-utils'; import { Job, JobStats, IndicesOptions } from './anomaly_detection_jobs'; import { RuntimeMappings } from './fields'; import { ES_AGGREGATION } from '../constants/aggregation_types'; -import { ErrorType } from '../util/errors'; export interface MlJobsResponse { jobs: Job[]; diff --git a/x-pack/plugins/ml/common/types/job_validation.ts b/x-pack/plugins/ml/common/types/job_validation.ts index 0c1db63ff3762..226166d45c956 100644 --- a/x-pack/plugins/ml/common/types/job_validation.ts +++ b/x-pack/plugins/ml/common/types/job_validation.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; export interface DatafeedValidationResponse { valid: boolean; diff --git a/x-pack/plugins/ml/common/types/modules.ts b/x-pack/plugins/ml/common/types/modules.ts index dd9f098cabe1c..2a6cc9bbd57ce 100644 --- a/x-pack/plugins/ml/common/types/modules.ts +++ b/x-pack/plugins/ml/common/types/modules.ts @@ -6,8 +6,8 @@ */ import type { SavedObjectAttributes } from '@kbn/core/types'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { Datafeed, Job } from './anomaly_detection_jobs'; -import type { ErrorType } from '../util/errors'; export interface ModuleJob { id: string; diff --git a/x-pack/plugins/ml/common/types/results.ts b/x-pack/plugins/ml/common/types/results.ts index cf25fc6081e16..3fda97b5740b1 100644 --- a/x-pack/plugins/ml/common/types/results.ts +++ b/x-pack/plugins/ml/common/types/results.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { LineAnnotationDatum, RectAnnotationDatum } from '@elastic/charts'; -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { EntityField } from '../util/anomaly_utils'; import type { Datafeed, JobId, ModelSnapshot } from './anomaly_detection_jobs'; import { ES_AGGREGATION, ML_JOB_AGGREGATION } from '../constants/aggregation_types'; diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index ab3b97d1e614d..adaf00fd9405f 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; export type JobType = 'anomaly-detector' | 'data-frame-analytics'; export type TrainedModelType = 'trained-model'; diff --git a/x-pack/plugins/ml/common/types/trained_models.ts b/x-pack/plugins/ml/common/types/trained_models.ts index b4767db5d5344..1de77b523266d 100644 --- a/x-pack/plugins/ml/common/types/trained_models.ts +++ b/x-pack/plugins/ml/common/types/trained_models.ts @@ -51,7 +51,7 @@ export interface TrainedModelStat { } >; }; - deployment_stats?: Omit; + deployment_stats?: TrainedModelDeploymentStatsResponse; model_size_stats?: TrainedModelModelSizeStats; } @@ -128,6 +128,7 @@ export interface InferenceConfigResponse { export interface TrainedModelDeploymentStatsResponse { model_id: string; + deployment_id: string; inference_threads: number; model_threads: number; state: DeploymentState; @@ -163,6 +164,8 @@ export interface TrainedModelDeploymentStatsResponse { } export interface AllocatedModel { + key: string; + deployment_id: string; inference_threads: number; allocation_status: { target_allocation_count: number; diff --git a/x-pack/plugins/ml/common/util/errors/types.ts b/x-pack/plugins/ml/common/util/errors/types.ts deleted file mode 100644 index 9f4b123e3e45d..0000000000000 --- a/x-pack/plugins/ml/common/util/errors/types.ts +++ /dev/null @@ -1,74 +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 { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface MLResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface MLErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface MLHttpFetchErrorBase extends IHttpFetchError { - body: T; -} - -export type MLHttpFetchError = MLHttpFetchErrorBase; - -export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isMLResponseError(error: any): error is MLResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} diff --git a/x-pack/plugins/ml/common/util/validators.ts b/x-pack/plugins/ml/common/util/validators.ts index 4cbef8470cfc0..e890db5893ad6 100644 --- a/x-pack/plugins/ml/common/util/validators.ts +++ b/x-pack/plugins/ml/common/util/validators.ts @@ -99,3 +99,13 @@ export function timeIntervalInputValidator() { return null; }; } + +export function dictionaryValidator(dict: string[], shouldInclude: boolean = false) { + const dictSet = new Set(dict); + return (value: string) => { + if (dictSet.has(value) !== shouldInclude) { + return { matchDict: value }; + } + return null; + }; +} diff --git a/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx b/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx index 77642001757ab..c1102680e5713 100644 --- a/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx +++ b/x-pack/plugins/ml/public/application/aiops/change_point_detection.tsx @@ -13,6 +13,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { ChangePointDetection } from '@kbn/aiops-plugin/public'; +import { useFieldStatsTrigger, FieldStatsFlyoutProvider } from '../components/field_stats_flyout'; import { useMlContext } from '../contexts/ml'; import { useMlKibana } from '../contexts/kibana'; import { HelpMenu } from '../components/help_menu'; @@ -46,20 +47,24 @@ export const ChangePointDetectionPage: FC = () => { ) : null} { appDependencies={pick(services, [ 'application', 'data', + 'executionContext', 'charts', 'fieldFormats', 'http', diff --git a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx index cdc0c079d541c..468afb044cb75 100644 --- a/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx +++ b/x-pack/plugins/ml/public/application/aiops/log_categorization.tsx @@ -49,6 +49,7 @@ export const LogCategorizationPage: FC = () => { appDependencies={pick(services, [ 'application', 'data', + 'executionContext', 'charts', 'fieldFormats', 'http', diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/__snapshots__/editor.test.tsx.snap b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/__snapshots__/editor.test.tsx.snap deleted file mode 100644 index 163bad28d8cc2..0000000000000 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/__snapshots__/editor.test.tsx.snap +++ /dev/null @@ -1,1083 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CustomUrlEditor renders the editor for a dashboard type URL with a label 1`] = ` - - -

    - -

    -
    - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - - - - } - labelType="label" - > - - - - - -
    -`; - -exports[`CustomUrlEditor renders the editor for a discover type URL with an entity and empty time range interval 1`] = ` - - -

    - -

    -
    - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - - - - } - labelType="label" - > - - - - - - } - labelType="label" - > - - - - - -
    -`; - -exports[`CustomUrlEditor renders the editor for a discover type URL with valid time range interval 1`] = ` - - -

    - -

    -
    - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - - - - } - labelType="label" - > - - - - - - } - labelType="label" - > - - - - - -
    -`; - -exports[`CustomUrlEditor renders the editor for a new dashboard type URL with no label 1`] = ` - - -

    - -

    -
    - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - - - - } - labelType="label" - > - - - - - -
    -`; - -exports[`CustomUrlEditor renders the editor for other type of URL with duplicate label 1`] = ` - - -

    - -

    -
    - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - -
    -`; - -exports[`CustomUrlEditor renders the editor for other type of URL with unique label 1`] = ` - - -

    - -

    -
    - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - - } - labelType="label" - > - - - -
    -`; diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/custom_time_range_picker.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/custom_time_range_picker.tsx new file mode 100644 index 0000000000000..620aabd1c842b --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/custom_time_range_picker.tsx @@ -0,0 +1,156 @@ +/* + * 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, { FC, useMemo, useState } from 'react'; +import moment, { type Moment } from 'moment'; +import { + EuiDatePicker, + EuiDatePickerRange, + EuiFlexItem, + EuiFlexGroup, + EuiFormRow, + EuiIconTip, + EuiSpacer, + EuiSwitch, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { useMlKibana } from '../../../contexts/kibana'; + +interface CustomUrlTimeRangePickerProps { + onCustomTimeRangeChange: (customTimeRange?: { start: Moment; end: Moment }) => void; + customTimeRange?: { start: Moment; end: Moment }; +} + +/* + * React component for the form for adding a custom time range. + */ +export const CustomTimeRangePicker: FC = ({ + onCustomTimeRangeChange, + customTimeRange, +}) => { + const [showCustomTimeRangeSelector, setShowCustomTimeRangeSelector] = useState(false); + const { + services: { + data: { + query: { + timefilter: { timefilter }, + }, + }, + }, + } = useMlKibana(); + + const onCustomTimeRangeSwitchChange = (checked: boolean) => { + if (checked === false) { + // Clear the custom time range so it isn't persisted + onCustomTimeRangeChange(undefined); + } + setShowCustomTimeRangeSelector(checked); + }; + + // If the custom time range is not set, default to the timefilter settings + const currentTimeRange = useMemo( + () => + customTimeRange ?? { + start: moment(timefilter.getAbsoluteTime().from), + end: moment(timefilter.getAbsoluteTime().to), + }, + [customTimeRange, timefilter] + ); + + const handleStartChange = (date: moment.Moment) => { + onCustomTimeRangeChange({ ...currentTimeRange, start: date }); + }; + const handleEndChange = (date: moment.Moment) => { + onCustomTimeRangeChange({ ...currentTimeRange, end: date }); + }; + + const { start, end } = currentTimeRange; + + return ( + <> + + + + + + + + } + > + + } + checked={showCustomTimeRangeSelector} + onChange={(e) => onCustomTimeRangeSwitchChange(e.target.checked)} + compressed + /> + + + + {showCustomTimeRangeSelector ? ( + <> + + + } + > + end} + startDateControl={ + + } + endDateControl={ + + } + /> + + + ) : null} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.test.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.test.tsx deleted file mode 100644 index ce7f31df4e86c..0000000000000 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.test.tsx +++ /dev/null @@ -1,163 +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. - */ - -// Mock the mlJobService that is used for testing custom URLs. -import { shallow } from 'enzyme'; - -jest.mock('../../../services/job_service', () => 'mlJobService'); - -import React from 'react'; - -import { CustomUrlEditor } from './editor'; -import { TIME_RANGE_TYPE, URL_TYPE } from './constants'; -import { CustomUrlSettings } from './utils'; -import { DataViewListItem } from '@kbn/data-views-plugin/common'; - -function prepareTest( - customUrl: CustomUrlSettings, - setEditCustomUrlFn: (url: CustomUrlSettings) => void -) { - const savedCustomUrls = [ - { - url_name: 'Show data', - time_range: 'auto', - url_value: - "discover#/?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=" + - '(index:e532ba80-b76f-11e8-a9dc-37914a458883,query:(language:lucene,query:\'airline:"$airline$"\'))', - }, - { - url_name: 'Show dashboard', - time_range: '1h', - url_value: - 'dashboards#/view/52ea8840-bbef-11e8-a04d-b1701b2b977e?_g=' + - "(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&" + - '_a=(filters:!(),query:(language:lucene,query:\'airline:"$airline$"\'))', - }, - { - url_name: 'Show airline', - time_range: 'auto', - url_value: 'http://airlinecodes.info/airline-code-$airline$', - }, - ]; - - const dashboards = [ - { id: 'dash1', title: 'Dashboard 1' }, - { id: 'dash2', title: 'Dashboard 2' }, - ]; - - const dataViewListItems = [ - { id: 'pattern1', title: 'Data view 1' }, - { id: 'pattern2', title: 'Data view 2' }, - ] as DataViewListItem[]; - - const queryEntityFieldNames = ['airline']; - - const props = { - customUrl, - setEditCustomUrl: setEditCustomUrlFn, - savedCustomUrls, - dashboards, - dataViewListItems, - queryEntityFieldNames, - }; - - return shallow(); -} - -describe('CustomUrlEditor', () => { - const setEditCustomUrl = jest.fn(() => {}); - const dashboardUrl = { - label: '', - timeRange: { - type: TIME_RANGE_TYPE.AUTO, - interval: '', - }, - type: URL_TYPE.KIBANA_DASHBOARD, - kibanaSettings: { - queryFieldNames: [], - dashboardId: 'dash1', - }, - }; - - const discoverUrl = { - label: 'Open Discover', - timeRange: { - type: TIME_RANGE_TYPE.INTERVAL, - interval: '', - }, - type: URL_TYPE.KIBANA_DISCOVER, - kibanaSettings: { - queryFieldNames: ['airline'], - discoverIndexPatternId: 'pattern1', - }, - }; - - const otherUrl = { - label: 'Show airline', - timeRange: { - type: TIME_RANGE_TYPE.AUTO, - interval: '', - }, - type: URL_TYPE.OTHER, - otherUrlSettings: { - urlValue: 'https://www.google.co.uk/search?q=airline+code+$airline$', - }, - }; - - test('renders the editor for a new dashboard type URL with no label', () => { - const wrapper = prepareTest(dashboardUrl, setEditCustomUrl); - expect(wrapper).toMatchSnapshot(); - }); - - test('renders the editor for a dashboard type URL with a label', () => { - const dashboardUrlEdit = { - ...dashboardUrl, - label: 'Open Dashboard 1', - }; - const wrapper = prepareTest(dashboardUrlEdit, setEditCustomUrl); - expect(wrapper).toMatchSnapshot(); - }); - - test('renders the editor for a discover type URL with an entity and empty time range interval', () => { - const wrapper = prepareTest(discoverUrl, setEditCustomUrl); - expect(wrapper).toMatchSnapshot(); - }); - - test('renders the editor for a discover type URL with valid time range interval', () => { - const discoverUrlEdit = { - ...discoverUrl, - timeRange: { - type: TIME_RANGE_TYPE.INTERVAL, - interval: '1h', - }, - }; - const wrapper = prepareTest(discoverUrlEdit, setEditCustomUrl); - expect(wrapper).toMatchSnapshot(); - }); - - test('renders the editor for other type of URL with duplicate label', () => { - const wrapper = prepareTest(otherUrl, setEditCustomUrl); - expect(wrapper).toMatchSnapshot(); - }); - - test('renders the editor for other type of URL with unique label', () => { - const otherUrlEdit = { - ...otherUrl, - label: 'View airline', - }; - const wrapper = prepareTest(otherUrlEdit, setEditCustomUrl); - expect(wrapper).toMatchSnapshot(); - }); - - test('calls setEditCustomUrl on updating a custom URL field', () => { - const wrapper = prepareTest(dashboardUrl, setEditCustomUrl); - const labelInput = wrapper.find('EuiFieldText').first(); - labelInput.simulate('change', { target: { value: 'Edit' } }); - wrapper.update(); - expect(setEditCustomUrl).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx index 9eddc02b5e1a4..523f59c32f224 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import React, { ChangeEvent, FC } from 'react'; +import React, { ChangeEvent, useState, useRef, useEffect, FC } from 'react'; +import { type Moment } from 'moment'; import { EuiComboBox, @@ -25,11 +26,17 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import { DataView } from '@kbn/data-views-plugin/public'; import { CustomUrlSettings, isValidCustomUrlSettingsTimeRange } from './utils'; import { isValidLabel } from '../../../util/custom_url_utils'; +import { type DataFrameAnalyticsConfig } from '../../../../../common/types/data_frame_analytics'; +import { type Job } from '../../../../../common/types/anomaly_detection_jobs'; import { TIME_RANGE_TYPE, TimeRangeType, URL_TYPE } from './constants'; import { UrlConfig } from '../../../../../common/types/custom_urls'; +import { CustomTimeRangePicker } from './custom_time_range_picker'; +import { useMlKibana } from '../../../contexts/kibana'; +import { getDropDownOptions } from './get_dropdown_options'; function getLinkToOptions() { return [ @@ -60,8 +67,9 @@ interface CustomUrlEditorProps { savedCustomUrls: UrlConfig[]; dashboards: Array<{ id: string; title: string }>; dataViewListItems: DataViewListItem[]; - queryEntityFieldNames: string[]; showTimeRangeSelector?: boolean; + showCustomTimeRangeSelector: boolean; + job: Job | DataFrameAnalyticsConfig; } /* @@ -73,9 +81,47 @@ export const CustomUrlEditor: FC = ({ savedCustomUrls, dashboards, dataViewListItems, - queryEntityFieldNames, - showTimeRangeSelector = true, + showTimeRangeSelector, + showCustomTimeRangeSelector, + job, }) => { + const [queryEntityFieldNames, setQueryEntityFieldNames] = useState([]); + const [hasTimefield, setHasTimefield] = useState(false); + + const { + services: { + data: { dataViews }, + }, + } = useMlKibana(); + + const isFirst = useRef(true); + + useEffect(() => { + async function getQueryEntityDropdownOptions() { + let dataViewToUse: DataView | undefined; + const dataViewId = customUrl?.kibanaSettings?.discoverIndexPatternId; + + try { + dataViewToUse = await dataViews.get(dataViewId ?? ''); + } catch (e) { + dataViewToUse = undefined; + } + if (dataViewToUse && dataViewToUse.timeFieldName) { + setHasTimefield(true); + } + const dropDownOptions = await getDropDownOptions(isFirst.current, job, dataViewToUse); + setQueryEntityFieldNames(dropDownOptions); + + if (isFirst.current) { + isFirst.current = false; + } + } + + if (job !== undefined) { + getQueryEntityDropdownOptions(); + } + }, [dataViews, job, customUrl?.kibanaSettings?.discoverIndexPatternId]); + if (customUrl === undefined) { return null; } @@ -94,6 +140,13 @@ export const CustomUrlEditor: FC = ({ }); }; + const onCustomTimeRangeChange = (timeRange?: { start: Moment; end: Moment }) => { + setEditCustomUrl({ + ...customUrl, + customTimeRange: timeRange, + }); + }; + const onDashboardChange = (e: ChangeEvent) => { const kibanaSettings = customUrl.kibanaSettings; setEditCustomUrl({ @@ -112,6 +165,7 @@ export const CustomUrlEditor: FC = ({ kibanaSettings: { ...kibanaSettings, discoverIndexPatternId: e.target.value, + queryFieldNames: [], }, }); }; @@ -306,6 +360,13 @@ export const CustomUrlEditor: FC = ({ /> )} + {type === URL_TYPE.KIBANA_DASHBOARD || + (type === URL_TYPE.KIBANA_DISCOVER && showCustomTimeRangeSelector && hasTimefield) ? ( + + ) : null} {(type === URL_TYPE.KIBANA_DASHBOARD || type === URL_TYPE.KIBANA_DISCOVER) && showTimeRangeSelector && ( diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/get_dropdown_options.ts b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/get_dropdown_options.ts new file mode 100644 index 0000000000000..6f94514252dba --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/get_dropdown_options.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 { DataView } from '@kbn/data-views-plugin/public'; +import { + isDataFrameAnalyticsConfigs, + type DataFrameAnalyticsConfig, +} from '../../../../../common/types/data_frame_analytics'; +import { Job, isAnomalyDetectionJob } from '../../../../../common/types/anomaly_detection_jobs'; +import { getQueryEntityFieldNames, getSupportedFieldNames } from './utils'; + +export function getDropDownOptions( + isFirstRender: boolean, + job: Job | DataFrameAnalyticsConfig, + dataView?: DataView +) { + if (isAnomalyDetectionJob(job) && isFirstRender) { + return getQueryEntityFieldNames(job); + } else if ((isDataFrameAnalyticsConfigs(job) || !isFirstRender) && dataView !== undefined) { + return getSupportedFieldNames(job, dataView); + } + return []; +} diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts index 2f1cd59bcfe50..f8ee16456e92a 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts @@ -6,12 +6,13 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { Moment } from 'moment'; import type { SerializableRecord } from '@kbn/utility-types'; import rison from '@kbn/rison'; import url from 'url'; import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; import { cleanEmptyKeys } from '@kbn/dashboard-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/common'; +import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { isFilterPinned, Filter } from '@kbn/es-query'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; import { TimeRange as EsQueryTimeRange } from '@kbn/es-query'; @@ -57,6 +58,7 @@ export interface CustomUrlSettings { // Note timeRange is only editable in new URLs for Dashboard and Discover URLs, // as for other URLs we have no way of knowing how the field will be used in the URL. timeRange: TimeRange; + customTimeRange?: { start: Moment; end: Moment }; kibanaSettings?: { dashboardId?: string; queryFieldNames?: string[]; @@ -92,7 +94,10 @@ export function getNewCustomUrlDefaults( // which matches the indices configured in the job datafeed. let query: estypes.QueryDslQueryContainer = {}; let indicesName: string | undefined; + let backupIndicesName: string | undefined; + let backupDataViewId: string | undefined; let jobId; + if ( isAnomalyDetectionJob(job) && dataViews !== undefined && @@ -106,12 +111,16 @@ export function getNewCustomUrlDefaults( jobId = job.job_id; } else if (isDataFrameAnalyticsConfigs(job) && dataViews !== undefined && dataViews.length > 0) { indicesName = job.dest.index; + backupIndicesName = job.source.index[0]; query = job.source?.query ?? {}; jobId = job.id; } const defaultDataViewId = dataViews.find((dv) => dv.title === indicesName)?.id; - kibanaSettings.discoverIndexPatternId = defaultDataViewId; + if (defaultDataViewId === undefined && backupIndicesName !== undefined) { + backupDataViewId = dataViews.find((dv) => dv.title === backupIndicesName)?.id; + } + kibanaSettings.discoverIndexPatternId = defaultDataViewId ?? backupDataViewId ?? ''; kibanaSettings.filters = defaultDataViewId === null ? [] : getFiltersForDSLQuery(query, defaultDataViewId, jobId); @@ -134,17 +143,23 @@ export function getNewCustomUrlDefaults( // Returns the list of supported field names that can be used // to add to the query used when linking to a Kibana dashboard or Discover. export function getSupportedFieldNames( - job: DataFrameAnalyticsConfig, + job: DataFrameAnalyticsConfig | Job, dataView: DataView ): string[] { - const resultsField = job.dest.results_field; const sortedFields = dataView.fields.getAll().sort((a, b) => a.name.localeCompare(b.name)) ?? []; - const categoryFields = sortedFields.filter( - (f) => + let filterFunction: (field: DataViewField) => boolean = (field: DataViewField) => + categoryFieldTypes.some((type) => { + return field.esTypes?.includes(type); + }); + + if (isDataFrameAnalyticsConfigs(job)) { + const resultsField = job.dest.results_field; + filterFunction = (f) => categoryFieldTypes.some((type) => { return f.esTypes?.includes(type); - }) && !f.name.startsWith(resultsField ?? DEFAULT_RESULTS_FIELD) - ); + }) && !f.name.startsWith(resultsField ?? DEFAULT_RESULTS_FIELD); + } + const categoryFields = sortedFields.filter(filterFunction); return categoryFields.map((field) => field.name); } @@ -208,6 +223,20 @@ export function buildCustomUrlFromSettings(settings: CustomUrlSettings): Promise } } +function getUrlRangeFromSettings(settings: CustomUrlSettings) { + let customStart; + let customEnd; + + if (settings.customTimeRange && settings.customTimeRange.start && settings.customTimeRange.end) { + customStart = settings.customTimeRange.start.toISOString(); + customEnd = settings.customTimeRange.end.toISOString(); + } + return { + from: customStart ?? '$earliest$', + to: customEnd ?? '$latest$', + }; +} + async function buildDashboardUrlFromSettings(settings: CustomUrlSettings): Promise { // Get the complete list of attributes for the selected dashboard (query, filters). const { dashboardId, queryFieldNames } = settings.kibanaSettings ?? {}; @@ -240,11 +269,13 @@ async function buildDashboardUrlFromSettings(settings: CustomUrlSettings): Promi const dashboard = getDashboard(); + const { from, to } = getUrlRangeFromSettings(settings); + const location = await dashboard?.locator?.getLocation({ dashboardId, timeRange: { - from: '$earliest$', - to: '$latest$', + from, + to, mode: 'absolute', }, filters, @@ -286,10 +317,12 @@ function buildDiscoverUrlFromSettings(settings: CustomUrlSettings) { // Add time settings to the global state URL parameter with $earliest$ and // $latest$ tokens which get substituted for times around the time of the // anomaly on which the URL will be run against. + const { from, to } = getUrlRangeFromSettings(settings); + const _g = rison.encode({ time: { - from: '$earliest$', - to: '$latest$', + from, + to, mode: 'absolute', }, }); diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx index 7ebab7b3a1359..4f9ad5245cf91 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx @@ -22,7 +22,6 @@ import { EuiModalFooter, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DataView } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; import { withKibana } from '@kbn/kibana-react-plugin/public'; @@ -31,8 +30,6 @@ import { MlKibanaReactContextValue } from '../../contexts/kibana'; import { CustomUrlEditor, CustomUrlList } from './custom_url_editor'; import { getNewCustomUrlDefaults, - getQueryEntityFieldNames, - getSupportedFieldNames, isValidCustomUrlSettings, buildCustomUrlFromSettings, getTestUrl, @@ -43,22 +40,10 @@ import { loadDataViewListItems, } from '../../jobs/jobs_list/components/edit_job_flyout/edit_utils'; import { openCustomUrlWindow } from '../../util/custom_url_utils'; -import { Job, isAnomalyDetectionJob } from '../../../../common/types/anomaly_detection_jobs'; import { UrlConfig } from '../../../../common/types/custom_urls'; import type { CustomUrlsWrapperProps } from './custom_urls_wrapper'; -import { - isDataFrameAnalyticsConfigs, - type DataFrameAnalyticsConfig, -} from '../../../../common/types/data_frame_analytics'; - -function getDropDownOptions(job: Job | DataFrameAnalyticsConfig, dataView?: DataView) { - if (isAnomalyDetectionJob(job)) { - return getQueryEntityFieldNames(job); - } else if (isDataFrameAnalyticsConfigs(job) && dataView !== undefined) { - return getSupportedFieldNames(job, dataView); - } - return []; -} +import { isAnomalyDetectionJob } from '../../../../common/types/anomaly_detection_jobs'; +import { isDataFrameAnalyticsConfigs } from '../../../../common/types/data_frame_analytics'; const MAX_NUMBER_DASHBOARDS = 1000; @@ -66,14 +51,12 @@ interface CustomUrlsState { customUrls: UrlConfig[]; dashboards: Array<{ id: string; title: string }>; dataViewListItems: DataViewListItem[]; - queryEntityFieldNames: string[]; editorOpen: boolean; editorSettings?: CustomUrlSettings; supportedFilterFields: string[]; } interface CustomUrlsProps extends CustomUrlsWrapperProps { kibana: MlKibanaReactContextValue; - dataView?: DataView; currentTimeFilter?: EsQueryTimeRange; } @@ -85,7 +68,6 @@ class CustomUrlsUI extends Component { customUrls: [], dashboards: [], dataViewListItems: [], - queryEntityFieldNames: [], editorOpen: false, supportedFilterFields: [], }; @@ -95,8 +77,6 @@ class CustomUrlsUI extends Component { return { job: props.job, customUrls: props.jobCustomUrls, - // For DFA uses the destination index Data View to get the query entities and falls back to source index Data View. - queryEntityFieldNames: getDropDownOptions(props.job, props.dataView), }; } @@ -223,25 +203,19 @@ class CustomUrlsUI extends Component { }; renderEditor() { - const { - customUrls, - editorOpen, - editorSettings, - dashboards, - dataViewListItems, - queryEntityFieldNames, - } = this.state; + const { customUrls, editorOpen, editorSettings, dashboards, dataViewListItems } = this.state; const editMode = this.props.editMode ?? 'inline'; const editor = ( ); diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx index a4a125d7cb52f..175b16dca74ea 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx @@ -5,16 +5,11 @@ * 2.0. */ -import React, { useEffect, useState, FC } from 'react'; -import { DataView } from '@kbn/data-views-plugin/public'; +import React, { FC } from 'react'; import { useMlKibana } from '../../contexts/kibana'; import { Job } from '../../../../common/types/anomaly_detection_jobs'; import { UrlConfig } from '../../../../common/types/custom_urls'; -import { getDataViewIdFromName } from '../../util/index_utils'; -import { - isDataFrameAnalyticsConfigs, - type DataFrameAnalyticsConfig, -} from '../../../../common/types/data_frame_analytics'; +import { type DataFrameAnalyticsConfig } from '../../../../common/types/data_frame_analytics'; import { CustomUrls } from './custom_urls'; export interface CustomUrlsWrapperProps { @@ -25,12 +20,9 @@ export interface CustomUrlsWrapperProps { } export const CustomUrlsWrapper: FC = (props) => { - const [dataView, setDataView] = useState(); - const { services: { data: { - dataViews, query: { timefilter: { timefilter }, }, @@ -38,40 +30,5 @@ export const CustomUrlsWrapper: FC = (props) => { }, } = useMlKibana(); - useEffect(() => { - let active = true; - - async function loadDataView() { - if (isDataFrameAnalyticsConfigs(props.job)) { - const destIndex = props.job.dest.index; - const sourceIndex = props.job.source.index[0]; - let dataViewIdSource: string | null; - let dataViewIdDest: string | null; - let dv: DataView | undefined; - - try { - dataViewIdSource = await getDataViewIdFromName(sourceIndex); - dataViewIdDest = await getDataViewIdFromName(destIndex); - dv = await dataViews.get(dataViewIdDest ?? dataViewIdSource ?? ''); - - if (dv === undefined) { - dv = await dataViews.get(dataViewIdSource ?? ''); - } - if (!active) return; - setDataView(dv); - } catch (e) { - dv = undefined; - } - - return dv; - } - } - - loadDataView(); - return () => { - active = false; - }; - }, [dataViews, props.job]); - - return ; + return ; }; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss deleted file mode 100644 index 551734bc2fcdc..0000000000000 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss +++ /dev/null @@ -1,27 +0,0 @@ -.mlDataGridChart__histogram { - width: 100%; - height: $euiSizeXL + $euiSizeXXL; -} - -.mlDataGridChart__legend { - @include euiTextTruncate; - @include euiFontSizeXS; - - color: $euiColorMediumShade; - display: block; - overflow-x: hidden; - margin: $euiSizeXS 0 0 0; - font-style: italic; - font-weight: normal; - text-align: left; -} - -.mlDataGridChart__legend--numeric { - text-align: right; -} - -.mlDataGridChart__legendBoolean { - width: 100%; - min-width: $euiButtonMinWidth; - td { text-align: center } -} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx index 06bb5a363d36a..f28d357f14869 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx @@ -5,18 +5,41 @@ * 2.0. */ -import React, { FC } from 'react'; -import classNames from 'classnames'; +import React, { type FC } from 'react'; +import { css } from '@emotion/react'; import { BarSeries, Chart, Settings, ScaleType } from '@elastic/charts'; -import { EuiDataGridColumn } from '@elastic/eui'; +import { euiTextTruncate, type EuiDataGridColumn } from '@elastic/eui'; -import './column_chart.scss'; +import { euiThemeVars } from '@kbn/ui-theme'; import { isUnsupportedChartData, ChartData } from '../../../../common/types/field_histograms'; import { useColumnChart } from './use_column_chart'; +const cssHistogram = css({ + width: '100%', + height: `calc(${euiThemeVars.euiSizeXL} + ${euiThemeVars.euiSizeXXL})`, +}); + +const cssHistogramLegend = css([ + css` + ${euiTextTruncate()} + `, + { + color: euiThemeVars.euiColorMediumShade, + display: 'block', + overflowX: 'hidden', + margin: `${euiThemeVars.euiSizeXS} 0 0 0`, + fontSize: euiThemeVars.euiFontSizeXS, + fontStyle: 'italic', + fontWeight: 'normal', + textAlign: 'left', + }, +]); + +const cssHistogramLegendNumeric = css([cssHistogramLegend, { textAlign: 'right' }]); + interface Props { chartData: ChartData; columnType: EuiDataGridColumn; @@ -41,6 +64,7 @@ const columnChartTheme = { }, scales: { barsPadding: 0.1 }, }; + export const ColumnChart: FC = ({ chartData, columnType, @@ -53,7 +77,7 @@ export const ColumnChart: FC = ({ return (
    {!isUnsupportedChartData(chartData) && data.length > 0 && ( -
    +
    = ({
    )}
    {legendText} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 86587112f37d7..a32aafe2b0622 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -12,16 +12,14 @@ import { useMemo } from 'react'; import { EuiDataGridCellValueElementProps, EuiDataGridStyle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { CoreSetup } from '@kbn/core/public'; - import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { getNestedProperty } from '@kbn/ml-nested-property'; - import { isCounterTimeSeriesMetric } from '@kbn/ml-agg-utils'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { FeatureImportance, FeatureImportanceClassName, diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss deleted file mode 100644 index f9cc09ef8c425..0000000000000 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.scss +++ /dev/null @@ -1,18 +0,0 @@ -.mlDataGrid { - - .euiDataGridRowCell--boolean { - text-transform: none; - } - - // Overrides to align the sorting arrow, actions icon and the column header when no chart is available, - // to the bottom of the cell when histogram charts are enabled. - // Note that overrides have to be used as currently it is not possible to add a custom class name - // for the EuiDataGridHeaderCell - see https://github.com/elastic/eui/issues/5106 - .euiDataGridHeaderCell { - .euiDataGridHeaderCell__sortingArrow, - .euiDataGridHeaderCell__icon, - .euiPopover { - margin-top: auto; - } - } -} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx index e641410500611..8a290ee186a98 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx @@ -7,8 +7,7 @@ import { isEqual } from 'lodash'; import React, { memo, useEffect, useCallback, useRef, FC } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; import { EuiButtonEmpty, @@ -27,8 +26,11 @@ import { EuiToolTip, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { CoreSetup } from '@kbn/core/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + import { DEFAULT_SAMPLER_SHARD_SIZE } from '../../../../common/constants/field_histograms'; import { ANALYSIS_CONFIG_TYPE, INDEX_STATUS } from '../../data_frame_analytics/common'; @@ -49,10 +51,22 @@ import { import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; import { DataFrameAnalysisConfigType } from '../../../../common/types/data_frame_analytics'; -import './data_grid.scss'; // TODO Fix row hovering + bar highlighting // import { hoveredRow$ } from './column_chart'; +const cssOverride = css({ + '.euiDataGridRowCell--boolean': { textTransform: 'none' }, + // Overrides to align the sorting arrow, actions icon and the column header when no chart is available, + // to the bottom of the cell when histogram charts are enabled. + // Note that overrides have to be used as currently it is not possible to add a custom class name + // for the EuiDataGridHeaderCell - see https://github.com/elastic/eui/issues/5106 + '.euiDataGridHeaderCell': { + '.euiDataGridHeaderCell__sortingArrow,.euiDataGridHeaderCell__icon,.euiPopover': { + marginTop: 'auto', + }, + }, +}); + export const DataGridTitle: FC<{ title: string }> = ({ title }) => ( {title} @@ -337,7 +351,7 @@ export const DataGrid: FC = memo( onMutation={onMutation} > {(mutationRef) => ( -
    +
    { diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx index f4c372d379357..e60c2f5b8f3ba 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx @@ -8,13 +8,14 @@ import moment from 'moment'; import { BehaviorSubject } from 'rxjs'; import React from 'react'; +import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; -import { euiPaletteColorBlind, EuiDataGridColumn } from '@elastic/eui'; +import { euiPaletteColorBlind, type EuiDataGridColumn } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; - import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { @@ -28,6 +29,18 @@ import { import { NON_AGGREGATABLE } from './common'; +const cssHistogramLegendBoolean = css({ + width: '100%', + // This was originally $euiButtonMinWidth, but that + // is no longer exported from the EUI package, + // so we're replicating it here inline. + minWidth: `calc(${euiThemeVars.euiSize} * 7)`, +}); + +const cssTextAlignCenter = css({ + textAlign: 'center', +}); + export const hoveredRow$ = new BehaviorSubject(null); export const BAR_COLOR = euiPaletteColorBlind()[0]; @@ -94,11 +107,15 @@ export const getLegendText = ( if (chartData.type === 'boolean') { return ( - +
    - {chartData.data[0] !== undefined && } - {chartData.data[1] !== undefined && } + {chartData.data[0] !== undefined && ( + + )} + {chartData.data[1] !== undefined && ( + + )}
    {chartData.data[0].key_as_string}{chartData.data[1].key_as_string}{chartData.data[0].key_as_string}{chartData.data[1].key_as_string}
    diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx index f65c57cc52f2f..3148468aa1bdd 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout_provider.tsx @@ -5,27 +5,20 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, type FC } from 'react'; import type { DataView } from '@kbn/data-plugin/common'; import type { FieldStatsServices } from '@kbn/unified-field-list-plugin/public'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; import type { FieldStatsProps } from '@kbn/unified-field-list-plugin/public'; -import { MLJobWizardFieldStatsFlyoutContext } from './use_field_stats_flytout_context'; +import { MLFieldStatsFlyoutContext } from './use_field_stats_flytout_context'; import { FieldStatsFlyout } from './field_stats_flyout'; -export const FieldStatsFlyoutProvider = ({ - dataView, - fieldStatsServices, - timeRangeMs, - dslQuery, - children, -}: { +export const FieldStatsFlyoutProvider: FC<{ dataView: DataView; fieldStatsServices: FieldStatsServices; timeRangeMs?: TimeRangeMs; dslQuery?: FieldStatsProps['dslQuery']; - children: React.ReactElement; -}) => { +}> = ({ dataView, fieldStatsServices, timeRangeMs, dslQuery, children }) => { const [isFieldStatsFlyoutVisible, setFieldStatsIsFlyoutVisible] = useState(false); const [fieldName, setFieldName] = useState(); const [fieldValue, setFieldValue] = useState(); @@ -36,7 +29,7 @@ export const FieldStatsFlyoutProvider = ({ ); return ( - {children} - + ); }; diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/index.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/index.ts index c808653bb9ff7..d844dd037bd5b 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/index.ts +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/index.ts @@ -9,7 +9,7 @@ export { FieldStatsFlyout } from './field_stats_flyout'; export { FieldStatsContent } from './field_stats_content'; export { FieldStatsFlyoutProvider } from './field_stats_flyout_provider'; export { - MLJobWizardFieldStatsFlyoutContext, + MLFieldStatsFlyoutContext, useFieldStatsFlyoutContext, } from './use_field_stats_flytout_context'; export { FieldStatsInfoButton } from './field_stats_info_button'; diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts b/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts index 882201a7cd9ea..2de9fda1ded17 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/use_field_stats_flytout_context.ts @@ -15,7 +15,7 @@ interface MLJobWizardFieldStatsFlyoutProps { setFieldValue: (v: string) => void; fieldValue?: string | number; } -export const MLJobWizardFieldStatsFlyoutContext = createContext({ +export const MLFieldStatsFlyoutContext = createContext({ isFlyoutVisible: false, setIsFlyoutVisible: () => {}, toggleFlyoutVisible: () => {}, @@ -24,5 +24,5 @@ export const MLJobWizardFieldStatsFlyoutContext = createContext = ({ // The internal state of the input query bar updated on every key stroke. const [searchInput, setSearchInput] = useState(query); const [idToSelectedMap, setIdToSelectedMap] = useState<{ [id: string]: boolean }>({}); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); const { services } = useMlKibana(); const { @@ -119,7 +118,7 @@ export const ExplorationQueryBar: FC = ({ convertedQuery = luceneStringToDsl(query.query as string); break; default: - setErrorMessage({ + setQueryErrorMessage({ query: query.query as string, message: i18n.translate('xpack.ml.queryBar.queryLanguageNotSupported', { defaultMessage: 'Query language is not supported', @@ -133,7 +132,7 @@ export const ExplorationQueryBar: FC = ({ language: query.language, }); } catch (e) { - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [query.query]); @@ -187,7 +186,7 @@ export const ExplorationQueryBar: FC = ({ return ( setErrorMessage(undefined)} + closePopover={() => setQueryErrorMessage(undefined)} input={ @@ -249,14 +248,14 @@ export const ExplorationQueryBar: FC = ({ )} } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.ml.stepDefineForm.invalidQuery', { defaultMessage: 'Invalid Query', })} {': '} - {errorMessage?.message.split('\n')[0]} + {queryErrorMessage?.message.split('\n')[0]} ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts index b52c06905792c..30749558a23a1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts @@ -10,9 +10,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; import { CoreSetup } from '@kbn/core/public'; - import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { MlApiServices } from '../../../../../services/ml_api_service'; import { DataLoader } from '../../../../../datavisualizer/index_based/data_loader'; @@ -35,7 +36,6 @@ import { FEATURE_IMPORTANCE, TOP_CLASSES } from '../../../../common/constants'; import { DEFAULT_RESULTS_FIELD } from '../../../../../../../common/constants/data_frame_analytics'; import { sortExplorationResultsFields, ML__ID_COPY } from '../../../../common/fields'; import { isRegressionAnalysis } from '../../../../common/analytics'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { useTrainedModelsApiService } from '../../../../../services/ml_api_service/trained_models'; import { FeatureImportanceBaseline } from '../../../../../../../common/types/feature_importance'; import { useExplorationDataGrid } from './use_exploration_data_grid'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx index 76b85e2384dfd..0ba51a7ca64da 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx @@ -11,6 +11,7 @@ import { cloneDeep, isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { DeepReadonly } from '../../../../../../../common/types/common'; import { DataFrameAnalyticsConfig, isOutlierAnalysis } from '../../../../common'; import { isClassificationAnalysis, isRegressionAnalysis } from '../../../../common/analytics'; @@ -19,7 +20,6 @@ import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana'; import { DEFAULT_NUM_TOP_FEATURE_IMPORTANCE_VALUES } from '../../hooks/use_create_analytics_form'; import { State } from '../../hooks/use_create_analytics_form/state'; import { DataFrameAnalyticsListRow } from '../analytics_list/common'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; interface PropDefinition { /** diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx index 4d1565c1769f3..01b6e3a3f50ad 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { useMlKibana } from '../../../../../contexts/kibana'; import { useToastNotificationService } from '../../../../../services/toast_notification_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index cddc4fcd092dc..4a6ed2176be25 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -10,7 +10,8 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; import { DuplicateDataViewError } from '@kbn/data-plugin/public'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts index 537b2016d9af3..c11490a660f10 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts @@ -6,7 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ml } from '../../../../../services/ml_api_service'; import { ToastNotificationService } from '../../../../../services/toast_notification_service'; import { refreshAnalyticsList$, REFRESH_ANALYTICS_LIST_STATE } from '../../../../common'; diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx index bbb18697dab1c..8f4cd30a4c115 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx @@ -12,7 +12,8 @@ import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@k import type { Query } from '@kbn/es-query'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../common/constants/search'; +import type { QueryErrorMessage } from '@kbn/ml-error-utils'; +import { SEARCH_QUERY_LANGUAGE } from '../../../../../common/constants/search'; import { InfluencersFilterQuery } from '../../../../../common/types/es_client'; import { useAnomalyExplorerContext } from '../../anomaly_explorer_context'; import { useMlKibana } from '../../../contexts/kibana'; @@ -129,7 +130,9 @@ export const ExplorerQueryBar: FC = ({ const [searchInput, setSearchInput] = useState( getInitSearchInputState({ filterActive, queryString }) ); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); useEffect( function updateSearchInputFromFilter() { @@ -160,14 +163,14 @@ export const ExplorerQueryBar: FC = ({ } } catch (e) { console.log('Invalid query syntax in search bar', e); // eslint-disable-line no-console - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } }; return ( = ({ }} /> } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessageQueryBar', { defaultMessage: 'Invalid query', })} {': '} - {errorMessage?.message.split('\n')[0]} + {queryErrorMessage?.message.split('\n')[0]} ); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts index e7ad6802c4401..f0f26a3918438 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts @@ -11,19 +11,20 @@ import { get, union, uniq } from 'lodash'; import moment from 'moment-timezone'; +import { lastValueFrom } from 'rxjs'; + import { ES_FIELD_TYPES } from '@kbn/field-types'; import { asyncForEach } from '@kbn/std'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { lastValueFrom } from 'rxjs'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE, ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, } from '../../../common/constants/search'; import { EntityField, getEntityFieldList } from '../../../common/util/anomaly_utils'; import { getDataViewIdFromName } from '../util/index_utils'; -import { extractErrorMessage } from '../../../common/util/errors'; import { ML_JOB_AGGREGATION } from '../../../common/constants/aggregation_types'; import { isSourceDataChartableForDetector, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js deleted file mode 100644 index f63511d857277..0000000000000 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js +++ /dev/null @@ -1,106 +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 PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import { EuiSpacer, EuiCallOut, EuiLoadingSpinner } from '@elastic/eui'; - -import { ml } from '../../../../services/ml_api_service'; -import { checkPermission } from '../../../../capabilities/check_capabilities'; -import { ML_DATA_PREVIEW_COUNT } from '../../../../../../common/util/job_utils'; -import { MLJobEditor } from '../ml_job_editor'; -import { FormattedMessage } from '@kbn/i18n-react'; - -export class DatafeedPreviewPane extends Component { - constructor(props) { - super(props); - - this.state = { - previewJson: '', - loading: true, - canPreviewDatafeed: true, - }; - } - - renderContent() { - const { previewJson, loading, canPreviewDatafeed } = this.state; - - if (canPreviewDatafeed === false) { - return ( - - } - color="warning" - iconType="warning" - > -

    - -

    -
    - ); - } else if (loading === true) { - return ; - } else { - return ; - } - } - - componentDidMount() { - const canPreviewDatafeed = - checkPermission('canPreviewDatafeed') && this.props.job.datafeed_config !== undefined; - this.setState({ canPreviewDatafeed }); - - updateDatafeedPreview(this.props.job, canPreviewDatafeed) - .then((previewJson) => { - this.setState({ previewJson, loading: false }); - }) - .catch((error) => { - console.log('Datafeed preview could not be loaded', error); - this.setState({ loading: false }); - }); - } - - render() { - return ( - - - {this.renderContent()} - - ); - } -} -DatafeedPreviewPane.propTypes = { - job: PropTypes.object.isRequired, -}; - -function updateDatafeedPreview(job, canPreviewDatafeed) { - return new Promise((resolve, reject) => { - if (canPreviewDatafeed) { - ml.jobs - .datafeedPreview(job.datafeed_config.datafeed_id) - .then((resp) => { - if (Array.isArray(resp)) { - resolve(JSON.stringify(resp.slice(0, ML_DATA_PREVIEW_COUNT), null, 2)); - } else { - resolve(''); - console.log('Datafeed preview could not be loaded', resp); - } - }) - .catch((error) => { - reject(error); - }); - } - }); -} diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.tsx new file mode 100644 index 0000000000000..1ad46ce7bfce5 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.tsx @@ -0,0 +1,102 @@ +/* + * 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, { FC, useEffect, useState } from 'react'; +import { EuiCallOut, EuiLoadingSpinner } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { ML_DATA_PREVIEW_COUNT } from '../../../../../../common/util/job_utils'; +import { useMlApiContext } from '../../../../contexts/kibana'; +import { usePermissionCheck } from '../../../../capabilities/check_capabilities'; +import { CombinedJob } from '../../../../../shared'; +import { MLJobEditor } from '../ml_job_editor'; + +interface Props { + job: CombinedJob; +} + +export const DatafeedPreviewPane: FC = ({ job }) => { + const { + jobs: { datafeedPreview }, + } = useMlApiContext(); + + const canPreviewDatafeed = usePermissionCheck('canPreviewDatafeed'); + const [loading, setLoading] = useState(false); + const [previewJson, setPreviewJson] = useState(''); + + useEffect(() => { + setLoading(true); + datafeedPreview(job.datafeed_config.datafeed_id).then((resp) => { + if (Array.isArray(resp)) { + if (resp.length === 0) { + setPreviewJson(null); + } else { + setPreviewJson(JSON.stringify(resp.slice(0, ML_DATA_PREVIEW_COUNT), null, 2)); + } + } else { + setPreviewJson(''); + } + + setLoading(false); + }); + }, [datafeedPreview, job]); + + if (canPreviewDatafeed === false) { + return ; + } + + return loading ? ( + + ) : ( + <> + {previewJson === null ? ( + + ) : ( + + )} + + ); +}; + +const InsufficientPermissions: FC = () => ( + + } + color="warning" + iconType="warning" + > +

    + +

    +
    +); + +const EmptyResults: FC = () => ( + + } + color="warning" + iconType="warning" + > +

    + +

    +
    +); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx index 8b3ed99709cb5..86cd23b46383e 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx @@ -9,10 +9,10 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../../../services/ml_api_service'; import { JobMessages } from '../../../../components/job_messages'; import { JobMessage } from '../../../../../../common/types/audit_message'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; import { useToastNotificationService } from '../../../../services/toast_notification_service'; import { useMlApiContext } from '../../../../contexts/kibana'; import { checkPermission } from '../../../../capabilities/check_capabilities'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js index e3c6458fabda9..b6166dd6cbd82 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js @@ -27,6 +27,7 @@ export class TimeRangeSelector extends Component { }; this.latestTimestamp = this.props.startTime; this.now = this.props.now; + this.twoWeeksAgo = moment(this.now).subtract(2, 'weeks').startOf('day'); } setStartTab = (tab) => { @@ -38,6 +39,9 @@ export class TimeRangeSelector extends Component { case 1: this.setStartTime(this.now); break; + case 2: + this.setStartTime(this.twoWeeksAgo); + break; default: break; } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts index f445afe634b90..2d28256336757 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts @@ -20,7 +20,7 @@ import { mlOnlyAggregations, } from '../../../../../../common/constants/aggregation_types'; import { getQueryFromSavedSearchObject } from '../../../../util/index_utils'; -import { +import type { Job, Datafeed, Detector, @@ -29,11 +29,11 @@ import { BucketSpan, CustomSettings, } from '../../../../../../common/types/anomaly_detection_jobs'; -import { Aggregation, Field, RuntimeMappings } from '../../../../../../common/types/fields'; +import type { Aggregation, Field, RuntimeMappings } from '../../../../../../common/types/fields'; import { combineFieldsAndAggs } from '../../../../../../common/util/fields_utils'; import { createEmptyJob, createEmptyDatafeed } from './util/default_configs'; import { mlJobService } from '../../../../services/job_service'; -import { JobRunner, ProgressSubscriber } from '../job_runner'; +import { JobRunner, type ProgressSubscriber } from '../job_runner'; import { JOB_TYPE, CREATED_BY_LABEL, @@ -42,7 +42,7 @@ import { import { collectAggs } from './util/general'; import { filterRuntimeMappings } from './util/filter_runtime_mappings'; import { parseInterval } from '../../../../../../common/util/parse_interval'; -import { Calendar } from '../../../../../../common/types/calendars'; +import type { Calendar } from '../../../../../../common/types/calendars'; import { mlCalendarService } from '../../../../services/calendar_service'; import { getDatafeedAggregations } from '../../../../../../common/util/datafeed_utils'; import { getFirstKeyInObject } from '../../../../../../common/util/object_utils'; @@ -542,6 +542,28 @@ export class JobCreator { this._datafeed_config.indices = indics; } + public get ignoreUnavailable(): boolean { + return !!this._datafeed_config.indices_options?.ignore_unavailable; + } + + public set ignoreUnavailable(ignore: boolean) { + if (ignore === true) { + if (this._datafeed_config.indices_options === undefined) { + this._datafeed_config.indices_options = {}; + } + this._datafeed_config.indices_options.ignore_unavailable = true; + } else { + if (this._datafeed_config.indices_options !== undefined) { + delete this._datafeed_config.indices_options.ignore_unavailable; + + // if no other properties are set, remove indices_options + if (Object.keys(this._datafeed_config.indices_options).length === 0) { + delete this._datafeed_config.indices_options; + } + } + } + } + public get scriptFields(): Field[] { return this._scriptFields; } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts index 159f1af10e69f..52d4d77106217 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { combineLatest, Observable, of, Subject, Subscription } from 'rxjs'; import { isEqual, cloneDeep } from 'lodash'; import { @@ -21,10 +20,13 @@ import { skipWhile, } from 'rxjs/operators'; import { useEffect, useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { type MLHttpFetchError, extractErrorMessage } from '@kbn/ml-error-utils'; + import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../../../../../common/constants/new_job'; import { ml } from '../../../../../services/ml_api_service'; import { JobValidator, VALIDATION_DELAY_MS } from '../../job_validator/job_validator'; -import { MLHttpFetchError, extractErrorMessage } from '../../../../../../../common/util/errors'; import { useMlKibana } from '../../../../../contexts/kibana'; import { JobCreator } from '../job_creator'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts index a22b6d9fd57a5..f01898fa905a1 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts @@ -17,9 +17,9 @@ import type { Filter, Query, DataViewBase } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; import type { Embeddable } from '@kbn/lens-plugin/public'; import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { MlApiServices } from '../../../services/ml_api_service'; import { getFiltersForDSLQuery } from '../../../../../common/util/job_utils'; -import type { ErrorType } from '../../../../../common/util/errors'; import { CREATED_BY_LABEL } from '../../../../../common/constants/new_job'; import { createQueries } from '../utils/new_job_utils'; import { createDatafeedId } from '../../../../../common/util/job_utils'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts index c7f5a02e75d5b..4552123184f5c 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts @@ -10,8 +10,8 @@ import { layerTypes } from '@kbn/lens-plugin/public'; import { i18n } from '@kbn/i18n'; +import type { ErrorType } from '@kbn/ml-error-utils'; import { JOB_TYPE } from '../../../../../common/constants/new_job'; -import { ErrorType } from '../../../../../common/util/errors'; import { getVisTypeFactory, isCompatibleLayer, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts index da2b8e29c0010..26972a454ff8b 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts @@ -10,7 +10,7 @@ import type { MapEmbeddable } from '@kbn/maps-plugin/public'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { Query } from '@kbn/es-query'; -import type { DashboardContainer } from '@kbn/dashboard-plugin/public'; +import type { DashboardAPI } from '@kbn/dashboard-plugin/public'; import { categoryFieldTypes } from '../../../../../common/util/fields_utils'; export interface LayerResult { @@ -28,8 +28,7 @@ export class VisualizationExtractor { public async getResultLayersFromEmbeddable(embeddable: MapEmbeddable): Promise { const layers: LayerResult[] = []; - const dataViews: DataView[] = - (embeddable.getRoot() as DashboardContainer)?.getAllDataViews() ?? []; + const dataViews: DataView[] = (embeddable.getRoot() as DashboardAPI)?.getAllDataViews() ?? []; // Keep track of geoFields for layers as they can be repeated const layerGeoFields: Record = {}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx index 71d68b895f605..9e68d9f6f8c37 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useState, useEffect, useCallback, useContext } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { @@ -23,7 +22,10 @@ import { EuiModalBody, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { JobCreatorContext } from '../../../job_creator_context'; import { AdvancedJobCreator } from '../../../../../common/job_creator'; import { resetAdvancedJob } from '../../../../../common/job_creator/util/general'; @@ -31,7 +33,6 @@ import { CombinedJob, Datafeed, } from '../../../../../../../../../common/types/anomaly_detection_jobs'; -import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; import type { DatafeedValidationResponse } from '../../../../../../../../../common/types/job_validation'; import { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/advanced_section.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/advanced_section.tsx index 6774db9225fd4..e0ad13e1d1c00 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/advanced_section.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/advanced_section.tsx @@ -20,6 +20,7 @@ import { DedicatedIndexSwitch } from './components/dedicated_index'; import { ModelMemoryLimitInput } from '../../../common/model_memory_limit'; import { JobCreatorContext } from '../../../job_creator_context'; import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job'; +import { IgnoreUnavailableSwitch } from './components/ignore_unavailable'; const buttonContent = i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.advancedSectionButton', @@ -43,12 +44,22 @@ export const AdvancedSection: FC = ({ advancedExpanded, setAdvancedExpand - + + + + + + + + + + + ); } @@ -71,13 +82,39 @@ export const AdvancedSection: FC = ({ advancedExpanded, setAdvancedExpand > - - + + + + + + + + + + + + + + + + + + + + ); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/annotations_switch.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/annotations_switch.tsx index 12294a2e3fed4..154d0785e244e 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/annotations_switch.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/annotations_switch.tsx @@ -7,7 +7,7 @@ import React, { FC, useState, useContext, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiCallOut, EuiSpacer, EuiSwitch } from '@elastic/eui'; +import { EuiCallOut, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { JobCreatorContext } from '../../../../../job_creator_context'; import { Description } from './description'; @@ -62,7 +62,6 @@ export const AnnotationsSwitch: FC = () => { iconType="help" /> )} - ); }; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/description.tsx new file mode 100644 index 0000000000000..5e40856084246 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/description.tsx @@ -0,0 +1,42 @@ +/* + * 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, FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCode, EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; + +export const Description: FC = memo(({ children }) => { + const title = i18n.translate( + 'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.ignoreUnavailable.title', + { + defaultMessage: 'Ignore unavailable indices', + } + ); + return ( + {title}} + description={ + + ignore_unavailable + + ), + }} + /> + } + > + + <>{children} + + + ); +}); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/ignore_unavailable_switch.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/ignore_unavailable_switch.tsx new file mode 100644 index 0000000000000..aa6be80329c76 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/ignore_unavailable_switch.tsx @@ -0,0 +1,44 @@ +/* + * 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, { FC, useState, useContext, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiSwitch } from '@elastic/eui'; +import { JobCreatorContext } from '../../../../../job_creator_context'; +import { Description } from './description'; + +export const IgnoreUnavailableSwitch: FC = () => { + const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext); + const [ignoreUnavailable, setIgnoreUnavailable] = useState(jobCreator.ignoreUnavailable); + + useEffect(() => { + jobCreator.ignoreUnavailable = ignoreUnavailable; + jobCreatorUpdate(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ignoreUnavailable]); + + function toggleIgnoreUnavailable() { + setIgnoreUnavailable(!ignoreUnavailable); + } + + return ( + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/index.ts b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/index.ts new file mode 100644 index 0000000000000..997ee4f81a175 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/ignore_unavailable/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 { IgnoreUnavailableSwitch } from './ignore_unavailable_switch'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx index 631922a388faa..167335bacab77 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx @@ -7,7 +7,7 @@ import React, { FC, useState, useContext, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiSwitch } from '@elastic/eui'; +import { EuiSwitch } from '@elastic/eui'; import { JobCreatorContext } from '../../../../../job_creator_context'; import { Description } from './description'; import { MMLCallout } from '../mml_callout'; @@ -58,7 +58,6 @@ export const ModelPlotSwitch: FC = () => { /> - ); }; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx index 8030292bd9e59..090fcf40d3f17 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx @@ -11,10 +11,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { from } from 'rxjs'; import { switchMap, takeWhile, tap } from 'rxjs/operators'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { ml } from '../../../../../../../services/ml_api_service'; -import { extractErrorProperties } from '../../../../../../../../../common/util/errors'; const NUMBER_OF_PREVIEW = 5; export const CategoryStoppedPartitions: FC = () => { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx index 4491dfab1abdd..b12b4261a0628 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx @@ -8,13 +8,13 @@ import React, { FC, useContext, useEffect, useState } from 'react'; import { EuiBasicTable, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { Results } from '../../../../../common/results_loader'; import { ml } from '../../../../../../../services/ml_api_service'; import { useToastNotificationService } from '../../../../../../../services/toast_notification_service'; import { NUMBER_OF_CATEGORY_EXAMPLES } from '../../../../../../../../../common/constants/categorization_job'; -import { extractErrorProperties } from '../../../../../../../../../common/util/errors'; export const TopCategories: FC = () => { const { displayErrorToast } = useToastNotificationService(); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx index 7fb483b54cf5d..75bc8772e0ac6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx @@ -6,12 +6,15 @@ */ import React, { FC, Fragment, useContext, useState } from 'react'; + import { EuiButton, EuiFlexItem } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { JobRunner } from '../../../../../common/job_runner'; import { useMlKibana } from '../../../../../../../contexts/kibana'; -import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; import { JobCreatorContext } from '../../../job_creator_context'; import { DATAFEED_STATE } from '../../../../../../../../../common/constants/states'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx index db8374f4bec44..aad21319cf627 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard_steps.tsx @@ -63,7 +63,7 @@ export const WizardSteps: FC = ({ currentStep, setCurrentStep }) => { const timeRangeMs = useMemo(() => { // If time range is available via jobCreator, use that // else mimic Discover and set timeRange to be now for data view without time field - return start && end ? { from: start, to: start } : undefined; + return start && end ? { from: start, to: end } : undefined; }, [start, end]); // store whether the advanced and additional sections have been expanded. diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx index 93b6086398358..cd6bd11096c65 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx @@ -18,11 +18,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ModuleJobUI } from '../page'; import { SETUP_RESULTS_WIDTH } from './module_jobs'; import { tabColor } from '../../../../../../common/util/group_color_utils'; import { JobOverride, DatafeedResponse } from '../../../../../../common/types/modules'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; interface JobItemProps { job: ModuleJobUI; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx index 079e698c3a5c0..95ac0b4043f57 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx @@ -18,8 +18,8 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { KibanaObjectUi } from '../page'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; export interface KibanaObjectItemProps { objectType: string; diff --git a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx index 198c01806e06b..6b86287b6faa1 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/allocated_models.tsx @@ -39,6 +39,33 @@ export const AllocatedModels: FC = ({ const euiTheme = useEuiTheme(); const columns: Array> = [ + { + id: 'deployment_id', + field: 'deployment_id', + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.deploymentIdHeader', { + defaultMessage: 'ID', + }), + width: '150px', + sortable: true, + truncateText: false, + 'data-test-subj': 'mlAllocatedModelsTableDeploymentId', + }, + { + name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelRoutingStateHeader', { + defaultMessage: 'Routing state', + }), + width: '100px', + 'data-test-subj': 'mlAllocatedModelsTableRoutingState', + render: (v: AllocatedModel) => { + const { routing_state: routingState, reason } = v.node.routing_state; + + return ( + + {routingState} + + ); + }, + }, { id: 'node_name', field: 'node.name', @@ -193,22 +220,6 @@ export const AllocatedModels: FC = ({ return v.node.number_of_pending_requests; }, }, - { - name: i18n.translate('xpack.ml.trainedModels.nodesList.modelsList.modelRoutingStateHeader', { - defaultMessage: 'Routing state', - }), - width: '100px', - 'data-test-subj': 'mlAllocatedModelsTableRoutingState', - render: (v: AllocatedModel) => { - const { routing_state: routingState, reason } = v.node.routing_state; - - return ( - - {routingState} - - ); - }, - }, ].filter((v) => !hideColumns.includes(v.id!)); return ( @@ -219,7 +230,7 @@ export const AllocatedModels: FC = ({ isExpandable={false} isSelectable={false} items={models} - itemId={'model_id'} + itemId={'key'} rowProps={(item) => ({ 'data-test-subj': `mlAllocatedModelTableRow row-${item.model_id}`, })} diff --git a/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx b/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx index 9068f679cf261..622fb0a961183 100644 --- a/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx +++ b/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx @@ -5,25 +5,27 @@ * 2.0. */ -import React, { FC, useState, useMemo } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { - EuiForm, + EuiButton, + EuiButtonEmpty, EuiButtonGroup, - EuiFormRow, + EuiCallOut, + EuiDescribedFormGroup, EuiFieldNumber, + EuiFieldText, + EuiForm, + EuiFormRow, + EuiLink, EuiModal, - EuiModalHeader, - EuiModalHeaderTitle, EuiModalBody, EuiModalFooter, - EuiButtonEmpty, - EuiButton, - EuiCallOut, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSelect, EuiSpacer, - EuiDescribedFormGroup, - EuiLink, } from '@elastic/eui'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; import type { Observable } from 'rxjs'; @@ -31,17 +33,26 @@ import type { CoreTheme, OverlayStart } from '@kbn/core/public'; import { css } from '@emotion/react'; import { numberValidator } from '@kbn/ml-agg-utils'; import { isCloudTrial } from '../services/ml_server_info'; -import { composeValidators, requiredValidator } from '../../../common/util/validators'; +import { + composeValidators, + dictionaryValidator, + requiredValidator, +} from '../../../common/util/validators'; +import { ModelItem } from './models_list'; interface DeploymentSetupProps { config: ThreadingParams; onConfigChange: (config: ThreadingParams) => void; + errors: Partial>; + isUpdate?: boolean; + deploymentsParams?: Record; } export interface ThreadingParams { numOfAllocations: number; threadsPerAllocations?: number; priority?: 'low' | 'normal'; + deploymentId?: string; } const THREADS_MAX_EXPONENT = 4; @@ -49,10 +60,21 @@ const THREADS_MAX_EXPONENT = 4; /** * Form for setting threading params. */ -export const DeploymentSetup: FC = ({ config, onConfigChange }) => { +export const DeploymentSetup: FC = ({ + config, + onConfigChange, + errors, + isUpdate, + deploymentsParams, +}) => { const numOfAllocation = config.numOfAllocations; const threadsPerAllocations = config.threadsPerAllocations; + const defaultDeploymentId = useMemo(() => { + return config.deploymentId; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const threadsPerAllocationsOptions = useMemo( () => new Array(THREADS_MAX_EXPONENT).fill(null).map((v, i) => { @@ -72,6 +94,70 @@ export const DeploymentSetup: FC = ({ config, onConfigChan return ( + + + + } + description={ + + } + > + + } + hasChildLabel={false} + isInvalid={!!errors.deploymentId} + error={ + + } + > + {!isUpdate ? ( + { + onConfigChange({ ...config, deploymentId: e.target.value }); + }} + data-test-subj={'mlModelsStartDeploymentModalDeploymentId'} + /> + ) : ( + { + return { text: v, value: v }; + })} + value={config.deploymentId} + onChange={(e) => { + const update = e.target.value; + onConfigChange({ + ...config, + deploymentId: update, + numOfAllocations: deploymentsParams![update].numOfAllocations, + }); + }} + data-test-subj={'mlModelsStartDeploymentModalDeploymentSelectId'} + /> + )} + + + {config.priority !== undefined ? ( = ({ config, onConfigChan }; interface StartDeploymentModalProps { - modelId: string; + model: ModelItem; startModelDeploymentDocUrl: string; onConfigChange: (config: ThreadingParams) => void; onClose: () => void; initialParams?: ThreadingParams; + modelAndDeploymentIds?: string[]; } /** * Modal window wrapper for {@link DeploymentSetup} */ export const StartUpdateDeploymentModal: FC = ({ - modelId, + model, onConfigChange, onClose, startModelDeploymentDocUrl, initialParams, + modelAndDeploymentIds, }) => { + const isUpdate = !!initialParams; + const [config, setConfig] = useState( initialParams ?? { numOfAllocations: 1, threadsPerAllocations: 1, priority: isCloudTrial() ? 'low' : 'normal', + deploymentId: model.model_id, } ); - const isUpdate = initialParams !== undefined; + const deploymentIdValidator = useMemo(() => { + if (isUpdate) { + return () => null; + } + + const otherModelAndDeploymentIds = [...(modelAndDeploymentIds ?? [])]; + otherModelAndDeploymentIds.splice(otherModelAndDeploymentIds?.indexOf(model.model_id), 1); + + return dictionaryValidator([ + ...model.deployment_ids, + ...otherModelAndDeploymentIds, + // check for deployment with the default ID + ...(model.deployment_ids.includes(model.model_id) ? [''] : []), + ]); + }, [modelAndDeploymentIds, model.deployment_ids, model.model_id, isUpdate]); const numOfAllocationsValidator = composeValidators( requiredValidator(), numberValidator({ min: 1, integerOnly: true }) ); - const errors = numOfAllocationsValidator(config.numOfAllocations); + const numOfAllocationsErrors = numOfAllocationsValidator(config.numOfAllocations); + const deploymentIdErrors = deploymentIdValidator(config.deploymentId ?? ''); + + const errors = { + ...(numOfAllocationsErrors ? { numOfAllocations: numOfAllocationsErrors } : {}), + ...(deploymentIdErrors ? { deploymentId: deploymentIdErrors } : {}), + }; return ( = ({ ) : ( )} @@ -313,7 +424,19 @@ export const StartUpdateDeploymentModal: FC = ({ /> - + >( + (acc, curr) => { + acc[curr.deployment_id] = { numOfAllocations: curr.number_of_allocations }; + return acc; + }, + {} + )} + /> @@ -346,7 +469,7 @@ export const StartUpdateDeploymentModal: FC = ({ form={'startDeploymentForm'} onClick={onConfigChange.bind(null, config)} fill - disabled={!!errors} + disabled={Object.keys(errors).length > 0} data-test-subj={'mlModelsStartDeploymentModalStartButton'} > {isUpdate ? ( @@ -373,9 +496,13 @@ export const StartUpdateDeploymentModal: FC = ({ * @param overlays * @param theme$ */ -export const getUserInputThreadingParamsProvider = +export const getUserInputModelDeploymentParamsProvider = (overlays: OverlayStart, theme$: Observable, startModelDeploymentDocUrl: string) => - (modelId: string, initialParams?: ThreadingParams): Promise => { + ( + model: ModelItem, + initialParams?: ThreadingParams, + deploymentIds?: string[] + ): Promise => { return new Promise(async (resolve) => { try { const modalSession = overlays.openModal( @@ -384,7 +511,8 @@ export const getUserInputThreadingParamsProvider = { modalSession.close(); diff --git a/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx b/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx index 4d6a3c744408a..0fd2068d8bcf3 100644 --- a/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useEffect, useState, useMemo, useCallback } from 'react'; +import React, { FC, useMemo, useCallback } from 'react'; import { omit, pick } from 'lodash'; import { EuiBadge, @@ -110,8 +110,6 @@ export function useListItemsFormatter() { } export const ExpandedRow: FC = ({ item }) => { - const [modelItems, setModelItems] = useState([]); - const formatToListItems = useListItemsFormatter(); const { @@ -144,42 +142,39 @@ export const ExpandedRow: FC = ({ item }) => { license_level, }; - useEffect( - function updateModelItems() { - (async function () { - const deploymentStats = stats.deployment_stats; - const modelSizeStats = stats.model_size_stats; + const deploymentStatItems: AllocatedModel[] = useMemo(() => { + const deploymentStats = stats.deployment_stats; + const modelSizeStats = stats.model_size_stats; - if (!deploymentStats || !modelSizeStats) return; + if (!deploymentStats || !modelSizeStats) return []; - const items: AllocatedModel[] = deploymentStats.nodes.map((n) => { - const nodeName = Object.values(n.node)[0].name; - return { - ...deploymentStats, - ...modelSizeStats, - node: { - ...pick(n, [ - 'average_inference_time_ms', - 'inference_count', - 'routing_state', - 'last_access', - 'number_of_pending_requests', - 'start_time', - 'throughput_last_minute', - 'number_of_allocations', - 'threads_per_allocation', - ]), - name: nodeName, - } as AllocatedModel['node'], - }; - }); + const items: AllocatedModel[] = deploymentStats.flatMap((perDeploymentStat) => { + return perDeploymentStat.nodes.map((n) => { + const nodeName = Object.values(n.node)[0].name; + return { + key: `${perDeploymentStat.deployment_id}_${nodeName}`, + ...perDeploymentStat, + ...modelSizeStats, + node: { + ...pick(n, [ + 'average_inference_time_ms', + 'inference_count', + 'routing_state', + 'last_access', + 'number_of_pending_requests', + 'start_time', + 'throughput_last_minute', + 'number_of_allocations', + 'threads_per_allocation', + ]), + name: nodeName, + } as AllocatedModel['node'], + }; + }); + }); - setModelItems(items); - })(); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [stats.deployment_stats] - ); + return items; + }, [stats]); const tabs: EuiTabbedContentTab[] = [ { @@ -313,7 +308,7 @@ export const ExpandedRow: FC = ({ item }) => {
    - {!!modelItems?.length ? ( + {!!deploymentStatItems?.length ? ( <> @@ -325,7 +320,7 @@ export const ExpandedRow: FC = ({ item }) => { - + @@ -379,7 +374,7 @@ export const ExpandedRow: FC = ({ item }) => { }, ] : []), - ...((pipelines && Object.keys(pipelines).length > 0) || stats.ingest + ...((isPopulatedObject(pipelines) && Object.keys(pipelines).length > 0) || stats.ingest ? [ { id: 'pipelines', @@ -389,8 +384,10 @@ export const ExpandedRow: FC = ({ item }) => { {' '} - {stats.pipeline_count} + /> + + {isPopulatedObject(pipelines) ? Object.keys(pipelines!).length : 0} + ), content: ( diff --git a/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx b/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx index 1800f9b13db03..39b108127b127 100644 --- a/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx +++ b/x-pack/plugins/ml/public/application/model_management/force_stop_dialog.tsx @@ -5,33 +5,108 @@ * 2.0. */ -import React, { FC } from 'react'; -import { EuiConfirmModal } from '@elastic/eui'; +import React, { type FC, useState, useMemo, useCallback } from 'react'; +import { + EuiCallOut, + EuiCheckboxGroup, + EuiCheckboxGroupOption, + EuiConfirmModal, + EuiSpacer, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import type { OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { isDefined } from '@kbn/ml-is-defined'; import type { ModelItem } from './models_list'; interface ForceStopModelConfirmDialogProps { model: ModelItem; onCancel: () => void; - onConfirm: () => void; + onConfirm: (deploymentIds: string[]) => void; } -export const ForceStopModelConfirmDialog: FC = ({ +/** + * Confirmation is required when there are multiple model deployments + * or associated pipelines. + */ +export const StopModelDeploymentsConfirmDialog: FC = ({ model, onConfirm, onCancel, }) => { + const [checkboxIdToSelectedMap, setCheckboxIdToSelectedMap] = useState>( + {} + ); + + const options: EuiCheckboxGroupOption[] = useMemo( + () => + model.deployment_ids.map((deploymentId) => { + return { + id: deploymentId, + label: deploymentId, + }; + }), + [model.deployment_ids] + ); + + const onChange = useCallback((id: string) => { + setCheckboxIdToSelectedMap((prev) => { + return { + ...prev, + [id]: !prev[id], + }; + }); + }, []); + + const selectedDeploymentIds = useMemo( + () => + model.deployment_ids.length > 1 + ? Object.keys(checkboxIdToSelectedMap).filter((id) => checkboxIdToSelectedMap[id]) + : model.deployment_ids, + [model.deployment_ids, checkboxIdToSelectedMap] + ); + + const deploymentPipelinesMap = useMemo(() => { + if (!isPopulatedObject(model.pipelines)) return {}; + return Object.entries(model.pipelines).reduce((acc, [pipelineId, pipelineDef]) => { + const deploymentIds: string[] = (pipelineDef?.processors ?? []) + .map((v) => v?.inference?.model_id) + .filter(isDefined); + deploymentIds.forEach((dId) => { + if (acc[dId]) { + acc[dId].push(pipelineId); + } else { + acc[dId] = [pipelineId]; + } + }); + return acc; + }, {} as Record); + }, [model.pipelines]); + + const pipelineWarning = useMemo(() => { + if (model.deployment_ids.length === 1 && isPopulatedObject(model.pipelines)) { + return Object.keys(model.pipelines); + } + return [ + ...new Set( + Object.entries(deploymentPipelinesMap) + .filter(([deploymentId]) => selectedDeploymentIds.includes(deploymentId)) + .flatMap(([, pipelineNames]) => pipelineNames) + ), + ].sort(); + }, [model, deploymentPipelinesMap, selectedDeploymentIds]); + return ( = { defaultMessage: 'Stop' } )} buttonColor="danger" + confirmButtonDisabled={model.deployment_ids.length > 1 && selectedDeploymentIds.length === 0} > - -
      - {Object.keys(model.pipelines!) - .sort() - .map((pipelineName) => { - return
    • {pipelineName}
    • ; - })} -
    + {model.deployment_ids.length > 1 ? ( + <> + + ), + }} + options={options} + idToSelectedMap={checkboxIdToSelectedMap} + onChange={onChange} + /> + + + ) : null} + + {pipelineWarning.length > 0 ? ( + <> + + } + color="warning" + iconType="warning" + > +

    +

      + {pipelineWarning.map((pipelineName) => { + return
    • {pipelineName}
    • ; + })} +
    +

    +
    + + ) : null}
    ); }; export const getUserConfirmationProvider = - (overlays: OverlayStart, theme: ThemeServiceStart) => async (forceStopModel: ModelItem) => { + (overlays: OverlayStart, theme: ThemeServiceStart) => + async (forceStopModel: ModelItem): Promise => { return new Promise(async (resolve, reject) => { try { const modalSession = overlays.openModal( toMountPoint( wrapWithTheme( - { modalSession.close(); - resolve(false); + reject(); }} - onConfirm={() => { + onConfirm={(deploymentIds: string[]) => { modalSession.close(); - resolve(true); + resolve(deploymentIds); }} />, theme.theme$ @@ -80,7 +188,7 @@ export const getUserConfirmationProvider = ) ); } catch (e) { - resolve(false); + reject(); } }); }; diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 7d4b6866f7a16..8fdb50f7b40f3 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -11,14 +11,14 @@ import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { EuiToolTip } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { + BUILT_IN_MODEL_TAG, DEPLOYMENT_STATE, TRAINED_MODEL_TYPE, - BUILT_IN_MODEL_TAG, } from '@kbn/ml-trained-models-utils'; import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models'; import { getUserConfirmationProvider } from './force_stop_dialog'; import { useToastNotificationService } from '../services/toast_notification_service'; -import { getUserInputThreadingParamsProvider } from './deployment_setup'; +import { getUserInputModelDeploymentParamsProvider } from './deployment_setup'; import { useMlKibana, useMlLocator, useNavigateToPath } from '../contexts/kibana'; import { getAnalysisType } from '../../../common/util/analytics_utils'; import { DataFrameAnalysisConfigType } from '../../../common/types/data_frame_analytics'; @@ -32,12 +32,14 @@ export function useModelActions({ onLoading, isLoading, fetchModels, + modelAndDeploymentIds, }: { isLoading: boolean; - onTestAction: (model: string) => void; + onTestAction: (model: ModelItem) => void; onModelsDeleteRequest: (modelsIds: string[]) => void; onLoading: (isLoading: boolean) => void; fetchModels: () => void; + modelAndDeploymentIds: string[]; }): Array> { const { services: { @@ -67,8 +69,9 @@ export function useModelActions({ [overlays, theme] ); - const getUserInputThreadingParams = useMemo( - () => getUserInputThreadingParamsProvider(overlays, theme.theme$, startModelDeploymentDocUrl), + const getUserInputModelDeploymentParams = useMemo( + () => + getUserInputModelDeploymentParamsProvider(overlays, theme.theme$, startModelDeploymentDocUrl), [overlays, theme.theme$, startModelDeploymentDocUrl] ); @@ -151,26 +154,27 @@ export function useModelActions({ type: 'icon', isPrimary: true, enabled: (item) => { - const { state } = item.stats?.deployment_stats ?? {}; - return ( - canStartStopTrainedModels && - !isLoading && - state !== DEPLOYMENT_STATE.STARTED && - state !== DEPLOYMENT_STATE.STARTING - ); + return canStartStopTrainedModels && !isLoading; }, available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH, onClick: async (item) => { - const threadingParams = await getUserInputThreadingParams(item.model_id); + const modelDeploymentParams = await getUserInputModelDeploymentParams( + item, + undefined, + modelAndDeploymentIds + ); - if (!threadingParams) return; + if (!modelDeploymentParams) return; try { onLoading(true); await trainedModelsApiService.startModelAllocation(item.model_id, { - number_of_allocations: threadingParams.numOfAllocations, - threads_per_allocation: threadingParams.threadsPerAllocations!, - priority: threadingParams.priority!, + number_of_allocations: modelDeploymentParams.numOfAllocations, + threads_per_allocation: modelDeploymentParams.threadsPerAllocations!, + priority: modelDeploymentParams.priority!, + deployment_id: !!modelDeploymentParams.deploymentId + ? modelDeploymentParams.deploymentId + : item.model_id, }); displaySuccessToast( i18n.translate('xpack.ml.trainedModels.modelsList.startSuccess', { @@ -213,18 +217,23 @@ export function useModelActions({ item.model_type === TRAINED_MODEL_TYPE.PYTORCH && canStartStopTrainedModels && !isLoading && - item.stats?.deployment_stats?.state === DEPLOYMENT_STATE.STARTED, + !!item.stats?.deployment_stats?.some((v) => v.state === DEPLOYMENT_STATE.STARTED), onClick: async (item) => { - const threadingParams = await getUserInputThreadingParams(item.model_id, { - numOfAllocations: item.stats?.deployment_stats?.number_of_allocations!, + const deploymentToUpdate = item.deployment_ids[0]; + + const deploymentParams = await getUserInputModelDeploymentParams(item, { + deploymentId: deploymentToUpdate, + numOfAllocations: item.stats!.deployment_stats.find( + (v) => v.deployment_id === deploymentToUpdate + )!.number_of_allocations, }); - if (!threadingParams) return; + if (!deploymentParams) return; try { onLoading(true); - await trainedModelsApiService.updateModelDeployment(item.model_id, { - number_of_allocations: threadingParams.numOfAllocations, + await trainedModelsApiService.updateModelDeployment(deploymentParams.deploymentId!, { + number_of_allocations: deploymentParams.numOfAllocations, }); displaySuccessToast( i18n.translate('xpack.ml.trainedModels.modelsList.updateSuccess', { @@ -265,26 +274,23 @@ export function useModelActions({ 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, + canStartStopTrainedModels && !isLoading && item.deployment_ids.length > 0, onClick: async (item) => { const requireForceStop = isPopulatedObject(item.pipelines); + const hasMultipleDeployments = item.deployment_ids.length > 1; - if (requireForceStop) { - const hasUserApproved = await getUserConfirmation(item); - if (!hasUserApproved) return; - } - - if (requireForceStop) { - const hasUserApproved = await getUserConfirmation(item); - if (!hasUserApproved) return; + let deploymentIds: string[] = item.deployment_ids; + if (requireForceStop || hasMultipleDeployments) { + try { + deploymentIds = await getUserConfirmation(item); + } catch (error) { + return; + } } try { onLoading(true); - await trainedModelsApiService.stopModelAllocation(item.model_id, { + await trainedModelsApiService.stopModelAllocation(deploymentIds, { force: requireForceStop, }); displaySuccessToast( @@ -363,28 +369,29 @@ export function useModelActions({ type: 'icon', isPrimary: true, available: isTestable, - onClick: (item) => onTestAction(item.model_id), - enabled: (item) => canTestTrainedModels && isTestable(item, true), + onClick: (item) => onTestAction(item), + enabled: (item) => canTestTrainedModels && isTestable(item, true) && !isLoading, }, ], [ - canDeleteTrainedModels, + urlLocator, + navigateToUrl, + navigateToPath, canStartStopTrainedModels, - canTestTrainedModels, - displayErrorToast, + isLoading, + getUserInputModelDeploymentParams, + modelAndDeploymentIds, + onLoading, + trainedModelsApiService, displaySuccessToast, + fetchModels, + displayErrorToast, getUserConfirmation, - getUserInputThreadingParams, + onModelsDeleteRequest, + canDeleteTrainedModels, isBuiltInModel, - navigateToPath, - navigateToUrl, onTestAction, - trainedModelsApiService, - urlLocator, - onModelsDeleteRequest, - onLoading, - fetchModels, - isLoading, + canTestTrainedModels, ] ); } diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 48a1e6266b235..5887dc05c5369 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -18,7 +18,7 @@ import { EuiTitle, SearchFilterConfig, } from '@elastic/eui'; - +import { groupBy } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; @@ -27,15 +27,21 @@ import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { usePageUrlState } from '@kbn/ml-url-state'; import { useTimefilter } from '@kbn/ml-date-picker'; -import { BUILT_IN_MODEL_TYPE, BUILT_IN_MODEL_TAG } from '@kbn/ml-trained-models-utils'; +import { + BUILT_IN_MODEL_TYPE, + BUILT_IN_MODEL_TAG, + DEPLOYMENT_STATE, +} from '@kbn/ml-trained-models-utils'; +import { isDefined } from '@kbn/ml-is-defined'; import { useModelActions } from './model_actions'; import { ModelsTableToConfigMapping } from '.'; import { ModelsBarStats, StatsBar } from '../components/stats_bar'; import { useMlKibana } from '../contexts/kibana'; import { useTrainedModelsApiService } from '../services/ml_api_service/trained_models'; -import { +import type { ModelPipelines, TrainedModelConfigResponse, + TrainedModelDeploymentStatsResponse, TrainedModelStat, } from '../../../common/types/trained_models'; import { DeleteModelsModal } from './delete_models_modal'; @@ -49,12 +55,13 @@ import { useRefresh } from '../routing/use_refresh'; import { SavedObjectsWarning } from '../components/saved_objects_warning'; import { TestTrainedModelFlyout } from './test_models'; -type Stats = Omit; +type Stats = Omit; export type ModelItem = TrainedModelConfigResponse & { type?: string[]; - stats?: Stats; + stats?: Stats & { deployment_stats: TrainedModelDeploymentStatsResponse[] }; pipelines?: ModelPipelines['pipelines'] | null; + deployment_ids: string[]; }; export type ModelItemFull = Required; @@ -120,7 +127,7 @@ export const ModelsList: FC = ({ const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); - const [showTestFlyout, setShowTestFlyout] = useState(null); + const [modelToTest, setModelToTest] = useState(null); const isBuiltInModel = useCallback( (item: ModelItem) => item.tags.includes(BUILT_IN_MODEL_TAG), @@ -150,11 +157,11 @@ export const ModelsList: FC = ({ type: [ model.model_type, ...Object.keys(model.inference_config), - ...(isBuiltInModel(model) ? [BUILT_IN_MODEL_TYPE] : []), + ...(isBuiltInModel(model as ModelItem) ? [BUILT_IN_MODEL_TYPE] : []), ], } : {}), - }; + } as ModelItem; newItems.push(tableItem); if (itemIdToExpandedRowMap[model.model_id]) { @@ -162,7 +169,8 @@ export const ModelsList: FC = ({ } } - // Need to fetch state for all models to enable/disable actions + // Need to fetch stats for all models to enable/disable actions + // TODO combine fetching models definitions and stats into a single function await fetchModelsStats(newItems); setItems(newItems); @@ -219,15 +227,19 @@ export const ModelsList: FC = ({ const { trained_model_stats: modelsStatsResponse } = await trainedModelsApiService.getTrainedModelStats(models.map((m) => m.model_id)); - for (const { model_id: id, ...stats } of modelsStatsResponse) { - const model = models.find((m) => m.model_id === id); - if (model) { - model.stats = { - ...(model.stats ?? {}), - ...stats, - }; - } - } + const groupByModelId = groupBy(modelsStatsResponse, 'model_id'); + + models.forEach((model) => { + const modelStats = groupByModelId[model.model_id]; + model.stats = { + ...(model.stats ?? {}), + ...modelStats[0], + deployment_stats: modelStats.map((d) => d.deployment_stats).filter(isDefined), + }; + model.deployment_ids = modelStats + .map((v) => v.deployment_stats?.deployment_id) + .filter(isDefined); + }); } return true; @@ -263,15 +275,23 @@ export const ModelsList: FC = ({ })); }, [items]); + const modelAndDeploymentIds = useMemo( + () => [ + ...new Set([...items.flatMap((v) => v.deployment_ids), ...items.map((i) => i.model_id)]), + ], + [items] + ); + /** * Table actions */ const actions = useModelActions({ isLoading, fetchModels: fetchModelsData, - onTestAction: setShowTestFlyout, + onTestAction: setModelToTest, onModelsDeleteRequest: setModelIdsToDelete, onLoading: setIsLoading, + modelAndDeploymentIds, }); const toggleDetails = async (item: ModelItem) => { @@ -351,11 +371,14 @@ export const ModelsList: FC = ({ name: i18n.translate('xpack.ml.trainedModels.modelsList.stateHeader', { defaultMessage: 'State', }), - sortable: (item) => item.stats?.deployment_stats?.state, align: 'left', - truncateText: true, + truncateText: false, render: (model: ModelItem) => { - const state = model.stats?.deployment_stats?.state; + const state = model.stats?.deployment_stats?.some( + (v) => v.state === DEPLOYMENT_STATE.STARTED + ) + ? DEPLOYMENT_STATE.STARTED + : ''; return state ? {state} : null; }, 'data-test-subj': 'mlModelsTableColumnDeploymentState', @@ -533,11 +556,8 @@ export const ModelsList: FC = ({ modelIds={modelIdsToDelete} /> )} - {showTestFlyout === null ? null : ( - + {modelToTest === null ? null : ( + )} ); diff --git a/x-pack/plugins/ml/public/application/model_management/pipelines/pipelines.tsx b/x-pack/plugins/ml/public/application/model_management/pipelines/pipelines.tsx index ac8156ae0053b..7eb20d77ec8d1 100644 --- a/x-pack/plugins/ml/public/application/model_management/pipelines/pipelines.tsx +++ b/x-pack/plugins/ml/public/application/model_management/pipelines/pipelines.tsx @@ -45,9 +45,8 @@ export const ModelPipelines: FC = ({ pipelines, ingestStats const pipelineDefinition = pipelines?.[pipelineName]; return ( - <> + @@ -81,7 +80,7 @@ export const ModelPipelines: FC = ({ pipelines, ingestStats initialIsOpen={initialIsOpen} > - {ingestStats?.pipelines ? ( + {ingestStats!.pipelines[pipelineName]?.processors ? ( @@ -93,7 +92,7 @@ export const ModelPipelines: FC = ({ pipelines, ingestStats - + ) : null} @@ -123,7 +122,7 @@ export const ModelPipelines: FC = ({ pipelines, ingestStats ) : null} - + ); })} diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts index 59fa138d0f29f..2eff762343077 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { map } from 'rxjs/operators'; import { SupportedPytorchTasksType } from '@kbn/ml-trained-models-utils'; -import { MLHttpFetchError } from '../../../../../common/util/errors'; +import type { MLHttpFetchError } from '@kbn/ml-error-utils'; import { trainedModelsApiProvider } from '../../../services/ml_api_service/trained_models'; import { getInferenceInfoComponent } from './inference_info'; @@ -61,6 +61,8 @@ export abstract class InferenceBase { protected abstract readonly inferenceTypeLabel: string; protected readonly modelInputField: string; + protected _deploymentId: string | null = null; + protected inputText$ = new BehaviorSubject([]); private inputField$ = new BehaviorSubject(''); private inferenceResult$ = new BehaviorSubject(null); @@ -76,7 +78,8 @@ export abstract class InferenceBase { constructor( protected readonly trainedModelsApi: ReturnType, protected readonly model: estypes.MlTrainedModelConfig, - protected readonly inputType: INPUT_TYPE + protected readonly inputType: INPUT_TYPE, + protected readonly deploymentId: string ) { this.modelInputField = model.input?.field_names[0] ?? DEFAULT_INPUT_FIELD; this.inputField$.next(this.modelInputField); @@ -243,7 +246,7 @@ export abstract class InferenceBase { ): estypes.IngestProcessorContainer[] { const processor: estypes.IngestProcessorContainer = { inference: { - model_id: this.model.model_id, + model_id: this.deploymentId ?? this.model.model_id, target_field: this.inferenceType, field_map: { [this.inputField$.getValue()]: this.modelInputField, @@ -277,7 +280,7 @@ export abstract class InferenceBase { const inferenceConfig = getInferenceConfig(); const resp = (await this.trainedModelsApi.inferTrainedModel( - this.model.model_id, + this.deploymentId ?? this.model.model_id, { docs: this.getInferDocs(), ...(inferenceConfig ? { inference_config: inferenceConfig } : {}), diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx index 5060f0033fd6c..4dbe900283657 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx @@ -8,7 +8,7 @@ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { FormattedMessage } from '@kbn/i18n-react'; + import { EuiSpacer, EuiButton, @@ -21,8 +21,10 @@ import { EuiForm, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ErrorMessage } from '../../inference_error'; -import { extractErrorMessage } from '../../../../../../common'; import type { InferrerType } from '..'; import { useIndexInput, InferenceInputFormIndexControls } from '../index_input'; import { RUNNING_STATE } from '../inference_base'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx index 3446bda477b4a..fc162a305c32b 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx @@ -6,13 +6,14 @@ */ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; - import useObservable from 'react-use/lib/useObservable'; -import { FormattedMessage } from '@kbn/i18n-react'; + import { EuiSpacer, EuiButton, EuiTabs, EuiTab, EuiForm } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ErrorMessage } from '../../inference_error'; -import { extractErrorMessage } from '../../../../../../common'; import type { InferrerType } from '..'; import { OutputLoadingContent } from '../../output_loading'; import { RUNNING_STATE } from '../inference_base'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/ner/ner_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/ner/ner_inference.ts index f01127e94cda6..15cdec114aad4 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/ner/ner_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/ner/ner_inference.ts @@ -36,9 +36,10 @@ export class NerInference extends InferenceBase { constructor( trainedModelsApi: ReturnType, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize(); } diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/question_answering/question_answering_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/question_answering/question_answering_inference.ts index 6c80703340d1d..b428442f8908d 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/question_answering/question_answering_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/question_answering/question_answering_inference.ts @@ -62,9 +62,10 @@ export class QuestionAnsweringInference extends InferenceBase, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize( [this.questionText$.pipe(map((questionText) => questionText !== ''))], diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/fill_mask_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/fill_mask_inference.ts index 2ec63d4453288..0c109292f16f1 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/fill_mask_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/fill_mask_inference.ts @@ -34,9 +34,10 @@ export class FillMaskInference extends InferenceBase constructor( trainedModelsApi: ReturnType, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize([ this.inputText$.pipe(map((inputText) => inputText.every((t) => t.includes(MASK)))), diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/lang_ident_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/lang_ident_inference.ts index ff6565de32b19..198d30f42b677 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/lang_ident_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/lang_ident_inference.ts @@ -30,9 +30,10 @@ export class LangIdentInference extends InferenceBase, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize(); } diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/text_classification_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/text_classification_inference.ts index 362e162c24b5e..3ee9588162544 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/text_classification_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/text_classification_inference.ts @@ -30,9 +30,10 @@ export class TextClassificationInference extends InferenceBase, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize(); } diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/zero_shot_classification_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/zero_shot_classification_inference.ts index 62af6f2dd55ae..19cc970826821 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/zero_shot_classification_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_classification/zero_shot_classification_inference.ts @@ -37,9 +37,10 @@ export class ZeroShotClassificationInference extends InferenceBase, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize( [this.labelsText$.pipe(map((labelsText) => labelsText !== ''))], diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_embedding/text_embedding_inference.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_embedding/text_embedding_inference.ts index 1200690d665aa..258e4161f626b 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/text_embedding/text_embedding_inference.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/text_embedding/text_embedding_inference.ts @@ -42,9 +42,10 @@ export class TextEmbeddingInference extends InferenceBase constructor( trainedModelsApi: ReturnType, model: estypes.MlTrainedModelConfig, - inputType: INPUT_TYPE + inputType: INPUT_TYPE, + deploymentId: string ) { - super(trainedModelsApi, model, inputType); + super(trainedModelsApi, model, inputType, deploymentId); this.initialize(); } diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx index 3749620b47b13..c719aa7368cfa 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx @@ -29,42 +29,42 @@ import { INPUT_TYPE } from './models/inference_base'; interface Props { model: estypes.MlTrainedModelConfig; inputType: INPUT_TYPE; + deploymentId: string; } -export const SelectedModel: FC = ({ model, inputType }) => { +export const SelectedModel: FC = ({ model, inputType, deploymentId }) => { const { trainedModels } = useMlApiContext(); - const inferrer: InferrerType | undefined = useMemo(() => { + const inferrer = useMemo(() => { if (model.model_type === TRAINED_MODEL_TYPE.PYTORCH) { const taskType = Object.keys(model.inference_config)[0]; switch (taskType) { case SUPPORTED_PYTORCH_TASKS.NER: - return new NerInference(trainedModels, model, inputType); + return new NerInference(trainedModels, model, inputType, deploymentId); break; case SUPPORTED_PYTORCH_TASKS.TEXT_CLASSIFICATION: - return new TextClassificationInference(trainedModels, model, inputType); + return new TextClassificationInference(trainedModels, model, inputType, deploymentId); break; case SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION: - return new ZeroShotClassificationInference(trainedModels, model, inputType); + return new ZeroShotClassificationInference(trainedModels, model, inputType, deploymentId); break; case SUPPORTED_PYTORCH_TASKS.TEXT_EMBEDDING: - return new TextEmbeddingInference(trainedModels, model, inputType); + return new TextEmbeddingInference(trainedModels, model, inputType, deploymentId); break; case SUPPORTED_PYTORCH_TASKS.FILL_MASK: - return new FillMaskInference(trainedModels, model, inputType); + return new FillMaskInference(trainedModels, model, inputType, deploymentId); break; case SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING: - return new QuestionAnsweringInference(trainedModels, model, inputType); + return new QuestionAnsweringInference(trainedModels, model, inputType, deploymentId); break; - default: break; } } else if (model.model_type === TRAINED_MODEL_TYPE.LANG_IDENT) { - return new LangIdentInference(trainedModels, model, inputType); + return new LangIdentInference(trainedModels, model, inputType, deploymentId); } - }, [inputType, model, trainedModels]); + }, [inputType, model, trainedModels, deploymentId]); useEffect(() => { return () => { diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx index 1ee8a853bb477..d3fa13b233f5f 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx @@ -5,50 +5,35 @@ * 2.0. */ -import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import React, { FC, useState, useEffect } from 'react'; +import React, { FC, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFlyout, - EuiFlyoutHeader, - EuiTitle, EuiFlyoutBody, + EuiFlyoutHeader, + EuiFormRow, + EuiSelect, EuiSpacer, EuiTab, EuiTabs, + EuiTitle, useEuiPaddingSize, } from '@elastic/eui'; import { SelectedModel } from './selected_model'; import { INPUT_TYPE } from './models/inference_base'; -import { useTrainedModelsApiService } from '../../services/ml_api_service/trained_models'; +import { type ModelItem } from '../models_list'; interface Props { - modelId: string; + model: ModelItem; onClose: () => void; } -export const TestTrainedModelFlyout: FC = ({ modelId, onClose }) => { +export const TestTrainedModelFlyout: FC = ({ model, onClose }) => { + const [deploymentId, setDeploymentId] = useState(model.deployment_ids[0]); const mediumPadding = useEuiPaddingSize('m'); - const trainedModelsApiService = useTrainedModelsApiService(); const [inputType, setInputType] = useState(INPUT_TYPE.TEXT); - const [model, setModel] = useState(null); - - useEffect( - function fetchModel() { - trainedModelsApiService.getTrainedModels(modelId).then((resp) => { - if (resp.length) { - setModel(resp[0]); - } - }); - }, - [modelId, trainedModelsApiService] - ); - - if (model === null) { - return null; - } return ( <> @@ -68,6 +53,32 @@ export const TestTrainedModelFlyout: FC = ({ modelId, onClose }) => { + {model.deployment_ids.length > 1 ? ( + <> + + } + > + { + return { text: v, value: v }; + })} + value={deploymentId} + onChange={(e) => { + setDeploymentId(e.target.value); + }} + /> + + + + ) : null} + = ({ modelId, onClose }) => { - + diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/utils.ts b/x-pack/plugins/ml/public/application/model_management/test_models/utils.ts index 2048af3e31173..bb0dc6e9973e8 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/utils.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/utils.ts @@ -22,7 +22,7 @@ export function isTestable(modelItem: ModelItem, checkForState = false) { Object.keys(modelItem.inference_config)[0] as SupportedPytorchTasksType ) && (checkForState === false || - modelItem.stats?.deployment_stats?.state === DEPLOYMENT_STATE.STARTED) + modelItem.stats?.deployment_stats?.some((v) => v.state === DEPLOYMENT_STATE.STARTED)) ) { return true; } diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts index 0e209c01ddb6a..1ab3a659442b1 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts @@ -368,8 +368,7 @@ export const jobsApiProvider = (httpService: HttpService) => ({ ) { const body = JSON.stringify({ jobId, snapshotId, replay, end, calendarEvents }); return httpService.http<{ - total: number; - categories: Array<{ count?: number; category: Category }>; + success: boolean; }>({ path: `${ML_BASE_PATH}/jobs/revert_model_snapshot`, method: 'POST', @@ -379,10 +378,7 @@ export const jobsApiProvider = (httpService: HttpService) => ({ datafeedPreview(datafeedId?: string, job?: Job, datafeed?: Datafeed) { const body = JSON.stringify({ datafeedId, job, datafeed }); - return httpService.http<{ - total: number; - categories: Array<{ count?: number; category: Category }>; - }>({ + return httpService.http({ path: `${ML_BASE_PATH}/jobs/datafeed_preview`, method: 'POST', body, 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 1548a298acd18..2975982eccad0 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 @@ -132,6 +132,7 @@ export function trainedModelsApiProvider(httpService: HttpService) { number_of_allocations: number; threads_per_allocation: number; priority: 'low' | 'normal'; + deployment_id?: string; } ) { return httpService.http<{ acknowledge: boolean }>({ @@ -141,11 +142,11 @@ export function trainedModelsApiProvider(httpService: HttpService) { }); }, - stopModelAllocation(modelId: string, options: { force: boolean } = { force: false }) { + stopModelAllocation(deploymentsIds: string[], options: { force: boolean } = { force: false }) { const force = options?.force; return httpService.http<{ acknowledge: boolean }>({ - path: `${apiBasePath}/trained_models/${modelId}/deployment/_stop`, + path: `${apiBasePath}/trained_models/${deploymentsIds.join(',')}/deployment/_stop`, method: 'POST', query: { force }, }); diff --git a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts index 2cfcf50c56514..c24ae573a9514 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts @@ -16,6 +16,7 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { each, get } from 'lodash'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { ErrorType } from '@kbn/ml-error-utils'; import { Dictionary } from '../../../../common/types/common'; import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils'; import { Datafeed, JobId } from '../../../../common/types/anomaly_detection_jobs'; @@ -28,7 +29,6 @@ import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { InfluencersFilterQuery } from '../../../../common/types/es_client'; import { RecordForInfluencer } from './results_service'; import { isRuntimeMappings } from '../../../../common'; -import { ErrorType } from '../../../../common/util/errors'; export interface ResultResponse { success: boolean; diff --git a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts index 8bc7bc8c87e35..585b881010d7a 100644 --- a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts +++ b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts @@ -8,13 +8,9 @@ import { i18n } from '@kbn/i18n'; import { ToastInput, ToastOptions, ToastsStart } from '@kbn/core/public'; import { useMemo } from 'react'; +import { extractErrorProperties, type ErrorType, MLRequestFailure } from '@kbn/ml-error-utils'; import { getToastNotifications } from '../../util/dependency_cache'; import { useNotifications } from '../../contexts/kibana'; -import { - ErrorType, - extractErrorProperties, - MLRequestFailure, -} from '../../../../common/util/errors'; export type ToastNotificationService = ReturnType; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js index e97cf8e2639fc..a767d7b65e4f3 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js @@ -5,10 +5,11 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { getToastNotifications } from '../../../util/dependency_cache'; import { ml } from '../../../services/ml_api_service'; -import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../common/util/errors'; export async function deleteCalendars(calendarsToDelete, callback) { if (calendarsToDelete === undefined || calendarsToDelete.length === 0) { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js index 64bc1eefccd79..3342680070e8b 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js @@ -16,9 +16,13 @@ import React, { Component } from 'react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { withKibana } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { FORECAST_REQUEST_STATE, JOB_STATE } from '../../../../../common/constants/states'; import { MESSAGE_LEVEL } from '../../../../../common/constants/message_levels'; -import { extractErrorMessage } from '../../../../../common/util/errors'; import { isJobVersionGte } from '../../../../../common/util/job_utils'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { Modal } from './modal'; @@ -26,9 +30,6 @@ import { PROGRESS_STATES } from './progress_states'; import { ml } from '../../../services/ml_api_service'; import { mlJobService } from '../../../services/job_service'; import { mlForecastService } from '../../../services/forecast_service'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { withKibana } from '@kbn/kibana-react-plugin/public'; export const FORECAST_DURATION_MAX_DAYS = 3650; // Max forecast duration allowed by analytics. diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx index c9ca8250bedd9..af42229d8ac79 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx @@ -7,11 +7,11 @@ import React, { FC, useEffect, useState, useCallback, useContext } from 'react'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { MlTooltipComponent } from '../../../components/chart_tooltip'; import { TimeseriesChart } from './timeseries_chart'; import { CombinedJob } from '../../../../../common/types/anomaly_detection_jobs'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../../common/constants/search'; -import { extractErrorMessage } from '../../../../../common/util/errors'; import { Annotation } from '../../../../../common/types/annotations'; import { useMlKibana, useNotifications } from '../../../contexts/kibana'; import { getBoundsRoundedToInterval } from '../../../util/time_buckets'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts index f9dd1fa94c4f0..7a7837ba71435 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts @@ -7,9 +7,9 @@ import { forkJoin, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../services/ml_api_service'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../common/constants/search'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { mlTimeSeriesSearchService } from '../timeseries_search_service'; import { mlResultsService, CriteriaField } from '../../services/results_service'; import { Job } from '../../../../common/types/anomaly_detection_jobs'; diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx index 7585ffe32118d..e2fff3bd286cf 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx @@ -5,14 +5,10 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React, { FC, useState, useCallback } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; -import type { Embeddable } from '@kbn/lens-plugin/public'; -import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexItem, @@ -30,11 +26,16 @@ import { EuiCallOut, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { Embeddable } from '@kbn/lens-plugin/public'; +import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { QuickLensJobCreator } from '../../../application/jobs/new_job/job_from_lens'; import type { LayerResult } from '../../../application/jobs/new_job/job_from_lens'; import type { CreateState } from '../../../application/jobs/new_job/job_from_dashboard'; import { JOB_TYPE, DEFAULT_BUCKET_SPAN } from '../../../../common/constants/new_job'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { basicJobValidation } from '../../../../common/util/job_utils'; import { JOB_ID_MAX_LENGTH } from '../../../../common/constants/validation'; import { invalidTimeIntervalMessage } from '../../../application/jobs/new_job/common/job_validator/util'; diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx index f012e928e6b61..5f804e209eaa3 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx @@ -6,13 +6,13 @@ */ import React, { FC } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; -import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_lens'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; +import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_lens'; interface Props { layer: LayerResult; diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts index ab8b15c94e260..2b1cc16f131b6 100644 --- a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -34,30 +34,63 @@ const DATA_FRAME_ANALYTICS_DEEP_LINK: AppDeepLink = { path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE}`, }; +const AIOPS_DEEP_LINK: AppDeepLink = { + id: 'aiOpsDeepLink', + title: i18n.translate('xpack.ml.deepLink.aiOps', { + defaultMessage: 'AIOps', + }), + // Default to the index select page for the explain log rate spikes since we don't have an AIops overview page + path: `/${ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT}`, + deepLinks: [ + { + id: 'explainLogRateSpikesDeepLink', + title: i18n.translate('xpack.ml.deepLink.explainLogRateSpikes', { + defaultMessage: 'Explain Log Rate Spikes', + }), + path: `/${ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT}`, + }, + { + id: 'logPatternAnalysisDeepLink', + title: i18n.translate('xpack.ml.deepLink.logPatternAnalysis', { + defaultMessage: 'Log Pattern Analysis', + }), + path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT}`, + }, + { + id: 'changePointDetectionsDeepLink', + title: i18n.translate('xpack.ml.deepLink.changePointDetection', { + defaultMessage: 'Change Point Detection', + }), + path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT}`, + }, + ], +}; + const MODEL_MANAGEMENT_DEEP_LINK: AppDeepLink = { id: 'mlModelManagementDeepLink', - title: i18n.translate('xpack.ml.deepLink.trainedModels', { - defaultMessage: 'Trained Models', + title: i18n.translate('xpack.ml.deepLink.modelManagement', { + defaultMessage: 'Model Management', }), path: `/${ML_PAGES.TRAINED_MODELS_MANAGE}`, deepLinks: [ { id: 'mlNodesOverviewDeepLink', - title: i18n.translate('xpack.ml.deepLink.modelManagement', { - defaultMessage: 'Model Management', + title: i18n.translate('xpack.ml.deepLink.trainedModels', { + defaultMessage: 'Trained Models', }), path: `/${ML_PAGES.TRAINED_MODELS_MANAGE}`, }, - { - id: 'mlMemoryUsageDeepLink', - title: i18n.translate('xpack.ml.deepLink.memoryUsage', { - defaultMessage: 'Memory usage', - }), - path: `/${ML_PAGES.MEMORY_USAGE}`, - }, ], }; +const MEMORY_USAGE_DEEP_LINK: AppDeepLink = { + id: 'mlMemoryUsageDeepLink', + title: i18n.translate('xpack.ml.deepLink.memoryUsage', { + defaultMessage: 'Memory Usage', + }), + path: `/${ML_PAGES.MEMORY_USAGE}`, +}; + const DATA_VISUALIZER_DEEP_LINK: AppDeepLink = { id: 'dataVisualizerDeepLink', title: i18n.translate('xpack.ml.deepLink.dataVisualizer', { @@ -107,6 +140,14 @@ const SETTINGS_DEEP_LINK: AppDeepLink = { ], }; +const NOTIFICATIONS_DEEP_LINK: AppDeepLink = { + id: 'mlNotificationsDeepLink', + title: i18n.translate('xpack.ml.deepLink.notifications', { + defaultMessage: 'Notifications', + }), + path: `/${ML_PAGES.NOTIFICATIONS}`, +}; + export function getDeepLinks(isFullLicense: boolean) { const deepLinks: AppDeepLink[] = [ DATA_VISUALIZER_DEEP_LINK, @@ -120,7 +161,10 @@ export function getDeepLinks(isFullLicense: boolean) { ANOMALY_DETECTION_DEEP_LINK, DATA_FRAME_ANALYTICS_DEEP_LINK, MODEL_MANAGEMENT_DEEP_LINK, - SETTINGS_DEEP_LINK + MEMORY_USAGE_DEEP_LINK, + SETTINGS_DEEP_LINK, + AIOPS_DEEP_LINK, + NOTIFICATIONS_DEEP_LINK ); } diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index f247be1004718..1d19fee5c8392 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -15,7 +15,6 @@ export * from '../common/types/modules'; export * from '../common/types/audit_message'; export * from '../common/util/anomaly_utils'; -export * from '../common/util/errors'; export * from '../common/util/validators'; export * from '../common/util/date_utils'; diff --git a/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts b/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts index 402a68c07e2d5..8b992817c843c 100644 --- a/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts +++ b/x-pack/plugins/ml/scripts/apidoc_scripts/content_page/content_page.ts @@ -24,27 +24,26 @@ const getContent = (groups: Group[]) => { .join('\n'); return `--- - id: uiMlKibanaRestApi - slug: /ml-team/docs/ui/rest-api/ml-kibana-rest-api - title: Machine Learning Kibana REST API - image: https://source.unsplash.com/400x175/?Nature - description: This page contains documentation for the ML Kibana REST API. - date: ${moment().format('YYYY-MM-DD')} - tags: ['machine learning','internal docs', 'UI'] - --- +id: uiMlKibanaRestApi +slug: /ml-team/docs/ui/rest-api/ml-kibana-rest-api +title: Machine Learning Kibana REST API +image: https://source.unsplash.com/400x175/?Nature +description: This page contains documentation for the ML Kibana REST API. +date: ${moment().format('YYYY-MM-DD')} +tags: ['machine learning','internal docs', 'UI'] +--- - _Updated for ${kibanaPackageJson.version}_ +_Updated for ${kibanaPackageJson.version}_ - Some of the features of the Machine Learning (ML) Kibana plugin are provided via a REST API, which is ideal for creating an integration with the ML plugin. +Some of the features of the Machine Learning (ML) Kibana plugin are provided via a REST API, which is ideal for creating an integration with the ML plugin. - Each API is experimental and can include breaking changes in any version of the ML plugin, or might have been entirely removed from the plugin. +Each API is experimental and can include breaking changes in any version of the ML plugin, or might have been entirely removed from the plugin. - - +- - The following APIs are available: +The following APIs are available: -${groupsStr} - `; +${groupsStr}`; }; export const generateContentPage = () => { 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 691279977740d..d7904c182d09e 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 @@ -491,7 +491,6 @@ export function getMlClient( return mlClient.startTrainedModelDeployment(...p); }, async updateTrainedModelDeployment(...p: Parameters) { - await modelIdsCheck(p); const { model_id: modelId, number_of_allocations: numberOfAllocations } = p[0]; return client.asInternalUser.transport.request({ method: 'POST', @@ -500,11 +499,9 @@ export function getMlClient( }); }, async stopTrainedModelDeployment(...p: Parameters) { - await modelIdsCheck(p); return mlClient.stopTrainedModelDeployment(...p); }, async inferTrainedModel(...p: Parameters) { - await modelIdsCheck(p); // Temporary workaround for the incorrect inferTrainedModelDeployment function in the esclient if ( // @ts-expect-error TS complains it's always false diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts index 6d4982ecbf464..9d286738b17da 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient } from '@kbn/core/server'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { getAnalysisType } from '../../../common/util/analytics_utils'; import { ANALYSIS_CONFIG_TYPE } from '../../../common/constants/data_frame_analytics'; import { @@ -25,7 +26,6 @@ import { isRegressionAnalysis, isClassificationAnalysis, } from '../../../common/util/analytics_utils'; -import { extractErrorMessage } from '../../../common/util/errors'; import { AnalysisConfig, DataFrameAnalyticsConfig, diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/manifest.json index b3395d82a9c29..d600e4a637acf 100755 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/manifest.json @@ -2,7 +2,7 @@ "id": "security_auth", "title": "Security: Authentication", "description": "Detect anomalous activity in your ECS-compatible authentication logs.", - "type": "auth data", + "type": "Auth data", "logoFile": "logo.json", "defaultIndexPattern": "auditbeat-*,logs-*,filebeat-*,winlogbeat-*", "query": { @@ -14,7 +14,7 @@ } } ], - "must_not": { "terms": { "_tier": [ "data_frozen", "data_cold" ] } } + "must_not": { "terms": { "_tier": ["data_frozen", "data_cold"] } } } }, "jobs": [ diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events.json index 7ca7a5ebd71e4..ac50e2f53535c 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events.json @@ -1,20 +1,16 @@ { "description": "Security: Authentication - Looks for an unusually large spike in successful authentication events. This can be due to password spraying, user enumeration, or brute force activity.", - "groups": [ - "security", - "authentication" - ], + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high count of logon events", + "detector_description": "Detects high count of logon events.", "function": "high_non_zero_count", "detector_index": 0 } ], - "influencers": [], - "model_prune_window": "30d" + "influencers": ["source.ip", "winlog.event_data.LogonType", "user.name", "host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -25,6 +21,8 @@ }, "custom_settings": { "created_by": "ml-module-security-auth", - "security_app_display_name": "Spike in Logon Events" + "security_app_display_name": "Spike in Logon Events", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events_for_a_source_ip.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events_for_a_source_ip.json index 47096f4c6413f..d23f8df88ef6a 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events_for_a_source_ip.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_events_for_a_source_ip.json @@ -1,25 +1,17 @@ { "description": "Security: Authentication - Looks for an unusually large spike in successful authentication events from a particular source IP address. This can be due to password spraying, user enumeration, or brute force activity.", - "groups": [ - "security", - "authentication" - ], + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high count of auth events for a source IP", + "detector_description": "Detects high count of auth events for a source IP.", "function": "high_non_zero_count", "by_field_name": "source.ip", "detector_index": 0 } ], - "influencers": [ - "source.ip", - "winlog.event_data.LogonType", - "user.name" - ], - "model_prune_window": "30d" + "influencers": ["source.ip", "winlog.event_data.LogonType", "user.name", "host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -30,6 +22,8 @@ }, "custom_settings": { "created_by": "ml-module-security-auth", - "security_app_display_name": "Spike in Logon Events from a Source IP" + "security_app_display_name": "Spike in Logon Events from a Source IP", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_fails.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_fails.json index 48586ef642ca6..db2db5ea00832 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_fails.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_high_count_logon_fails.json @@ -1,20 +1,16 @@ { "description": "Security: Authentication - Looks for an unusually large spike in authentication failure events. This can be due to password spraying, user enumeration, or brute force activity and may be a precursor to account takeover or credentialed access.", - "groups": [ - "security", - "authentication" - ], + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high count of logon fails", + "detector_description": "Detects high count of logon fails.", "function": "high_non_zero_count", "detector_index": 0 } ], - "influencers": [], - "model_prune_window": "30d" + "influencers": ["source.ip", "user.name", "host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -25,6 +21,8 @@ }, "custom_settings": { "created_by": "ml-module-security-auth", - "security_app_display_name": "Spike in Failed Logon Events" + "security_app_display_name": "Spike in Failed Logon Events", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_hour_for_a_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_hour_for_a_user.json index 1f421ed298b9f..57477497aeb62 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_hour_for_a_user.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_hour_for_a_user.json @@ -1,23 +1,17 @@ { - "description": "Security: Authentication - looks for a user logging in at a time of day that is unusual for the user. This can be due to credentialed access via a compromised account when the user and the threat actor are in different time zones. In addition, unauthorized user activity often takes place during non-business hours.", - "groups": [ - "security", - "authentication" - ], + "description": "Security: Authentication - Looks for a user logging in at a time of day that is unusual for the user. This can be due to credentialed access via a compromised account when the user and the threat actor are in different time zones. In addition, unauthorized user activity often takes place during non-business hours.", + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare hour for a user", + "detector_description": "Detects rare hour for a user.", "function": "time_of_day", "by_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "source.ip", - "user.name" - ] + "influencers": ["source.ip", "user.name", "host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -28,6 +22,8 @@ }, "custom_settings": { "created_by": "ml-module-security-auth", - "security_app_display_name": "Unusual Hour for a User to Logon" + "security_app_display_name": "Unusual Hour for a User to Logon", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_source_ip_for_a_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_source_ip_for_a_user.json index 98a249074a67a..81185ef5039c7 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_source_ip_for_a_user.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_source_ip_for_a_user.json @@ -1,24 +1,18 @@ { - "description": "Security: Authentication - looks for a user logging in from an IP address that is unusual for the user. This can be due to credentialed access via a compromised account when the user and the threat actor are in different locations. An unusual source IP address for a username could also be due to lateral movement when a compromised account is used to pivot between hosts.", - "groups": [ - "security", - "authentication" - ], + "description": "Security: Authentication - Looks for a user logging in from an IP address that is unusual for the user. This can be due to credentialed access via a compromised account when the user and the threat actor are in different locations. An unusual source IP address for a username could also be due to lateral movement when a compromised account is used to pivot between hosts.", + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare source IP for a user", + "detector_description": "Detects rare source IP for a user.", "function": "rare", "by_field_name": "source.ip", "partition_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "source.ip", - "user.name" - ] + "influencers": ["source.ip", "user.name", "host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -29,6 +23,8 @@ }, "custom_settings": { "created_by": "ml-module-security-auth", - "security_app_display_name": "Unusual Source IP for a User to Logon from" + "security_app_display_name": "Unusual Source IP for a User to Logon from", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_user.json index e2488480e61d1..58530fe085014 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_user.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/auth_rare_user.json @@ -1,23 +1,17 @@ { - "description": "Security: Authentication - looks for an unusual user name in the authentication logs. An unusual user name is one way of detecting credentialed access by means of a new or dormant user account. A user account that is normally inactive, because the user has left the organization, which becomes active, may be due to credentialed access using a compromised account password. Threat actors will sometimes also create new users as a means of persisting in a compromised web application.", - "groups": [ - "security", - "authentication" - ], + "description": "Security: Authentication - Looks for an unusual user name in the authentication logs. An unusual user name is one way of detecting credentialed access by means of a new or dormant user account. A user account that is normally inactive, because the user has left the organization, which becomes active, may be due to credentialed access using a compromised account password. Threat actors will sometimes also create new users as a means of persisting in a compromised web application.", + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare user", + "detector_description": "Detects rare user authentication.", "function": "rare", "by_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "source.ip", - "user.name" - ] + "influencers": ["source.ip", "user.name", "host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -28,6 +22,8 @@ }, "custom_settings": { "created_by": "ml-module-security-auth", - "security_app_display_name": "Rare User Logon" + "security_app_display_name": "Rare User Logon", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/datafeed_suspicious_login_activity.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/datafeed_suspicious_login_activity.json index 386b9fab25667..59a9129e7b7bf 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/datafeed_suspicious_login_activity.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/datafeed_suspicious_login_activity.json @@ -1,15 +1,10 @@ { "job_id": "JOB_ID", - "indices": [ - "INDEX_PATTERN_NAME" - ], + "indices": ["INDEX_PATTERN_NAME"], "max_empty_searches": 10, "query": { "bool": { - "filter": [ - {"term": { "event.category": "authentication" }}, - {"term": { "agent.type": "auditbeat" }} - ] + "filter": [{ "term": { "event.category": "authentication" } }] } } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/suspicious_login_activity.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/suspicious_login_activity.json index 00e810b5348e7..bbe420b3ec0eb 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/suspicious_login_activity.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_auth/ml/suspicious_login_activity.json @@ -1,24 +1,17 @@ { - "description": "Security: Auditbeat - Detect unusually high number of authentication attempts.", - "groups": [ - "security", - "auditbeat", - "authentication" - ], + "description": "Security: Authentication - Detects unusually high number of authentication attempts.", + "groups": ["security", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high number of authentication attempts", + "detector_description": "Detects high number of authentication attempts for a host.", "function": "high_non_zero_count", - "partition_field_name": "host.name" + "partition_field_name": "host.name", + "detector_index": 0 } ], - "influencers": [ - "host.name", - "user.name", - "source.ip" - ], + "influencers": ["host.name", "user.name", "source.ip"], "model_prune_window": "30d" }, "allow_lazy_open": true, @@ -31,11 +24,7 @@ "custom_settings": { "created_by": "ml-module-security-auth", "security_app_display_name": "Unusual Login Activity", - "custom_urls": [ - { - "url_name": "IP Address Details", - "url_value": "security/network/ml-network/ip/$source.ip$?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ] + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/manifest.json index 93797b9e3e758..52b406a0da7cb 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/manifest.json @@ -1,16 +1,14 @@ { "id": "security_cloudtrail", "title": "Security: Cloudtrail", - "description": "Detect suspicious activity recorded in your cloudtrail logs.", - "type": "Filebeat data", + "description": "Detect suspicious activity recorded in Cloudtrail logs.", + "type": "Cloudtrail data", "logoFile": "logo.json", - "defaultIndexPattern": "filebeat-*", + "defaultIndexPattern": "logs-*,filebeat-*", "query": { "bool": { - "filter": [ - {"term": {"event.dataset": "aws.cloudtrail"}} - ], - "must_not": { "terms": { "_tier": [ "data_frozen", "data_cold" ] } } + "filter": [{ "term": { "event.dataset": "aws.cloudtrail" } }], + "must_not": { "terms": { "_tier": ["data_frozen", "data_cold"] } } } }, "jobs": [ diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/high_distinct_count_error_message.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/high_distinct_count_error_message.json index 11b5f4625a484..2ba7c4fdf4085 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/high_distinct_count_error_message.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/high_distinct_count_error_message.json @@ -1,24 +1,17 @@ { "description": "Security: Cloudtrail - Looks for a spike in the rate of an error message which may simply indicate an impending service failure but these can also be byproducts of attempted or successful persistence, privilege escalation, defense evasion, discovery, lateral movement, or collection activity by a threat actor.", - "groups": [ - "security", - "cloudtrail" - ], + "groups": ["security", "cloudtrail"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high_distinct_count(\"aws.cloudtrail.error_message\")", + "detector_description": "Detects high distinct count of Cloudtrail error messages.", "function": "high_distinct_count", - "field_name": "aws.cloudtrail.error_message" + "field_name": "aws.cloudtrail.error_message", + "detector_index": 0 } ], - "influencers": [ - "aws.cloudtrail.user_identity.arn", - "source.ip", - "source.geo.city_name" - ], - "model_prune_window": "30d" + "influencers": ["aws.cloudtrail.user_identity.arn", "source.ip", "source.geo.city_name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -29,6 +22,8 @@ }, "custom_settings": { "created_by": "ml-module-security-cloudtrail", - "security_app_display_name": "Spike in AWS Error Messages" + "security_app_display_name": "Spike in AWS Error Messages", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_error_code.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_error_code.json index c54c8e8378f2c..7752430876e3f 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_error_code.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_error_code.json @@ -1,23 +1,17 @@ { "description": "Security: Cloudtrail - Looks for unusual errors. Rare and unusual errors may simply indicate an impending service failure but they can also be byproducts of attempted or successful persistence, privilege escalation, defense evasion, discovery, lateral movement, or collection activity by a threat actor.", - "groups": [ - "security", - "cloudtrail" - ], + "groups": ["security", "cloudtrail"], "analysis_config": { "bucket_span": "60m", "detectors": [ { - "detector_description": "rare by \"aws.cloudtrail.error_code\"", + "detector_description": "Detects rare Cloudtrail error codes.", "function": "rare", - "by_field_name": "aws.cloudtrail.error_code" + "by_field_name": "aws.cloudtrail.error_code", + "detector_index": 0 } ], - "influencers": [ - "aws.cloudtrail.user_identity.arn", - "source.ip", - "source.geo.city_name" - ] + "influencers": ["aws.cloudtrail.user_identity.arn", "source.ip", "source.geo.city_name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -28,6 +22,8 @@ }, "custom_settings": { "created_by": "ml-module-security-cloudtrail", - "security_app_display_name": "Rare AWS Error Code" + "security_app_display_name": "Rare AWS Error Code", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_city.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_city.json index 2ed28884be94f..f7be6fe8cc8d7 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_city.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_city.json @@ -1,24 +1,18 @@ { "description": "Security: Cloudtrail - Looks for AWS API calls that, while not inherently suspicious or abnormal, are sourcing from a geolocation (city) that is unusual. This can be the result of compromised credentials or keys.", - "groups": [ - "security", - "cloudtrail" - ], + "groups": ["security", "cloudtrail"], "analysis_config": { "bucket_span": "60m", "detectors": [ { - "detector_description": "rare by \"event.action\" partition by \"source.geo.city_name\"", + "detector_description": "Detects rare event actions for a city.", "function": "rare", "by_field_name": "event.action", - "partition_field_name": "source.geo.city_name" + "partition_field_name": "source.geo.city_name", + "detector_index": 0 } ], - "influencers": [ - "aws.cloudtrail.user_identity.arn", - "source.ip", - "source.geo.city_name" - ] + "influencers": ["aws.cloudtrail.user_identity.arn", "source.ip", "source.geo.city_name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -29,6 +23,8 @@ }, "custom_settings": { "created_by": "ml-module-security-cloudtrail", - "security_app_display_name": "Unusual City for an AWS Command" + "security_app_display_name": "Unusual City for an AWS Command", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_country.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_country.json index 1f14357e73444..d73f51f34de3a 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_country.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_country.json @@ -1,24 +1,18 @@ { "description": "Security: Cloudtrail - Looks for AWS API calls that, while not inherently suspicious or abnormal, are sourcing from a geolocation (country) that is unusual. This can be the result of compromised credentials or keys.", - "groups": [ - "security", - "cloudtrail" - ], + "groups": ["security", "cloudtrail"], "analysis_config": { "bucket_span": "60m", "detectors": [ { - "detector_description": "rare by \"event.action\" partition by \"source.geo.country_iso_code\"", + "detector_description": "Detects rare event actions for an ISO code.", "function": "rare", "by_field_name": "event.action", - "partition_field_name": "source.geo.country_iso_code" + "partition_field_name": "source.geo.country_iso_code", + "detector_index": 0 } ], - "influencers": [ - "aws.cloudtrail.user_identity.arn", - "source.ip", - "source.geo.country_iso_code" - ] + "influencers": ["aws.cloudtrail.user_identity.arn", "source.ip", "source.geo.country_iso_code"] }, "allow_lazy_open": true, "analysis_limits": { @@ -29,6 +23,8 @@ }, "custom_settings": { "created_by": "ml-module-security-cloudtrail", - "security_app_display_name": "Unusual Country for an AWS Command" + "security_app_display_name": "Unusual Country for an AWS Command", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_username.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_username.json index 76cce7fb829ca..a508028619833 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_username.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_cloudtrail/ml/rare_method_for_a_username.json @@ -1,23 +1,22 @@ { "description": "Security: Cloudtrail - Looks for AWS API calls that, while not inherently suspicious or abnormal, are sourcing from a user context that does not normally call the method. This can be the result of compromised credentials or keys as someone uses a valid account to persist, move laterally, or exfil data.", - "groups": [ - "security", - "cloudtrail" - ], + "groups": ["security", "cloudtrail"], "analysis_config": { "bucket_span": "60m", "detectors": [ { - "detector_description": "rare by \"event.action\" partition by \"user.name\"", + "detector_description": "Detects rare event actions for a user.", "function": "rare", "by_field_name": "event.action", - "partition_field_name": "user.name" + "partition_field_name": "user.name", + "detector_index": 0 } ], "influencers": [ "user.name", "source.ip", - "source.geo.city_name" + "source.geo.city_name", + "aws.cloudtrail.user_identity.arn" ] }, "allow_lazy_open": true, @@ -29,6 +28,8 @@ }, "custom_settings": { "created_by": "ml-module-security-cloudtrail", - "security_app_display_name": "Unusual AWS Command for a User" + "security_app_display_name": "Unusual AWS Command for a User", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/manifest.json index 269f90dea4471..cfff61e304c0e 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/manifest.json @@ -2,7 +2,7 @@ "id": "security_linux_v3", "title": "Security: Linux", "description": "Anomaly detection jobs for Linux host-based threat hunting and detection.", - "type": "linux data", + "type": "Linux data", "logoFile": "logo.json", "defaultIndexPattern": "auditbeat-*,logs-*", "query": { @@ -43,10 +43,7 @@ ], "must_not": { "terms": { - "_tier": [ - "data_frozen", - "data_cold" - ] + "_tier": ["data_frozen", "data_cold"] } } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_activity.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_activity.json index 29f6bf1d98412..b276bcc7856ba 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_activity.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_activity.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for unusual processes using the network which could indicate command-and-control, lateral movement, persistence, or data exfiltration activity.", - "groups": [ - "auditbeat", - "endpoint", - "linux", - "network", - "security" - ], + "groups": ["linux", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.name values.", + "detector_description": "Detects rare processes.", "function": "rare", - "by_field_name": "process.name" + "by_field_name": "process.name", + "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name", - "destination.ip" - ] + "influencers": ["host.name", "process.name", "user.name", "destination.ip"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4004", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux Network Activity" + "security_app_display_name": "Unusual Linux Network Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_port_activity.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_port_activity.json index 34b97358260ac..a551d6c2c204f 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_port_activity.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_network_port_activity.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for unusual destination port activity that could indicate command-and-control, persistence mechanism, or data exfiltration activity.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "network" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare destination.port values.", + "detector_description": "Detects rare destination ports.", "function": "rare", - "by_field_name": "destination.port" + "by_field_name": "destination.port", + "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name", - "destination.ip" - ] + "influencers": ["host.name", "process.name", "user.name", "destination.ip"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4005", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux Network Port Activity" + "security_app_display_name": "Unusual Linux Network Port Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_process_all_hosts.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_process_all_hosts.json index a20a508391fb9..dea5fa3a5db31 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_process_all_hosts.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_process_all_hosts.json @@ -1,65 +1,30 @@ { "description": "Security: Linux - Looks for processes that are unusual to all Linux hosts. Such unusual processes may indicate unauthorized software, malware, or persistence mechanisms.", - "groups": [ - "auditbeat", - "endpoint", - "linux", - "process", - "security" - ], + "groups": ["linux", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.name values.", + "detector_description": "Detects rare processes.", "function": "rare", "by_field_name": "process.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "512mb", - "categorization_examples_limit": 4 - + "model_memory_limit": "512mb" }, "data_description": { "time_field": "@timestamp", "time_format": "epoch_ms" }, "custom_settings": { - "job_tags": { - "euid": "4003", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Anomalous Process for a Linux Population" + "security_app_display_name": "Anomalous Process for a Linux Population", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_user_name.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_user_name.json index 72be89bd79aad..05d46860b145f 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_user_name.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_anomalous_user_name.json @@ -1,64 +1,30 @@ { "description": "Security: Linux - Rare and unusual users that are not normally active may indicate unauthorized changes or activity by an unauthorized user which may be credentialed access or lateral movement.", - "groups": [ - "auditbeat", - "endpoint", - "linux", - "process", - "security" - ], + "groups": ["linux", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", "by_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "32mb", - "categorization_examples_limit": 4 + "model_memory_limit": "32mb" }, "data_description": { "time_field": "@timestamp", "time_format": "epoch_ms" }, "custom_settings": { - "job_tags": { - "euid": "4008", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux Username" + "security_app_display_name": "Unusual Linux Username", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_configuration_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_configuration_discovery.json index 1481b7a03a559..fccfa9493e8c2 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_configuration_discovery.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_configuration_discovery.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for commands related to system network configuration discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used by a threat actor to engage in system network configuration discovery to increase their understanding of connected networks and hosts. This information may be used to shape follow-up behaviors such as lateral movement or additional discovery.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "process.args", - "user.name" - ] + "influencers": ["process.name", "host.name", "process.args", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "40012", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux System Network Configuration Discovery" + "security_app_display_name": "Unusual Linux Network Configuration Discovery", + "managed": true, + "job_revision": 4 } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_connection_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_connection_discovery.json index 2b1cf43ac94d3..32dc04c079db1 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_connection_discovery.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_network_connection_discovery.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for commands related to system network connection discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used by a threat actor to engage in system network connection discovery to increase their understanding of connected services and systems. This information may be used to shape follow-up behaviors such as lateral movement or additional discovery.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "process.args", - "user.name" - ] + "influencers": ["process.name", "host.name", "process.args", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4013", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux Network Connection Discovery" + "security_app_display_name": "Unusual Linux Network Connection Discovery", + "managed": true, + "job_revision": 4 } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_process.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_process.json index fcec32acd69b5..6897876ad6ba3 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_process.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_process.json @@ -1,46 +1,30 @@ { "description": "Security: Linux - Looks for anomalous access to the metadata service by an unusual process. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", - "groups": [ - "auditbeat", - "endpoint", - "linux", - "process", - "security" - ], + "groups": ["linux", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.name values.", + "detector_description": "Detects rare processes.", "function": "rare", "by_field_name": "process.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "user.name", - "process.name" - ] + "influencers": ["host.name", "user.name", "process.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "32mb", - "categorization_examples_limit": 4 + "model_memory_limit": "32mb" }, "data_description": { "time_field": "@timestamp", "time_format": "epoch_ms" }, "custom_settings": { - "job_tags": { - "euid": "4009", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "security_app_display_name": "Unusual Linux Process Calling the Metadata Service" + "security_app_display_name": "Unusual Linux Process Calling the Metadata Service", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_user.json index d8414c8bf22bd..ad81023d69383 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_user.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_metadata_user.json @@ -1,45 +1,30 @@ { "description": "Security: Linux - Looks for anomalous access to the metadata service by an unusual user. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", - "groups": [ - "auditbeat", - "endpoint", - "linux", - "process", - "security" - ], + "groups": ["linux", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", "by_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "user.name" - ] + "influencers": ["host.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "32mb", - "categorization_examples_limit": 4 + "model_memory_limit": "32mb" }, "data_description": { "time_field": "@timestamp", "time_format": "epoch_ms" }, "custom_settings": { - "job_tags": { - "euid": "4010", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "security_app_display_name": "Unusual Linux User Calling the Metadata Service" + "security_app_display_name": "Unusual Linux User Calling the Metadata Service", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_sudo_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_sudo_user.json index a99e5f95572f7..11be6277c4220 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_sudo_user.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_sudo_user.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for sudo activity from an unusual user context. Unusual user context changes can be due to privilege escalation.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "process.args", - "user.name" - ] + "influencers": ["process.name", "host.name", "process.args", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4017", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Sudo Activity" + "security_app_display_name": "Unusual Sudo Activity", + "managed": true, + "job_revision": 4 } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_user_compiler.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_user_compiler.json index 9c8ca5316ace3..08dbbc60d02f7 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_user_compiler.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_rare_user_compiler.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for compiler activity by a user context which does not normally run compilers. This can be ad-hoc software changes or unauthorized software deployment. This can also be due to local privilege elevation via locally run exploits or malware activity.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.title", - "host.name", - "process.working_directory", - "user.name" - ] + "influencers": ["process.title", "host.name", "process.working_directory", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,24 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4018", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Anomalous Linux Compiler Activity" + "security_app_display_name": "Anomalous Linux Compiler Activity", + "managed": true, + "job_revision": 4 } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_information_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_information_discovery.json index 0202854934285..255d0347654b0 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_information_discovery.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_information_discovery.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for commands related to system information discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used to engage in system information discovery to gather detailed information about system configuration and software versions. This may be a precursor to the selection of a persistence mechanism or a method of privilege elevation.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "process.args", - "user.name" - ] + "influencers": ["process.name", "host.name", "process.args", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4014", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux System Information Discovery Activity" + "security_app_display_name": "Unusual Linux System Information Discovery Activity", + "managed": true, + "job_revision": 4 } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_process_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_process_discovery.json index 23e6e607ccf08..03e57ce2237af 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_process_discovery.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_process_discovery.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for commands related to system process discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used to engage in system process discovery to increase their understanding of software applications running on a target host or network. This may be a precursor to the selection of a persistence mechanism or a method of privilege elevation.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "process.args", - "user.name" - ] + "influencers": ["process.name", "host.name", "process.args", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4015", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux Process Discovery Activity" + "security_app_display_name": "Unusual Linux Process Discovery Activity", + "managed": true, + "job_revision": 4 } -} \ No newline at end of file +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_user_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_user_discovery.json index 8659e7a8f1f91..2b1c4dc595777 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_user_discovery.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_linux_system_user_discovery.json @@ -1,27 +1,17 @@ { "description": "Security: Linux - Looks for commands related to system user or owner discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used to engage in system owner or user discovery to identify currently active or primary users of a system. This may be a precursor to additional discovery, credential dumping, or privilege elevation activity.", - "groups": [ - "security", - "auditbeat", - "endpoint", - "linux", - "process" - ], + "groups": ["security", "linux"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", - "by_field_name": "user.name" + "by_field_name": "user.name", + "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "process.args", - "user.name" - ] + "influencers": ["process.name", "host.name", "process.args", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "4016", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Linux System Owner or User Discovery Activity" + "security_app_display_name": "Unusual Linux User Discovery Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_rare_process_by_host_linux.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_rare_process_by_host_linux.json index a072007a0f13c..ce0e7f413f676 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_rare_process_by_host_linux.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_linux/ml/v3_rare_process_by_host_linux.json @@ -1,65 +1,31 @@ { "description": "Security: Linux - Looks for processes that are unusual to a particular Linux host. Such unusual processes may indicate unauthorized software, malware, or persistence mechanisms.", - "groups": [ - "auditbeat", - "endpoint", - "linux", - "process", - "security" - ], + "groups": ["linux", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "For each host.name, detects rare process.name values.", + "detector_description": "Detects rare processes for a host.", "function": "rare", "by_field_name": "process.name", "partition_field_name": "host.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "256mb", - "categorization_examples_limit": 4 + "model_memory_limit": "256mb" }, "data_description": { "time_field": "@timestamp", "time_format": "epoch_ms" }, "custom_settings": { - "job_tags": { - "euid": "4002", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-linux-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Process for a Linux Host" + "security_app_display_name": "Unusual Process for a Linux Host", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/manifest.json index bed522d4e954a..edf6c66a213bd 100755 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/manifest.json @@ -2,7 +2,7 @@ "id": "security_network", "title": "Security: Network", "description": "Detect anomalous network activity in your ECS-compatible network logs.", - "type": "network data", + "type": "Network data", "logoFile": "logo.json", "defaultIndexPattern": "logs-*,filebeat-*,packetbeat-*", "query": { @@ -14,7 +14,7 @@ } } ], - "must_not": { "terms": { "_tier": [ "data_frozen", "data_cold" ] } } + "must_not": { "terms": { "_tier": ["data_frozen", "data_cold"] } } } }, "jobs": [ diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_by_destination_country.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_by_destination_country.json index 4479fe8f8c662..b19a3f0e27812 100755 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_by_destination_country.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_by_destination_country.json @@ -1,14 +1,11 @@ { "description": "Security: Network - Looks for an unusually large spike in network activity to one destination country in the network logs. This could be due to unusually large amounts of reconnaissance or enumeration traffic. Data exfiltration activity may also produce such a surge in traffic to a destination country which does not normally appear in network traffic or business work-flows. Malware instances and persistence mechanisms may communicate with command-and-control (C2) infrastructure in their country of origin, which may be an unusual destination country for the source network.", - "groups": [ - "security", - "network" - ], + "groups": ["security", "network"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high_non_zero_count by \"destination.geo.country_name\"", + "detector_description": "Detects high count by country.", "function": "high_non_zero_count", "by_field_name": "destination.geo.country_name", "detector_index": 0 @@ -19,8 +16,7 @@ "destination.as.organization.name", "source.ip", "destination.ip" - ], - "model_prune_window": "30d" + ] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,6 +27,8 @@ }, "custom_settings": { "created_by": "ml-module-security-network", - "security_app_display_name": "Spike in Network Traffic to a Country" + "security_app_display_name": "Spike in Network Traffic to a Country", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_denies.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_denies.json index 984bfea22fa2d..1477e951d3ce9 100755 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_denies.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_denies.json @@ -1,14 +1,11 @@ { "description": "Security: Network - Looks for an unusually large spike in network traffic that was denied by network ACLs or firewall rules. Such a burst of denied traffic is usually either 1) a misconfigured application or firewall or 2) suspicious or malicious activity. Unsuccessful attempts at network transit, in order to connect to command-and-control (C2), or engage in data exfiltration, may produce a burst of failed connections. This could also be due to unusually large amounts of reconnaissance or enumeration traffic. Denial-of-service attacks or traffic floods may also produce such a surge in traffic.", - "groups": [ - "security", - "network" - ], + "groups": ["security", "network"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high_count", + "detector_description": "Detects high count of network denies.", "function": "high_count", "detector_index": 0 } @@ -18,8 +15,7 @@ "destination.as.organization.name", "source.ip", "destination.port" - ], - "model_prune_window": "30d" + ] }, "allow_lazy_open": true, "analysis_limits": { @@ -30,6 +26,8 @@ }, "custom_settings": { "created_by": "ml-module-security-network", - "security_app_display_name": "Spike in Firewall Denies" + "security_app_display_name": "Spike in Firewall Denies", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_events.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_events.json index ba740d581a27e..81b516204fbc1 100755 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_events.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/high_count_network_events.json @@ -1,14 +1,11 @@ { "description": "Security: Network - Looks for an unusually large spike in network traffic. Such a burst of traffic, if not caused by a surge in business activity, can be due to suspicious or malicious activity. Large-scale data exfiltration may produce a burst of network traffic; this could also be due to unusually large amounts of reconnaissance or enumeration traffic. Denial-of-service attacks or traffic floods may also produce such a surge in traffic.", - "groups": [ - "security", - "network" - ], + "groups": ["security", "network"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high_count", + "detector_description": "Detects high count of network events.", "function": "high_count", "detector_index": 0 } @@ -18,8 +15,7 @@ "destination.as.organization.name", "source.ip", "destination.ip" - ], - "model_prune_window": "30d" + ] }, "allow_lazy_open": true, "analysis_limits": { @@ -30,6 +26,8 @@ }, "custom_settings": { "created_by": "ml-module-security-network", - "security_app_display_name": "Spike in Network Traffic" + "security_app_display_name": "Spike in Network Traffic", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/rare_destination_country.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/rare_destination_country.json index 123b802c475fb..4b8799d65b746 100755 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/rare_destination_country.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_network/ml/rare_destination_country.json @@ -1,14 +1,11 @@ { "description": "Security: Network - looks for an unusual destination country name in the network logs. This can be due to initial access, persistence, command-and-control, or exfiltration activity. For example, when a user clicks on a link in a phishing email or opens a malicious document, a request may be sent to download and run a payload from a server in a country which does not normally appear in network traffic or business work-flows. Malware instances and persistence mechanisms may communicate with command-and-control (C2) infrastructure in their country of origin, which may be an unusual destination country for the source network.", - "groups": [ - "security", - "network" - ], + "groups": ["security", "network"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare by \"destination.geo.country_name\"", + "detector_description": "Detects rare country names.", "function": "rare", "by_field_name": "destination.geo.country_name", "detector_index": 0 @@ -30,6 +27,8 @@ }, "custom_settings": { "created_by": "ml-module-security-network", - "security_app_display_name": "Network Traffic to Rare Destination Country" + "security_app_display_name": "Network Traffic to Rare Destination Country", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/manifest.json index f7a65d0137f26..799363b8fbac1 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/manifest.json @@ -1,16 +1,14 @@ { "id": "security_packetbeat", "title": "Security: Packetbeat", - "description": "Detect suspicious network activity in Packetbeat data.", + "description": "Detect suspicious activity in Packetbeat data.", "type": "Packetbeat data", "logoFile": "logo.json", - "defaultIndexPattern": "packetbeat-*", + "defaultIndexPattern": "packetbeat-*,logs-*", "query": { "bool": { - "filter": [ - {"term": {"agent.type": "packetbeat"}} - ], - "must_not": { "terms": { "_tier": [ "data_frozen", "data_cold" ] } } + "filter": [{ "term": { "agent.type": "packetbeat" } }], + "must_not": { "terms": { "_tier": ["data_frozen", "data_cold"] } } } }, "jobs": [ diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_dns_tunneling.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_dns_tunneling.json index 449c8af238b56..334435732a07e 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_dns_tunneling.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_dns_tunneling.json @@ -1,18 +1,16 @@ { "job_id": "JOB_ID", - "indices": [ - "INDEX_PATTERN_NAME" - ], + "indices": ["INDEX_PATTERN_NAME"], "max_empty_searches": 10, "query": { "bool": { - "filter": [ - {"term": {"event.dataset": "dns"}}, - {"term": {"agent.type": "packetbeat"}} + "filter": [{ "term": { "agent.type": "packetbeat" } }], + "should": [ + { "term": { "event.dataset": "dns" } }, + { "term": { "event.dataset": "network_traffic.dns" } } ], - "must_not": [ - {"bool": {"filter": {"term": {"destination.ip": "169.254.169.254"}}}} - ] + "minimum_should_match": 1, + "must_not": [{ "bool": { "filter": { "term": { "destination.ip": "169.254.169.254" } } } }] } } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_dns_question.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_dns_question.json index 3a4055eb55ba0..fe87d86ee352f 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_dns_question.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_dns_question.json @@ -1,18 +1,16 @@ { "job_id": "JOB_ID", - "indices": [ - "INDEX_PATTERN_NAME" - ], + "indices": ["INDEX_PATTERN_NAME"], "max_empty_searches": 10, "query": { "bool": { - "filter": [ - {"term": {"event.dataset": "dns"}}, - {"term": {"agent.type": "packetbeat"}} + "filter": [{ "term": { "agent.type": "packetbeat" } }], + "should": [ + { "term": { "event.dataset": "dns" } }, + { "term": { "event.dataset": "network_traffic.dns" } } ], - "must_not": [ - {"bool": {"filter": {"term": {"dns.question.type": "PTR"}}}} - ] + "minimum_should_match": 1, + "must_not": [{ "bool": { "filter": { "term": { "dns.question.type": "PTR" } } } }] } } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_user_agent.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_user_agent.json index 5986c326ea80f..79a297595d8d7 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_user_agent.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/datafeed_packetbeat_rare_user_agent.json @@ -1,18 +1,16 @@ { "job_id": "JOB_ID", - "indices": [ - "INDEX_PATTERN_NAME" - ], + "indices": ["INDEX_PATTERN_NAME"], "max_empty_searches": 10, "query": { "bool": { - "filter": [ - {"term": {"event.dataset": "http"}}, - {"term": {"agent.type": "packetbeat"}} + "filter": [{ "term": { "agent.type": "packetbeat" } }], + "should": [ + { "term": { "event.dataset": "http" } }, + { "term": { "event.dataset": "network_traffic.http" } } ], - "must_not": [ - {"wildcard": {"user_agent.original": {"value": "Mozilla*"}}} - ] + "minimum_should_match": 1, + "must_not": [{ "wildcard": { "user_agent.original": { "value": "Mozilla*" } } }] } } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_dns_tunneling.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_dns_tunneling.json index 313bd8e1bea39..54b8ddf2e7a14 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_dns_tunneling.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_dns_tunneling.json @@ -1,23 +1,17 @@ { "description": "Security: Packetbeat - Looks for unusual DNS activity that could indicate command-and-control or data exfiltration activity.", - "groups": [ - "security", - "packetbeat", - "dns" - ], + "groups": ["security", "packetbeat", "dns"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "high_info_content(\"dns.question.name\") over tld", + "detector_description": "Detects high info content of DNS questions over a population of TLDs.", "function": "high_info_content", "field_name": "dns.question.name", "over_field_name": "dns.question.etld_plus_one", "custom_rules": [ { - "actions": [ - "skip_result" - ], + "actions": ["skip_result"], "conditions": [ { "applies_to": "actual", @@ -29,12 +23,7 @@ ] } ], - "influencers": [ - "destination.ip", - "host.name", - "dns.question.etld_plus_one" - ], - "model_prune_window": "30d" + "influencers": ["destination.ip", "host.name", "dns.question.etld_plus_one"] }, "allow_lazy_open": true, "analysis_limits": { @@ -45,12 +34,8 @@ }, "custom_settings": { "created_by": "ml-module-security-packetbeat", - "custom_urls": [ - { - "url_name": "Host Details", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "DNS Tunneling" + "security_app_display_name": "DNS Tunneling", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_dns_question.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_dns_question.json index 36c8b3acd722e..049d4e3babd23 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_dns_question.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_dns_question.json @@ -1,22 +1,16 @@ { "description": "Security: Packetbeat - Looks for unusual DNS activity that could indicate command-and-control activity.", - "groups": [ - "security", - "packetbeat", - "dns" - ], + "groups": ["security", "packetbeat", "dns"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare by \"dns.question.name\"", + "detector_description": "Detects rare DNS question names.", "function": "rare", "by_field_name": "dns.question.name" } ], - "influencers": [ - "host.name" - ] + "influencers": ["host.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -27,12 +21,8 @@ }, "custom_settings": { "created_by": "ml-module-security-packetbeat", - "custom_urls": [ - { - "url_name": "Host Details", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual DNS Activity" + "security_app_display_name": "Unusual DNS Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_server_domain.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_server_domain.json index 3f3c137e8fd34..d8df5c4986b99 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_server_domain.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_server_domain.json @@ -1,24 +1,16 @@ { "description": "Security: Packetbeat - Looks for unusual HTTP or TLS destination domain activity that could indicate execution, persistence, command-and-control or data exfiltration activity.", - "groups": [ - "security", - "packetbeat", - "web" - ], + "groups": ["security", "packetbeat"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare by \"server.domain\"", + "detector_description": "Detects rare server domains.", "function": "rare", "by_field_name": "server.domain" } ], - "influencers": [ - "host.name", - "destination.ip", - "source.ip" - ] + "influencers": ["host.name", "destination.ip", "source.ip"] }, "allow_lazy_open": true, "analysis_limits": { @@ -29,12 +21,8 @@ }, "custom_settings": { "created_by": "ml-module-security-packetbeat", - "custom_urls": [ - { - "url_name": "Host Details", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Network Destination Domain Name" + "security_app_display_name": "Unusual Network Destination Domain Name", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_urls.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_urls.json index afa430bd835f2..055204dd1c376 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_urls.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_urls.json @@ -1,23 +1,16 @@ { "description": "Security: Packetbeat - Looks for unusual web browsing URL activity that could indicate execution, persistence, command-and-control or data exfiltration activity.", - "groups": [ - "security", - "packetbeat", - "web" - ], + "groups": ["security", "packetbeat"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare by \"url.full\"", + "detector_description": "Detects rare URLs.", "function": "rare", "by_field_name": "url.full" } ], - "influencers": [ - "host.name", - "destination.ip" - ] + "influencers": ["host.name", "destination.ip"] }, "allow_lazy_open": true, "analysis_limits": { @@ -28,12 +21,8 @@ }, "custom_settings": { "created_by": "ml-module-security-packetbeat", - "custom_urls": [ - { - "url_name": "Host Details", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Web Request" + "security_app_display_name": "Unusual Web Request", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_user_agent.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_user_agent.json index bb2d524b41c1f..c947e4f1d509b 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_user_agent.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_packetbeat/ml/packetbeat_rare_user_agent.json @@ -1,23 +1,16 @@ { "description": "Security: Packetbeat - Looks for unusual HTTP user agent activity that could indicate execution, persistence, command-and-control or data exfiltration activity.", - "groups": [ - "security", - "packetbeat", - "web" - ], + "groups": ["security", "packetbeat"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "rare by \"user_agent.original\"", + "detector_description": "Detects rare web user agents.", "function": "rare", "by_field_name": "user_agent.original" } ], - "influencers": [ - "host.name", - "destination.ip" - ] + "influencers": ["host.name", "destination.ip"] }, "allow_lazy_open": true, "analysis_limits": { @@ -28,12 +21,8 @@ }, "custom_settings": { "created_by": "ml-module-security-packetbeat", - "custom_urls": [ - { - "url_name": "Host Details", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Web User Agent" + "security_app_display_name": "Unusual Web User Agent", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_rare_process_by_host_windows.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_rare_process_by_host_windows.json index 6b7e5dcf56f1f..38fa9e2e4e904 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_rare_process_by_host_windows.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_rare_process_by_host_windows.json @@ -1,67 +1,30 @@ { "description": "Security: Windows - Looks for processes that are unusual to a particular Windows host. Such unusual processes may indicate unauthorized software, malware, or persistence mechanisms.", - "groups": [ - "endpoint", - "event-log", - "process", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "For each host.name, detects rare process.name values.", + "detector_description": "Detects rare processes per host.", "function": "rare", "by_field_name": "process.name", "partition_field_name": "host.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "256mb", - "categorization_examples_limit": 4 + "model_memory_limit": "256mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8001", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Process for a Windows Host" + "security_app_display_name": "Unusual Process for a Windows Host", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_network_activity.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_network_activity.json index 04ee9912c15e3..2e04fa91be336 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_network_activity.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_network_activity.json @@ -1,66 +1,29 @@ { "description": "Security: Windows - Looks for unusual processes using the network which could indicate command-and-control, lateral movement, persistence, or data exfiltration activity.", - "groups": [ - "endpoint", - "network", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.name values.", + "detector_description": "Detects rare processes.", "function": "rare", "by_field_name": "process.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name", - "destination.ip" - ] + "influencers": ["host.name", "process.name", "user.name", "destination.ip"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "64mb", - "categorization_examples_limit": 4 + "model_memory_limit": "64mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8003", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Windows Network Activity" + "security_app_display_name": "Unusual Windows Network Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_path_activity.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_path_activity.json index d5c931b3c46e8..c9f0579309c6b 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_path_activity.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_path_activity.json @@ -1,65 +1,29 @@ { "description": "Security: Windows - Looks for activity in unusual paths that may indicate execution of malware or persistence mechanisms. Windows payloads often execute from user profile paths.", - "groups": [ - "endpoint", - "network", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.working_directory values.", + "detector_description": "Detects rare working directories.", "function": "rare", "by_field_name": "process.working_directory", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "256mb", - "categorization_examples_limit": 4 + "model_memory_limit": "256mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8004", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Windows Path Activity" + "security_app_display_name": "Unusual Windows Path Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_all_hosts.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_all_hosts.json index 1474763cec7b9..08baa6587f9ff 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_all_hosts.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_all_hosts.json @@ -1,66 +1,29 @@ { "description": "Security: Windows - Looks for processes that are unusual to all Windows hosts. Such unusual processes may indicate execution of unauthorized software, malware, or persistence mechanisms.", - "groups": [ - "endpoint", - "event-log", - "process", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.executable values.", + "detector_description": "Detects rare process executable values.", "function": "rare", - "by_field_name": "process.executable", + "by_field_name": "process.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "256mb", - "categorization_examples_limit": 4 + "model_memory_limit": "256mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8002", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Anomalous Process for a Windows Population" + "security_app_display_name": "Anomalous Process for a Windows Population", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_creation.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_creation.json index 2966630fad878..1bf46c2d416a9 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_creation.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_process_creation.json @@ -1,67 +1,30 @@ { "description": "Security: Windows - Looks for unusual process relationships which may indicate execution of malware or persistence mechanisms.", - "groups": [ - "endpoint", - "event-log", - "process", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "For each process.parent.name, detects rare process.name values.", + "detector_description": "Detects rare processes per parent process.", "function": "rare", "by_field_name": "process.name", "partition_field_name": "process.parent.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "256mb", - "categorization_examples_limit": 4 + "model_memory_limit": "256mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8005", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Anomalous Windows Process Creation" + "security_app_display_name": "Anomalous Windows Process Creation", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json index b01641b2ef3ad..5472ad77e1b70 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json @@ -1,28 +1,17 @@ { "description": "Security: Windows - Looks for unusual powershell scripts that may indicate execution of malware, or persistence mechanisms.", - "groups": [ - "endpoint", - "event-log", - "process", - "windows", - "winlogbeat", - "powershell", - "security" - ], + "groups": ["windows", "powershell", "security"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects high information content in powershell.file.script_block_text values.", + "detector_description": "Detects high information content in powershell scripts.", "function": "high_info_content", - "field_name": "powershell.file.script_block_text" + "field_name": "powershell.file.script_block_text", + "detector_index": 0 } ], - "influencers": [ - "host.name", - "user.name", - "file.path" - ] + "influencers": ["host.name", "user.name", "file.path"] }, "allow_lazy_open": true, "analysis_limits": { @@ -32,24 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8006", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by user name", - "url_value": "siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:'user.name%20:%20%22$user.name$%22',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Suspicious Powershell Script" + "security_app_display_name": "Suspicious Powershell Script", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_service.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_service.json index 9716c8365e317..b2530538a9263 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_service.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_service.json @@ -1,27 +1,17 @@ { - "groups": [ - "endpoint", - "event-log", - "process", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "description": "Security: Windows - Looks for rare and unusual Windows service names which may indicate execution of unauthorized services, malware, or persistence mechanisms.", "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare winlog.event_data.ServiceName values.", + "detector_description": "Detects rare service names.", "function": "rare", - "by_field_name": "winlog.event_data.ServiceName" + "by_field_name": "winlog.event_data.ServiceName", + "detector_index": 0 } ], - "influencers": [ - "host.name", - "winlog.event_data.ServiceName" - ] + "influencers": ["host.name", "winlog.event_data.ServiceName"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,20 +21,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8007", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Windows Service" + "security_app_display_name": "Unusual Windows Service", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_user_name.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_user_name.json index eda4b768b5308..659e58cfdba32 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_user_name.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_user_name.json @@ -1,66 +1,29 @@ { "description": "Security: Windows - Rare and unusual users that are not normally active may indicate unauthorized changes or activity by an unauthorized user which may be credentialed access or lateral movement.", - "groups": [ - "endpoint", - "event-log", - "process", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", "by_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "256mb", - "categorization_examples_limit": 4 + "model_memory_limit": "256mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8008", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Windows Username" + "security_app_display_name": "Unusual Windows Username", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_process.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_process.json index ab4fd311d6646..953a00a8fff52 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_process.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_process.json @@ -1,47 +1,29 @@ { "description": "Security: Windows - Looks for anomalous access to the metadata service by an unusual process. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", - "groups": [ - "security", - "endpoint", - "process", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare process.name values.", + "detector_description": "Detects rare process names.", "function": "rare", "by_field_name": "process.name", "detector_index": 0 } ], - "influencers": [ - "process.name", - "host.name", - "user.name" - ] + "influencers": ["process.name", "host.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "32mb", - "categorization_examples_limit": 4 + "model_memory_limit": "32mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8011", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "security_app_display_name": "Unusual Windows Process Calling the Metadata Service" + "security_app_display_name": "Unusual Windows Process Calling the Metadata Service", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_user.json index fe8a634d49921..df55cb3d67709 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_user.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_metadata_user.json @@ -1,46 +1,29 @@ { "description": "Security: Windows - Looks for anomalous access to the metadata service by an unusual user. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", - "groups": [ - "endpoint", - "process", - "security", - "sysmon", - "windows", - "winlogbeat" - ], + "groups": ["security", "windows"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", "by_field_name": "user.name", "detector_index": 0 } ], - "influencers": [ - "host.name", - "user.name" - ] + "influencers": ["host.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { - "model_memory_limit": "32mb", - "categorization_examples_limit": 4 + "model_memory_limit": "32mb" }, "data_description": { - "time_field": "@timestamp", - "time_format": "epoch_ms" + "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8012", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "security_app_display_name": "Unusual Windows User Calling the Metadata Service" + "security_app_display_name": "Unusual Windows User Calling the Metadata Service", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_runas_event.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_runas_event.json index b95aa1144f440..87d9d4b172f63 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_runas_event.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_runas_event.json @@ -1,27 +1,16 @@ { "description": "Security: Windows - Unusual user context switches can be due to privilege escalation.", - "groups": [ - "endpoint", - "event-log", - "security", - "windows", - "winlogbeat", - "authentication" - ], + "groups": ["security", "windows", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", "by_field_name": "user.name" } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +20,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8009", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Windows User Privilege Elevation Activity" + "security_app_display_name": "Unusual Windows User Privilege Elevation Activity", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_type10_remote_login.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_type10_remote_login.json index a6ec19401190f..e118f761453be 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_type10_remote_login.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_rare_user_type10_remote_login.json @@ -1,27 +1,16 @@ { "description": "Security: Windows - Unusual RDP (remote desktop protocol) user logins can indicate account takeover or credentialed access.", - "groups": [ - "endpoint", - "event-log", - "security", - "windows", - "winlogbeat", - "authentication" - ], + "groups": ["security", "windows", "authentication"], "analysis_config": { "bucket_span": "15m", "detectors": [ { - "detector_description": "Detects rare user.name values.", + "detector_description": "Detects rare usernames.", "function": "rare", "by_field_name": "user.name" } ], - "influencers": [ - "host.name", - "process.name", - "user.name" - ] + "influencers": ["host.name", "process.name", "user.name"] }, "allow_lazy_open": true, "analysis_limits": { @@ -31,32 +20,9 @@ "time_field": "@timestamp" }, "custom_settings": { - "job_tags": { - "euid": "8013", - "maturity": "release", - "author": "@randomuserid/Elastic", - "version": "3", - "updated_date": "5/16/2022" - }, "created_by": "ml-module-security-windows-v3", - "custom_urls": [ - { - "url_name": "Host Details by process name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Host Details by user name", - "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by process name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - }, - { - "url_name": "Hosts Overview by user name", - "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" - } - ], - "security_app_display_name": "Unusual Windows Remote User" + "security_app_display_name": "Unusual Windows Remote User", + "managed": true, + "job_revision": 4 } } diff --git a/x-pack/plugins/ml/server/models/model_management/__mocks__/mock_deployment_response.json b/x-pack/plugins/ml/server/models/model_management/__mocks__/mock_deployment_response.json index cc36165debe5d..cbeda1e304a16 100644 --- a/x-pack/plugins/ml/server/models/model_management/__mocks__/mock_deployment_response.json +++ b/x-pack/plugins/ml/server/models/model_management/__mocks__/mock_deployment_response.json @@ -7,6 +7,7 @@ }, "pipeline_count" : 0, "deployment_stats": { + "deployment_id": "distilbert-base-uncased-finetuned-sst-2-english", "model_id": "distilbert-base-uncased-finetuned-sst-2-english", "inference_threads": 1, "model_threads": 1, @@ -102,6 +103,7 @@ }, "pipeline_count" : 0, "deployment_stats": { + "deployment_id": "elastic__distilbert-base-cased-finetuned-conll03-english", "model_id": "elastic__distilbert-base-cased-finetuned-conll03-english", "inference_threads": 1, "model_threads": 1, @@ -197,6 +199,7 @@ }, "pipeline_count" : 0, "deployment_stats": { + "deployment_id": "sentence-transformers__msmarco-minilm-l-12-v3", "model_id": "sentence-transformers__msmarco-minilm-l-12-v3", "inference_threads": 1, "model_threads": 1, @@ -292,6 +295,7 @@ }, "pipeline_count" : 0, "deployment_stats": { + "deployment_id": "typeform__mobilebert-uncased-mnli", "model_id": "typeform__mobilebert-uncased-mnli", "inference_threads": 1, "model_threads": 1, diff --git a/x-pack/plugins/ml/server/models/model_management/memory_usage.test.ts b/x-pack/plugins/ml/server/models/model_management/memory_usage.test.ts index e08a94e4d951c..3f85487a4cbf3 100644 --- a/x-pack/plugins/ml/server/models/model_management/memory_usage.test.ts +++ b/x-pack/plugins/ml/server/models/model_management/memory_usage.test.ts @@ -150,7 +150,6 @@ describe('Model service', () => { }, nodes: [ { - name: 'node3', allocated_models: [ { allocation_status: { @@ -158,12 +157,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'distilbert-base-uncased-finetuned-sst-2-english', inference_threads: 1, + key: 'distilbert-base-uncased-finetuned-sst-2-english_node3', model_id: 'distilbert-base-uncased-finetuned-sst-2-english', model_size_bytes: 267386880, - required_native_memory_bytes: 534773760, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -171,6 +170,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 534773760, + state: 'started', }, { allocation_status: { @@ -178,12 +179,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'elastic__distilbert-base-cased-finetuned-conll03-english', inference_threads: 1, + key: 'elastic__distilbert-base-cased-finetuned-conll03-english_node3', model_id: 'elastic__distilbert-base-cased-finetuned-conll03-english', model_size_bytes: 260947500, - required_native_memory_bytes: 521895000, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -191,6 +192,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 521895000, + state: 'started', }, { allocation_status: { @@ -198,12 +201,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'sentence-transformers__msmarco-minilm-l-12-v3', inference_threads: 1, + key: 'sentence-transformers__msmarco-minilm-l-12-v3_node3', model_id: 'sentence-transformers__msmarco-minilm-l-12-v3', model_size_bytes: 133378867, - required_native_memory_bytes: 266757734, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -211,6 +214,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 266757734, + state: 'started', }, { allocation_status: { @@ -218,12 +223,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'typeform__mobilebert-uncased-mnli', inference_threads: 1, + key: 'typeform__mobilebert-uncased-mnli_node3', model_id: 'typeform__mobilebert-uncased-mnli', model_size_bytes: 100139008, - required_native_memory_bytes: 200278016, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -231,6 +236,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 200278016, + state: 'started', }, ], attributes: { @@ -239,7 +246,6 @@ describe('Model service', () => { }, id: '3qIoLFnbSi-DwVrYioUCdw', memory_overview: { - ml_max_in_bytes: 1073741824, anomaly_detection: { total: 0, }, @@ -250,6 +256,7 @@ describe('Model service', () => { jvm: 1073741824, total: 15599742976, }, + ml_max_in_bytes: 1073741824, trained_models: { by_model: [ { @@ -272,10 +279,10 @@ describe('Model service', () => { total: 1555161790, }, }, + name: 'node3', roles: ['data', 'ingest', 'master', 'ml', 'transform'], }, { - name: 'node2', allocated_models: [ { allocation_status: { @@ -283,18 +290,20 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'distilbert-base-uncased-finetuned-sst-2-english', inference_threads: 1, + key: 'distilbert-base-uncased-finetuned-sst-2-english_node2', model_id: 'distilbert-base-uncased-finetuned-sst-2-english', model_size_bytes: 267386880, - required_native_memory_bytes: 534773760, model_threads: 1, - state: 'started', node: { routing_state: { reason: 'The object cannot be set twice!', routing_state: 'failed', }, }, + required_native_memory_bytes: 534773760, + state: 'started', }, { allocation_status: { @@ -302,18 +311,20 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'elastic__distilbert-base-cased-finetuned-conll03-english', inference_threads: 1, + key: 'elastic__distilbert-base-cased-finetuned-conll03-english_node2', model_id: 'elastic__distilbert-base-cased-finetuned-conll03-english', model_size_bytes: 260947500, - required_native_memory_bytes: 521895000, model_threads: 1, - state: 'started', node: { routing_state: { reason: 'The object cannot be set twice!', routing_state: 'failed', }, }, + required_native_memory_bytes: 521895000, + state: 'started', }, { allocation_status: { @@ -321,18 +332,20 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'sentence-transformers__msmarco-minilm-l-12-v3', inference_threads: 1, + key: 'sentence-transformers__msmarco-minilm-l-12-v3_node2', model_id: 'sentence-transformers__msmarco-minilm-l-12-v3', model_size_bytes: 133378867, - required_native_memory_bytes: 266757734, model_threads: 1, - state: 'started', node: { routing_state: { reason: 'The object cannot be set twice!', routing_state: 'failed', }, }, + required_native_memory_bytes: 266757734, + state: 'started', }, { allocation_status: { @@ -340,18 +353,20 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'typeform__mobilebert-uncased-mnli', inference_threads: 1, + key: 'typeform__mobilebert-uncased-mnli_node2', model_id: 'typeform__mobilebert-uncased-mnli', model_size_bytes: 100139008, - required_native_memory_bytes: 200278016, model_threads: 1, - state: 'started', node: { routing_state: { reason: 'The object cannot be set twice!', routing_state: 'failed', }, }, + required_native_memory_bytes: 200278016, + state: 'started', }, ], attributes: { @@ -360,7 +375,6 @@ describe('Model service', () => { }, id: 'DpCy7SOBQla3pu0Dq-tnYw', memory_overview: { - ml_max_in_bytes: 1073741824, anomaly_detection: { total: 0, }, @@ -371,6 +385,7 @@ describe('Model service', () => { jvm: 1073741824, total: 15599742976, }, + ml_max_in_bytes: 1073741824, trained_models: { by_model: [ { @@ -393,6 +408,7 @@ describe('Model service', () => { total: 1555161790, }, }, + name: 'node2', roles: ['data', 'master', 'ml', 'transform'], }, { @@ -403,12 +419,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'distilbert-base-uncased-finetuned-sst-2-english', inference_threads: 1, + key: 'distilbert-base-uncased-finetuned-sst-2-english_node1', model_id: 'distilbert-base-uncased-finetuned-sst-2-english', model_size_bytes: 267386880, - required_native_memory_bytes: 534773760, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -416,6 +432,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 534773760, + state: 'started', }, { allocation_status: { @@ -423,12 +441,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'elastic__distilbert-base-cased-finetuned-conll03-english', inference_threads: 1, + key: 'elastic__distilbert-base-cased-finetuned-conll03-english_node1', model_id: 'elastic__distilbert-base-cased-finetuned-conll03-english', model_size_bytes: 260947500, - required_native_memory_bytes: 521895000, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -436,6 +454,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 521895000, + state: 'started', }, { allocation_status: { @@ -443,12 +463,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'sentence-transformers__msmarco-minilm-l-12-v3', inference_threads: 1, + key: 'sentence-transformers__msmarco-minilm-l-12-v3_node1', model_id: 'sentence-transformers__msmarco-minilm-l-12-v3', model_size_bytes: 133378867, - required_native_memory_bytes: 266757734, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -456,6 +476,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 266757734, + state: 'started', }, { allocation_status: { @@ -463,12 +485,12 @@ describe('Model service', () => { state: 'started', target_allocation_count: 3, }, + deployment_id: 'typeform__mobilebert-uncased-mnli', inference_threads: 1, + key: 'typeform__mobilebert-uncased-mnli_node1', model_id: 'typeform__mobilebert-uncased-mnli', model_size_bytes: 100139008, - required_native_memory_bytes: 200278016, model_threads: 1, - state: 'started', node: { average_inference_time_ms: 0, inference_count: 0, @@ -476,6 +498,8 @@ describe('Model service', () => { routing_state: 'started', }, }, + required_native_memory_bytes: 200278016, + state: 'started', }, ], attributes: { @@ -484,7 +508,6 @@ describe('Model service', () => { }, id: 'pt7s6lKHQJaP4QHKtU-Q0Q', memory_overview: { - ml_max_in_bytes: 1073741824, anomaly_detection: { total: 0, }, @@ -495,6 +518,7 @@ describe('Model service', () => { jvm: 1073741824, total: 15599742976, }, + ml_max_in_bytes: 1073741824, trained_models: { by_model: [ { diff --git a/x-pack/plugins/ml/server/models/model_management/memory_usage.ts b/x-pack/plugins/ml/server/models/model_management/memory_usage.ts index 29c51055efe5b..541b396b0a6e5 100644 --- a/x-pack/plugins/ml/server/models/model_management/memory_usage.ts +++ b/x-pack/plugins/ml/server/models/model_management/memory_usage.ts @@ -199,6 +199,7 @@ export class MemoryUsageService { ...rest, ...modelSizeState, node: nodeRest, + key: `${rest.deployment_id}_${node.name}`, }; }); diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 81e8316f1bda6..db150f7c42f32 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -252,7 +252,11 @@ export class MlServerPlugin } if (plugins.usageCollection) { - registerCollector(plugins.usageCollection, coreSetup.savedObjects.getKibanaIndex()); + const getIndexForType = (type: string) => + coreSetup + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); + registerCollector(plugins.usageCollection, getIndexForType); } if (plugins.cases) { 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 dd0a15fa20f83..034ad761f2863 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -19,6 +19,7 @@ export const threadingParamsSchema = schema.maybe( number_of_allocations: schema.number(), threads_per_allocation: schema.number(), priority: schema.oneOf([schema.literal('low'), schema.literal('normal')]), + deployment_id: schema.maybe(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 c09b46f7c23cb..06a0849488a9c 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -10,13 +10,13 @@ import { RouteInitialization } from '../types'; import { wrapError } from '../client/error_wrapper'; import { getInferenceQuerySchema, + inferTrainedModelBody, + inferTrainedModelQuery, modelIdSchema, optionalModelIdSchema, + pipelineSimulateBody, putTrainedModelQuerySchema, - inferTrainedModelQuery, - inferTrainedModelBody, threadingParamsSchema, - pipelineSimulateBody, updateDeploymentParamsSchema, } from './schemas/inference_schema'; @@ -59,14 +59,33 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) const result = body.trained_model_configs as TrainedModelConfigResponse[]; try { if (withPipelines) { + // Also need to retrieve the list of deployment IDs from stats + const stats = await mlClient.getTrainedModelsStats({ + ...(modelId ? { model_id: modelId } : {}), + size: 10000, + }); + + const modelDeploymentsMap = stats.trained_model_stats.reduce((acc, curr) => { + if (!curr.deployment_stats) return acc; + // @ts-ignore elasticsearch-js client is missing deployment_id + const deploymentId = curr.deployment_stats.deployment_id; + if (acc[curr.model_id]) { + acc[curr.model_id].push(deploymentId); + } else { + acc[curr.model_id] = [deploymentId]; + } + return acc; + }, {} as Record); + const modelIdsAndAliases: string[] = Array.from( - new Set( - result + new Set([ + ...result .map(({ model_id: id, metadata }) => { return [id, ...(metadata?.model_aliases ?? [])]; }) - .flat() - ) + .flat(), + ...Object.values(modelDeploymentsMap).flat(), + ]) ); const pipelinesResponse = await modelsProvider(client).getModelsPipelines( @@ -81,6 +100,12 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) ...(pipelinesResponse.get(alias) ?? {}), }; }, {}), + ...(modelDeploymentsMap[model.model_id] ?? []).reduce((acc, deploymentId) => { + return { + ...acc, + ...(pipelinesResponse.get(deploymentId) ?? {}), + }; + }, {}), }; } } diff --git a/x-pack/plugins/ml/server/usage/collector.ts b/x-pack/plugins/ml/server/usage/collector.ts index be09ccae6367a..876e604fcf6f2 100644 --- a/x-pack/plugins/ml/server/usage/collector.ts +++ b/x-pack/plugins/ml/server/usage/collector.ts @@ -31,7 +31,10 @@ export interface MlUsageData { }; } -export function registerCollector(usageCollection: UsageCollectionSetup, kibanaIndex: string) { +export function registerCollector( + usageCollection: UsageCollectionSetup, + getIndexForType: (type: string) => Promise +) { const collector = usageCollection.makeUsageCollector({ type: 'ml', schema: { @@ -86,11 +89,12 @@ export function registerCollector(usageCollection: UsageCollectionSetup, kibanaI }, }, }, - isReady: () => !!kibanaIndex, + isReady: () => true, fetch: async ({ esClient }) => { + const alertIndex = await getIndexForType('alert'); const result = await esClient.search( { - index: kibanaIndex, + index: alertIndex, size: 0, body: { query: { @@ -137,7 +141,7 @@ export function registerCollector(usageCollection: UsageCollectionSetup, kibanaI }; }>( { - index: kibanaIndex, + index: alertIndex, size: 10000, body: { query: { diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 6179571a28515..8288c9998fc7d 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -30,7 +30,6 @@ "@kbn/charts-plugin", "@kbn/cloud-plugin", "@kbn/config-schema", - "@kbn/core-http-browser", "@kbn/dashboard-plugin", "@kbn/data-plugin", "@kbn/data-views-plugin", @@ -90,5 +89,6 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/ml-error-utils", ], } diff --git a/x-pack/plugins/monitoring/server/alerts/base_rule.ts b/x-pack/plugins/monitoring/server/alerts/base_rule.ts index 3e08d370af56b..17491ffc760cb 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_rule.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_rule.ts @@ -103,6 +103,13 @@ export class BaseRule { actionVariables: { context: actionVariables, }, + // As there is "[key: string]: unknown;" in CommonAlertParams, + // we couldn't figure out a schema for validation and created a follow on issue: + // https://github.com/elastic/kibana/issues/153754 + // Below validate function should be overwritten in each monitoring rule type + validate: { + params: { validate: (params) => params }, + }, }; } diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index c81fb90e614cb..5996eb125b6dc 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -104,7 +104,7 @@ export class MonitoringPlugin kibanaStats: { uuid: this.initializerContext.env.instanceUuid, name: serverInfo.name, - index: coreSetup.savedObjects.getKibanaIndex(), + index: coreSetup.savedObjects.getDefaultIndex(), host: serverInfo.hostname, locale: i18n.getLocale(), port: serverInfo.port.toString(), diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index 1c30a8439cf3c..fdfe532ced5db 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -72,7 +72,7 @@ export class MonitoringCollectionPlugin implements Plugin { global.console = originalConsole; }); - it('renders', async () => { - const plugins = { - usageCollection: { reportUiCounter: noop }, - data: { - query: { + const mockSearchSessionClear = jest.fn(); + + const plugins = { + usageCollection: { reportUiCounter: noop }, + data: { + query: { + timefilter: { timefilter: { - timefilter: { - setTime: jest.fn(), - getTime: jest.fn().mockReturnValue({}), - getTimeDefaults: jest.fn().mockReturnValue({}), - getRefreshInterval: jest.fn().mockReturnValue({}), - getRefreshIntervalDefaults: jest.fn().mockReturnValue({}), - }, + setTime: jest.fn(), + getTime: jest.fn().mockReturnValue({}), + getTimeDefaults: jest.fn().mockReturnValue({}), + getRefreshInterval: jest.fn().mockReturnValue({}), + getRefreshIntervalDefaults: jest.fn().mockReturnValue({}), }, }, }, - } as unknown as ObservabilityPublicPluginsStart; - - const core = { - application: { currentAppId$: new Observable(), navigateToUrl: noop }, - chrome: { - docTitle: { change: noop }, - setBreadcrumbs: noop, - setHelpExtension: noop, + search: { + session: { + clear: mockSearchSessionClear, + }, }, - i18n: { Context: ({ children }: { children: React.ReactNode }) => children }, - uiSettings: { get: () => false }, - http: { basePath: { prepend: (path: string) => path } }, - theme: themeServiceMock.createStartContract(), - } as unknown as CoreStart; + }, + } as unknown as ObservabilityPublicPluginsStart; - const params = { - element: window.document.createElement('div'), - history: createMemoryHistory(), - setHeaderActionMenu: noop, - theme$: themeServiceMock.createTheme$(), - } as unknown as AppMountParameters; + const core = { + application: { currentAppId$: new Observable(), navigateToUrl: noop }, + chrome: { + docTitle: { change: noop }, + setBreadcrumbs: noop, + setHelpExtension: noop, + }, + i18n: { Context: ({ children }: { children: React.ReactNode }) => children }, + uiSettings: { get: () => false }, + http: { basePath: { prepend: (path: string) => path } }, + theme: themeServiceMock.createStartContract(), + } as unknown as CoreStart; - const config = { - unsafe: { - alertDetails: { - logs: { enabled: false }, - metrics: { enabled: false }, - uptime: { enabled: false }, - }, + const params = { + element: window.document.createElement('div'), + history: createMemoryHistory(), + setHeaderActionMenu: noop, + theme$: themeServiceMock.createTheme$(), + } as unknown as AppMountParameters; + + const config = { + unsafe: { + alertDetails: { + logs: { enabled: false }, + metrics: { enabled: false }, + uptime: { enabled: false }, }, - } as ConfigSchema; + }, + } as ConfigSchema; + it('renders', async () => { expect(() => { const unmount = renderApp({ core, @@ -90,9 +97,30 @@ describe('renderApp', () => { }, reportUiCounter: jest.fn(), }, - kibanaVersion: '8.7.0', + kibanaVersion: '8.8.0', }); unmount(); }).not.toThrowError(); }); + + it('should clear search sessions when unmounting', () => { + const unmount = renderApp({ + core, + config, + plugins, + appMountParameters: params, + observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), + ObservabilityPageTemplate: KibanaPageTemplate, + usageCollection: { + components: { + ApplicationUsageTrackingProvider: (props) => null, + }, + reportUiCounter: jest.fn(), + }, + kibanaVersion: '8.8.0', + }); + unmount(); + + expect(mockSearchSessionClear).toBeCalled(); + }); }); diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index a82a5a1dce2dc..d11173e60aae7 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -128,6 +128,11 @@ export const renderApp = ({ element ); return () => { + // This needs to be present to fix https://github.com/elastic/kibana/issues/155704 + // as the Overview page renders the UX Section component. That component renders a Lens embeddable + // via the ExploratoryView app, which uses search sessions. Therefore on unmounting we need to clear + // these sessions. + plugins.data.search.session.clear(); ReactDOM.unmountComponentAtNode(element); }; }; diff --git a/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx b/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx new file mode 100644 index 0000000000000..f72e4b008f390 --- /dev/null +++ b/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +const SLO_FEEDBACK_LINK = 'https://ela.st/slo-feedback'; + +export function FeedbackButton() { + return ( + + {i18n.translate('xpack.observability.slo.feedbackButtonLabel', { + defaultMessage: 'Tell us what you think!', + })} + + ); +} diff --git a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx index 218f1c8bd84c3..4d5e826ab4129 100644 --- a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx +++ b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx @@ -8,17 +8,18 @@ import { EuiBadge, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; + import { paths } from '../../../config/paths'; import { useKibana } from '../../../utils/kibana_react'; - import { ActiveAlerts } from '../../../hooks/slo/use_fetch_active_alerts'; -import { toAlertsPageQueryFilter } from '../../../pages/slos/helpers/alerts_page_query_filter'; export interface Props { activeAlerts?: ActiveAlerts; + slo: SLOWithSummaryResponse; } -export function SloActiveAlertsBadge({ activeAlerts }: Props) { +export function SloActiveAlertsBadge({ slo, activeAlerts }: Props) { const { application: { navigateToUrl }, http: { basePath }, @@ -27,9 +28,9 @@ export function SloActiveAlertsBadge({ activeAlerts }: Props) { const handleActiveAlertsClick = () => { if (activeAlerts) { navigateToUrl( - `${basePath.prepend(paths.observability.alerts)}?_a=${toAlertsPageQueryFilter( - activeAlerts - )}` + `${basePath.prepend(paths.observability.alerts)}?_a=(kuery:'slo.id:"${ + slo.id + }"',rangeFrom:now-15m,rangeTo:now,status:active)` ); } }; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts index 4e90e51060df0..b8dc07a00ab32 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_active_alerts.ts @@ -58,6 +58,13 @@ export function useFetchActiveAlerts({ sloIds = [] }: Params): UseFetchActiveAle query: { bool: { filter: [ + { + range: { + '@timestamp': { + gte: 'now-15m/m', + }, + }, + }, { term: { 'kibana.alert.rule.rule_type_id': 'slo.rules.burnRate', diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts index 0c0b5a7108952..153e2fb95c966 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_rules_for_slo.ts @@ -11,13 +11,13 @@ import { RefetchQueryFilters, useQuery, } from '@tanstack/react-query'; -import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; +import type { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import { useKibana } from '../../utils/kibana_react'; type SloId = string; interface Params { - sloIds: SloId[]; + sloIds?: SloId[]; } // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -42,7 +42,7 @@ export interface UseFetchRulesForSloResponse { ) => Promise>> | undefined, unknown>>; } -export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSloResponse { +export function useFetchRulesForSlo({ sloIds }: Params): UseFetchRulesForSloResponse { const { http } = useKibana().services; const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery( @@ -51,7 +51,11 @@ export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSl queryFn: async () => { try { const body = JSON.stringify({ - filter: `alert.attributes.params.sloId:(${sloIds.join(' or ')})`, + filter: `${sloIds?.reduce((acc, sloId, index, array) => { + return `${acc}alert.attributes.params.sloId:${sloId}${ + index < array.length - 1 ? ' or ' : '' + }`; + }, '')}`, fields: ['params.sloId', 'name'], per_page: 1000, }); @@ -60,7 +64,7 @@ export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSl body, }); - const init = sloIds.reduce((acc, sloId) => ({ ...acc, [sloId]: [] }), {}); + const init = sloIds?.reduce((acc, sloId) => ({ ...acc, [sloId]: [] }), {}); return response.data.reduce( (acc, rule) => ({ diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx index dce255a6d244b..f8d1b4b43af74 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx @@ -6,11 +6,12 @@ */ import React, { Fragment } from 'react'; -import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting'; import { useParams } from 'react-router-dom'; import { Chance } from 'chance'; import { waitFor } from '@testing-library/react'; import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting'; import { Subset } from '../../typings'; import { render } from '../../utils/test_helper'; @@ -120,10 +121,18 @@ describe('Alert details', () => { mockKibana(); }); + const renderComponent = () => + render( + + + , + config + ); + it('should show the alert detail page with all necessary components', async () => { useFetchAlertDetailMock.mockReturnValue([false, alert]); - const alertDetails = render(, config); + const alertDetails = renderComponent(); await waitFor(() => expect(alertDetails.queryByTestId('centerJustifiedSpinner')).toBeFalsy()); @@ -136,7 +145,7 @@ describe('Alert details', () => { it('should show error loading the alert details', async () => { useFetchAlertDetailMock.mockReturnValue([false, alertWithNoData]); - const alertDetails = render(, config); + const alertDetails = renderComponent(); expect(alertDetails.queryByTestId('alertDetailsError')).toBeTruthy(); expect(alertDetails.queryByTestId('centerJustifiedSpinner')).toBeFalsy(); @@ -146,7 +155,7 @@ describe('Alert details', () => { it('should show loading spinner', async () => { useFetchAlertDetailMock.mockReturnValue([true, alertWithNoData]); - const alertDetails = render(, config); + const alertDetails = renderComponent(); expect(alertDetails.queryByTestId('centerJustifiedSpinner')).toBeTruthy(); expect(alertDetails.queryByTestId('alertDetailsError')).toBeFalsy(); diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx index d994669fbc6b0..7bdb4d1054640 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx @@ -9,7 +9,7 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { useParams } from 'react-router-dom'; import { EuiEmptyPrompt, EuiPanel, EuiSpacer } from '@elastic/eui'; -import { ALERT_RULE_TYPE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; +import { ALERT_RULE_CATEGORY, ALERT_RULE_TYPE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; import { useKibana } from '../../utils/kibana_react'; @@ -17,7 +17,7 @@ import { useFetchRule } from '../../hooks/use_fetch_rule'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useFetchAlertDetail } from '../../hooks/use_fetch_alert_detail'; -import { PageTitle } from './components/page_title'; +import { PageTitle, pageTitleContent } from './components/page_title'; import { HeaderActions } from './components/header_actions'; import { AlertSummary, AlertSummaryField } from './components/alert_summary'; import { CenterJustifiedSpinner } from '../../components/center_justified_spinner'; @@ -33,6 +33,9 @@ interface AlertDetailsPathParams { } export const ALERT_DETAILS_PAGE_ID = 'alert-details-o11y'; +const defaultBreadcrumb = i18n.translate('xpack.observability.breadcrumbs.alertDetails', { + defaultMessage: 'Alert details', +}); export function AlertDetails() { const { @@ -69,6 +72,9 @@ export function AlertDetails() { defaultMessage: 'Alerts', }), }, + { + text: alert ? pageTitleContent(alert.fields[ALERT_RULE_CATEGORY]) : defaultBreadcrumb, + }, ]); if (isLoading) { @@ -130,6 +136,7 @@ export function AlertDetails() { rule={rule} timeZone={timeZone} setAlertSummaryFields={setSummaryFields} + ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))} /> )} diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx index 41172644b9cf7..201e19a5c94a6 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx @@ -34,6 +34,18 @@ export interface PageTitleProps { alert: TopAlert | null; } +export function pageTitleContent(ruleCategory: string) { + return ( + + ); +} + export function PageTitle({ alert }: PageTitleProps) { const { euiTheme } = useEuiTheme(); @@ -41,13 +53,7 @@ export function PageTitle({ alert }: PageTitleProps) { return (
    - + {pageTitleContent(alert.fields[ALERT_RULE_CATEGORY])} diff --git a/x-pack/plugins/observability/public/pages/rules/rules.tsx b/x-pack/plugins/observability/public/pages/rules/rules.tsx index e7a63cfbdb81d..40e4c2192a0c0 100644 --- a/x-pack/plugins/observability/public/pages/rules/rules.tsx +++ b/x-pack/plugins/observability/public/pages/rules/rules.tsx @@ -62,14 +62,16 @@ export function RulesPage() { useHashQuery: false, }); - const { lastResponse, search, status, type } = urlStateStorage.get<{ + const { lastResponse, params, search, status, type } = urlStateStorage.get<{ lastResponse: string[]; + params: Record; search: string; status: RuleStatus[]; type: string[]; - }>('_a') || { lastResponse: [], search: '', status: [], type: [] }; + }>('_a') || { lastResponse: [], params: {}, search: '', status: [], type: [] }; const [stateLastResponse, setLastResponse] = useState(lastResponse); + const [stateParams, setParams] = useState>(params); const [stateSearch, setSearch] = useState(search); const [stateStatus, setStatus] = useState(status); const [stateType, setType] = useState(type); @@ -80,23 +82,28 @@ export function RulesPage() { const handleStatusFilterChange = (newStatus: RuleStatus[]) => { setStatus(newStatus); - urlStateStorage.set('_a', { lastResponse, search, status: newStatus, type }); + urlStateStorage.set('_a', { lastResponse, params, search, status: newStatus, type }); }; const handleLastRunOutcomeFilterChange = (newLastResponse: string[]) => { setRefresh(new Date()); setLastResponse(newLastResponse); - urlStateStorage.set('_a', { lastResponse: newLastResponse, search, status, type }); + urlStateStorage.set('_a', { lastResponse: newLastResponse, params, search, status, type }); }; const handleTypeFilterChange = (newType: string[]) => { setType(newType); - urlStateStorage.set('_a', { lastResponse, search, status, type: newType }); + urlStateStorage.set('_a', { lastResponse, params, search, status, type: newType }); }; const handleSearchFilterChange = (newSearch: string) => { setSearch(newSearch); - urlStateStorage.set('_a', { lastResponse, search: newSearch, status, type }); + urlStateStorage.set('_a', { lastResponse, params, search: newSearch, status, type }); + }; + + const handleRuleParamFilterChange = (newParams: Record) => { + setParams(newParams); + urlStateStorage.set('_a', { lastResponse, params: newParams, search, status, type }); }; return ( @@ -143,6 +150,7 @@ export function RulesPage() { refresh={stateRefresh} ruleDetailsRoute="alerts/rules/:ruleId" rulesListKey="observability_rulesListColumns" + ruleParamFilter={stateParams} showActionFilter={false} statusFilter={stateStatus} searchFilter={stateSearch} @@ -155,6 +163,7 @@ export function RulesPage() { 'ruleExecutionState', ]} onLastRunOutcomeFilterChange={handleLastRunOutcomeFilterChange} + onRuleParamFilterChange={handleRuleParamFilterChange} onSearchFilterChange={handleSearchFilterChange} onStatusFilterChange={handleStatusFilterChange} onTypeFilterChange={handleTypeFilterChange} diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx index 79fe2d7d862ea..99ce1ba03f9b6 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx @@ -52,6 +52,14 @@ export function HeaderControl({ isLoading, slo }: Props) { setRuleFlyoutVisibility(true); }; + const handleNavigateToRules = () => { + navigateToUrl( + basePath.prepend( + `${paths.observability.rules}?_a=(lastResponse:!(),search:%27%27,params:(sloId:%27${slo?.id}%27),status:!(),type:!())` + ) + ); + }; + const handleNavigateToApm = () => { if ( slo?.indicator.type === 'sli.apm.transactionDuration' || @@ -121,10 +129,20 @@ export function HeaderControl({ isLoading, slo }: Props) { {i18n.translate( 'xpack.observability.slo.sloDetails.headerControl.createBurnRateRule', { - defaultMessage: 'Create alert rule', + defaultMessage: 'Create new alert rule', } )} , + + {i18n.translate('xpack.observability.slo.sloDetails.headerControl.manageRules', { + defaultMessage: 'Manage rules', + })} + , ].concat( !!slo && isApmIndicatorType(slo.indicator.type) ? [ @@ -145,6 +163,7 @@ export function HeaderControl({ isLoading, slo }: Props) { )} /> + {!!slo && isRuleFlyoutVisible ? ( ; } @@ -38,7 +32,6 @@ export function HeaderTitle(props: Props) { {slo.name} - ); diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/slo_details.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/slo_details.tsx index cb0e9d37c9d5c..97a7a2915b11a 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/slo_details.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/slo_details.tsx @@ -5,10 +5,21 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiNotificationBadge, + EuiSpacer, + EuiTabbedContent, + EuiTabbedContentTab, +} from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; -import React from 'react'; +import React, { Fragment } from 'react'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { i18n } from '@kbn/i18n'; +import { useFetchActiveAlerts } from '../../../hooks/slo/use_fetch_active_alerts'; +import { useKibana } from '../../../utils/kibana_react'; import { formatHistoricalData } from '../../../utils/slo/chart_data_formatter'; import { useFetchHistoricalSummary } from '../../../hooks/slo/use_fetch_historical_summary'; import { ErrorBudgetChartPanel } from './error_budget_chart_panel'; @@ -19,8 +30,18 @@ export interface Props { slo: SLOWithSummaryResponse; isAutoRefreshing: boolean; } +const ALERTS_TABLE_ID = 'xpack.observability.slo.sloDetails.alertTable'; +const OVERVIEW_TAB = 'overview'; +const ALERTS_TAB = 'alerts'; export function SloDetails({ slo, isAutoRefreshing }: Props) { + const { + triggersActionsUi: { alertsTableConfigurationRegistry, getAlertsStateTable: AlertsStateTable }, + } = useKibana().services; + + const { data: activeAlerts } = useFetchActiveAlerts({ + sloIds: [slo.id], + }); const { isLoading: historicalSummaryLoading, sloHistoricalSummaryResponse = {} } = useFetchHistoricalSummary({ sloIds: [slo.id], shouldRefetch: isAutoRefreshing }); @@ -30,23 +51,80 @@ export function SloDetails({ slo, isAutoRefreshing }: Props) { ); const historicalSliData = formatHistoricalData(sloHistoricalSummaryResponse[slo.id], 'sli_value'); + const tabs: EuiTabbedContentTab[] = [ + { + id: OVERVIEW_TAB, + name: i18n.translate('xpack.observability.slo.sloDetails.tab.overviewLabel', { + defaultMessage: 'Overview', + }), + 'data-test-subj': 'overviewTab', + content: ( + + + + + + + + + + + + + + + + + ), + }, + { + id: ALERTS_TAB, + name: i18n.translate('xpack.observability.slo.sloDetails.tab.alertsLabel', { + defaultMessage: 'Alerts', + }), + 'data-test-subj': 'alertsTab', + append: ( + + {(activeAlerts && activeAlerts[slo.id]?.count) ?? 0} + + ), + content: ( + + + + + + + + + ), + }, + ]; + return ( - - - - - - - - - - - - - + ); } diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx index 7fc212dbd4275..dbbacc6b1be91 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx @@ -15,10 +15,11 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import React from 'react'; +import React, { useRef } from 'react'; import { EuiIcon, EuiLoadingChart, useEuiTheme } from '@elastic/eui'; import numeral from '@elastic/numeral'; import moment from 'moment'; +import { useActiveCursor } from '@kbn/charts-plugin/public'; import { ChartData } from '../../../typings'; import { useKibana } from '../../../utils/kibana_react'; @@ -45,18 +46,29 @@ export function WideChart({ chart, data, id, isLoading, state }: Props) { const color = state === 'error' ? euiTheme.colors.danger : euiTheme.colors.success; const ChartComponent = chart === 'area' ? AreaSeries : LineSeries; + const chartRef = useRef(null); + const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { + isDateHistogram: true, + }); + if (isLoading) { return ; } return ( - + } + onPointerUpdate={handleCursorUpdate} + externalPointerEvents={{ + tooltip: { visible: true }, + }} + pointerUpdateDebounce={0} + pointerUpdateTrigger={'x'} /> { useKibanaMock.mockReturnValue({ services: { application: { navigateToUrl: mockNavigate }, - charts: chartPluginMock.createSetupContract(), + charts: chartPluginMock.createStartContract(), http: { basePath: { prepend: mockBasePathPrepend, @@ -158,22 +158,19 @@ describe('SLO Details Page', () => { expect(screen.queryAllByTestId('wideChartLoading').length).toBe(0); }); - it('renders the active alerts badge', async () => { + it("renders a 'Edit' button under actions menu", async () => { const slo = buildSlo(); - useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); useParamsMock.mockReturnValue(slo.id); useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); - useFetchActiveAlertsMock.mockReturnValue({ - isLoading: false, - data: { [slo.id]: { count: 2, ruleIds: ['rule-1', 'rule-2'] } }, - }); + useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); render(); - expect(screen.getByTestId('o11ySloActiveAlertsBadge')).toBeTruthy(); + fireEvent.click(screen.getByTestId('o11yHeaderControlActionsButton')); + expect(screen.queryByTestId('sloDetailsHeaderControlPopoverEdit')).toBeTruthy(); }); - it("renders a 'Edit' button under actions menu", async () => { + it("renders a 'Create alert rule' button under actions menu", async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); @@ -182,19 +179,25 @@ describe('SLO Details Page', () => { render(); fireEvent.click(screen.getByTestId('o11yHeaderControlActionsButton')); - expect(screen.queryByTestId('sloDetailsHeaderControlPopoverEdit')).toBeTruthy(); + expect(screen.queryByTestId('sloDetailsHeaderControlPopoverCreateRule')).toBeTruthy(); }); - it("renders a 'Create alert rule' button under actions menu", async () => { + it('renders the Overview tab by default', async () => { const slo = buildSlo(); useParamsMock.mockReturnValue(slo.id); useFetchSloDetailsMock.mockReturnValue({ isLoading: false, slo }); useLicenseMock.mockReturnValue({ hasAtLeast: () => true }); + useFetchActiveAlertsMock.mockReturnValue({ + isLoading: false, + data: { [slo.id]: { count: 2, ruleIds: ['rule-1', 'rule-2'] } }, + }); render(); - fireEvent.click(screen.getByTestId('o11yHeaderControlActionsButton')); - expect(screen.queryByTestId('sloDetailsHeaderControlPopoverCreateRule')).toBeTruthy(); + expect(screen.queryByTestId('overviewTab')).toBeTruthy(); + expect(screen.queryByTestId('overviewTab')?.getAttribute('aria-selected')).toBe('true'); + expect(screen.queryByTestId('alertsTab')).toBeTruthy(); + expect(screen.queryByTestId('alertsTab')?.getAttribute('aria-selected')).toBe('false'); }); describe('when an APM SLO is loaded', () => { diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx index bec6e6608abcc..48401ca29f94e 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx @@ -26,6 +26,7 @@ import { paths } from '../../config/paths'; import type { SloDetailsPathParams } from './types'; import type { ObservabilityAppServices } from '../../application/types'; import { AutoRefreshButton } from '../slos/components/auto_refresh_button'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SloDetailsPage() { const { @@ -69,6 +70,7 @@ export function SloDetailsPage() { isAutoRefreshing={isAutoRefreshing} onClick={handleToggleAutoRefresh} />, + , ], bottomBorder: false, }} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx index f27d6299a548c..195bc7933a74d 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { useLocation, useHistory } from 'react-router-dom'; import { @@ -24,6 +24,7 @@ import { useKibana } from '../../../utils/kibana_react'; import { useCreateSlo } from '../../../hooks/slo/use_create_slo'; import { useUpdateSlo } from '../../../hooks/slo/use_update_slo'; import { useShowSections } from '../hooks/use_show_sections'; +import { useFetchRulesForSlo } from '../../../hooks/slo/use_fetch_rules_for_slo'; import { useSectionFormValidation } from '../helpers/use_section_form_validation'; import { SloEditFormDescriptionSection } from './slo_edit_form_description_section'; import { SloEditFormObjectiveSection } from './slo_edit_form_objective_section'; @@ -57,6 +58,10 @@ export function SloEditForm({ slo }: Props) { const history = useHistory(); const { search } = useLocation(); + const { data: rules, isInitialLoading } = useFetchRulesForSlo({ + sloIds: slo?.id ? [slo.id] : undefined, + }); + const urlStateStorage = createKbnUrlStateStorage({ history, useHash: false, @@ -76,6 +81,12 @@ export function SloEditForm({ slo }: Props) { setIsAddRuleFlyoutOpen(true); } + useEffect(() => { + if (isEditMode && rules && rules[slo.id].length && isCreateRuleCheckboxChecked) { + setIsCreateRuleCheckboxChecked(false); + } + }, [isCreateRuleCheckboxChecked, isEditMode, rules, slo]); + const methods = useForm({ defaultValues: { ...SLO_EDIT_FORM_DEFAULT_VALUES, ...urlParams }, values: transformSloResponseToCreateSloInput(slo), @@ -211,6 +222,7 @@ export function SloEditForm({ slo }: Props) { diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx index 57e27810815d6..ede2d15d3f23d 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx @@ -16,6 +16,7 @@ import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { useLicense } from '../../hooks/use_license'; import { SloEditForm } from './components/slo_edit_form'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SloEditPage() { const { @@ -58,7 +59,7 @@ export function SloEditPage() { : i18n.translate('xpack.observability.sloCreatePageTitle', { defaultMessage: 'Create new SLO', }), - rightSideItems: [], + rightSideItems: [], bottomBorder: false, }} data-test-subj="slosEditPage" diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx index 15d36f3525ed1..89d5eadf4d9ad 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.tsx @@ -31,7 +31,7 @@ export function SloBadges({ activeAlerts, rules, slo, onClickRuleBadge }: Props) - + ); diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx index c60617152cb00..2b80e8852bdae 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx @@ -6,9 +6,10 @@ */ import React from 'react'; -import { EuiBadge, EuiIcon, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; + import { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; export interface Props { @@ -26,16 +27,9 @@ export function SloRulesBadge({ rules, onClick }: Props) { })} display="block" > - - - + + + ); } diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx index ffe07cbb9a902..049481486eb9e 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx @@ -92,6 +92,14 @@ export function SloListItem({ queryClient.invalidateQueries(['fetchRulesForSlo']); }; + const handleNavigateToRules = () => { + navigateToUrl( + basePath.prepend( + `${paths.observability.rules}?_a=(lastResponse:!(),search:%27%27,params:(sloId:%27${slo?.id}%27),status:!(),type:!())` + ) + ); + }; + const handleClone = () => { const newSlo = transformValuesToCreateSLOInput( transformSloResponseToCreateSloInput({ ...slo, name: `[Copy] ${slo.name}` })! @@ -203,7 +211,18 @@ export function SloListItem({ data-test-subj="sloActionsCreateRule" > {i18n.translate('xpack.observability.slo.slo.item.actions.createRule', { - defaultMessage: 'Create Alert rule', + defaultMessage: 'Create new Alert rule', + })} + , + + {i18n.translate('xpack.observability.slo.slo.item.actions.manageRules', { + defaultMessage: 'Manage rules', })} , { - it('computes the query filter correctly', async () => { - expect( - toAlertsPageQueryFilter({ count: 2, ruleIds: ['rule-1', 'rule-2'] }) - ).toMatchInlineSnapshot( - `"(kuery:'kibana.alert.rule.uuid:\\"rule-1\\" or kibana.alert.rule.uuid:\\"rule-2\\"',rangeFrom:now-15m,rangeTo:now,status:all)"` - ); - }); -}); diff --git a/x-pack/plugins/observability/public/pages/slos/helpers/alerts_page_query_filter.ts b/x-pack/plugins/observability/public/pages/slos/helpers/alerts_page_query_filter.ts deleted file mode 100644 index 3fa407fab1bd8..0000000000000 --- a/x-pack/plugins/observability/public/pages/slos/helpers/alerts_page_query_filter.ts +++ /dev/null @@ -1,17 +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 { ActiveAlerts } from '../../../hooks/slo/use_fetch_active_alerts'; - -export function toAlertsPageQueryFilter(activeAlerts: ActiveAlerts): string { - const kuery = activeAlerts.ruleIds - .map((ruleId) => `kibana.alert.rule.uuid:"${ruleId}"`) - .join(' or '); - - const query = `(kuery:'${kuery}',rangeFrom:now-15m,rangeTo:now,status:all)`; - return query; -} diff --git a/x-pack/plugins/observability/public/pages/slos/slos.tsx b/x-pack/plugins/observability/public/pages/slos/slos.tsx index e224158ef9c2a..b81998d8452de 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.tsx @@ -21,6 +21,7 @@ import { AutoRefreshButton } from './components/auto_refresh_button'; import { paths } from '../../config/paths'; import type { ObservabilityAppServices } from '../../application/types'; import { HeaderTitle } from './components/header_title'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SlosPage() { const { @@ -69,7 +70,7 @@ export function SlosPage() { rightSideItems: [ , + , ], bottomBorder: false, }} diff --git a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts index e4d9de76ef1e6..f2a22b77d0a61 100644 --- a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts +++ b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts @@ -8,6 +8,7 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ALERT_REASON } from '@kbn/rule-data-utils'; +import { SLO_ID_FIELD } from '../../common/field_names/infra_metrics'; import { ConfigSchema } from '../plugin'; import { ObservabilityRuleTypeRegistry } from './create_observability_rule_type_registry'; import { SLO_BURN_RATE_RULE_ID } from '../../common/constants'; @@ -25,7 +26,7 @@ export const registerObservabilityRuleTypes = ( format: ({ fields }) => { return { reason: fields[ALERT_REASON] ?? '-', - link: '/app/observability/slos', + link: `/app/observability/slos/${fields[SLO_ID_FIELD]}`, }; }, iconClass: 'bell', diff --git a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx index 5a562958cb299..a0e5a1f0e037b 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx +++ b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx @@ -65,6 +65,7 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { useChartsBaseTheme: () => {}, useChartsTheme: () => {}, }, + activeCursor: () => {}, }, data: {}, dataViews: { diff --git a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts index 91c0b9f8c8ffd..6f61c5f6277f2 100644 --- a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts +++ b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts @@ -65,7 +65,7 @@ export function sloBurnRateRuleType( alerts: { context: SLO_RULE_REGISTRATION_CONTEXT, mappings: { fieldMap: { ...legacyExperimentalFieldMap, ...sloRuleFieldMap } }, - useEcs: true, + useEcs: false, useLegacyAlerts: true, }, }; diff --git a/x-pack/plugins/observability_onboarding/.prettierrc b/x-pack/plugins/observability_onboarding/.prettierrc new file mode 100644 index 0000000000000..650cb880f6f5a --- /dev/null +++ b/x-pack/plugins/observability_onboarding/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": true +} diff --git a/x-pack/plugins/observability_onboarding/README.md b/x-pack/plugins/observability_onboarding/README.md new file mode 100644 index 0000000000000..f416ae6748dcc --- /dev/null +++ b/x-pack/plugins/observability_onboarding/README.md @@ -0,0 +1,3 @@ +# Observability onboarding plugin + +This plugin provides an onboarding framework for observability solutions: Logs and APM. diff --git a/x-pack/plugins/observability_onboarding/common/fetch_options.ts b/x-pack/plugins/observability_onboarding/common/fetch_options.ts new file mode 100644 index 0000000000000..3a72a72762dee --- /dev/null +++ b/x-pack/plugins/observability_onboarding/common/fetch_options.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. + */ + +import type { HttpFetchOptions } from '@kbn/core/public'; + +export type FetchOptions = Omit & { + pathname: string; + method?: string; + body?: any; +}; diff --git a/x-pack/plugins/observability_onboarding/common/index.ts b/x-pack/plugins/observability_onboarding/common/index.ts new file mode 100644 index 0000000000000..6c03dd09627aa --- /dev/null +++ b/x-pack/plugins/observability_onboarding/common/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 const PLUGIN_ID = 'observabilityOnboarding'; +export const PLUGIN_NAME = 'observabilityOnboarding'; diff --git a/x-pack/plugins/observability_onboarding/jest.config.js b/x-pack/plugins/observability_onboarding/jest.config.js new file mode 100644 index 0000000000000..66a2f768ca0ff --- /dev/null +++ b/x-pack/plugins/observability_onboarding/jest.config.js @@ -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. + */ + +const path = require('path'); + +module.exports = { + preset: '@kbn/test', + rootDir: path.resolve(__dirname, '../../..'), + roots: ['/x-pack/plugins/observability_onboarding'], +}; diff --git a/x-pack/plugins/observability_onboarding/kibana.jsonc b/x-pack/plugins/observability_onboarding/kibana.jsonc new file mode 100644 index 0000000000000..673bc0f78b120 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/kibana.jsonc @@ -0,0 +1,23 @@ +{ + "type": "plugin", + "id": "@kbn/observability-onboarding-plugin", + "owner": "@elastic/apm-ui", + "plugin": { + "id": "observabilityOnboarding", + "server": true, + "browser": true, + "configPath": ["xpack", "observability_onboarding"], + "requiredPlugins": [ + "data", + "observability", + ], + "optionalPlugins": [ + "cloud", + "usageCollection", + ], + "requiredBundles": [ + "kibanaReact" + ], + "extraPublicDirs": ["common"] + } +} diff --git a/x-pack/plugins/observability_onboarding/public/application/app.tsx b/x-pack/plugins/observability_onboarding/public/application/app.tsx new file mode 100644 index 0000000000000..28d96eda8df26 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/application/app.tsx @@ -0,0 +1,179 @@ +/* + * 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 { EuiErrorBoundary } from '@elastic/eui'; +import { Theme, ThemeProvider } from '@emotion/react'; +import { + APP_WRAPPER_CLASS, + AppMountParameters, + CoreStart, +} from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import { + KibanaContextProvider, + KibanaThemeProvider, + RedirectAppLinks, + useKibana, + useUiSetting$, +} from '@kbn/kibana-react-plugin/public'; +import { useBreadcrumbs } from '@kbn/observability-plugin/public'; +import { RouterProvider, createRouter } from '@kbn/typed-react-router-config'; +import { euiDarkVars, euiLightVars } from '@kbn/ui-theme'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Redirect, RouteComponentProps, RouteProps } from 'react-router-dom'; +import { Home } from '../components/app/home'; +import { + ObservabilityOnboardingPluginSetupDeps, + ObservabilityOnboardingPluginStartDeps, +} from '../plugin'; + +export type BreadcrumbTitle< + T extends { [K in keyof T]?: string | undefined } = {} +> = string | ((props: RouteComponentProps) => string) | null; + +export interface RouteDefinition< + T extends { [K in keyof T]?: string | undefined } = any +> extends RouteProps { + breadcrumb: BreadcrumbTitle; +} + +export const onBoardingTitle = i18n.translate( + 'xpack.observability_onboarding.breadcrumbs.onboarding', + { + defaultMessage: 'Onboarding', + } +); + +export const onboardingRoutes: RouteDefinition[] = [ + { + exact: true, + path: '/', + render: () => , + breadcrumb: onBoardingTitle, + }, +]; + +function ObservabilityOnboardingApp() { + const [darkMode] = useUiSetting$('theme:darkMode'); + + const { http } = useKibana().services; + const basePath = http.basePath.get(); + + useBreadcrumbs([ + { + text: onBoardingTitle, + href: basePath + '/app/observabilityOnboarding', + }, + { + text: i18n.translate('xpack.observability_onboarding.breadcrumbs.logs', { + defaultMessage: 'Logs', + }), + }, + ]); + + return ( + ({ + ...outerTheme, + eui: darkMode ? euiDarkVars : euiLightVars, + darkMode, + })} + > +
    + +
    +
    + ); +} + +export const observabilityOnboardingRouter = createRouter({}); + +export function ObservabilityOnboardingAppRoot({ + appMountParameters, + core, + deps, + corePlugins: { observability, data }, +}: { + appMountParameters: AppMountParameters; + core: CoreStart; + deps: ObservabilityOnboardingPluginSetupDeps; + corePlugins: ObservabilityOnboardingPluginStartDeps; +}) { + const { history } = appMountParameters; + const i18nCore = core.i18n; + const plugins = { ...deps }; + + return ( + + + + + + + + + + + + + + ); +} + +/** + * This module is rendered asynchronously in the Kibana platform. + */ + +export const renderApp = ({ + core, + deps, + appMountParameters, + corePlugins, +}: { + core: CoreStart; + deps: ObservabilityOnboardingPluginSetupDeps; + appMountParameters: AppMountParameters; + corePlugins: ObservabilityOnboardingPluginStartDeps; +}) => { + const { element } = appMountParameters; + + ReactDOM.render( + , + element + ); + return () => { + corePlugins.data.search.session.clear(); + ReactDOM.unmountComponentAtNode(element); + }; +}; diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx new file mode 100644 index 0000000000000..4b4b3dfdbf3af --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/index.tsx @@ -0,0 +1,99 @@ +/* + * 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, EuiSpacer } from '@elastic/eui'; +import React, { ComponentType, useRef, useState } from 'react'; +import { + FilmstripFrame, + FilmstripTransition, + TransitionState, +} from '../../shared/filmstrip_transition'; +import { + Provider as WizardProvider, + Step as WizardStep, +} from './logs_onboarding_wizard'; +import { HorizontalSteps } from './logs_onboarding_wizard/horizontal_steps'; +import { PageTitle } from './logs_onboarding_wizard/page_title'; + +export function Home({ animated = true }: { animated?: boolean }) { + if (animated) { + return ; + } + return ; +} + +function StillTransitionsWizard() { + return ( + + + + + + + + ); +} + +const TRANSITION_DURATION = 180; + +function AnimatedTransitionsWizard() { + const [transition, setTransition] = useState('ready'); + const TransitionComponent = useRef(() => null); + + function onChangeStep({ + direction, + StepComponent, + }: { + direction: 'back' | 'next'; + StepComponent: ComponentType; + }) { + setTransition(direction); + TransitionComponent.current = StepComponent; + setTimeout(() => { + setTransition('ready'); + }, TRANSITION_DURATION + 10); + } + + return ( + + + + + + + + + + + + + { + // eslint-disable-next-line react/jsx-pascal-case + transition === 'back' ? : null + } + + + + + + { + // eslint-disable-next-line react/jsx-pascal-case + transition === 'next' ? : null + } + + + + + + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/configure_logs.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/configure_logs.tsx new file mode 100644 index 0000000000000..6c01111d5f43d --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/configure_logs.tsx @@ -0,0 +1,218 @@ +/* + * 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, { PropsWithChildren, useState } from 'react'; +import { + EuiTitle, + EuiText, + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiSpacer, + EuiCard, + EuiIcon, + EuiIconProps, +} from '@elastic/eui'; +import { + StepPanel, + StepPanelContent, + StepPanelFooter, +} from '../../../shared/step_panel'; +import { useWizard } from '.'; + +export function ConfigureLogs() { + const { goToStep, goBack, getState, setState } = useWizard(); + const wizardState = getState(); + const [logsType, setLogsType] = useState(wizardState.logsType); + const [uploadType, setUploadType] = useState(wizardState.uploadType); + + function onContinue() { + if (logsType && uploadType) { + setState({ ...getState(), logsType, uploadType }); + goToStep('installElasticAgent'); + } + } + + function createLogsTypeToggle(type: NonNullable) { + return () => { + if (type === logsType) { + setLogsType(undefined); + } else { + setLogsType(type); + } + }; + } + + function createUploadToggle(type: NonNullable) { + return () => { + if (type === uploadType) { + setUploadType(undefined); + } else { + setUploadType(type); + } + }; + } + + function onBack() { + goBack(); + } + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Back +
    , + + Continue + , + ]} + /> + + ); +} + +function LogsTypeSection({ + title, + description, + children, +}: PropsWithChildren<{ title: string; description: string }>) { + return ( + <> + +

    {title}

    +
    + + +

    {description}

    +
    + + {children} + + ); +} + +function OptionCard({ + title, + iconType, + onClick, + isSelected, +}: { + title: string; + iconType: EuiIconProps['type']; + onClick: () => void; + isSelected: boolean; +}) { + return ( + } + title={title} + titleSize="xs" + paddingSize="m" + style={{ height: 56 }} + onClick={onClick} + hasBorder={true} + display={isSelected ? 'primary' : undefined} + /> + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/horizontal_steps.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/horizontal_steps.tsx new file mode 100644 index 0000000000000..789f09bb7d574 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/horizontal_steps.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 React from 'react'; +import { EuiStepsHorizontal } from '@elastic/eui'; +import { useWizard } from '.'; + +export function HorizontalSteps() { + const { getPath } = useWizard(); + const [currentStep, ...previousSteps] = getPath().reverse(); + + function getStatus(stepKey: ReturnType[0]) { + if (currentStep === stepKey) { + return 'current'; + } + if (previousSteps.includes(stepKey)) { + return 'complete'; + } + return 'incomplete'; + } + + return ( + {}, + }, + { + title: 'Configure logs', + status: getStatus('configureLogs'), + onClick: () => {}, + }, + { + title: 'Install shipper', + status: getStatus('installElasticAgent'), + onClick: () => {}, + }, + { + title: 'Import data', + status: getStatus('importData'), + onClick: () => {}, + }, + ]} + /> + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/import_data.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/import_data.tsx new file mode 100644 index 0000000000000..7b64ca81ccaee --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/import_data.tsx @@ -0,0 +1,105 @@ +/* + * 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 { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiLoadingSpinner, + EuiSpacer, + EuiSteps, + EuiText, +} from '@elastic/eui'; +import React from 'react'; +import { useWizard } from '.'; +import { useFetcher } from '../../../../hooks/use_fetcher'; +import { + StepPanel, + StepPanelContent, + StepPanelFooter, +} from '../../../shared/step_panel'; + +export function ImportData() { + const { goToStep, goBack } = useWizard(); + + const { data } = useFetcher((callApi) => { + return callApi('GET /internal/observability_onboarding/get_status'); + }, []); + + function onContinue() { + goToStep('inspect'); + } + + function onBack() { + goBack(); + } + + return ( + + + +

    + It might take a few minutes for the data to get to Elasticsearch. If + you're not seeing any, try generating some to verify. If + you're having trouble connecting, check out the troubleshooting + guide. +

    +
    + + + + + + + + +

    Listening for incoming logs

    +
    +
    +
    +
    + + + + + + Need some help? + + +
    + + Back + , + + Continue + , + ]} + /> +
    + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/index.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/index.tsx new file mode 100644 index 0000000000000..d7f3eeca3da46 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/index.tsx @@ -0,0 +1,58 @@ +/* + * 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 { NameLogs } from './name_logs'; +import { ConfigureLogs } from './configure_logs'; +import { InstallElasticAgent } from './install_elastic_agent'; +import { createWizardContext } from '../../../../context/create_wizard_context'; +import { ImportData } from './import_data'; +import { Inspect } from './inspect'; + +interface WizardState { + datasetName: string; + logsType?: + | 'system' + | 'sys' + | 'http-endpoint' + | 'opentelemetry' + | 'amazon-firehose' + | 'log-file' + | 'service'; + uploadType?: 'log-file' | 'api-key'; + elasticAgentPlatform: 'linux-tar' | 'macos' | 'windows' | 'deb' | 'rpm'; + alternativeShippers: { + filebeat: boolean; + fluentbit: boolean; + logstash: boolean; + fluentd: boolean; + }; +} + +const initialState: WizardState = { + datasetName: '', + elasticAgentPlatform: 'linux-tar', + alternativeShippers: { + filebeat: false, + fluentbit: false, + logstash: false, + fluentd: false, + }, +}; + +const { Provider, Step, useWizard } = createWizardContext({ + initialState, + initialStep: 'nameLogs', + steps: { + nameLogs: NameLogs, + configureLogs: ConfigureLogs, + installElasticAgent: InstallElasticAgent, + importData: ImportData, + inspect: Inspect, + }, +}); + +export { Provider, Step, useWizard }; diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/inspect.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/inspect.tsx new file mode 100644 index 0000000000000..94b0040984edd --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/inspect.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiButton, EuiTitle, EuiSpacer } from '@elastic/eui'; +import { + StepPanel, + StepPanelContent, + StepPanelFooter, +} from '../../../shared/step_panel'; +import { useWizard } from '.'; + +export function Inspect() { + const { goBack, getState, getPath, getUsage } = useWizard(); + return ( + + + +

    State

    +
    +
    {JSON.stringify(getState(), null, 4)}
    + + +

    Path

    +
    +
    {JSON.stringify(getPath(), null, 4)}
    + + +

    Usage

    +
    +
    {JSON.stringify(getUsage(), null, 4)}
    +
    + + Back + , + ]} + /> +
    + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/install_elastic_agent.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/install_elastic_agent.tsx new file mode 100644 index 0000000000000..f75ecee1d3992 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/install_elastic_agent.tsx @@ -0,0 +1,199 @@ +/* + * 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, { PropsWithChildren, useState } from 'react'; +import { + EuiTitle, + EuiText, + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiSpacer, + EuiCard, + EuiIcon, + EuiIconProps, + EuiButtonGroup, + EuiCodeBlock, +} from '@elastic/eui'; +import { + StepPanel, + StepPanelContent, + StepPanelFooter, +} from '../../../shared/step_panel'; +import { useWizard } from '.'; + +export function InstallElasticAgent() { + const { goToStep, goBack, getState, setState } = useWizard(); + const wizardState = getState(); + const [elasticAgentPlatform, setElasticAgentPlatform] = useState( + wizardState.elasticAgentPlatform + ); + const [alternativeShippers, setAlternativeShippers] = useState( + wizardState.alternativeShippers + ); + + function onContinue() { + setState({ ...getState(), elasticAgentPlatform, alternativeShippers }); + goToStep('importData'); + } + + function createAlternativeShipperToggle( + type: NonNullable + ) { + return () => { + setAlternativeShippers({ + ...alternativeShippers, + [type]: !alternativeShippers[type], + }); + }; + } + + function onBack() { + goBack(); + } + + return ( + + + +

    + Select a platform and run the command to install, enroll, and start + the Elastic Agent. Do this for each host. For other platforms, see + our downloads page. Review host requirements and other installation + options. +

    +
    + + + setElasticAgentPlatform(id as typeof elasticAgentPlatform) + } + /> + + + {PLATFORM_COMMAND[elasticAgentPlatform]} + + + + + + + + + + + + + + + + + + + + + +
    + + Back + , + + Continue + , + ]} + /> +
    + ); +} + +function LogsTypeSection({ + title, + description, + children, +}: PropsWithChildren<{ title: string; description: string }>) { + return ( + <> + +

    {title}

    +
    + + +

    {description}

    +
    + + {children} + + ); +} + +function OptionCard({ + title, + iconType, + onClick, + isSelected, +}: { + title: string; + iconType: EuiIconProps['type']; + onClick: () => void; + isSelected: boolean; +}) { + return ( + } + title={title} + titleSize="xs" + paddingSize="m" + style={{ height: 56 }} + onClick={onClick} + hasBorder={true} + display={isSelected ? 'primary' : undefined} + /> + ); +} + +const PLATFORM_COMMAND = { + 'linux-tar': `curl -O https://elastic.co/agent-setup.sh && sudo bash agent-setup.sh -- service.name=my-service --url=https://elasticsearch:8220 --enrollment-token=SRSc2ozWUItWXNuWE5oZzdERFU6anJtY0FIzhSRGlzeTJYcUF5UklfUQ==`, + macos: `curl -O https://elastic.co/agent-setup.sh && sudo bash agent-setup.sh -- service.name=my-service --url=https://elasticsearch:8220 --enrollment-token=SRSc2ozWUItWXNuWE5oZzdERFU6anJtY0FIzhSRGlzeTJYcUF5UklfUQ==`, + windows: `curl -O https://elastic.co/agent-setup.sh && sudo bash agent-setup.sh -- service.name=my-service --url=https://elasticsearch:8220 --enrollment-token=SRSc2ozWUItWXNuWE5oZzdERFU6anJtY0FIzhSRGlzeTJYcUF5UklfUQ==`, + deb: `curl -O https://elastic.co/agent-setup.sh && sudo bash agent-setup.sh -- service.name=my-service --url=https://elasticsearch:8220 --enrollment-token=SRSc2ozWUItWXNuWE5oZzdERFU6anJtY0FIzhSRGlzeTJYcUF5UklfUQ==`, + rpm: `curl -O https://elastic.co/agent-setup.sh && sudo bash agent-setup.sh -- service.name=my-service --url=https://elasticsearch:8220 --enrollment-token=SRSc2ozWUItWXNuWE5oZzdERFU6anJtY0FIzhSRGlzeTJYcUF5UklfUQ==`, +} as const; diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/name_logs.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/name_logs.tsx new file mode 100644 index 0000000000000..253ce03282a5e --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/name_logs.tsx @@ -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 React, { useState } from 'react'; +import { + EuiText, + EuiButton, + EuiButtonEmpty, + EuiForm, + EuiFormRow, + EuiFieldText, + EuiSpacer, +} from '@elastic/eui'; +import { + StepPanel, + StepPanelContent, + StepPanelFooter, +} from '../../../shared/step_panel'; +import { useWizard } from '.'; + +export function NameLogs() { + const { goToStep, getState, setState } = useWizard(); + const wizardState = getState(); + const [datasetName, setDatasetName] = useState(wizardState.datasetName); + + function onContinue() { + setState({ ...getState(), datasetName }); + goToStep('configureLogs'); + } + + return ( + + + +

    Pick a name for your logs, this will become your dataset name.

    +
    + + + + setDatasetName(event.target.value)} + /> + + +
    + + Skip for now + , + + Save and Continue + , + ]} + /> +
    + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/page_title.tsx b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/page_title.tsx new file mode 100644 index 0000000000000..dde2797c1cf23 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/app/home/logs_onboarding_wizard/page_title.tsx @@ -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 React from 'react'; +import { EuiTitle } from '@elastic/eui'; +import { useWizard } from '.'; + +export function PageTitle() { + const { getPath } = useWizard(); + const [currentStep] = getPath().reverse(); + + if (currentStep === 'installElasticAgent') { + return ( + +

    Select your shipper

    +
    + ); + } + + if (currentStep === 'importData') { + return ( + +

    Incoming logs

    +
    + ); + } + + return ( + +

    Collect and analyze my logs

    +
    + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/shared/filmstrip_transition.tsx b/x-pack/plugins/observability_onboarding/public/components/shared/filmstrip_transition.tsx new file mode 100644 index 0000000000000..df877c2856733 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/shared/filmstrip_transition.tsx @@ -0,0 +1,68 @@ +/* + * 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, { PropsWithChildren } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export type TransitionState = 'ready' | 'back' | 'next'; + +export function FilmstripTransition({ + children, + duration, + transition, +}: PropsWithChildren<{ duration: number; transition: TransitionState }>) { + return ( +
    + {children} +
    + ); +} + +export function FilmstripFrame({ + children, + position, +}: PropsWithChildren<{ position: 'left' | 'center' | 'right' }>) { + return ( + + {children} + {/* {children}*/} + + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/components/shared/step_panel.tsx b/x-pack/plugins/observability_onboarding/public/components/shared/step_panel.tsx new file mode 100644 index 0000000000000..bd3c2bfc13e3c --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/components/shared/step_panel.tsx @@ -0,0 +1,68 @@ +/* + * 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, { ReactNode } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiPanelProps, + EuiTitle, +} from '@elastic/eui'; + +interface StepPanelProps { + title: string; + panelProps?: EuiPanelProps; + children?: ReactNode; +} + +export function StepPanel(props: StepPanelProps) { + const { title, children } = props; + const panelProps = props.panelProps ?? null; + return ( + + + + +

    {title}

    +
    +
    + {children} +
    +
    + ); +} + +interface StepPanelContentProps { + children?: ReactNode; +} +export function StepPanelContent(props: StepPanelContentProps) { + const { children } = props; + return {children}; +} + +interface StepPanelFooterProps { + children?: ReactNode; + items?: ReactNode[]; +} +export function StepPanelFooter(props: StepPanelFooterProps) { + const { items = [], children } = props; + return ( + + {children} + {items && ( + + {items.map((itemReactNode, index) => ( + + {itemReactNode} + + ))} + + )} + + ); +} diff --git a/x-pack/plugins/observability_onboarding/public/context/create_wizard_context.tsx b/x-pack/plugins/observability_onboarding/public/context/create_wizard_context.tsx new file mode 100644 index 0000000000000..7e7937ebc7e14 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/context/create_wizard_context.tsx @@ -0,0 +1,180 @@ +/* + * 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, { + ComponentType, + ReactNode, + createContext, + useContext, + useState, + useRef, +} from 'react'; + +interface WizardContext { + CurrentStep: ComponentType; + goToStep: (step: StepKey) => void; + goBack: () => void; + getState: () => T; + setState: (state: T) => void; + getPath: () => StepKey[]; + getUsage: () => { + timeSinceStart: number; + navEvents: Array<{ + type: string; + step: StepKey; + timestamp: number; + duration: number; + }>; + }; +} + +export function createWizardContext< + T, + StepKey extends string, + InitialStepKey extends StepKey +>({ + initialState, + initialStep, + steps, +}: { + initialState: T; + initialStep: InitialStepKey; + steps: Record; +}) { + const context = createContext>({ + CurrentStep: () => null, + goToStep: () => {}, + goBack: () => {}, + getState: () => initialState, + setState: () => {}, + getPath: () => [], + getUsage: () => ({ timeSinceStart: 0, navEvents: [] }), + }); + + function Provider({ + children, + onChangeStep, + transitionDuration, + }: { + children?: ReactNode; + onChangeStep?: (stepChangeEvent: { + direction: 'back' | 'next'; + stepKey: StepKey; + StepComponent: ComponentType; + }) => void; + transitionDuration?: number; + }) { + const [step, setStep] = useState(initialStep); + const pathRef = useRef([initialStep]); + const usageRef = useRef['getUsage']>>({ + timeSinceStart: 0, + navEvents: [ + { type: 'initial', step, timestamp: Date.now(), duration: 0 }, + ], + }); + const [state, setState] = useState(initialState); + return ( + { + setStep(stepKey); + }, transitionDuration); + } else { + setStep(stepKey); + } + }, + goBack() { + if (step === initialStep) { + return; + } + const path = pathRef.current; + path.pop(); + const lastStep = path[path.length - 1]; + const navEvents = usageRef.current.navEvents; + const currentNavEvent = navEvents[navEvents.length - 1]; + const timestamp = Date.now(); + currentNavEvent.duration = timestamp - currentNavEvent.timestamp; + usageRef.current.navEvents.push({ + type: 'back', + step: lastStep, + timestamp, + duration: 0, + }); + if (onChangeStep) { + onChangeStep({ + direction: 'back', + stepKey: lastStep, + StepComponent: steps[lastStep], + }); + } + if (transitionDuration) { + setTimeout(() => { + setStep(lastStep); + }, transitionDuration); + } else { + setStep(lastStep); + } + }, + getState: () => state as T, + setState: (_state: T) => { + setState(_state); + }, + getPath: () => [...pathRef.current], + getUsage: () => { + const currentTime = Date.now(); + const navEvents = usageRef.current.navEvents; + const firstNavEvent = navEvents[0]; + const lastNavEvent = navEvents[navEvents.length - 1]; + lastNavEvent.duration = currentTime - lastNavEvent.timestamp; + return { + timeSinceStart: currentTime - firstNavEvent.timestamp, + navEvents, + }; + }, + }} + > + {children} + + ); + } + + function Step() { + const { CurrentStep } = useContext(context); + return ; + } + + function useWizard() { + const { CurrentStep: _, ...rest } = useContext(context); + return rest; + } + + return { context, Provider, Step, useWizard }; +} diff --git a/x-pack/plugins/observability_onboarding/public/hooks/use_fetcher.tsx b/x-pack/plugins/observability_onboarding/public/hooks/use_fetcher.tsx new file mode 100644 index 0000000000000..5e96225144d34 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/hooks/use_fetcher.tsx @@ -0,0 +1,209 @@ +/* + * 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 React, { useEffect, useMemo, useState } from 'react'; +import type { + IHttpFetchError, + ResponseErrorBody, +} from '@kbn/core-http-browser'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useInspectorContext } from '@kbn/observability-plugin/public'; +import { + AutoAbortedObservabilityClient, + callObservabilityOnboardingApi, +} from '../services/rest/create_call_api'; + +export enum FETCH_STATUS { + LOADING = 'loading', + SUCCESS = 'success', + FAILURE = 'failure', + NOT_INITIATED = 'not_initiated', +} + +export const isPending = (fetchStatus: FETCH_STATUS) => + fetchStatus === FETCH_STATUS.LOADING || + fetchStatus === FETCH_STATUS.NOT_INITIATED; + +export interface FetcherResult { + data?: Data; + status: FETCH_STATUS; + error?: IHttpFetchError; +} + +function getDetailsFromErrorResponse( + error: IHttpFetchError +) { + const message = error.body?.message ?? error.response?.statusText; + return ( + <> + {message} ({error.response?.status}) +
    + {i18n.translate('xpack.observability_onboarding.fetcher.error.url', { + defaultMessage: `URL`, + })} +
    + {error.response?.url} + + ); +} + +const createAutoAbortedClient = ( + signal: AbortSignal, + addInspectorRequest: (result: FetcherResult) => void +): AutoAbortedObservabilityClient => { + return ((endpoint, options) => { + return callObservabilityOnboardingApi(endpoint, { + ...options, + signal, + } as any) + .catch((err) => { + addInspectorRequest({ + status: FETCH_STATUS.FAILURE, + data: err.body?.attributes, + }); + throw err; + }) + .then((response) => { + addInspectorRequest({ + data: response, + status: FETCH_STATUS.SUCCESS, + }); + return response; + }); + }) as AutoAbortedObservabilityClient; +}; + +// fetcher functions can return undefined OR a promise. Previously we had a more simple type +// but it led to issues when using object destructuring with default values +type InferResponseType = Exclude extends Promise< + infer TResponseType +> + ? TResponseType + : unknown; + +export function useFetcher( + fn: (callApi: AutoAbortedObservabilityClient) => TReturn, + fnDeps: any[], + options: { + preservePreviousData?: boolean; + showToastOnError?: boolean; + } = {} +): FetcherResult> & { refetch: () => void } { + const { notifications } = useKibana(); + const { preservePreviousData = true, showToastOnError = true } = options; + const [result, setResult] = useState< + FetcherResult> + >({ + data: undefined, + status: FETCH_STATUS.NOT_INITIATED, + }); + const [counter, setCounter] = useState(0); + const { addInspectorRequest } = useInspectorContext(); + + useEffect(() => { + let controller: AbortController = new AbortController(); + + async function doFetch() { + controller.abort(); + + controller = new AbortController(); + + const signal = controller.signal; + + const promise = fn(createAutoAbortedClient(signal, addInspectorRequest)); + // if `fn` doesn't return a promise it is a signal that data fetching was not initiated. + // This can happen if the data fetching is conditional (based on certain inputs). + // In these cases it is not desirable to invoke the global loading spinner, or change the status to success + if (!promise) { + return; + } + + setResult((prevResult) => ({ + data: preservePreviousData ? prevResult.data : undefined, // preserve data from previous state while loading next state + status: FETCH_STATUS.LOADING, + error: undefined, + })); + + try { + const data = await promise; + // when http fetches are aborted, the promise will be rejected + // and this code is never reached. For async operations that are + // not cancellable, we need to check whether the signal was + // aborted before updating the result. + if (!signal.aborted) { + setResult({ + data, + status: FETCH_STATUS.SUCCESS, + error: undefined, + } as FetcherResult>); + } + } catch (e) { + const err = e as Error | IHttpFetchError; + + if (!signal.aborted) { + const errorDetails = + 'response' in err ? getDetailsFromErrorResponse(err) : err.message; + + if (showToastOnError) { + notifications.toasts.danger({ + title: i18n.translate( + 'xpack.observability_onboarding.fetcher.error.title', + { + defaultMessage: `Error while fetching resource`, + } + ), + + body: ( +
    +
    + {i18n.translate( + 'xpack.observability_onboarding.fetcher.error.status', + { + defaultMessage: `Error`, + } + )} +
    + + {errorDetails} +
    + ), + }); + } + setResult({ + data: undefined, + status: FETCH_STATUS.FAILURE, + error: e, + }); + } + } + } + + doFetch(); + + return () => { + controller.abort(); + }; + /* eslint-disable react-hooks/exhaustive-deps */ + }, [ + counter, + preservePreviousData, + showToastOnError, + ...fnDeps, + /* eslint-enable react-hooks/exhaustive-deps */ + ]); + + return useMemo(() => { + return { + ...result, + refetch: () => { + // this will invalidate the deps to `useEffect` and will result in a new request + setCounter((count) => count + 1); + }, + }; + }, [result]); +} diff --git a/x-pack/plugins/observability_onboarding/public/index.ts b/x-pack/plugins/observability_onboarding/public/index.ts new file mode 100644 index 0000000000000..e703c46392e6d --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { PluginInitializer, PluginInitializerContext } from '@kbn/core/public'; +import { + ObservabilityOnboardingPlugin, + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginStart, +} from './plugin'; + +export const plugin: PluginInitializer< + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginStart +> = (ctx: PluginInitializerContext) => new ObservabilityOnboardingPlugin(ctx); + +export type { + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginStart, +}; diff --git a/x-pack/plugins/observability_onboarding/public/plugin.ts b/x-pack/plugins/observability_onboarding/public/plugin.ts new file mode 100644 index 0000000000000..35c22b7548705 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/plugin.ts @@ -0,0 +1,99 @@ +/* + * 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 { + ObservabilityPublicSetup, + ObservabilityPublicStart, +} from '@kbn/observability-plugin/public'; +import { + HttpStart, + AppMountParameters, + CoreSetup, + CoreStart, + DEFAULT_APP_CATEGORIES, + Plugin, + PluginInitializerContext, + AppNavLinkStatus, +} from '@kbn/core/public'; +import { + DataPublicPluginSetup, + DataPublicPluginStart, +} from '@kbn/data-plugin/public'; +import type { ObservabilityOnboardingConfig } from '../server'; + +export type ObservabilityOnboardingPluginSetup = void; +export type ObservabilityOnboardingPluginStart = void; + +export interface ObservabilityOnboardingPluginSetupDeps { + data: DataPublicPluginSetup; + observability: ObservabilityPublicSetup; +} + +export interface ObservabilityOnboardingPluginStartDeps { + http: HttpStart; + data: DataPublicPluginStart; + observability: ObservabilityPublicStart; +} + +export class ObservabilityOnboardingPlugin + implements + Plugin< + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginStart + > +{ + constructor(private ctx: PluginInitializerContext) {} + + public setup( + core: CoreSetup, + plugins: ObservabilityOnboardingPluginSetupDeps + ) { + const { + ui: { enabled: isObservabilityOnboardingUiEnabled }, + } = this.ctx.config.get(); + + const pluginSetupDeps = plugins; + + // set xpack.observability_onboarding.ui.enabled: true + // and go to /app/observabilityOnboarding + if (isObservabilityOnboardingUiEnabled) { + core.application.register({ + navLinkStatus: AppNavLinkStatus.hidden, + id: 'observabilityOnboarding', + title: 'Observability Onboarding', + order: 8500, + euiIconType: 'logoObservability', + category: DEFAULT_APP_CATEGORIES.observability, + keywords: [], + async mount(appMountParameters: AppMountParameters) { + // Load application bundle and Get start service + const [{ renderApp }, [coreStart, corePlugins]] = await Promise.all([ + import('./application/app'), + core.getStartServices(), + ]); + + const { createCallApi } = await import( + './services/rest/create_call_api' + ); + + createCallApi(core); + + return renderApp({ + core: coreStart, + deps: pluginSetupDeps, + appMountParameters, + corePlugins: corePlugins as ObservabilityOnboardingPluginStartDeps, + }); + }, + }); + } + } + public start( + core: CoreStart, + plugins: ObservabilityOnboardingPluginStartDeps + ) {} +} diff --git a/x-pack/plugins/observability_onboarding/public/services/rest/call_api.ts b/x-pack/plugins/observability_onboarding/public/services/rest/call_api.ts new file mode 100644 index 0000000000000..12249c35b7f50 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/services/rest/call_api.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreSetup, CoreStart } from '@kbn/core/public'; +import { FetchOptions } from '../../../common/fetch_options'; + +function getFetchOptions(fetchOptions: FetchOptions) { + const { body, ...rest } = fetchOptions; + + return { + ...rest, + ...(body !== undefined ? { body: JSON.stringify(body) } : {}), + query: { + ...fetchOptions.query, + }, + }; +} + +export type CallApi = typeof callApi; + +export async function callApi( + { http }: CoreStart | CoreSetup, + fetchOptions: FetchOptions +): Promise { + const { + pathname, + method = 'get', + ...options + } = getFetchOptions(fetchOptions); + + const lowercaseMethod = method.toLowerCase() as + | 'get' + | 'post' + | 'put' + | 'delete' + | 'patch'; + + const res = await http[lowercaseMethod](pathname, options); + + return res; +} diff --git a/x-pack/plugins/observability_onboarding/public/services/rest/create_call_api.ts b/x-pack/plugins/observability_onboarding/public/services/rest/create_call_api.ts new file mode 100644 index 0000000000000..e8c39fa783dd2 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/public/services/rest/create_call_api.ts @@ -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 { CoreSetup, CoreStart } from '@kbn/core/public'; +import type { RouteRepositoryClient } from '@kbn/server-route-repository'; +import { formatRequest } from '@kbn/server-route-repository'; +import { FetchOptions } from '../../../common/fetch_options'; +import type { ObservabilityOnboardingServerRouteRepository } from '../../../server/routes'; +import { CallApi, callApi } from './call_api'; + +export type ObservabilityOnboardingClientOptions = Omit< + FetchOptions, + 'query' | 'body' | 'pathname' | 'signal' +> & { + signal: AbortSignal | null; +}; + +export type ObservabilityOnboardingClient = RouteRepositoryClient< + ObservabilityOnboardingServerRouteRepository, + ObservabilityOnboardingClientOptions +>; + +export type AutoAbortedObservabilityClient = RouteRepositoryClient< + ObservabilityOnboardingServerRouteRepository, + Omit +>; + +export let callObservabilityOnboardingApi: ObservabilityOnboardingClient = + () => { + throw new Error( + 'callObservabilityOnboardingApi has to be initialized before used. Call createCallApi first.' + ); + }; + +export function createCallApi(core: CoreStart | CoreSetup) { + callObservabilityOnboardingApi = ((endpoint, options) => { + const { params } = options as unknown as { + params?: Partial>; + }; + + const { method, pathname } = formatRequest(endpoint, params?.path); + + return callApi(core, { + ...options, + method, + pathname, + body: params?.body, + query: params?.query, + } as unknown as Parameters[1]); + }) as ObservabilityOnboardingClient; +} diff --git a/x-pack/plugins/observability_onboarding/server/index.ts b/x-pack/plugins/observability_onboarding/server/index.ts new file mode 100644 index 0000000000000..7a6b9b9587b53 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/index.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 { schema, TypeOf } from '@kbn/config-schema'; +import { + PluginConfigDescriptor, + PluginInitializerContext, +} from '@kbn/core/server'; +import { ObservabilityOnboardingPlugin } from './plugin'; + +const configSchema = schema.object({ + ui: schema.object({ + enabled: schema.boolean({ defaultValue: false }), + }), +}); + +export type ObservabilityOnboardingConfig = TypeOf; + +// plugin config +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: configSchema, +}; + +export function plugin(initializerContext: PluginInitializerContext) { + return new ObservabilityOnboardingPlugin(initializerContext); +} + +export type { + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginStart, +} from './types'; diff --git a/x-pack/plugins/observability_onboarding/server/plugin.ts b/x-pack/plugins/observability_onboarding/server/plugin.ts new file mode 100644 index 0000000000000..3045ad66c869e --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/plugin.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 type { + CoreSetup, + CoreStart, + Logger, + Plugin, + PluginInitializerContext, +} from '@kbn/core/server'; +import { mapValues } from 'lodash'; +import { getObservabilityOnboardingServerRouteRepository } from './routes'; +import { registerRoutes } from './routes/register_routes'; +import { ObservabilityOnboardingRouteHandlerResources } from './routes/types'; +import { + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginSetupDependencies, + ObservabilityOnboardingPluginStart, + ObservabilityOnboardingPluginStartDependencies, +} from './types'; +import { ObservabilityOnboardingConfig } from '.'; + +export class ObservabilityOnboardingPlugin + implements + Plugin< + ObservabilityOnboardingPluginSetup, + ObservabilityOnboardingPluginStart, + ObservabilityOnboardingPluginSetupDependencies, + ObservabilityOnboardingPluginStartDependencies + > +{ + private readonly logger: Logger; + constructor( + private readonly initContext: PluginInitializerContext + ) { + this.initContext = initContext; + this.logger = this.initContext.logger.get(); + } + + public setup( + core: CoreSetup, + plugins: ObservabilityOnboardingPluginSetupDependencies + ) { + this.logger.debug('observability_onboarding: Setup'); + + const resourcePlugins = mapValues(plugins, (value, key) => { + return { + setup: value, + start: () => + core.getStartServices().then((services) => { + const [, pluginsStartContracts] = services; + return pluginsStartContracts[ + key as keyof ObservabilityOnboardingPluginStartDependencies + ]; + }), + }; + }) as ObservabilityOnboardingRouteHandlerResources['plugins']; + + registerRoutes({ + core, + logger: this.logger, + repository: getObservabilityOnboardingServerRouteRepository(), + plugins: resourcePlugins, + }); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('observability_onboarding: Started'); + + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/observability_onboarding/server/routes/create_observability_onboarding_server_route.ts b/x-pack/plugins/observability_onboarding/server/routes/create_observability_onboarding_server_route.ts new file mode 100644 index 0000000000000..47c3d026bc665 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/routes/create_observability_onboarding_server_route.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 { createServerRouteFactory } from '@kbn/server-route-repository'; +import { + ObservabilityOnboardingRouteCreateOptions, + ObservabilityOnboardingRouteHandlerResources, +} from './types'; + +export const createObservabilityOnboardingServerRoute = + createServerRouteFactory< + ObservabilityOnboardingRouteHandlerResources, + ObservabilityOnboardingRouteCreateOptions + >(); diff --git a/x-pack/plugins/observability_onboarding/server/routes/index.ts b/x-pack/plugins/observability_onboarding/server/routes/index.ts new file mode 100644 index 0000000000000..6a1067465787c --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/routes/index.ts @@ -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 type { + EndpointOf, + ServerRouteRepository, +} from '@kbn/server-route-repository'; +import { statusRouteRepository } from './status/route'; + +function getTypedObservabilityOnboardingServerRouteRepository() { + const repository = { + ...statusRouteRepository, + }; + + return repository; +} + +export const getObservabilityOnboardingServerRouteRepository = + (): ServerRouteRepository => { + return getTypedObservabilityOnboardingServerRouteRepository(); + }; + +export type ObservabilityOnboardingServerRouteRepository = ReturnType< + typeof getTypedObservabilityOnboardingServerRouteRepository +>; + +export type APIEndpoint = + EndpointOf; diff --git a/x-pack/plugins/observability_onboarding/server/routes/register_routes.ts b/x-pack/plugins/observability_onboarding/server/routes/register_routes.ts new file mode 100644 index 0000000000000..83000c1eaeec4 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/routes/register_routes.ts @@ -0,0 +1,103 @@ +/* + * 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 { errors } from '@elastic/elasticsearch'; +import Boom from '@hapi/boom'; +import { CoreSetup, Logger, RouteRegistrar } from '@kbn/core/server'; +import { + ServerRouteRepository, + decodeRequestParams, + parseEndpoint, + routeValidationObject, +} from '@kbn/server-route-repository'; +import * as t from 'io-ts'; +import { ObservabilityOnboardingRequestHandlerContext } from '../types'; +import { ObservabilityOnboardingRouteHandlerResources } from './types'; + +interface RegisterRoutes { + core: CoreSetup; + repository: ServerRouteRepository; + logger: Logger; + plugins: ObservabilityOnboardingRouteHandlerResources['plugins']; +} + +export function registerRoutes({ + repository, + core, + logger, + plugins, +}: RegisterRoutes) { + const routes = Object.values(repository); + + const router = core.http.createRouter(); + + routes.forEach((route) => { + const { endpoint, options, handler, params } = route; + const { pathname, method } = parseEndpoint(endpoint); + + ( + router[method] as RouteRegistrar< + typeof method, + ObservabilityOnboardingRequestHandlerContext + > + )( + { + path: pathname, + validate: routeValidationObject, + options, + }, + async (context, request, response) => { + try { + const decodedParams = decodeRequestParams( + { + params: request.params, + body: request.body, + query: request.query, + }, + params ?? t.strict({}) + ); + + const data = (await handler({ + context, + request, + logger, + params: decodedParams, + plugins, + })) as any; + + if (data === undefined) { + return response.noContent(); + } + + return response.ok({ body: data }); + } catch (error) { + if (Boom.isBoom(error)) { + logger.error(error.output.payload.message); + return response.customError({ + statusCode: error.output.statusCode, + body: { message: error.output.payload.message }, + }); + } + + logger.error(error); + const opts = { + statusCode: 500, + body: { + message: error.message, + }, + }; + + if (error instanceof errors.RequestAbortedError) { + opts.statusCode = 499; + opts.body.message = 'Client closed request'; + } + + return response.customError(opts); + } + } + ); + }); +} diff --git a/x-pack/plugins/observability_onboarding/server/routes/status/route.ts b/x-pack/plugins/observability_onboarding/server/routes/status/route.ts new file mode 100644 index 0000000000000..438bbe7e8b6a5 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/routes/status/route.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 { createObservabilityOnboardingServerRoute } from '../create_observability_onboarding_server_route'; + +const statusRoute = createObservabilityOnboardingServerRoute({ + endpoint: 'GET /internal/observability_onboarding/get_status', + options: { + tags: [], + }, + async handler(resources): Promise<{ status: 'incomplete' | 'complete' }> { + return { status: 'complete' }; + }, +}); + +export const statusRouteRepository = { + ...statusRoute, +}; diff --git a/x-pack/plugins/observability_onboarding/server/routes/types.ts b/x-pack/plugins/observability_onboarding/server/routes/types.ts new file mode 100644 index 0000000000000..c8f1c0dc99560 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/routes/types.ts @@ -0,0 +1,35 @@ +/* + * 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 { KibanaRequest, Logger } from '@kbn/core/server'; +import { ObservabilityOnboardingServerRouteRepository } from '.'; +import { + ObservabilityOnboardingPluginSetupDependencies, + ObservabilityOnboardingPluginStartDependencies, + ObservabilityOnboardingRequestHandlerContext, +} from '../types'; + +export type { ObservabilityOnboardingServerRouteRepository }; + +export interface ObservabilityOnboardingRouteHandlerResources { + context: ObservabilityOnboardingRequestHandlerContext; + logger: Logger; + request: KibanaRequest; + plugins: { + [key in keyof ObservabilityOnboardingPluginSetupDependencies]: { + setup: Required[key]; + start: () => Promise< + Required[key] + >; + }; + }; +} + +export interface ObservabilityOnboardingRouteCreateOptions { + options: { + tags: string[]; + }; +} diff --git a/x-pack/plugins/observability_onboarding/server/types.ts b/x-pack/plugins/observability_onboarding/server/types.ts new file mode 100644 index 0000000000000..99e7157178ff1 --- /dev/null +++ b/x-pack/plugins/observability_onboarding/server/types.ts @@ -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 { CustomRequestHandlerContext } from '@kbn/core/server'; +import { + PluginSetup as DataPluginSetup, + PluginStart as DataPluginStart, +} from '@kbn/data-plugin/server'; +import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server'; + +export interface ObservabilityOnboardingPluginSetupDependencies { + data: DataPluginSetup; + observability: ObservabilityPluginSetup; +} + +export interface ObservabilityOnboardingPluginStartDependencies { + data: DataPluginStart; + observability: undefined; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ObservabilityOnboardingPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ObservabilityOnboardingPluginStart {} + +export type ObservabilityOnboardingRequestHandlerContext = + CustomRequestHandlerContext<{}>; diff --git a/x-pack/plugins/observability_onboarding/tsconfig.json b/x-pack/plugins/observability_onboarding/tsconfig.json new file mode 100644 index 0000000000000..4534db3d6d37a --- /dev/null +++ b/x-pack/plugins/observability_onboarding/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + }, + "include": [ + "../../../typings/**/*", + "common/**/*", + "public/**/*", + "typings/**/*", + "public/**/*.json", + "server/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/data-plugin", + "@kbn/kibana-react-plugin", + "@kbn/observability-plugin", + "@kbn/i18n", + "@kbn/core-http-browser", + "@kbn/ui-theme", + "@kbn/typed-react-router-config", + "@kbn/server-route-repository", + "@kbn/config-schema", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/x-pack/plugins/osquery/server/lib/saved_query/saved_object_mappings.ts b/x-pack/plugins/osquery/server/lib/saved_query/saved_object_mappings.ts index f3bc667513609..70bee246ae886 100644 --- a/x-pack/plugins/osquery/server/lib/saved_query/saved_object_mappings.ts +++ b/x-pack/plugins/osquery/server/lib/saved_query/saved_object_mappings.ts @@ -7,6 +7,7 @@ import { produce } from 'immer'; import type { SavedObjectsType } from '@kbn/core/server'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { savedQuerySavedObjectType, packSavedObjectType, @@ -27,6 +28,7 @@ export const usageMetricSavedObjectMappings: SavedObjectsType['mappings'] = { export const usageMetricType: SavedObjectsType = { name: usageMetricSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', mappings: usageMetricSavedObjectMappings, @@ -74,6 +76,7 @@ export const savedQuerySavedObjectMappings: SavedObjectsType['mappings'] = { export const savedQueryType: SavedObjectsType = { name: savedQuerySavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', mappings: savedQuerySavedObjectMappings, @@ -159,6 +162,7 @@ export const packSavedObjectMappings: SavedObjectsType['mappings'] = { export const packType: SavedObjectsType = { name: packSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', mappings: packSavedObjectMappings, @@ -232,6 +236,7 @@ export const packAssetSavedObjectMappings: SavedObjectsType['mappings'] = { export const packAssetType: SavedObjectsType = { name: packAssetSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, management: { importableAndExportable: true, diff --git a/x-pack/plugins/osquery/tsconfig.json b/x-pack/plugins/osquery/tsconfig.json index 162bedbf9ed60..6d67d852b6073 100644 --- a/x-pack/plugins/osquery/tsconfig.json +++ b/x-pack/plugins/osquery/tsconfig.json @@ -70,6 +70,7 @@ "@kbn/logging", "@kbn/safer-lodash-set", "@kbn/shared-ux-router", - "@kbn/securitysolution-ecs" + "@kbn/securitysolution-ecs", + "@kbn/core-saved-objects-server" ] } diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow01.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow01.json index 2fb57821610f8..f571b9681d751 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow01.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow01.json @@ -1,1436 +1,1436 @@ {"create": {"_index": "profiling-events-5pow01", "_id": "stUU7YYBBkbVtX3nu_4f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "RdYU7YYBBkbVtX3nuwHT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "xmUU7YYBO2e_P_QbvJmg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eqelKqbeHiTw1Jlw68liwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eqelKqbeHiTw1Jlw68liwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "It8U7YYByh-A-Biyv8mc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dVb-MiyMMGjQnN4CNy5W_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dVb-MiyMMGjQnN4CNy5W_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "XtYU7YYBBkbVtX3nyQUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YVVpCpnnAN7nqi22_B110A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YVVpCpnnAN7nqi22_B110A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "LdYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "KdYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_PmLUDiMT9Fiy_kfGXHxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_PmLUDiMT9Fiy_kfGXHxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-5pow01", "_id": "MdYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RrLvz0R4S4ONxlxpZkei3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RrLvz0R4S4ONxlxpZkei3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "N9YU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mbOVGyx5XatnK0SRKgRKUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mbOVGyx5XatnK0SRKgRKUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "NNYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g3jLco5iklv9rjHlmxCtzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g3jLco5iklv9rjHlmxCtzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "KNYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yZrBKxKhbw4I5T2D2ia0Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yZrBKxKhbw4I5T2D2ia0Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "ENYV7YYBBkbVtX3nB1HM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "J-AV7YYByh-A-BiyCxxq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydVfMca4pTKtV_nMQvo2kQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydVfMca4pTKtV_nMQvo2kQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YeAV7YYByh-A-BiyGi5B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YM8VOmaiYixjkGqh3aYLRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YM8VOmaiYixjkGqh3aYLRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "6NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["53PCQ4S8hGae7xDUxkptJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["53PCQ4S8hGae7xDUxkptJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "7dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qwzw6oIfyawdflY_bB-eDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qwzw6oIfyawdflY_bB-eDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-5pow01", "_id": "qWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eh1qER1qLyoMW0w6ZkEkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eh1qER1qLyoMW0w6ZkEkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "smYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_zNN2R6gCnlCmrGYYAK4_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_zNN2R6gCnlCmrGYYAK4_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "qGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_CuNkg8IjplIBsYDC3MqZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_CuNkg8IjplIBsYDC3MqZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "UmYV7YYBO2e_P_Qbhkg5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tXy3kfx8JYVKb1u9vHWM4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tXy3kfx8JYVKb1u9vHWM4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "1WYV7YYBO2e_P_Qbg0Jm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u4n3YuffBdoPIiHaB1UC0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u4n3YuffBdoPIiHaB1UC0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} {"create": {"_index": "profiling-events-5pow01", "_id": "RWYV7YYBO2e_P_QbmFzZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZCa7vDPVTmdjv0FBSHomYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZCa7vDPVTmdjv0FBSHomYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "bNcV7YYBBkbVtX3nsgwF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MxaBJ5vAlZJbFL1ZFA-tNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MxaBJ5vAlZJbFL1ZFA-tNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "hmYV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kk6lQFGFmE5-o8l9P-PnVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kk6lQFGFmE5-o8l9P-PnVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "19cV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8eRxSiE_6KOXeGPJZDEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8eRxSiE_6KOXeGPJZDEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "StcV7YYBBkbVtX3nthkq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yuDdBF0iwQiPnskDDqWYwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yuDdBF0iwQiPnskDDqWYwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "1NcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9BGZHTzs6oj_j1YiF-r26w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9BGZHTzs6oj_j1YiF-r26w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "1dcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9eJFc1RqWTK4Nh5sHxlOdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9eJFc1RqWTK4Nh5sHxlOdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "jdcV7YYBBkbVtX3nuB51"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "--AV7YYByh-A-Biyxbvb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mcFH-Ijp7M4Pm2g7nfowcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mcFH-Ijp7M4Pm2g7nfowcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "k2YV7YYBO2e_P_QbxXYP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mPA9NkH3378cVYxn3yS3sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mPA9NkH3378cVYxn3yS3sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "JuAV7YYByh-A-Biy0cDF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_2Gi4xXPiktjMQVPnPo6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_2Gi4xXPiktjMQVPnPo6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "VtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jY6an4TJNzicxfsoO4aEFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jY6an4TJNzicxfsoO4aEFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "FtcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QhuAxDp-mAXxSlQCTHCDJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QhuAxDp-mAXxSlQCTHCDJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "XdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jR2WafQ5aT4KiR_9VxLM1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jR2WafQ5aT4KiR_9VxLM1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "leEW7YYByh-A-BiyQzHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHeythk0HZH6YmI9vQ5rRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHeythk0HZH6YmI9vQ5rRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-5pow01", "_id": "iGYW7YYBO2e_P_QbRObI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sdYsXSOFq3ZV5V3ZTd5OZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sdYsXSOFq3ZV5V3ZTd5OZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "8uEW7YYByh-A-BiyQCi1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2Jekw_GmjkRbs2II8a1AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2Jekw_GmjkRbs2II8a1AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "CWYW7YYBO2e_P_QbP9-R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f6Keqe1sXyk36jAJ3WN1sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f6Keqe1sXyk36jAJ3WN1sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "3tcW7YYBBkbVtX3nQo0Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7uqXjdMn8cKJH0c7LBBRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7uqXjdMn8cKJH0c7LBBRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "ueEW7YYByh-A-BiyRDN8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r4lcZDimr4HL3ZJBoS4zaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r4lcZDimr4HL3ZJBoS4zaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "hmYW7YYBO2e_P_QbP91J"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "CmYW7YYBO2e_P_QbUe_C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["18dt2YlDI5SQuyr5uDM2hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["18dt2YlDI5SQuyr5uDM2hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "W-EW7YYByh-A-Biyc1w8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKhp4VrunMdJZkEZm9IkqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKhp4VrunMdJZkEZm9IkqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "7uEW7YYByh-A-BiyfVwi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "jdcW7YYBBkbVtX3nf735"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DAkV_97hJaROs8HKSG_NUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DAkV_97hJaROs8HKSG_NUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "QOEW7YYByh-A-BiyjnPr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K8gQh5zdfmwr_L8d6j_v5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K8gQh5zdfmwr_L8d6j_v5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "1NcW7YYBBkbVtX3nksnh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "mGcW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iVK1cbIgag654ehUa-HUNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iVK1cbIgag654ehUa-HUNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "peEW7YYByh-A-BiyrYy7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "6GcW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kHZvNVXXuZ4FaC6U3PxZfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kHZvNVXXuZ4FaC6U3PxZfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "p9cW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "tdcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["41RJH9BALozcwHa5Gm2tSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["41RJH9BALozcwHa5Gm2tSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "D-EW7YYByh-A-BiyzK0R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oFFMBClb7YDKbss0-D49Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oFFMBClb7YDKbss0-D49Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "pGcW7YYBO2e_P_Qb0Fc4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AoxNz9Y_PEGGL6UFqTd8NA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AoxNz9Y_PEGGL6UFqTd8NA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "pWcW7YYBO2e_P_QbzlQs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "uOEW7YYByh-A-BiyzrTg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hgbYFeQR5UbL1ILQeJXsrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hgbYFeQR5UbL1ILQeJXsrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "79cW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "vuEW7YYByh-A-Biy4MbF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "udgW7YYBBkbVtX3n2wD9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "zeEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dl4T3akeu1eU8F-sCfOUww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dl4T3akeu1eU8F-sCfOUww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "x-EW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0TcViARYA_NarblNdiqUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0TcViARYA_NarblNdiqUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "xuEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8O4Oo3VCILgT6pGMxLQiaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8O4Oo3VCILgT6pGMxLQiaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "yOEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "BdgX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EKNw_XLZvm5U0bSAHP5Qhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EKNw_XLZvm5U0bSAHP5Qhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "y-EX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KbIwDU7bE16YP2ns0ZA4pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KbIwDU7bE16YP2ns0ZA4pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "KmcX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3HYswCLIguo6i_KRnM6AYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3HYswCLIguo6i_KRnM6AYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "B-EX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DbVr5WH8AZycf302C0td3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DbVr5WH8AZycf302C0td3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-5pow01", "_id": "5WcX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zii4wg2T59k_VWZoCJQUDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zii4wg2T59k_VWZoCJQUDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "TOEX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["84RiA4pbVL7KMlDvnXnbFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["84RiA4pbVL7KMlDvnXnbFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "BtgX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XiTL4w9S8KltLkj8tdlEIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XiTL4w9S8KltLkj8tdlEIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "hGcX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8j8JNdpbtu6ZzDCgLiiQag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8j8JNdpbtu6ZzDCgLiiQag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "zOEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RTGr7Nm-Ia9juXQJ0VJo4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RTGr7Nm-Ia9juXQJ0VJo4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "0uEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "uWcX7YYBO2e_P_QbOr_Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jKJw7MgwzsyLy5Y5-ZGSMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jKJw7MgwzsyLy5Y5-ZGSMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "A2cX7YYBO2e_P_QbObof"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7QawPKHJF79qrjka8nzLbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7QawPKHJF79qrjka8nzLbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "Q-IX7YYByh-A-BiyOQRi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsE2Ss7VQy9Y1xUvJ14HPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsE2Ss7VQy9Y1xUvJ14HPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "9-IX7YYByh-A-BiyOALn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkp5EyZaH9EKC1Tx2EnCYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkp5EyZaH9EKC1Tx2EnCYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "i9gX7YYBBkbVtX3nSErh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXipnObSe0dCYjfUl0jbxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXipnObSe0dCYjfUl0jbxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YeIX7YYByh-A-BiyShVm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LibGknFXAn9fd0n8hPZURw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LibGknFXAn9fd0n8hPZURw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-5pow01", "_id": "S2cX7YYBO2e_P_QbScVi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFcmZLo-GvC7WdK5tCotfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFcmZLo-GvC7WdK5tCotfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "kOIX7YYByh-A-BiyZymA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fqlDalQnR0z4CfFMV3Mv9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fqlDalQnR0z4CfFMV3Mv9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "-dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W4eaTfNJQRBDVqF5v5x57A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W4eaTfNJQRBDVqF5v5x57A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "_tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x92QEPdFkYeW4x8Mit4TyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x92QEPdFkYeW4x8Mit4TyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "-tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "BNgX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0AOiOeHK39oqr5eNGKOkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0AOiOeHK39oqr5eNGKOkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "6eIX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VV8E-OYfEEKqtlp023Tqng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VV8E-OYfEEKqtlp023Tqng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YmcX7YYBO2e_P_Qbh_kU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_OQKwd7_zKSX8IYHdhu4OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "702806677431836"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_OQKwd7_zKSX8IYHdhu4OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "702806677431836"} {"create": {"_index": "profiling-events-5pow01", "_id": "5NgX7YYBBkbVtX3ni4Pc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FGf4xT_jVUAejwXntzL3PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FGf4xT_jVUAejwXntzL3PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "puIX7YYByh-A-BiynF0B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yjN3QcXIO7ZJpjPqQPEBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yjN3QcXIO7ZJpjPqQPEBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "LNkY7YYBBkbVtX3nJQGU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-5pow01", "_id": "w-IY7YYByh-A-BiyJL2w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "K2gY7YYBO2e_P_QbNYgr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FcDW-9IPZrZmO_AgR-UVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FcDW-9IPZrZmO_AgR-UVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "1dkY7YYBBkbVtX3nOAq2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T3wubxICfJSY8FNVzGkEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T3wubxICfJSY8FNVzGkEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "-GgY7YYBO2e_P_QbNYme"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "a-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["32T4OaSlAZyX3gvcGH9I7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["32T4OaSlAZyX3gvcGH9I7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "Z-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fWJaqb09QzwUMPXDtHMSXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fWJaqb09QzwUMPXDtHMSXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "aeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "TmkY7YYBO2e_P_Qb4CzM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "0dkY7YYBBkbVtX3n8KgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xefg2tu-dTR7fu4bq6TlVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xefg2tu-dTR7fu4bq6TlVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "MGkY7YYBO2e_P_Qb8DhZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4nXxkGYVgHbeGTI3oHepdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4nXxkGYVgHbeGTI3oHepdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "G9kY7YYBBkbVtX3n7qav"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bjA-twM-arP4DofwAmuiCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bjA-twM-arP4DofwAmuiCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "P-MY7YYByh-A-Biy71tL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jIUkkqlhs_xaucQSfOkxdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jIUkkqlhs_xaucQSfOkxdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "qGkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "0tkY7YYBBkbVtX3n7qNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uRrKKaf_gbp1De235zmPrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uRrKKaf_gbp1De235zmPrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "mdkY7YYBBkbVtX3n861d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nUFT-4VjV49edA4VHVD06g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nUFT-4VjV49edA4VHVD06g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "OOMZ7YYByh-A-BiyIY5d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "TNkZ7YYBBkbVtX3nHs4s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PlNxGYc1KQXo_krOBCj9YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PlNxGYc1KQXo_krOBCj9YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "P2kZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "PN8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5QxSgtn_YPXxJ3jCeAVHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5QxSgtn_YPXxJ3jCeAVHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} {"create": {"_index": "profiling-events-5pow01", "_id": "5d8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5hG8KKglQrQ3G7KSXA2QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5hG8KKglQrQ3G7KSXA2QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "Qt8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tk-Rn8r6-wqzqI-bfiAJ7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tk-Rn8r6-wqzqI-bfiAJ7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "M98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-D2Xan0xr7Iyy5r8CY20RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-D2Xan0xr7Iyy5r8CY20RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "KtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ed2Wt5gOq97H8-8youFpYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ed2Wt5gOq97H8-8youFpYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "IdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EXarUgAL9HIosZihvCe9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EXarUgAL9HIosZihvCe9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "DdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "D9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbcOgGVzXu_Kl1MHENboNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbcOgGVzXu_Kl1MHENboNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "GdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BfRadBAJgVIPCs4sRWRCsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BfRadBAJgVIPCs4sRWRCsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-5pow01", "_id": "CtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YiY7TtFv0EXQiZMyJynqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YiY7TtFv0EXQiZMyJynqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "KdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHgSIcaSuS6XNpC67kiXTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHgSIcaSuS6XNpC67kiXTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "LNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["759vzPaqX5H2_0qTOKee0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["759vzPaqX5H2_0qTOKee0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "INYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XDv5HwoOhhJwuGtzx9aiqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XDv5HwoOhhJwuGtzx9aiqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "K9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PKfrUz68RAX4mdNriJ73lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PKfrUz68RAX4mdNriJ73lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "GtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XAkh0cI6mI0TEjgeMQjJRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XAkh0cI6mI0TEjgeMQjJRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "JtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TrAEEkzHCQIrkyMsb-wF4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TrAEEkzHCQIrkyMsb-wF4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "MNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6BMEwaZdEOxcFFELpK3iqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6BMEwaZdEOxcFFELpK3iqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "PGYV7YYBO2e_P_QbWBy9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJPjpnk4438S9AxhBdL7Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJPjpnk4438S9AxhBdL7Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "1WYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["codND57fF0ln0PPsgzvoNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["codND57fF0ln0PPsgzvoNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "4GYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NRT6b-EmSsUKrT0-0ibcag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NRT6b-EmSsUKrT0-0ibcag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "3GYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6YWns3NF2PVmevxSMrfdSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6YWns3NF2PVmevxSMrfdSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "32YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oe_nHyIGjMEfD9kwUevsMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oe_nHyIGjMEfD9kwUevsMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "3OAW7YYByh-A-BiyAu7q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-riZP-fh7uXaUsCqBO2ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-riZP-fh7uXaUsCqBO2ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "KmYW7YYBO2e_P_QbAabd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EVbkX_ePnzMrnOl-TBl5FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EVbkX_ePnzMrnOl-TBl5FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-5pow01", "_id": "oGYW7YYBO2e_P_QbAKJr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bawlMqqRTjOm5tziwkLcwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bawlMqqRTjOm5tziwkLcwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "--AW7YYByh-A-BiyEfoC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "j2YW7YYBO2e_P_QbFsMV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_D9JvJUWXzC0H3Nib_YHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_D9JvJUWXzC0H3Nib_YHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "I2YW7YYBO2e_P_QbFcGw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_IL9L_uv3CfGfQbo7Tbz2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_IL9L_uv3CfGfQbo7Tbz2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "fGgX7YYBO2e_P_QbqRsl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zyeCF78Ljkj_liCk-aIaJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zyeCF78Ljkj_liCk-aIaJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "8mgX7YYBO2e_P_Qbpxl-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3QVerrpALkFsA-z-U___AA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3QVerrpALkFsA-z-U___AA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "YuIX7YYByh-A-Biypl4D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "LdgX7YYBBkbVtX3nua7-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "x9gX7YYBBkbVtX3nuq9-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "XuIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QNP7PZqJy6OGXmZc1fiP_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QNP7PZqJy6OGXmZc1fiP_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "WuIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MM2CztTXvV5i9K2i-2RGNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MM2CztTXvV5i9K2i-2RGNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "SeIX7YYByh-A-Biy9ZTm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "fuIX7YYByh-A-Biy-Jux"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6NkVutVoJ0m5j8aVYyp0Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6NkVutVoJ0m5j8aVYyp0Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "pmgX7YYBO2e_P_Qb-WKP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNwCIBshkIMvUtAdsIyUXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNwCIBshkIMvUtAdsIyUXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} {"create": {"_index": "profiling-events-5pow01", "_id": "xOIX7YYByh-A-Biy9pUg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AP5Eq7B7RisKC973OjTPaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AP5Eq7B7RisKC973OjTPaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "dOIX7YYByh-A-Biy95dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pp5lsGmp-JSx0DYM6KPKrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pp5lsGmp-JSx0DYM6KPKrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "s9gY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WSjAZWkrBfhyqCpr7c2wpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WSjAZWkrBfhyqCpr7c2wpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "r9gY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_9qdqX3M61Erctug7dsAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_9qdqX3M61Erctug7dsAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "1tgY7YYBBkbVtX3nI__a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pixjUDno8EQPnhCn1ap_SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pixjUDno8EQPnhCn1ap_SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "nOIY7YYByh-A-BiyI7hA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HYzllkhJBtq1_HQGHScByA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HYzllkhJBtq1_HQGHScByA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "pWgY7YYBO2e_P_QbNIap"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ium0M6gtUd_sKOi4qCX1xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ium0M6gtUd_sKOi4qCX1xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "itkY7YYBBkbVtX3nVRw-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8mfKeyebNB7GEjVrotPPKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8mfKeyebNB7GEjVrotPPKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "7NkY7YYBBkbVtX3nUxhO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcrgeMf65aey4TtBdOxT8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcrgeMf65aey4TtBdOxT8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "CWgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "2GgY7YYBO2e_P_QbcsmS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Oqbb6FakSaKBSmm5UVh16A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Oqbb6FakSaKBSmm5UVh16A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "6uIY7YYByh-A-BiyZPvI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ESbYg3aZAaH86uOl-CijNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ESbYg3aZAaH86uOl-CijNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "B2gY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-Q9uCXR-TIx0LsEoXVwIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-Q9uCXR-TIx0LsEoXVwIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "BmgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8nNNC34bhCi_Q3XemgSrmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8nNNC34bhCi_Q3XemgSrmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "BWgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HPhJ76yG2kEeQYFKH7p-MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HPhJ76yG2kEeQYFKH7p-MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "buMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3PF9c3wvWuSHWSRQ7lpy-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3PF9c3wvWuSHWSRQ7lpy-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "cOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8wMbNj2bmC_k-f1sjP1tvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "22781733237518"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8wMbNj2bmC_k-f1sjP1tvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "22781733237518"} {"create": {"_index": "profiling-events-5pow01", "_id": "i2gY7YYBO2e_P_Qbk-kz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wBeDaSzmKMf_8mF4P9fF3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wBeDaSzmKMf_8mF4P9fF3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "OWgY7YYBO2e_P_Qbk-uk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IdBN0EzRB0f6Qp7f7scKtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IdBN0EzRB0f6Qp7f7scKtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "NdkY7YYBBkbVtX3nlFIk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L3YM-JzWQGZBl6Hww0qX5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L3YM-JzWQGZBl6Hww0qX5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "C2gY7YYBO2e_P_QblO3v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I5V2d7T-ngpDaQd5S4eJBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I5V2d7T-ngpDaQd5S4eJBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "gNkY7YYBBkbVtX3nkU6M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJHpr4fLpWoSKqg-aUPBfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJHpr4fLpWoSKqg-aUPBfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "lmgY7YYBO2e_P_QbkeIU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yVg35eksppyHad0lI1eXKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yVg35eksppyHad0lI1eXKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "a2gY7YYBO2e_P_Qbr_2i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "89kY7YYBBkbVtX3nsW-T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["twP61I8BoQSVRAEu87hitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["twP61I8BoQSVRAEu87hitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "UNkY7YYBBkbVtX3npWjG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "iGkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IsqdVnLNhl2x75Zl1gQDqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IsqdVnLNhl2x75Zl1gQDqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "pWkY7YYBO2e_P_QbwxTd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EMABXmd9W1xztmohmhT4jw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EMABXmd9W1xztmohmhT4jw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "f9kY7YYBBkbVtX3n8ay8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdOVIhl_n9Wje--mxIItNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdOVIhl_n9Wje--mxIItNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "a-MY7YYByh-A-Biy8WB6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vFkcrQtWCVTfQjjlGu2S_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vFkcrQtWCVTfQjjlGu2S_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "82kY7YYBO2e_P_Qb8z_u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YEm7NQBrTH5QHQqIE3fDrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YEm7NQBrTH5QHQqIE3fDrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "GtkY7YYBBkbVtX3n7qav"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "rOMY7YYByh-A-Biy8mGJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YQv8Jjxrz6pIHbJnxDZTDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YQv8Jjxrz6pIHbJnxDZTDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "s2kZ7YYBO2e_P_QbH1uL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AG_6FvO14ax3UdwVieto8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AG_6FvO14ax3UdwVieto8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "J-MZ7YYByh-A-BiyIpJU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOL7hTlazWG39CR6gZV56w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOL7hTlazWG39CR6gZV56w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "BGkZ7YYBO2e_P_QbHVUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OtPO4_Cde7GWru30XAUPmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OtPO4_Cde7GWru30XAUPmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "deMZ7YYByh-A-BiyLpYf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m6Tpe3Eo4Y_x5AamWL8GLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m6Tpe3Eo4Y_x5AamWL8GLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "IN8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOolLKwTF6c0fdaMu4zrpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOolLKwTF6c0fdaMu4zrpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "gGUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qvp6aS0dEuRo-26h2BBtzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qvp6aS0dEuRo-26h2BBtzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "mWUU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "sNUU7YYBBkbVtX3nu_4f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CryaWZekzG3MnYg7CufHdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CryaWZekzG3MnYg7CufHdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "K98U7YYByh-A-Biyu8Z1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "498U7YYByh-A-BiyyM0P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0-Jsd5mQCWnt_-lPVIShHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0-Jsd5mQCWnt_-lPVIShHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "OtYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLT0UoH40niOjk-XVme7dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLT0UoH40niOjk-XVme7dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "GWUU7YYBO2e_P_QbvZyb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "H98U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["81I56QjbyDYSIFcetHM2Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["81I56QjbyDYSIFcetHM2Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "oN8U7YYByh-A-BiyusTM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hJqYLUumz9zXvS_kxlOwXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hJqYLUumz9zXvS_kxlOwXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "D2UU7YYBO2e_P_Qbvp4b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2ssOcOjXCCaYX7ZddtppA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2ssOcOjXCCaYX7ZddtppA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "5GUU7YYBO2e_P_QbwKIN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j_ZkdluVAC4IXHAbI6Pmjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j_ZkdluVAC4IXHAbI6Pmjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "Ot8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uFGWThWg3zgxDL3xxQAwYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uFGWThWg3zgxDL3xxQAwYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "oNYU7YYBBkbVtX3nvAI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28flplgbX9OoTxrrq9LhNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28flplgbX9OoTxrrq9LhNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-5pow01", "_id": "Md8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAJ3qCHOXo3xE1EGVnJuHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAJ3qCHOXo3xE1EGVnJuHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "Rd8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YRYK-waaBK93YQxC-u6FpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YRYK-waaBK93YQxC-u6FpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "4t8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PYBUfV4nZR3PAgyIKhIwDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PYBUfV4nZR3PAgyIKhIwDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "RN8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ypQufrPd-vWE7YGaekcTfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ypQufrPd-vWE7YGaekcTfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "JdYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PFbB3COAmbAYRaYoh99KYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PFbB3COAmbAYRaYoh99KYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "JtYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KaCen5lChBQlFEf5iOW4fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KaCen5lChBQlFEf5iOW4fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "498U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nixBByAIlNzP6S-DgkxohA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nixBByAIlNzP6S-DgkxohA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "198U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aUWb9UKO7qICsUy_ccfdaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aUWb9UKO7qICsUy_ccfdaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "N98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4f9KZiG-idTZu0O-sRt4aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4f9KZiG-idTZu0O-sRt4aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "Od8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nyNpeOOTv9ufpl_gGUbV4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nyNpeOOTv9ufpl_gGUbV4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "R98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k9vLKRFLFVoj2RZU6JVbBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k9vLKRFLFVoj2RZU6JVbBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "Ot8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n-PAN0ssaXvJ6kY18i9tog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n-PAN0ssaXvJ6kY18i9tog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "QN8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ROD9hyXKyG1xyIp3eNp24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ROD9hyXKyG1xyIp3eNp24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "I9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SOHLJ-nmGdCO3sK7plOv2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SOHLJ-nmGdCO3sK7plOv2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "CdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uh-jwsuxuUYFlAJ62euRwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uh-jwsuxuUYFlAJ62euRwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "JNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["87tmMz7dkdhga3ssbWBSBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["87tmMz7dkdhga3ssbWBSBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "FtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mxx8ugWwWszTb7eJBegR_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mxx8ugWwWszTb7eJBegR_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "GNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lBjHl88ojwoksS7PDXJBaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lBjHl88ojwoksS7PDXJBaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "J9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZSMUUx94Y3yXU6mhbsNCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZSMUUx94Y3yXU6mhbsNCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "N9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHUuby7f7Ki-mhiDAG_3SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHUuby7f7Ki-mhiDAG_3SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "EtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-f-8xMNzAVnOWhCPzAg7Cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-f-8xMNzAVnOWhCPzAg7Cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "FdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rmqpLjKhFVehwbUcabYxkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rmqpLjKhFVehwbUcabYxkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "xOAV7YYByh-A-BiyCx2w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r1HvPKUhWfo1c_dGIcqb1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r1HvPKUhWfo1c_dGIcqb1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YeAV7YYByh-A-BiyChop"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "aOAV7YYByh-A-BiyCBR8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y2rsoafmE6xytYWP5sYCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "66636157595941"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y2rsoafmE6xytYWP5sYCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "66636157595941"} {"create": {"_index": "profiling-events-5pow01", "_id": "MNYV7YYBBkbVtX3nCFPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EM9AISJikuWZSi4uSs5f_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EM9AISJikuWZSi4uSs5f_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "8eAV7YYByh-A-BiyDB-G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kOsAFOokw3TMOocYazB7hA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kOsAFOokw3TMOocYazB7hA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "QdYV7YYBBkbVtX3nDFzR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dOsagAt-XXDxs5XGCBbstQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dOsagAt-XXDxs5XGCBbstQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "yeAV7YYByh-A-BiyGSzz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L7NiiM2JcpyLYptGtnS-lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L7NiiM2JcpyLYptGtnS-lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "cmUV7YYBO2e_P_QbG-2M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "EGUV7YYBO2e_P_QbG-_2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "8dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5ViqVqqhAWPiT5DHT3ocA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5ViqVqqhAWPiT5DHT3ocA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "6dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uk1ygFuU89LLnNUfPAM8KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uk1ygFuU89LLnNUfPAM8KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "7tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yI2e6HYAfhTSJaxYuulCOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yI2e6HYAfhTSJaxYuulCOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "q2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "r2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDmVY9Mljfrd1uHcDiDp-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDmVY9Mljfrd1uHcDiDp-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "pWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xVGi1i7nlJYkT__QgtZrJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xVGi1i7nlJYkT__QgtZrJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "adYV7YYBBkbVtX3nV7Q-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hbFdZ00lApIoSJEOlowBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hbFdZ00lApIoSJEOlowBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "GmYV7YYBO2e_P_QbWBoF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIUTQnmo7hmDvvAn77UZ1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIUTQnmo7hmDvvAn77UZ1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "RdYV7YYBBkbVtX3nWrnf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T7kTFHjAtS6OtzybnvJ0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T7kTFHjAtS6OtzybnvJ0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "RWYV7YYBO2e_P_QbWSCB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Sz3zcn_jRruHSw5ug1i1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Sz3zcn_jRruHSw5ug1i1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "uuAV7YYByh-A-BiyWmE2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "mtYV7YYBBkbVtX3nWreQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ENrq2_MBwld_ERQVMIbQlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ENrq2_MBwld_ERQVMIbQlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "4mYV7YYBO2e_P_QbWyOI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q_0hpIuT4vi1WRoDxA9V3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q_0hpIuT4vi1WRoDxA9V3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "OmYV7YYBO2e_P_QbWBy9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "1GYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPAXeu9JRh62VS0TzctJEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPAXeu9JRh62VS0TzctJEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "4WYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "12YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5NYvRSd87djiQAuRZMHZrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5NYvRSd87djiQAuRZMHZrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "2GYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfmPxWX0umuPnDn2aoiurQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfmPxWX0umuPnDn2aoiurQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "idYV7YYBBkbVtX3nheD0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NHR-vq-GiKf-T9dij8d0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NHR-vq-GiKf-T9dij8d0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "69YV7YYBBkbVtX3nh-MJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5hO63TnTaHm6rWDJ9tLlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5hO63TnTaHm6rWDJ9tLlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "f-AV7YYByh-A-Biyo5MJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZfWmwYaJIIOUGCRBPlr6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZfWmwYaJIIOUGCRBPlr6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "99YV7YYBBkbVtX3nlvPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R5Cb48qStI1GlPaxKWm-mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R5Cb48qStI1GlPaxKWm-mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "29cV7YYBBkbVtX3nsw-Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "PGYV7YYBO2e_P_Qbsmir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JB6F-U_ns7SY5JIwmO_kFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JB6F-U_ns7SY5JIwmO_kFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "KGYV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "1tcV7YYBBkbVtX3n1C00"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dJyQsD0pMmiwvo0yyQz-Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dJyQsD0pMmiwvo0yyQz-Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YuAV7YYByh-A-Biy08WI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "aWYV7YYBO2e_P_Qb44h4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXgAgM2hMcqzn0fnoAoP0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXgAgM2hMcqzn0fnoAoP0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "NWYW7YYBO2e_P_QbBq4R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VAqxR_4yWhELwHpSX2G6ng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VAqxR_4yWhELwHpSX2G6ng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "zNcW7YYBBkbVtX3nBWBe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRG5hVGVXLYVZ0h02g0udQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRG5hVGVXLYVZ0h02g0udQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "hGYW7YYBO2e_P_QbFL_E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vwNl340FtK4II3OTHfAxDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vwNl340FtK4II3OTHfAxDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ktcW7YYBBkbVtX3nFmxw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H0WY9BQOdRjXYQkYwkFdgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H0WY9BQOdRjXYQkYwkFdgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ceEW7YYByh-A-BiyEwXO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "G2YW7YYBO2e_P_QbEbqq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ig2MzPdh_XK7em8mWoJag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ig2MzPdh_XK7em8mWoJag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "XtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qDjdfIDXYeKpMzfOZsKweg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qDjdfIDXYeKpMzfOZsKweg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "WNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t4n19LeK4zvHCEOuBRHoDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t4n19LeK4zvHCEOuBRHoDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "W9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch2MeEpHv6ftyPFPGwDJPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch2MeEpHv6ftyPFPGwDJPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "WdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "amYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PR0G3Br-iqix1uCUZkKS_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PR0G3Br-iqix1uCUZkKS_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "DNcW7YYBBkbVtX3nU5wV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JK8YqiAWSqqVOym-FM3Bcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JK8YqiAWSqqVOym-FM3Bcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "FuEW7YYByh-A-Biyclc2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8m9XmKLa72WdntoQwSY0tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8m9XmKLa72WdntoQwSY0tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-5pow01", "_id": "z9cW7YYBBkbVtX3nkscq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bbBiaFslvpreG7iqHkAtng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bbBiaFslvpreG7iqHkAtng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "_-EW7YYByh-A-BiyknyA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ul5WCOLuBGGX66Anz_J-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ul5WCOLuBGGX66Anz_J-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZmcW7YYBO2e_P_QbjiKj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x4JagFwIYKM4hCWjdkk5Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x4JagFwIYKM4hCWjdkk5Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "JdcW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVY8nef_n-I9Q52QhyCFfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVY8nef_n-I9Q52QhyCFfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "F2cW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sl8hikPZI3gmfoj4I-xFMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sl8hikPZI3gmfoj4I-xFMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "HWcW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h0lEtzKJzcNxepmOT3KRtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h0lEtzKJzcNxepmOT3KRtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "ktcW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["txZXHAJurNaMIlI0kux2YA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["txZXHAJurNaMIlI0kux2YA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "09cW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MIjdoSZWUGoqrMkmoKBGPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MIjdoSZWUGoqrMkmoKBGPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "r9cW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "ttcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-vSsOv3oIeGq1jnkLewmKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-vSsOv3oIeGq1jnkLewmKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "q9cW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ir6dnl0cXTDA9lqUj6YdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ir6dnl0cXTDA9lqUj6YdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "tNcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YABibb_jw0z2mFZJ8rsBIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YABibb_jw0z2mFZJ8rsBIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "qtcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5L0Vg1E8eRaEol71UFTwGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5L0Vg1E8eRaEol71UFTwGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "qNcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rAVnotLNqZZX90k5rHuXLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rAVnotLNqZZX90k5rHuXLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-5pow01", "_id": "rtcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vQXtdmIzgIVlhx1gewz_RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vQXtdmIzgIVlhx1gewz_RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} {"create": {"_index": "profiling-events-5pow01", "_id": "8tcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "KGcW7YYBO2e_P_Qbzlaj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VVLBSGTuYWH3O356lNUySg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VVLBSGTuYWH3O356lNUySg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "oeEW7YYByh-A-Biy38Nt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LU5M-Y2vAZAPnKMCmcDaJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LU5M-Y2vAZAPnKMCmcDaJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-5pow01", "_id": "z2cW7YYBO2e_P_Qb3FtF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "BdgW7YYBBkbVtX3n4AZK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6DJ5rUntpH_kTGPTanZjBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6DJ5rUntpH_kTGPTanZjBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "zOEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zqEaDpKRyJAOpyXtzl9UkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zqEaDpKRyJAOpyXtzl9UkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "wdgX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "umcX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["udcCD1ZwYlzlR2BrHqM6qQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["udcCD1ZwYlzlR2BrHqM6qQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "NWcX7YYBO2e_P_QbDZHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "z-EX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja9MBlCW9JbhLw8tshjLeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja9MBlCW9JbhLw8tshjLeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "HNgX7YYBBkbVtX3nOTzV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "I-IX7YYByh-A-BiyPQuP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "uuIX7YYByh-A-BiyPQlQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "iWcX7YYBO2e_P_QbOrtJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "mOIX7YYByh-A-BiySRIf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nobPGa726Uz_QIRAEzxZhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nobPGa726Uz_QIRAEzxZhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "uGcX7YYBO2e_P_QbXN28"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P-dCdUT1LEJyae6UYwKugg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P-dCdUT1LEJyae6UYwKugg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "DWcX7YYBO2e_P_QbZ-K9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "AtgX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YjeaaN9Gs9Jtblq8lj7A3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YjeaaN9Gs9Jtblq8lj7A3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "BtgX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ArhssJT4V7-kmdsezZTRQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ArhssJT4V7-kmdsezZTRQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ANgX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w4AKEYruYsyRiuNl0wOumw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w4AKEYruYsyRiuNl0wOumw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "_dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcPyRyUca9zMz9MzDr7aHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcPyRyUca9zMz9MzDr7aHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "6OIX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wO6E0aI_adazJwV1nEPvow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wO6E0aI_adazJwV1nEPvow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "5-IX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0R9Tk_AwuvgNuleyrD0E-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0R9Tk_AwuvgNuleyrD0E-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "tWgX7YYBO2e_P_QbigJT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q9GZUSBL9TB0CdE5vyfEsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q9GZUSBL9TB0CdE5vyfEsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "6-IX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HFDZtj7y0Bw2k96K0Shk-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HFDZtj7y0Bw2k96K0Shk-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "0dgX7YYBBkbVtX3niXvV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Egb8M192QRouZ1YPjNwqmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Egb8M192QRouZ1YPjNwqmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "nmgX7YYBO2e_P_QbphZT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "6tgX7YYBBkbVtX3nt6W3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vOZHtJ4ahW-g2TWd1-Whrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vOZHtJ4ahW-g2TWd1-Whrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "79gX7YYBBkbVtX3nt6I3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ycogR2C1hH5eXGjaW9oPeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ycogR2C1hH5eXGjaW9oPeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "KmgX7YYBO2e_P_Qbtih6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pHFN0qaDz6OHVNs6LDyfew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pHFN0qaDz6OHVNs6LDyfew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "M2gX7YYBO2e_P_QbtSOl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RsVBVY52cTTp5FCtYm6r4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RsVBVY52cTTp5FCtYm6r4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "hNgX7YYBBkbVtX3nt6R3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pgb9JoAUQxoSCvdXn7xEkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pgb9JoAUQxoSCvdXn7xEkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "ENgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KBx8UMYQRpX3PQkFGueoQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KBx8UMYQRpX3PQkFGueoQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "EdgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3bvdBbzWBhiwCbUR097jxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3bvdBbzWBhiwCbUR097jxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "E9gX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cPi-mObQCSuLuQtVOYID8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cPi-mObQCSuLuQtVOYID8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "F9gX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GAokC6Zv-UfUvWotAYqkSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GAokC6Zv-UfUvWotAYqkSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "X-IX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wl2yvlpS90Ypoy2M-skpww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wl2yvlpS90Ypoy2M-skpww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "tmgX7YYBO2e_P_Qb6Vmk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MT3qrLXJyyFa5mMadoI1ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MT3qrLXJyyFa5mMadoI1ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "6tgX7YYBBkbVtX3n9-Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNA5mzQxt3Xt7EAz1m9YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNA5mzQxt3Xt7EAz1m9YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "SOIX7YYByh-A-Biy9ZTm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ess3oHhLNEi0m2JAz0_5DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ess3oHhLNEi0m2JAz0_5DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "qeIX7YYByh-A-Biy9ZKm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pt09Zvf__ya1eNp2CoZNVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pt09Zvf__ya1eNp2CoZNVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "p9gX7YYBBkbVtX3n9t6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ayg1IWi6ap3XN7RjaMkRog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ayg1IWi6ap3XN7RjaMkRog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "tdgY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0E7LlamNni9h1zgUjdYD5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0E7LlamNni9h1zgUjdYD5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "stgY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-49a_E8AcF9JV2D17KJ99g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-49a_E8AcF9JV2D17KJ99g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "G-IY7YYByh-A-BiyQttA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9hAbTICOesyJ3rX_TlmZDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9hAbTICOesyJ3rX_TlmZDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "JtkY7YYBBkbVtX3nUxcK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "hdkY7YYBBkbVtX3nVyEZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bredr3OvHQiC2uo7mFYNAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bredr3OvHQiC2uo7mFYNAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-5pow01", "_id": "M2gY7YYBO2e_P_QbY7vw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tT2A_bCpClAyR0rNtUXbYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tT2A_bCpClAyR0rNtUXbYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "GtkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_eN577uJw5hksIBqBf1iCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_eN577uJw5hksIBqBf1iCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "FeIY7YYByh-A-BiyZf0D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xTmXxLtxYtdjX3OFWgcBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xTmXxLtxYtdjX3OFWgcBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "3WgY7YYBO2e_P_QbZsEV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SuyrLdAGlB-Gqd6pTlCwTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SuyrLdAGlB-Gqd6pTlCwTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "3GgY7YYBO2e_P_QbZsEV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP6GIsw4ofWcnUGlBduuVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP6GIsw4ofWcnUGlBduuVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "3dkY7YYBBkbVtX3nkU_F"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UL06CNiVyxEFpIouFPRx3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UL06CNiVyxEFpIouFPRx3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "_-MY7YYByh-A-BiykRT_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_7SjIJ79HdCt2_IZxFKFsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_7SjIJ79HdCt2_IZxFKFsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "zWgY7YYBO2e_P_QbkeNU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sSkvuXEJhjIUI110bPCy-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sSkvuXEJhjIUI110bPCy-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "x2gY7YYBO2e_P_QbkN-c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1o1T1TIStxTZj-e2WTNkwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1o1T1TIStxTZj-e2WTNkwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "hWkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "jGkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-5pow01", "_id": "imkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LozaztVRNbKlSptg74c_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LozaztVRNbKlSptg74c_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "sdUU7YYBBkbVtX3nu_4f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QYRd432ews7Dx4JLAryfRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QYRd432ews7Dx4JLAryfRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "9t8U7YYByh-A-BiyusJq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxA4A64BqMWXOrNZbvu1iA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxA4A64BqMWXOrNZbvu1iA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "Kt8U7YYByh-A-Biyu8Z1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zI9JvucnvxyxLZyzixdcpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zI9JvucnvxyxLZyzixdcpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "DmUU7YYBO2e_P_Qbvp4b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bFqi88DUwWkr_8kK2-MSRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bFqi88DUwWkr_8kK2-MSRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "od8U7YYByh-A-BiyusTM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u26YAXespQsbQjR7YsAYzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u26YAXespQsbQjR7YsAYzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "XdYU7YYBBkbVtX3nyQUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPOCasATDNT8t_l-saejjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPOCasATDNT8t_l-saejjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "4t8U7YYByh-A-BiyyM0P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sWUvdmC1yhMffRymX3J_5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sWUvdmC1yhMffRymX3J_5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-5pow01", "_id": "9d8U7YYByh-A-BiyusJq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4u9WOOyrWYLdgsjOh9aCUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4u9WOOyrWYLdgsjOh9aCUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "n9YU7YYBBkbVtX3nvAI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dt_oZZ2sQo9aPZAJj8jPTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dt_oZZ2sQo9aPZAJj8jPTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "oNYU7YYBBkbVtX3nvwQZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lq2lfj5xkTFOSbFr4_BQ2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lq2lfj5xkTFOSbFr4_BQ2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "Od8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["an9gjQnke-IYWAGoKUs5KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["an9gjQnke-IYWAGoKUs5KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "gWUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zl3Lkb99x2SkFZzpGc0tBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zl3Lkb99x2SkFZzpGc0tBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-5pow01", "_id": "NdYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "Nt8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6eTapoSsPn6zyk1_cvguaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6eTapoSsPn6zyk1_cvguaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "M9YU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "K9YU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWfVfitdsTIFX4dhe6CakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWfVfitdsTIFX4dhe6CakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "MtYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s_LM4tNvgy4k7bBRfDcNqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s_LM4tNvgy4k7bBRfDcNqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "598U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jvc_WE7B1F8hMVB_gxFucA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jvc_WE7B1F8hMVB_gxFucA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "5t8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jxx94k5bF0AyU24TvMCnFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jxx94k5bF0AyU24TvMCnFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "6N8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sJC4CV2eRcloTSQEGQH29Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sJC4CV2eRcloTSQEGQH29Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "Mt8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AREehA9nDZJasQeEH6svQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AREehA9nDZJasQeEH6svQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "4d8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "EdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAgkJzf4StR0guQvtrxwfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAgkJzf4StR0guQvtrxwfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "MtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LrLWZ5bevl0fyb8pVLrxUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LrLWZ5bevl0fyb8pVLrxUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-5pow01", "_id": "KNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aFWcqgahvwzy1xUY69A0Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aFWcqgahvwzy1xUY69A0Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "HNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihAOVqchKA5mXlZP4M1IsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihAOVqchKA5mXlZP4M1IsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "DNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P_F4N85n6ygrRQ1ObfKSJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P_F4N85n6ygrRQ1ObfKSJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "_9YV7YYBBkbVtX3nCVTP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JI7mG0vgGSTA2uia9-1jSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JI7mG0vgGSTA2uia9-1jSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "hNYV7YYBBkbVtX3nC1gN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fmIQ76zzVZ9EWAQ55W78zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fmIQ76zzVZ9EWAQ55W78zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "reAV7YYByh-A-BiyBg34"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "kdYV7YYBBkbVtX3nBk4o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VrBz5ulfwdPTqnMaGIpcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VrBz5ulfwdPTqnMaGIpcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "q9YV7YYBBkbVtX3nClae"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SHi_az7OQcBjeyPt41wowA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "95381405781962"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SHi_az7OQcBjeyPt41wowA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "95381405781962"} {"create": {"_index": "profiling-events-5pow01", "_id": "NWUV7YYBO2e_P_QbGelA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yc_2GTJ_IVPE7f4u8QXDeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yc_2GTJ_IVPE7f4u8QXDeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-5pow01", "_id": "CWUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "MuAV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "aWUV7YYBO2e_P_QbGOeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bsoNX49ITduR-HMxcIbsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bsoNX49ITduR-HMxcIbsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "8NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PUKA7gaaH9QtcEUOhnkXBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PUKA7gaaH9QtcEUOhnkXBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "59YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8THUiHTgWMDGXf1IWeY26Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8THUiHTgWMDGXf1IWeY26Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "69YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "pmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RSnmXvhjCRc5PWjsdxAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RSnmXvhjCRc5PWjsdxAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "rWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96zUk00wJUkz6pqWJ4UVBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96zUk00wJUkz6pqWJ4UVBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "s2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "79YV7YYBBkbVtX3nVq52"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOgvcGNEugo-q4Mte_An1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOgvcGNEugo-q4Mte_An1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "TNYV7YYBBkbVtX3nVrHh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oSCp9cFxZ1aVa9L0c22cCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oSCp9cFxZ1aVa9L0c22cCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "CWYV7YYBO2e_P_QbWyI8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z5-B-mtdUNg5G8Toj1uZ9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z5-B-mtdUNg5G8Toj1uZ9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "ueAV7YYByh-A-BiyVV6i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "2mYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GdQO73uJU5ltMBM9sQEM4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GdQO73uJU5ltMBM9sQEM4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "3mYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C6VUfIIv3MbNvll1xucbVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C6VUfIIv3MbNvll1xucbVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "dOAV7YYByh-A-BiyhoV2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8bQaxjHqOXy8jFaY6w3jpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8bQaxjHqOXy8jFaY6w3jpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "GdcV7YYBBkbVtX3npQJl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "SdYV7YYBBkbVtX3nmPtF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "2OAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p0Pia_VKvNIpcjOrg5PBFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p0Pia_VKvNIpcjOrg5PBFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "5WYV7YYBO2e_P_Qbsmn3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g-gvaAwuroQsfSOFcGq40g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g-gvaAwuroQsfSOFcGq40g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "hWYV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nON9RmBx4umF5B_Of_VNaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nON9RmBx4umF5B_Of_VNaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "LWYV7YYBO2e_P_QbtnB5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B3eCfffgWDywlQf98in5qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B3eCfffgWDywlQf98in5qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "7NcV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "7dcV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YxRH1n6rM_I4hLiGtKmVMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YxRH1n6rM_I4hLiGtKmVMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "a9cV7YYBBkbVtX3nsgwF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "J2YV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPIMyWIkL5MsP1Bo20O32w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPIMyWIkL5MsP1Bo20O32w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "Y-AV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bc6HNlC8Sl7niDN9L84HCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bc6HNlC8Sl7niDN9L84HCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "jdcV7YYBBkbVtX3ntBPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8P7h-qet3p603NxSf5JWTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8P7h-qet3p603NxSf5JWTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "umYV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "HeAV7YYByh-A-Biy0sIJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Aq5HJRyvf0tzNYqtMdi3JQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Aq5HJRyvf0tzNYqtMdi3JQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZtcV7YYBBkbVtX3nxSih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UcSfB9O7oaCsfgTNqnRSmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UcSfB9O7oaCsfgTNqnRSmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "sdcV7YYBBkbVtX3nxil1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eyk6myVZ88WlljRICe8V4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eyk6myVZ88WlljRICe8V4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "BtcV7YYBBkbVtX3nxSdX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Iz58o69gDrMyQIJrUSlESQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Iz58o69gDrMyQIJrUSlESQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} {"create": {"_index": "profiling-events-5pow01", "_id": "puAV7YYByh-A-Biy5dxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cfo59YpRKB0q5iQSQJ-VYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cfo59YpRKB0q5iQSQJ-VYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "TtcV7YYBBkbVtX3n5UgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vMqHPs9EIjuvSlEC4OiiMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vMqHPs9EIjuvSlEC4OiiMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "y9cW7YYBBkbVtX3nBF4I"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JVsUuzDWhnoUag5GJcXYzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JVsUuzDWhnoUag5GJcXYzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "VWYW7YYBO2e_P_QbBawR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fMEGhVur8bO2mv1boqOVuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fMEGhVur8bO2mv1boqOVuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "_eAW7YYByh-A-BiyAekA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8y9d1sq41lKVZ2hOEtOWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8y9d1sq41lKVZ2hOEtOWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-5pow01", "_id": "e9cW7YYBBkbVtX3nEmVM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "V9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPnyqYXZ1627cOAm3Dt4dA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPnyqYXZ1627cOAm3Dt4dA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} {"create": {"_index": "profiling-events-5pow01", "_id": "WtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWQBAJ7ncYkjoV_tk9YLSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWQBAJ7ncYkjoV_tk9YLSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "U9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["muSA4-3A5tqLjcddDaeDBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["muSA4-3A5tqLjcddDaeDBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "GNcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "YuEW7YYByh-A-BiyQi3_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AePsFsEWIAKcD6i5fTcKwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AePsFsEWIAKcD6i5fTcKwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "iOEW7YYByh-A-BiyPiek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BgnwfcudspKPFADqFnojuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BgnwfcudspKPFADqFnojuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-5pow01", "_id": "19cW7YYBBkbVtX3nRZBy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I9TiskxOBE6uewdlBEfbaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I9TiskxOBE6uewdlBEfbaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "HuEW7YYByh-A-BiyUD3c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OYGXc31yJI5bR-H2iNSwHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OYGXc31yJI5bR-H2iNSwHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "LWYW7YYBO2e_P_QbUvPO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FpeKkethPGO1uv-qrij4uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FpeKkethPGO1uv-qrij4uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "COEW7YYByh-A-BiyUkAI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oF-wY_acT328qrZ3ugnkzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oF-wY_acT328qrZ3ugnkzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "_9cW7YYBBkbVtX3nYqmZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y4O8D8hxYkhFJTYN-hGU2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y4O8D8hxYkhFJTYN-hGU2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "rOEW7YYByh-A-BiyfmHu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYsp1kqfqOBHCXrvmwLiMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYsp1kqfqOBHCXrvmwLiMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "PtcW7YYBBkbVtX3nfbnk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zrel0O8Eu19Ixn8b1A7RrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zrel0O8Eu19Ixn8b1A7RrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "xOEW7YYByh-A-Biyclr3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ6jYHQvenC-oIeTFn_Bnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ6jYHQvenC-oIeTFn_Bnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "LOEW7YYByh-A-BiyfmAg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q0wzD6Wsaoym2okQ8aY53w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q0wzD6Wsaoym2okQ8aY53w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-5pow01", "_id": "EtcW7YYBBkbVtX3nkcYc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-o7jkJLtD4xHchcLgzNuuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-o7jkJLtD4xHchcLgzNuuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "nuEW7YYByh-A-BiykHkK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "JtcW7YYBBkbVtX3njsRV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "HeEW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eXATor8dtVm3LPIO_GNaZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eXATor8dtVm3LPIO_GNaZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "1dcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ofbkGVhqHskFPiKv2X28g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ofbkGVhqHskFPiKv2X28g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "auEW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bQ285knnYYuMww0WgMbI2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bQ285knnYYuMww0WgMbI2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "mOEW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cn2EpQqWCVem8DknXDkNXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cn2EpQqWCVem8DknXDkNXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "pGcW7YYBO2e_P_QboDfC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "5-EW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ewqZUIOmU9Ti-nJquCY7dQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ewqZUIOmU9Ti-nJquCY7dQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "yGcW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxLFvg4n6uQItdMk3gw_xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxLFvg4n6uQItdMk3gw_xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "99cW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "a-EW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "W9cW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4I-ntmDjAgUXJfwbuBJNjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4I-ntmDjAgUXJfwbuBJNjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "5uEW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-EkvDjA94Zr1iIohgty7mQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-EkvDjA94Zr1iIohgty7mQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "1NcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KPRFk1hV8cCqMtGiVI0VUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KPRFk1hV8cCqMtGiVI0VUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "QNcW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ay1JvUpYidc_jtVVQh5xJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ay1JvUpYidc_jtVVQh5xJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "htcW7YYBBkbVtX3nrNrT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7pCWL6qVpk6cdOVpmC7rqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7pCWL6qVpk6cdOVpmC7rqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "rNcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5ssUCcghlPpbufn_FI7lhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5ssUCcghlPpbufn_FI7lhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "stcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NWTYSV7vGDryRONnCUqo1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NWTYSV7vGDryRONnCUqo1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "qdcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yg3TaXRMJTka-oF2wrTuxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yg3TaXRMJTka-oF2wrTuxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-5pow01", "_id": "7tcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nd9rMkTYCiObUWdQEYWSwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nd9rMkTYCiObUWdQEYWSwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "EeEW7YYByh-A-Biy0Llz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YDo1NT9KzNVeSq1G9W3WWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YDo1NT9KzNVeSq1G9W3WWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-5pow01", "_id": "7dcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TwmBGtDgORQiem0fqXxYUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TwmBGtDgORQiem0fqXxYUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "39gW7YYBBkbVtX3n3QKs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hD5xCbVnNYVsLiq_B3NCDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hD5xCbVnNYVsLiq_B3NCDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "wuEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "jmcX7YYBO2e_P_QbDpI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IcP7NfEq0GawQQCHmZWcRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IcP7NfEq0GawQQCHmZWcRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "g2cX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UUY2L_ithWPFsPGJM4Kw3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UUY2L_ithWPFsPGJM4Kw3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "deEX7YYByh-A-BiyDedc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ra9c-heZALvJmUxSmzUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ra9c-heZALvJmUxSmzUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "VNgX7YYBBkbVtX3nCiNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "j-EX7YYByh-A-BiyC-Nk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OqL1jazxhGNp3BmuN0BL6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OqL1jazxhGNp3BmuN0BL6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "kmcX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["scKMoVIbbsXT0ePI0cAHEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["scKMoVIbbsXT0ePI0cAHEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "7OEX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4cstsRMDoVu7vb1ZvH1EzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4cstsRMDoVu7vb1ZvH1EzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "zeEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "-2cX7YYBO2e_P_QbScad"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "d2cX7YYBO2e_P_QbS8t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mnK-jqHbwNjcoomJsw59gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mnK-jqHbwNjcoomJsw59gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "CeIX7YYByh-A-BiySRTi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hYfhfJkQW17kyXXXeL8OrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hYfhfJkQW17kyXXXeL8OrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "utgX7YYBBkbVtX3nSkv7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "7GcX7YYBO2e_P_QbSMNE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "h-IX7YYByh-A-BiyXSiy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-BNILa1SCuDbNciG6XDXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-BNILa1SCuDbNciG6XDXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "B9gX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EJns5ogzEJmEGiD1xX0ydA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EJns5ogzEJmEGiD1xX0ydA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "AdgX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pWn_IRU-j_6Nwh-gfuAqfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pWn_IRU-j_6Nwh-gfuAqfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-5pow01", "_id": "6uIX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fDQou-XRb52d9gCJh1ayOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fDQou-XRb52d9gCJh1ayOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "L-IX7YYByh-A-BiyhkDE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cELS3s9xDUsfqdE_Tc5Ajw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cELS3s9xDUsfqdE_Tc5Ajw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "JtgX7YYBBkbVtX3nmo13"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "K2gX7YYBO2e_P_QbnBSM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ymCCYcwH_madRuyjsHk5Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ymCCYcwH_madRuyjsHk5Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "zOIX7YYByh-A-BiyqF8h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QDgIPJ6K1Rf5OSw95yEFHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QDgIPJ6K1Rf5OSw95yEFHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "nWgX7YYBO2e_P_QbuSt5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mpjzTZhXzUC8aYg4OfeyGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mpjzTZhXzUC8aYg4OfeyGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-5pow01", "_id": "TtgX7YYBBkbVtX3nuKdo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ngVpwVwgO4T6nb-06wRKNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ngVpwVwgO4T6nb-06wRKNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "FNgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZiZ1khLLMUzgYWGwfUZAEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZiZ1khLLMUzgYWGwfUZAEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "GNgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QOIxcZGbFuLnj5qiY-JZYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QOIxcZGbFuLnj5qiY-JZYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "EtgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vthGd_F8mbZA2w12Nf4aww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vthGd_F8mbZA2w12Nf4aww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "FtgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "WeIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["23D9WYWWXJZPmgi3LP_trA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["23D9WYWWXJZPmgi3LP_trA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "zWgX7YYBO2e_P_Qb5lIe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GQY3N3qZRu8haCsdxq571g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GQY3N3qZRu8haCsdxq571g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "W-IX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2qSXnyvIGQkSNpP5wPgdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2qSXnyvIGQkSNpP5wPgdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} {"create": {"_index": "profiling-events-5pow01", "_id": "8dgX7YYBBkbVtX3n6NKY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XdZpM6-FAy3LMUV5bnSRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XdZpM6-FAy3LMUV5bnSRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "WOIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nwg53akFiAKZJpHiqCwAbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nwg53akFiAKZJpHiqCwAbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "E9gX7YYBBkbVtX3n589_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OoPYq5Aw6d1wKTV_c9_UOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OoPYq5Aw6d1wKTV_c9_UOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "UdgX7YYBBkbVtX3n-ONq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "QtgX7YYBBkbVtX3n-eUA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "udgX7YYBBkbVtX3n9dtq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "-eIX7YYByh-A-Biy95jl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ocpdZMlRpRzCexEbOVmPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ocpdZMlRpRzCexEbOVmPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "qNgX7YYBBkbVtX3n9t6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dnsAy4vOc46KZJiS5dGT6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dnsAy4vOc46KZJiS5dGT6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "6NgX7YYBBkbVtX3n-eZU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AuqG1b42cXBbKiNJcLaKpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AuqG1b42cXBbKiNJcLaKpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "sNgY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yrneF8Y5HnZdPRsa0iSPNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yrneF8Y5HnZdPRsa0iSPNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "sdgY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFJedgXKyIDWLxlCPDwfQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFJedgXKyIDWLxlCPDwfQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "tNgY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FTqEftgEgF-4HalIRfrGpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FTqEftgEgF-4HalIRfrGpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "OeIY7YYByh-A-BiyKMx-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yyOgLKUZuSQUa5BkL2jvpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yyOgLKUZuSQUa5BkL2jvpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "VmgY7YYBO2e_P_QbI30D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6EgKcwZlK3OnMM95lvD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6EgKcwZlK3OnMM95lvD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "XtkY7YYBBkbVtX3nQwsG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "X2gY7YYBO2e_P_QbN44i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v3iq4oJQ3VCG0e1sWoxtkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v3iq4oJQ3VCG0e1sWoxtkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "aGgY7YYBO2e_P_QbVarR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4atjVCntPFZjlZxUD6MXCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4atjVCntPFZjlZxUD6MXCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "LtkY7YYBBkbVtX3nVR6C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "V-IY7YYByh-A-BiyVPT1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["09uypqtTxXlIuIrZVnABBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["09uypqtTxXlIuIrZVnABBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "HdkY7YYBBkbVtX3nVyNb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "jGgY7YYBO2e_P_QbZcCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-iLOmSM0bOvxtv9W5h6VXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-iLOmSM0bOvxtv9W5h6VXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "z9kY7YYBBkbVtX3nZSze"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AnUv3QN_2ZayAg10mwHHFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AnUv3QN_2ZayAg10mwHHFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "LmgY7YYBO2e_P_QbccJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TkPEPsUQlEC8_tTSu1y8wA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TkPEPsUQlEC8_tTSu1y8wA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "KmgY7YYBO2e_P_QbccS0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2p1e_ByfhPk84t_WqyZsxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2p1e_ByfhPk84t_WqyZsxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "auMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4qft_3sVVVVKL2SEz3KAyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4qft_3sVVVVKL2SEz3KAyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "b-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["htfRCBFoc4VoJwgN8Ytl-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["htfRCBFoc4VoJwgN8Ytl-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "bOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epUUcKloArUaO4HmSd6-0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epUUcKloArUaO4HmSd6-0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "7GgY7YYBO2e_P_QbkuQ0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "auMY7YYByh-A-Biykhac"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2uW4N0T57kNGJTVG5_1zTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2uW4N0T57kNGJTVG5_1zTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "E2gY7YYBO2e_P_Qbr__k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jUHt5y4xbMsVQ2NY3U5mxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jUHt5y4xbMsVQ2NY3U5mxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "h2kY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vRLmJJNBX8J2JJ9imi8dPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vRLmJJNBX8J2JJ9imi8dPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "xNkY7YYBBkbVtX3ns3bd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "E2kY7YYBO2e_P_QbvwpB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4BeOljMY7zmWSgDKbEbSZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4BeOljMY7zmWSgDKbEbSZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "G-MY7YYByh-A-BiywDft"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "R9kY7YYBBkbVtX3n3pnE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SMLewOlFeXmKZa6xL_ARDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SMLewOlFeXmKZa6xL_ARDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "79kY7YYBBkbVtX3n05Ok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "89kY7YYBBkbVtX3n4Z2b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c2STw5Dy59fyAI6ZtoR41g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c2STw5Dy59fyAI6ZtoR41g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "ueMY7YYByh-A-Biy00jx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "E9kY7YYBBkbVtX3n8Kqc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3MSb55knyBJ7ClwjPXRNwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3MSb55knyBJ7ClwjPXRNwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-5pow01", "_id": "BmkY7YYBO2e_P_Qb7zcM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PicgGG7wbtdmW_0WJKDC-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PicgGG7wbtdmW_0WJKDC-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "SGkY7YYBO2e_P_Qb8Tn-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wu5Ui6X1wYCeANyAsyHaFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wu5Ui6X1wYCeANyAsyHaFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "cGkY7YYBO2e_P_Qb7jUi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tU6VK5zLihoNeJDRhxPnUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tU6VK5zLihoNeJDRhxPnUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "MOMY7YYByh-A-Biy8V8f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "1mkY7YYBO2e_P_Qb8jzI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pZUry6bTXYygY6NfqwYQNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pZUry6bTXYygY6NfqwYQNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "9dkZ7YYBBkbVtX3nALZa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CWudsqc4YRwwO2UAdX1LxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CWudsqc4YRwwO2UAdX1LxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ndkZ7YYBBkbVtX3nAbtG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "meMY7YYByh-A-Biy_2g-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "yGkZ7YYBO2e_P_QbH1kG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7kg8w388m41akTi9Kihyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7kg8w388m41akTi9Kihyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ytkZ7YYBBkbVtX3nIM9b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qD_J237PVcJWQeJzWEaj4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qD_J237PVcJWQeJzWEaj4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "x-MZ7YYByh-A-BiyH4VG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "TGkZ7YYBO2e_P_QbMXJP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-5pow01", "_id": "odYU7YYBBkbVtX3nvwQZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "odYU7YYBBkbVtX3nvAI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ieFm4DhmWNYMrTtIZLOTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ieFm4DhmWNYMrTtIZLOTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "f98U7YYByh-A-Biyvcco"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YZ6XTwSTsk_RRpTARdTTcg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YZ6XTwSTsk_RRpTARdTTcg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "vd8U7YYByh-A-Biyyc_A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F4DUvBkuKPYx1hCGNzwitQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F4DUvBkuKPYx1hCGNzwitQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "I98U7YYByh-A-Biyv8mc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XQeY0u1F2xnHmZQvstPXhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XQeY0u1F2xnHmZQvstPXhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "vN8U7YYByh-A-Biyyc_A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FN29r4iQqyKpKryFAHklNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FN29r4iQqyKpKryFAHklNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "OdYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "Id8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "mGUU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MWvxOIZDGq4hR0RiTlBjWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MWvxOIZDGq4hR0RiTlBjWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-5pow01", "_id": "4N8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iLTslrGORIyXKfkvn0rVCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iLTslrGORIyXKfkvn0rVCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "MN8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ccyeq1IpEdYyyzMGVkI9Ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ccyeq1IpEdYyyzMGVkI9Ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "MNYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28hHkhN7jPc2yLRpJAYfPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28hHkhN7jPc2yLRpJAYfPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "L98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_j480Qg9v5TNK0lQGeFwAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_j480Qg9v5TNK0lQGeFwAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "Pt8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8AqERkmGja0aVhFHauF_yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8AqERkmGja0aVhFHauF_yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "Rt8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2bYjKMpMW5W361PJ9SbEqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2bYjKMpMW5W361PJ9SbEqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "ytYU7YYBBkbVtX3n1xTG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "t2UU7YYBO2e_P_Qb3r-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dPivlAC6aaFKRkKmSRwlpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dPivlAC6aaFKRkKmSRwlpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "NtYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96g1R53V5QyPuXTUHSgw4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96g1R53V5QyPuXTUHSgw4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "J9YU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F_APHoeVxOWNqwDMoBgBUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F_APHoeVxOWNqwDMoBgBUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "NN8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kCi3XJtF81OLZhjrXcqzHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kCi3XJtF81OLZhjrXcqzHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "5N8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsjTmLeWgJZGEXKE2bGYPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsjTmLeWgJZGEXKE2bGYPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "H9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eg4GNNub3CPns1G5g2R71w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eg4GNNub3CPns1G5g2R71w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "ItYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FMEbu46FVTF9FY-0Ogn2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FMEbu46FVTF9FY-0Ogn2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "LtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnpinE4u8LaMWLZMBdXuZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnpinE4u8LaMWLZMBdXuZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "M9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wOaaLLn26MWCq1Ch7gi66A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wOaaLLn26MWCq1Ch7gi66A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "NdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hLX1mOZZ4FB_ggjZCD1W-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hLX1mOZZ4FB_ggjZCD1W-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "NtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o3FJgYr9HoLMDfWyiRLq9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o3FJgYr9HoLMDfWyiRLq9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "ENYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFwqiF8mGnNqqISBiOraPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFwqiF8mGnNqqISBiOraPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "E9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbnTibb7iUG5Z59b5ewlIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbnTibb7iUG5Z59b5ewlIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "HdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pkyFSPLbfCpCJS0cbldBzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pkyFSPLbfCpCJS0cbldBzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "F9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eviuUsW23vOjlBWQv0bv5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eviuUsW23vOjlBWQv0bv5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "MdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aRGWrc208dGoT33fGEbwGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aRGWrc208dGoT33fGEbwGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "SWUV7YYBO2e_P_QbDOFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLtkTN9H0P9GQGUpxzaGrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLtkTN9H0P9GQGUpxzaGrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "6OAV7YYByh-A-BiyHC88"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hSPX2ocR_Ka7dmSG_0BvUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hSPX2ocR_Ka7dmSG_0BvUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "rWUV7YYBO2e_P_QbF-XY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HBL0k7Q3NY1Rzs8CB4mIaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HBL0k7Q3NY1Rzs8CB4mIaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "39YV7YYBBkbVtX3nF2Us"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uVz8NwCzYiroPS8zol4cYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uVz8NwCzYiroPS8zol4cYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "k-AV7YYByh-A-BiyFSTD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "89YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7WB2ChzRmsP63O7cEov2qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7WB2ChzRmsP63O7cEov2qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "79YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yLwY_MOsydDU7XEbyC_1EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yLwY_MOsydDU7XEbyC_1EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "8tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["is-GrshzXGfvyrs7C84YDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["is-GrshzXGfvyrs7C84YDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "7NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pNtMkp20SCCEh-TxrA7W_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pNtMkp20SCCEh-TxrA7W_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "p2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5yQFzmK6rVAYH_IWw9mY4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5yQFzmK6rVAYH_IWw9mY4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "rGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "tGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "sWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nay4Cu7cpfWvHwjKfzebpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nay4Cu7cpfWvHwjKfzebpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "o2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "pGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["56T0aIwgwSEUNL-7riuYkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["56T0aIwgwSEUNL-7riuYkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "3WYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A998Aw2wlvaHmpTDQoJVWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A998Aw2wlvaHmpTDQoJVWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "0mYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOiM2iaG3zJbqgtGW26o0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOiM2iaG3zJbqgtGW26o0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "22YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2CWGwjnZxZvrumi7qK8KzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2CWGwjnZxZvrumi7qK8KzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "GtYV7YYBBkbVtX3nhuK9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVUJWL9ZnL1p_YLKqzUSFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVUJWL9ZnL1p_YLKqzUSFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "QmYV7YYBO2e_P_QbiE9B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L6cJEyVPJDgBEJDXdVk3pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L6cJEyVPJDgBEJDXdVk3pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} {"create": {"_index": "profiling-events-5pow01", "_id": "gNcV7YYBBkbVtX3npABx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RhMJrUxrd57e6G7g2-PKcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RhMJrUxrd57e6G7g2-PKcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "9tYV7YYBBkbVtX3nmPyP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MLI30dzAv_XVLHnFXWzCzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MLI30dzAv_XVLHnFXWzCzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "WdcV7YYBBkbVtX3nsg5Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3KY9CUj1lI4EPyAmsjiKpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3KY9CUj1lI4EPyAmsjiKpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-5pow01", "_id": "sGYV7YYBO2e_P_QbtW5b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pq-E12uy1vBeK4HeCjUqKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pq-E12uy1vBeK4HeCjUqKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "jNcV7YYBBkbVtX3ntBPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["me0oRgVcR_uBmJ_kCe-VfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["me0oRgVcR_uBmJ_kCe-VfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "NeAV7YYByh-A-Biyt6sa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZEifOwXBahtAWgTrRIWHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZEifOwXBahtAWgTrRIWHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "uWYV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Gg373ZQ4MQ2jkh8DEBd7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Gg373ZQ4MQ2jkh8DEBd7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZuAV7YYByh-A-BiytqnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "YuAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LKEaCr3J8DRAWmFUoWCNBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LKEaCr3J8DRAWmFUoWCNBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "6dcV7YYBBkbVtX3n0ilU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZtcV7YYBBkbVtX3n0iyh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E8q5-T4I0EEq3oPd2J28VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E8q5-T4I0EEq3oPd2J28VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "MuAV7YYByh-A-Biyxr4p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h78omCSCOG8EoQ0xkchTYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h78omCSCOG8EoQ0xkchTYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "LOAV7YYByh-A-Biy4tjs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "B-AV7YYByh-A-Biy4dWo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0RLFnq52wVIAB0sP7d89Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0RLFnq52wVIAB0sP7d89Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "tdcV7YYBBkbVtX3n4j9s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q-SfQ_r9EJdGkmFMOGPAZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q-SfQ_r9EJdGkmFMOGPAZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-5pow01", "_id": "TWYW7YYBO2e_P_QbAaSR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acGHnAm6JFFvJ2ZoZKt_fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acGHnAm6JFFvJ2ZoZKt_fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "G-EW7YYByh-A-BiyEwMv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cqS65a_0vS0KD1oFWfGPCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cqS65a_0vS0KD1oFWfGPCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "8WYW7YYBO2e_P_QbELUW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7ut68c_tuuoqFzX7ruLfoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7ut68c_tuuoqFzX7ruLfoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "BtcW7YYBBkbVtX3nFGcu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0XhwDDngWLjSP8tIl3SEwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0XhwDDngWLjSP8tIl3SEwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "n-EW7YYByh-A-BiyEgDd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUuQ2lSMaZyy3BCVVsDF1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUuQ2lSMaZyy3BCVVsDF1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "VNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oKjEqCTMwkPftp0JIk3zEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oKjEqCTMwkPftp0JIk3zEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "F9cW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RWz2cEGgcNrcd3eaLVUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RWz2cEGgcNrcd3eaLVUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "FdcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "XNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xricnf20K4kRE1JxfxLKUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xricnf20K4kRE1JxfxLKUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "8-EW7YYByh-A-BiyQy5a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bciyx_9NZlf5osbnTw9ncg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bciyx_9NZlf5osbnTw9ncg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "WeEW7YYByh-A-BiyRTUo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RBIYJkbW5-Ss2DSyBgMD1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RBIYJkbW5-Ss2DSyBgMD1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "yeEW7YYByh-A-BiyQitl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g15iIWCPMhS3lvfL06AkwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g15iIWCPMhS3lvfL06AkwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "NeEW7YYByh-A-BiyTjpF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XpxK_Q-DP0fSfpiLzuOV7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XpxK_Q-DP0fSfpiLzuOV7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "gmYW7YYBO2e_P_QbU_aU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wGWkKDGzXSSBbLkENAOIkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wGWkKDGzXSSBbLkENAOIkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "c2cW7YYBO2e_P_QbfRJk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qtJZCZ940TmjMXNEWgVXDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qtJZCZ940TmjMXNEWgVXDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "bWcW7YYBO2e_P_QbkSjL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0NRB9624MZLIKmkHE-1rew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0NRB9624MZLIKmkHE-1rew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YOEW7YYByh-A-Biyj3ZQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "y-EW7YYByh-A-BiyjG3E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "yWcW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "0tcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["86WQcXcUxaHfJUCEplsqoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["86WQcXcUxaHfJUCEplsqoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "6WcW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIjbpW4afQJ6fKp4bSOkMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIjbpW4afQJ6fKp4bSOkMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "m2cW7YYBO2e_P_QbnzSu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6OaUumRb8P6q4GlOGK0eGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6OaUumRb8P6q4GlOGK0eGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "QdcW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Xgi2WyDfYTM06WuIqjfkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Xgi2WyDfYTM06WuIqjfkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "XNcW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lWGBthO0cXLKT_wGxBJl5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lWGBthO0cXLKT_wGxBJl5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "rdcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["57Fil5UcCT4QMA8PK7lldw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["57Fil5UcCT4QMA8PK7lldw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "t9cW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7k09_Fy75Q9-PpHdDlKvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7k09_Fy75Q9-PpHdDlKvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ptcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "sNcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MP07z4BgOJ1bvy0UuehdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MP07z4BgOJ1bvy0UuehdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-5pow01", "_id": "nuEW7YYByh-A-BiyzK7K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hy11GM4V5rJ1R_KKBReCYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hy11GM4V5rJ1R_KKBReCYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow01", "_id": "xeEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQljazbrYNKb17CR1zcj2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQljazbrYNKb17CR1zcj2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "yeEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "S-EX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKsocjlsvM68oICIvKxy7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKsocjlsvM68oICIvKxy7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "zOEX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r5Qk0y0lu82qLRvIh-Mz7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r5Qk0y0lu82qLRvIh-Mz7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "qGcX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "u2cX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "GGcX7YYBO2e_P_QbDI3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OFdK4mvMOorRf1NaABBLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OFdK4mvMOorRf1NaABBLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "7eEX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u263nW_qRlDonfyrGeQDiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u263nW_qRlDonfyrGeQDiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "zuEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZzgSlsPZ6clXTiCMgWgdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZzgSlsPZ6clXTiCMgWgdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "0eEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY70RGM6lV3NgAwSeTX8Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY70RGM6lV3NgAwSeTX8Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "ytgX7YYBBkbVtX3nOTqZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c7sDL1ZEUDJ12LHKKH-P_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c7sDL1ZEUDJ12LHKKH-P_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "i9gX7YYBBkbVtX3nPUb7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KiwE-vKZHKC3n7ALbEtWxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KiwE-vKZHKC3n7ALbEtWxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "MtgX7YYBBkbVtX3nOz8i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["67VSY7gMnvXQykqHE0WxBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["67VSY7gMnvXQykqHE0WxBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "UmcX7YYBO2e_P_QbOr2M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oTHLMe0BewCEp798WVpJtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oTHLMe0BewCEp798WVpJtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "ztgX7YYBBkbVtX3nPEMb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6XkFhPi9lM3BiwzJEIoaIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6XkFhPi9lM3BiwzJEIoaIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "IGcX7YYBO2e_P_QbPMHK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rjTw1uwIATCPa-CkaMdjEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rjTw1uwIATCPa-CkaMdjEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "-GcX7YYBO2e_P_QbS8zy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XIeRmjQa-cdLu_obwSXJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XIeRmjQa-cdLu_obwSXJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "SWcX7YYBO2e_P_QbTM67"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-y7rTRuLTj8cfBQbfYKy2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-y7rTRuLTj8cfBQbfYKy2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "EOIX7YYByh-A-BiyShe9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ui68wyUVCJcxQ5nqpWzN1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ui68wyUVCJcxQ5nqpWzN1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "h-IX7YYByh-A-BiyWyEq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lgc01vu6tLGgLO8IPeQGXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lgc01vu6tLGgLO8IPeQGXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "6OIX7YYByh-A-BiyXCQS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uD9v9EeBRS5EzcNLZ8q2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uD9v9EeBRS5EzcNLZ8q2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-5pow01", "_id": "7tgX7YYBBkbVtX3nXVoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vdmPYvdso3nyHwU3P-BxHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vdmPYvdso3nyHwU3P-BxHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "gmcX7YYBO2e_P_QbWtiF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m8UxqcMGCNBvKBluS5X8zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m8UxqcMGCNBvKBluS5X8zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "jeIX7YYByh-A-BiyWR5r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "A9gX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "_9gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Lcmvo890HG8Y4rQbXwRxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Lcmvo890HG8Y4rQbXwRxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "Q9gX7YYBBkbVtX3ni4AA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "ceIX7YYByh-A-BiyqGFv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "suIX7YYByh-A-Biym1t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PU4AlGgy6OVgX5g2hXwflQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PU4AlGgy6OVgX5g2hXwflQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "YGgX7YYBO2e_P_QbnBJG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_X9dFQVvkPI4ha0P8p2JiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_X9dFQVvkPI4ha0P8p2JiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "tdgX7YYBBkbVtX3nqJPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EszYJlG3uJtSxM3Y3d7bAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EszYJlG3uJtSxM3Y3d7bAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "ceIX7YYByh-A-BiymlgR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIxtnf4ZytccTyNG23fGog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIxtnf4ZytccTyNG23fGog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "FdgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oYwYA56C57graUtOG96_nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oYwYA56C57graUtOG96_nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "XeIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "WNgX7YYBBkbVtX3n5c3Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6oJOlriSsxoHCj15KtT0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6oJOlriSsxoHCj15KtT0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "B-IX7YYByh-A-Biy5oju"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kvqyEWe3mfnleSrT6I-tHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kvqyEWe3mfnleSrT6I-tHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "feIX7YYByh-A-Biy-Jux"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "7OIX7YYByh-A-Biy9JDf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whKjwtCVRYeUJb75GUn0Fw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whKjwtCVRYeUJb75GUn0Fw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "82gX7YYBO2e_P_Qb9V0w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZGgX7YYBO2e_P_Qb91-r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["caAMI7G9uz-EPxuo9p-Rlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["caAMI7G9uz-EPxuo9p-Rlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "UtgX7YYBBkbVtX3n-ONq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IO7igLUjHuSwhRGut0RlMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IO7igLUjHuSwhRGut0RlMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "6dgX7YYBBkbVtX3n9-Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JLHzPwzEV5rRN9RuEzoMPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JLHzPwzEV5rRN9RuEzoMPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "-OIX7YYByh-A-Biy95jl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4lZbIrmqX0dcJVBKGnWp9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4lZbIrmqX0dcJVBKGnWp9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "iWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuPwjyefoJQ1lu-T5igwEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuPwjyefoJQ1lu-T5igwEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "IeIY7YYByh-A-BiyJLxn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N1nRjzqOIB8y-j3xmzMaSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N1nRjzqOIB8y-j3xmzMaSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "cOIY7YYByh-A-BiyJsRk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QbcK_gbMTYuvwl_FoMECaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QbcK_gbMTYuvwl_FoMECaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "-2gY7YYBO2e_P_QbN5Lt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrEr2m1NreTQiIcNz23_Gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrEr2m1NreTQiIcNz23_Gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "veIY7YYByh-A-BiyONo2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "xuIY7YYByh-A-BiyQtx5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["42JG9a6NRfwi2CO7Z1RPNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["42JG9a6NRfwi2CO7Z1RPNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "1-IY7YYByh-A-BiyNNZi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x8C8r_joS9eFrngYSfAD9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x8C8r_joS9eFrngYSfAD9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "f-IY7YYByh-A-BiyVPIV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0UeOgQYKC7zcrsZ5ZxtGIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0UeOgQYKC7zcrsZ5ZxtGIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "LGgY7YYBO2e_P_QbZb97"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AN24Ay2FFm6R_uskGlbDvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AN24Ay2FFm6R_uskGlbDvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "GdkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vv8H2tPfs7d9zjnnJhB0rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vv8H2tPfs7d9zjnnJhB0rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "o2gY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "VtkY7YYBBkbVtX3nZCoi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "ntkY7YYBBkbVtX3nZCuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xFHH2tMDnbWLZHLGtCUC2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xFHH2tMDnbWLZHLGtCUC2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "LWgY7YYBO2e_P_QbZb97"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ng7Kn6I7osQY62ITDyHvMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ng7Kn6I7osQY62ITDyHvMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "MNkY7YYBBkbVtX3ncS0m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oHVZwEtujopOZewM6A1sxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oHVZwEtujopOZewM6A1sxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "3mgY7YYBO2e_P_QbcsdE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pczYn9bA4SlIUvF6oLM4Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pczYn9bA4SlIUvF6oLM4Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "NuIY7YYByh-A-Biycv3l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_wx1WU4Q3GTegN_cAxP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_wx1WU4Q3GTegN_cAxP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "62gY7YYBO2e_P_QbY7m_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MvydvJcdA5Fm40P_1i2ixQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MvydvJcdA5Fm40P_1i2ixQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow01", "_id": "beMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EG9jBmB3lh64ME0jyCkfBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EG9jBmB3lh64ME0jyCkfBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "ZuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9T2neRyvBAjupi4KvqMEkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9T2neRyvBAjupi4KvqMEkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "GmgY7YYBO2e_P_QbkN5i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2j8VUArr_b9AewT6WEQL_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2j8VUArr_b9AewT6WEQL_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "v-MY7YYByh-A-Biykxdq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HsiX5vdQunBNkC7dzQqFjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HsiX5vdQunBNkC7dzQqFjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "euMY7YYByh-A-BiylBpj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SwjluL3-fAPsYBuygjQN9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SwjluL3-fAPsYBuygjQN9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "BdkY7YYBBkbVtX3nklHO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow01", "_id": "iWkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y5wRxzE9W7SQh2wOeWm08A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y5wRxzE9W7SQh2wOeWm08A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "BtkY7YYBBkbVtX3nsGt1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5BsomDuMI7TNerJ9VXCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5BsomDuMI7TNerJ9VXCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow01", "_id": "9NkY7YYBBkbVtX3nxYcT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "fdkY7YYBBkbVtX3n1JXT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GaCLxWirBhJtu1rdEHoD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GaCLxWirBhJtu1rdEHoD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "2mkY7YYBO2e_P_Qb4S1Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-xs-IN1fdS4tlSIAXAM4kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-xs-IN1fdS4tlSIAXAM4kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "12kY7YYBO2e_P_Qb8jzI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U9D_YJUEsrwBEswWxHC35w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U9D_YJUEsrwBEswWxHC35w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "bNkY7YYBBkbVtX3n76eU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Is6WKpq1rPgjROiY1ySbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Is6WKpq1rPgjROiY1ySbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow01", "_id": "-tkZ7YYBBkbVtX3nAr6P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m7PtklSiInmoO66e9Bc5vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m7PtklSiInmoO66e9Bc5vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow01", "_id": "DdkZ7YYBBkbVtX3nALrp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z1K4WqC6eykbHpG2pCP39Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z1K4WqC6eykbHpG2pCP39Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow01", "_id": "cWkZ7YYBO2e_P_QbImDZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsokQSi3I4rVgRAwb8fhbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsokQSi3I4rVgRAwb8fhbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow01", "_id": "_OMZ7YYByh-A-BiyHoO_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["njwJdQnjALlyqqAczuUyDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["njwJdQnjALlyqqAczuUyDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "SmkZ7YYBO2e_P_QbHVji"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ITsishoJBrPM8Hg7nurVvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ITsishoJBrPM8Hg7nurVvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "SuMZ7YYByh-A-BiyIIcW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Os-4RhVkjeRwXnMgi8sCPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Os-4RhVkjeRwXnMgi8sCPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "Z-MZ7YYByh-A-BiyIIrY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow01", "_id": "pmkZ7YYBO2e_P_QbIV77"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaQK44tICLO4ljAwiqTd8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaQK44tICLO4ljAwiqTd8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow01", "_id": "hNkZ7YYBBkbVtX3nL9qN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DuNugRyUNKQa9O6ipjRVvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DuNugRyUNKQa9O6ipjRVvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow01", "_id": "k-MZ7YYByh-A-BiyMZoV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnrTN3oNHBWQmiPNUfJdZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnrTN3oNHBWQmiPNUfJdZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow02.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow02.json index 310c3b6f5a6d3..d2f9a1867566e 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow02.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow02.json @@ -1,278 +1,278 @@ {"create": {"_index": "profiling-events-5pow02", "_id": "RtYU7YYBBkbVtX3nuwHT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOolLKwTF6c0fdaMu4zrpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOolLKwTF6c0fdaMu4zrpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "GGUU7YYBO2e_P_QbvZyb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bFqi88DUwWkr_8kK2-MSRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bFqi88DUwWkr_8kK2-MSRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "x2UU7YYBO2e_P_QbvJmg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "NNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wOaaLLn26MWCq1Ch7gi66A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wOaaLLn26MWCq1Ch7gi66A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "G9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XAkh0cI6mI0TEjgeMQjJRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XAkh0cI6mI0TEjgeMQjJRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "PNYV7YYBBkbVtX3nC1r7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "eeAV7YYByh-A-BiyCBIn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLtkTN9H0P9GQGUpxzaGrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLtkTN9H0P9GQGUpxzaGrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "X-AV7YYByh-A-BiyGi5B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "6tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uk1ygFuU89LLnNUfPAM8KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uk1ygFuU89LLnNUfPAM8KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow02", "_id": "sGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDmVY9Mljfrd1uHcDiDp-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDmVY9Mljfrd1uHcDiDp-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "GGYV7YYBO2e_P_QbVxih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T7kTFHjAtS6OtzybnvJ0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T7kTFHjAtS6OtzybnvJ0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "-9YV7YYBBkbVtX3nW7rm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "1mYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["codND57fF0ln0PPsgzvoNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["codND57fF0ln0PPsgzvoNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "02YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOiM2iaG3zJbqgtGW26o0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOiM2iaG3zJbqgtGW26o0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "1tcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9eJFc1RqWTK4Nh5sHxlOdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9eJFc1RqWTK4Nh5sHxlOdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "FNcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["muSA4-3A5tqLjcddDaeDBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["muSA4-3A5tqLjcddDaeDBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "BGgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-iLOmSM0bOvxtv9W5h6VXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-iLOmSM0bOvxtv9W5h6VXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "nGgY7YYBO2e_P_QbZLxc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TkPEPsUQlEC8_tTSu1y8wA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TkPEPsUQlEC8_tTSu1y8wA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "VdkY7YYBBkbVtX3nZCoi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP6GIsw4ofWcnUGlBduuVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP6GIsw4ofWcnUGlBduuVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "aOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fWJaqb09QzwUMPXDtHMSXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fWJaqb09QzwUMPXDtHMSXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "neMY7YYByh-A-Biysi1P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "KtYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_PmLUDiMT9Fiy_kfGXHxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_PmLUDiMT9Fiy_kfGXHxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-5pow02", "_id": "y9cW7YYBBkbVtX3nc7iI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYsp1kqfqOBHCXrvmwLiMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYsp1kqfqOBHCXrvmwLiMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "vWcW7YYBO2e_P_QbjiAN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "GOEW7YYByh-A-Biyj3ir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "mWcW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "KWcX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UUY2L_ithWPFsPGJM4Kw3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UUY2L_ithWPFsPGJM4Kw3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow02", "_id": "NmcX7YYBO2e_P_QbCoY2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "1NgX7YYBBkbVtX3nTUyD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "XOIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2qSXnyvIGQkSNpP5wPgdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2qSXnyvIGQkSNpP5wPgdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} {"create": {"_index": "profiling-events-5pow02", "_id": "tWgX7YYBO2e_P_Qb51bB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XdZpM6-FAy3LMUV5bnSRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XdZpM6-FAy3LMUV5bnSRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-5pow02", "_id": "bmgX7YYBO2e_P_Qb9Fyf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "NWkZ7YYBO2e_P_QbH13S"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsokQSi3I4rVgRAwb8fhbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsokQSi3I4rVgRAwb8fhbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-5pow02", "_id": "Q-MZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "lWkZ7YYBO2e_P_QbHVZZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OtPO4_Cde7GWru30XAUPmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OtPO4_Cde7GWru30XAUPmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "-WkZ7YYBO2e_P_QbLGTb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DuNugRyUNKQa9O6ipjRVvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DuNugRyUNKQa9O6ipjRVvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "LtYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "Qd8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ROD9hyXKyG1xyIp3eNp24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ROD9hyXKyG1xyIp3eNp24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow02", "_id": "L9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnpinE4u8LaMWLZMBdXuZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnpinE4u8LaMWLZMBdXuZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "GeAV7YYByh-A-BiyBxBv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VrBz5ulfwdPTqnMaGIpcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VrBz5ulfwdPTqnMaGIpcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "d9YV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bsoNX49ITduR-HMxcIbsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bsoNX49ITduR-HMxcIbsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "_GYV7YYBO2e_P_QbWR0q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "O2YV7YYBO2e_P_QbWBy9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "4mYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "4tYV7YYBBkbVtX3nhd6z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVUJWL9ZnL1p_YLKqzUSFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVUJWL9ZnL1p_YLKqzUSFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "WNcV7YYBBkbVtX3nsg5Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "99cV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "-NcV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow02", "_id": "adcV7YYBBkbVtX3n5EbR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXgAgM2hMcqzn0fnoAoP0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXgAgM2hMcqzn0fnoAoP0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "-mYV7YYBO2e_P_Qb5ooi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cfo59YpRKB0q5iQSQJ-VYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cfo59YpRKB0q5iQSQJ-VYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "TGYW7YYBO2e_P_QbAaSR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-riZP-fh7uXaUsCqBO2ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-riZP-fh7uXaUsCqBO2ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow02", "_id": "5uAW7YYByh-A-BiyD_jF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "zmYW7YYBO2e_P_QbQeID"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "52YW7YYBO2e_P_QbUOsU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OYGXc31yJI5bR-H2iNSwHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OYGXc31yJI5bR-H2iNSwHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "FdcW7YYBBkbVtX3nk83N"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x4JagFwIYKM4hCWjdkk5Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x4JagFwIYKM4hCWjdkk5Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "s9cW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NWTYSV7vGDryRONnCUqo1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NWTYSV7vGDryRONnCUqo1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "JOEW7YYByh-A-Biyz7ao"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "5mcX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["84RiA4pbVL7KMlDvnXnbFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["84RiA4pbVL7KMlDvnXnbFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "YeEX7YYByh-A-BiyDev8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "hWcX7YYBO2e_P_QbXuFm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P-dCdUT1LEJyae6UYwKugg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P-dCdUT1LEJyae6UYwKugg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "8-IX7YYByh-A-BiyqWJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3QVerrpALkFsA-z-U___AA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3QVerrpALkFsA-z-U___AA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "uGgX7YYBO2e_P_Qbtynt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "MtgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "ZWgX7YYBO2e_P_Qb91-r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "deIX7YYByh-A-Biy95dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6NkVutVoJ0m5j8aVYyp0Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6NkVutVoJ0m5j8aVYyp0Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow02", "_id": "bWgY7YYBO2e_P_QbJn6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yyOgLKUZuSQUa5BkL2jvpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yyOgLKUZuSQUa5BkL2jvpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "aOIY7YYByh-A-BiyJ8dT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6EgKcwZlK3OnMM95lvD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6EgKcwZlK3OnMM95lvD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "bGgY7YYBO2e_P_QbVq6T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bredr3OvHQiC2uo7mFYNAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bredr3OvHQiC2uo7mFYNAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-5pow02", "_id": "CGgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-Q9uCXR-TIx0LsEoXVwIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-Q9uCXR-TIx0LsEoXVwIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "hmkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "J9kY7YYBBkbVtX3ns3Wc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["twP61I8BoQSVRAEu87hitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["twP61I8BoQSVRAEu87hitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "i2kY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LozaztVRNbKlSptg74c_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LozaztVRNbKlSptg74c_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "VGkY7YYBO2e_P_Qb1CiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "FuMY7YYByh-A-Biy713T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vFkcrQtWCVTfQjjlGu2S_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vFkcrQtWCVTfQjjlGu2S_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "yeMY7YYByh-A-Biy_WPI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "jeMZ7YYByh-A-BiyIYwb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "LNYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWfVfitdsTIFX4dhe6CakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWfVfitdsTIFX4dhe6CakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "P98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8AqERkmGja0aVhFHauF_yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8AqERkmGja0aVhFHauF_yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "Q98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tk-Rn8r6-wqzqI-bfiAJ7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tk-Rn8r6-wqzqI-bfiAJ7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "Nd8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kCi3XJtF81OLZhjrXcqzHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kCi3XJtF81OLZhjrXcqzHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "FNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbnTibb7iUG5Z59b5ewlIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbnTibb7iUG5Z59b5ewlIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "HtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pkyFSPLbfCpCJS0cbldBzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pkyFSPLbfCpCJS0cbldBzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "PNYV7YYBBkbVtX3nG2w1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hSPX2ocR_Ka7dmSG_0BvUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hSPX2ocR_Ka7dmSG_0BvUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "qmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eh1qER1qLyoMW0w6ZkEkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eh1qER1qLyoMW0w6ZkEkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "2WYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfmPxWX0umuPnDn2aoiurQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfmPxWX0umuPnDn2aoiurQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow02", "_id": "29YV7YYBBkbVtX3nl_UY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R5Cb48qStI1GlPaxKWm-mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R5Cb48qStI1GlPaxKWm-mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "3NcV7YYBBkbVtX3nsw-Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8eRxSiE_6KOXeGPJZDEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8eRxSiE_6KOXeGPJZDEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "QWYV7YYBO2e_P_Qbxnnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E8q5-T4I0EEq3oPd2J28VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E8q5-T4I0EEq3oPd2J28VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "HuEW7YYByh-A-BiyFQcS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_IL9L_uv3CfGfQbo7Tbz2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_IL9L_uv3CfGfQbo7Tbz2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "etcW7YYBBkbVtX3nQYxi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AePsFsEWIAKcD6i5fTcKwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AePsFsEWIAKcD6i5fTcKwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "SWYW7YYBO2e_P_QbT-o1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XpxK_Q-DP0fSfpiLzuOV7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XpxK_Q-DP0fSfpiLzuOV7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "sdcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MP07z4BgOJ1bvy0UuehdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MP07z4BgOJ1bvy0UuehdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-5pow02", "_id": "8NcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow02", "_id": "w-EW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "yuEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "kWcX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4cstsRMDoVu7vb1ZvH1EzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4cstsRMDoVu7vb1ZvH1EzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "6mcX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OqL1jazxhGNp3BmuN0BL6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OqL1jazxhGNp3BmuN0BL6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "QuIX7YYByh-A-BiyPQgM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jKJw7MgwzsyLy5Y5-ZGSMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jKJw7MgwzsyLy5Y5-ZGSMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "-9gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow02", "_id": "YGgX7YYBO2e_P_Qbpxge"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yjN3QcXIO7ZJpjPqQPEBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yjN3QcXIO7ZJpjPqQPEBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "oeIX7YYByh-A-Biyum7A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "uNgX7YYBBkbVtX3n9dtq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ayg1IWi6ap3XN7RjaMkRog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ayg1IWi6ap3XN7RjaMkRog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "3WgX7YYBO2e_P_Qb-GAl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "d9kY7YYBBkbVtX3nNgnQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrEr2m1NreTQiIcNz23_Gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrEr2m1NreTQiIcNz23_Gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "1GgY7YYBO2e_P_QbZb1H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "VNkY7YYBBkbVtX3n8Kve"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "k9kY7YYBBkbVtX3n86-p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jIUkkqlhs_xaucQSfOkxdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jIUkkqlhs_xaucQSfOkxdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "EOMZ7YYByh-A-BiyMJmk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnrTN3oNHBWQmiPNUfJdZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnrTN3oNHBWQmiPNUfJdZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "ft8U7YYByh-A-Biyvcco"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sWUvdmC1yhMffRymX3J_5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sWUvdmC1yhMffRymX3J_5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-5pow02", "_id": "Pd8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5QxSgtn_YPXxJ3jCeAVHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5QxSgtn_YPXxJ3jCeAVHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} {"create": {"_index": "profiling-events-5pow02", "_id": "ON8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4f9KZiG-idTZu0O-sRt4aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4f9KZiG-idTZu0O-sRt4aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "O98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n-PAN0ssaXvJ6kY18i9tog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n-PAN0ssaXvJ6kY18i9tog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "DtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "JdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["87tmMz7dkdhga3ssbWBSBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["87tmMz7dkdhga3ssbWBSBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "C9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YiY7TtFv0EXQiZMyJynqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YiY7TtFv0EXQiZMyJynqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "LdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["759vzPaqX5H2_0qTOKee0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["759vzPaqX5H2_0qTOKee0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "EWUV7YYBO2e_P_QbBuCR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "zWUV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "rmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96zUk00wJUkz6pqWJ4UVBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96zUk00wJUkz6pqWJ4UVBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow02", "_id": "39YV7YYBBkbVtX3nWbXf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oSCp9cFxZ1aVa9L0c22cCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oSCp9cFxZ1aVa9L0c22cCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-5pow02", "_id": "f9cV7YYBBkbVtX3ntRfi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZEifOwXBahtAWgTrRIWHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZEifOwXBahtAWgTrRIWHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "CtcV7YYBBkbVtX3ntRYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "VdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oKjEqCTMwkPftp0JIk3zEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oKjEqCTMwkPftp0JIk3zEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "smYW7YYBO2e_P_QbP-Dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BgnwfcudspKPFADqFnojuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BgnwfcudspKPFADqFnojuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-5pow02", "_id": "vGYW7YYBO2e_P_QbPtv2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I9TiskxOBE6uewdlBEfbaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I9TiskxOBE6uewdlBEfbaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow02", "_id": "HOEW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iVK1cbIgag654ehUa-HUNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iVK1cbIgag654ehUa-HUNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "FmcW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxLFvg4n6uQItdMk3gw_xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxLFvg4n6uQItdMk3gw_xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "UWcW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow02", "_id": "bmcW7YYBO2e_P_Qb3mI-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "0OEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja9MBlCW9JbhLw8tshjLeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja9MBlCW9JbhLw8tshjLeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "dmcX7YYBO2e_P_QbTdFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XIeRmjQa-cdLu_obwSXJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XIeRmjQa-cdLu_obwSXJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "BdgX7YYBBkbVtX3ne3aS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0AOiOeHK39oqr5eNGKOkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0AOiOeHK39oqr5eNGKOkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow02", "_id": "IeIX7YYByh-A-Biymlrf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "b2gX7YYBO2e_P_Qb9Fyf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNA5mzQxt3Xt7EAz1m9YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNA5mzQxt3Xt7EAz1m9YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "rdgX7YYBBkbVtX3n9Ngk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pp5lsGmp-JSx0DYM6KPKrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pp5lsGmp-JSx0DYM6KPKrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "9WgY7YYBO2e_P_Qble4x"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UL06CNiVyxEFpIouFPRx3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UL06CNiVyxEFpIouFPRx3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow02", "_id": "FuMY7YYByh-A-Biykxnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow02", "_id": "HdkY7YYBBkbVtX3nlFSh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJHpr4fLpWoSKqg-aUPBfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJHpr4fLpWoSKqg-aUPBfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow02", "_id": "vmkY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uRrKKaf_gbp1De235zmPrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uRrKKaf_gbp1De235zmPrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow03.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow03.json index 1c0d2a047f09c..fbcc736aa8729 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow03.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow03.json @@ -1,42 +1,42 @@ {"create": {"_index": "profiling-events-5pow03", "_id": "r2YV7YYBO2e_P_QbtW5b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow03", "_id": "u9cW7YYBBkbVtX3nk8tE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow03", "_id": "EmcW7YYBO2e_P_QbkCRr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow03", "_id": "RuEW7YYByh-A-BiyrYsf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow03", "_id": "etcW7YYBBkbVtX3ny_KF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow03", "_id": "8dcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow03", "_id": "9-EW7YYByh-A-Biy3L6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow03", "_id": "_NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-5pow03", "_id": "YOAV7YYByh-A-BiyGi5B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow03", "_id": "LGYV7YYBO2e_P_QbVhYN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow03", "_id": "42YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow03", "_id": "V9gX7YYBBkbVtX3n9uDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-5pow03", "_id": "neAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow03", "_id": "y-EW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow03", "_id": "qOEX7YYByh-A-BiyDuyF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow03", "_id": "5WUU7YYBO2e_P_QbwKIN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-5pow03", "_id": "L9YU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow03", "_id": "2eAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-5pow03", "_id": "xOEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-5pow03", "_id": "I9gX7YYBBkbVtX3n9Npj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow03", "_id": "JtkZ7YYBBkbVtX3nItGe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow04.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow04.json index 43b8821f8e5e3..6833eb218274c 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow04.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow04.json @@ -1,8 +1,8 @@ {"create": {"_index": "profiling-events-5pow04", "_id": "5GYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-5pow04", "_id": "0uAV7YYByh-A-BiyVFqd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-5pow04", "_id": "1eMZ7YYByh-A-BiyIIiV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-5pow04", "_id": "CtcW7YYBBkbVtX3nz_cq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow05.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow05.json index f9506ae14722e..774edc97b20b7 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow05.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow05.json @@ -1,2 +1,2 @@ {"create": {"_index": "profiling-events-5pow05", "_id": "xdcW7YYBBkbVtX3nz_hr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow06.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow06.json index 4a59bf2fc203a..551c1f5404232 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow06.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_5pow06.json @@ -1,2 +1,2 @@ {"create": {"_index": "profiling-events-5pow06", "_id": "m-EW7YYByh-A-Biyz7fk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} diff --git a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_all.json b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_all.json index adf765e760286..e686f7512fa6c 100644 --- a/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_all.json +++ b/x-pack/plugins/profiling/e2e/es_archivers/profiling_events_all.json @@ -1,7068 +1,7068 @@ {"create": {"_index": "profiling-events-all", "_id": "ZOEW7YYByh-A-BiyvJ5t"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "C2cW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EWcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jxSybvAJREHA93t9a-9ZDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jxSybvAJREHA93t9a-9ZDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "39cW7YYBBkbVtX3nvOX4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DJsG1PTKQj3bE1cuaZCjpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DJsG1PTKQj3bE1cuaZCjpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "EmcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "odcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YGL51-V8LSysDnURshRDsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YGL51-V8LSysDnURshRDsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "feEW7YYByh-A-BiyvqKW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWmN7UoDEV82g0X1jR-lag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWmN7UoDEV82g0X1jR-lag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CWcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rAVnotLNqZZX90k5rHuXLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rAVnotLNqZZX90k5rHuXLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "u2cW7YYBO2e_P_Qbu0Sm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "B2cW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfaU4OGqPjwdE9T5hHw_Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfaU4OGqPjwdE9T5hHw_Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-all", "_id": "JWcW7YYBO2e_P_Qbzlaj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQ1nyOGbOtedL7gx4fO8XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQ1nyOGbOtedL7gx4fO8XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LuEW7YYByh-A-BiyzrNj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rwXtW2ufmEwH1DrvNBzUGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rwXtW2ufmEwH1DrvNBzUGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CNcW7YYBBkbVtX3nz_cq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KANLoEfpUHNjFay_wQgAsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KANLoEfpUHNjFay_wQgAsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_WcW7YYBO2e_P_Qby0_R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HVz81HFfQwHn9QYh-be1Sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HVz81HFfQwHn9QYh-be1Sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DOEW7YYByh-A-BiyzK0R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjtKZoprzXCLbmVAEEoqNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjtKZoprzXCLbmVAEEoqNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GOEW7YYByh-A-BiyzbAL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dWcW7YYBO2e_P_Qb0FnB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y7KI8KMMWDvf5U1WSCufNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y7KI8KMMWDvf5U1WSCufNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_GcW7YYBO2e_P_Qby0_R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ku3dJg7uoNqA3OiimkD9KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ku3dJg7uoNqA3OiimkD9KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "dGcW7YYBO2e_P_Qb0FnB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaQK44tICLO4ljAwiqTd8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaQK44tICLO4ljAwiqTd8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "6tcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29lvFdoiBP4NTHqtmd8Y8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29lvFdoiBP4NTHqtmd8Y8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "pGcW7YYBO2e_P_QbzlQs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GRsBLFcqVzFUAZS_v_mreQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GRsBLFcqVzFUAZS_v_mreQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "mNcW7YYBBkbVtX3ny_BB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jyjn_5qXfbWtQo79W0qlsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jyjn_5qXfbWtQo79W0qlsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "w9cW7YYBBkbVtX3nz_hr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "yuEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q8rLi45IFUbGCdbAHGZ4vQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q8rLi45IFUbGCdbAHGZ4vQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "-2cW7YYBO2e_P_Qby0_R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cAbQL1Yf_EXwq1Xvj5YzBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cAbQL1Yf_EXwq1Xvj5YzBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "l2cW7YYBO2e_P_QbzFFQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5_wX-Er1trjNDpVBu_jsDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5_wX-Er1trjNDpVBu_jsDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "wOEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DrEPteno4vchlHw0ws65OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DrEPteno4vchlHw0ws65OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "JmcW7YYBO2e_P_Qbzlaj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "wtcW7YYBBkbVtX3nz_hr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ivEY-EqI5b0E3M_68jqmVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ivEY-EqI5b0E3M_68jqmVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "69cW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U0Kn8_bALG7-cg-DY86kNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U0Kn8_bALG7-cg-DY86kNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "uNgW7YYBBkbVtX3n2wD9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9kcejzSJCXOEAAMTuFifhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9kcejzSJCXOEAAMTuFifhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SWcW7YYBO2e_P_Qb3V8e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n73U5dOg61JklJT6WKmxuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n73U5dOg61JklJT6WKmxuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zmcW7YYBO2e_P_Qb3FtF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ifj_nYmYbVre3Goy-3d1XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ifj_nYmYbVre3Goy-3d1XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hOEW7YYByh-A-Biy3cBp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6DJ5rUntpH_kTGPTanZjBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6DJ5rUntpH_kTGPTanZjBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9uEW7YYByh-A-Biy3L6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bSoXiwGVYIVR1L3DbcUUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bSoXiwGVYIVR1L3DbcUUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "gOEW7YYByh-A-Biy-9iI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t263LuBpXeJT_eypTrtUJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t263LuBpXeJT_eypTrtUJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "itgW7YYBBkbVtX3n_Blb"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hrR6ASxOEteokggjxZKMeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hrR6ASxOEteokggjxZKMeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t2cW7YYBO2e_P_Qb_nuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wCmvw-7GQGL1yAvmTCUcTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wCmvw-7GQGL1yAvmTCUcTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "weEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oZVmtNwsNi_g0dsbCFubSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oZVmtNwsNi_g0dsbCFubSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "DGcW7YYBO2e_P_Qb_XhM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7iLZSCd-GhxzDJmUOWlltQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7iLZSCd-GhxzDJmUOWlltQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "w2cW7YYBO2e_P_Qb_4GC"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0TcViARYA_NarblNdiqUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0TcViARYA_NarblNdiqUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "feEW7YYByh-A-Biy-9iI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JAE0oBjlHd_LFeKNeOVAFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JAE0oBjlHd_LFeKNeOVAFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ueEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xtRwIn-B17Zk-6fqHdRi2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xtRwIn-B17Zk-6fqHdRi2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "2-EW7YYByh-A-Biy-tTN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xE2zyuyXFlIJ5r66uy5RMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xE2zyuyXFlIJ5r66uy5RMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "pGcX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UuDeBu8oU2omluou-0a1Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UuDeBu8oU2omluou-0a1Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jmcX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ALNfUngsI4IwTJ9rHQZUfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ALNfUngsI4IwTJ9rHQZUfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6OEX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VaLam_KQiz8POCW3aoer2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VaLam_KQiz8POCW3aoer2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JWcX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yK6OONpk1_skJktfJLfkBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yK6OONpk1_skJktfJLfkBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fmcX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EKNw_XLZvm5U0bSAHP5Qhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EKNw_XLZvm5U0bSAHP5Qhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JGcX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KbIwDU7bE16YP2ns0ZA4pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KbIwDU7bE16YP2ns0ZA4pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uWcX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fSsmeetWqxUvZQmnispuzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fSsmeetWqxUvZQmnispuzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vdgX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eLxFTlFN_8U9FW9T5uVFVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eLxFTlFN_8U9FW9T5uVFVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "NWcX7YYBO2e_P_QbCoY2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eAg08WF8lmIVlNh_qYyNeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eAg08WF8lmIVlNh_qYyNeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "UNgX7YYBBkbVtX3nCiNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4cstsRMDoVu7vb1ZvH1EzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4cstsRMDoVu7vb1ZvH1EzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ANgX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lhxBmjNk6lw5l8hDy9uvfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lhxBmjNk6lw5l8hDy9uvfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "AtgX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZORFq6EEwMoX6Tu_RTCb-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZORFq6EEwMoX6Tu_RTCb-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jOEX7YYByh-A-BiyC-Nk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JRIGBFzel1pbRLTjBi-ZHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JRIGBFzel1pbRLTjBi-ZHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "KGcX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D_3OCCgKV6Kk8ntzJs_Wng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D_3OCCgKV6Kk8ntzJs_Wng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "yuEX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BnraydbvEwL6mkTBVZOVLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BnraydbvEwL6mkTBVZOVLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "tWcX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RarVOrNELjnQUHfPoLUVBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RarVOrNELjnQUHfPoLUVBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6WcX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y99kgy97ko1q-GgFUQMIvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y99kgy97ko1q-GgFUQMIvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6eEX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9fNDHkA5rTj57PGtFze_-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9fNDHkA5rTj57PGtFze_-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "XOEX7YYByh-A-BiyDev8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["672APJhXj5EKzZzWjY4QzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["672APJhXj5EKzZzWjY4QzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "SeEX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DbVr5WH8AZycf302C0td3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DbVr5WH8AZycf302C0td3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "o2cX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PonqCaU3e7VApDLeDylGQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PonqCaU3e7VApDLeDylGQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "4WcX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8x65WqPnBjtVuuc0TRdiaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8x65WqPnBjtVuuc0TRdiaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "SOEX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gWcX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QCaQz16pLyZGIJ3JyzyIYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QCaQz16pLyZGIJ3JyzyIYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "jGcX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9AhQj1Cjybxb6G_U8nBwuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9AhQj1Cjybxb6G_U8nBwuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6uEX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XiTL4w9S8KltLkj8tdlEIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XiTL4w9S8KltLkj8tdlEIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "X-EX7YYByh-A-BiyDev8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dvz9mFWSe_1LoPFwkrAW1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dvz9mFWSe_1LoPFwkrAW1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "i2cX7YYBO2e_P_QbDpI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["43Mh5txMzJNoI6svI0SbQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["43Mh5txMzJNoI6svI0SbQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "j2cX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ll6esE1FGRvBZYuvkkd9xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ll6esE1FGRvBZYuvkkd9xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "juEX7YYByh-A-BiyC-Nk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u263nW_qRlDonfyrGeQDiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u263nW_qRlDonfyrGeQDiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "pWcX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HXFKn82mEOX8HQ_gs-IncA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HXFKn82mEOX8HQ_gs-IncA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "puEX7YYByh-A-BiyDuyF"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "wOEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ri5pW0t6s5lXro7RV78vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ri5pW0t6s5lXro7RV78vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "x-EX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GQlu4cDmBP0J7ys3CIDFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GQlu4cDmBP0J7ys3CIDFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZuEX7YYByh-A-BiyHvcj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["88HbSgrFc7eu2ajG25n_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["88HbSgrFc7eu2ajG25n_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2tgX7YYBBkbVtX3nHC_T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZzgSlsPZ6clXTiCMgWgdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZzgSlsPZ6clXTiCMgWgdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GGcX7YYBO2e_P_QbG5ZA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YP8JnSQ_Ut135bkI0n3-mA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YP8JnSQ_Ut135bkI0n3-mA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "veEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1FaSioKA40L9zkdwioOgrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1FaSioKA40L9zkdwioOgrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "weEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XELwzOLZflYDWTPYdFF2sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XELwzOLZflYDWTPYdFF2sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "xuEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9l-A9YSFZEx7xj9VRJkH9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9l-A9YSFZEx7xj9VRJkH9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "yeEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uS4XSdjWfr8HqtkqPLeplg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uS4XSdjWfr8HqtkqPLeplg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "BmcX7YYBO2e_P_QbKZ_b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "x9gX7YYBBkbVtX3nOTqZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "G9gX7YYBBkbVtX3nOTzV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rMpzXndoIcEiY0-GRAGnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rMpzXndoIcEiY0-GRAGnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qtgX7YYBBkbVtX3nOj0O"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7v_9tj1Xdjf6ueI8cLpeFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7v_9tj1Xdjf6ueI8cLpeFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "3-IX7YYByh-A-BiyPAaT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nYdp-GsAnSl5-IbdkTmdVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nYdp-GsAnSl5-IbdkTmdVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "MdgX7YYBBkbVtX3nOz8i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0a9NYjgpQ8iJm6UEGWaqBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0a9NYjgpQ8iJm6UEGWaqBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "H-IX7YYByh-A-BiyPQuP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zi9qwHR7xXnRG3K6zMFidA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zi9qwHR7xXnRG3K6zMFidA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ydgX7YYBBkbVtX3nOTqZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1PJ7alh7hduQ9X2Hed5fQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1PJ7alh7hduQ9X2Hed5fQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Y9gX7YYBBkbVtX3nO0KT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jSF_4ZsjFVCSFvLBYrF7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jSF_4ZsjFVCSFvLBYrF7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aeIX7YYByh-A-BiyOwXV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ohsTjz0QiBj_Cb9rZpyfbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ohsTjz0QiBj_Cb9rZpyfbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qdgX7YYBBkbVtX3nOj0O"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MNgX7YYBBkbVtX3nOz8i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k_oZ8en1b76mhL2hb9QZEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k_oZ8en1b76mhL2hb9QZEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "itgX7YYBBkbVtX3nPUb7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k3oNyMpKPtIZvbqyj2iu3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k3oNyMpKPtIZvbqyj2iu3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "T2cX7YYBO2e_P_QbOr2M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epnqAxFCsbQSVItuSr9wEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epnqAxFCsbQSVItuSr9wEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} {"create": {"_index": "profiling-events-all", "_id": "QeIX7YYByh-A-BiyPQgM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5WP7zBBeosgRaaVkLBLtbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5WP7zBBeosgRaaVkLBLtbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "uOIX7YYByh-A-BiyPQlQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9_N4V4eyXNvSUkP63EDRaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9_N4V4eyXNvSUkP63EDRaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "auIX7YYByh-A-BiyOwXV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6XkFhPi9lM3BiwzJEIoaIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6XkFhPi9lM3BiwzJEIoaIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "dmcX7YYBO2e_P_QbS8t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XIeRmjQa-cdLu_obwSXJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XIeRmjQa-cdLu_obwSXJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YuIX7YYByh-A-BiySxi3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "c2cX7YYBO2e_P_QbTdFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QOIxcZGbFuLnj5qiY-JZYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QOIxcZGbFuLnj5qiY-JZYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uNgX7YYBBkbVtX3nSkv7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1NlId-XCC76cuSxZt5Lxmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1NlId-XCC76cuSxZt5Lxmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "92cX7YYBO2e_P_QbS8zy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HhM1qSGV_MIoNaDRcG0zzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HhM1qSGV_MIoNaDRcG0zzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E-IX7YYByh-A-BiyTBx8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b9stHUYeSwgP0eNPB72Qfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b9stHUYeSwgP0eNPB72Qfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YOIX7YYByh-A-BiyShVm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DOIX7YYByh-A-BiyTBo1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BDlisnvqa1LLQOmq1q0Eow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BDlisnvqa1LLQOmq1q0Eow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B-IX7YYByh-A-BiySRTi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JpUjFqAsKBeLb9NfBebEOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JpUjFqAsKBeLb9NfBebEOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} {"create": {"_index": "profiling-events-all", "_id": "BeIX7YYByh-A-BiySRTi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5BdZpdZPV1aCql-5O4HKoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5BdZpdZPV1aCql-5O4HKoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "nmcX7YYBO2e_P_QbSsgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yoM9M2D5c2dT8Htn9_oXJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yoM9M2D5c2dT8Htn9_oXJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "-GcX7YYBO2e_P_QbScad"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zlmxsTTPMJDp5d_OFnqBkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zlmxsTTPMJDp5d_OFnqBkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "FmcX7YYBO2e_P_QbS8o-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["i3VVbQEF8y09CAolsSQBvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["i3VVbQEF8y09CAolsSQBvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "R2cX7YYBO2e_P_QbTM67"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "FWcX7YYBO2e_P_QbTdAJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6mcX7YYBO2e_P_QbXNt0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qzypMgPc5-kylY6xJuiLOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qzypMgPc5-kylY6xJuiLOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vmcX7YYBO2e_P_QbWtY3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OXyCKNOqgn9jhCQIhnA3bQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OXyCKNOqgn9jhCQIhnA3bQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HmcX7YYBO2e_P_QbWdTt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uvyv7L8ko2gzorH4AufYNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uvyv7L8ko2gzorH4AufYNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nmcX7YYBO2e_P_QbaOMC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JOxaiAnBjaW3GYfe6qy1IQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JOxaiAnBjaW3GYfe6qy1IQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "nmcX7YYBO2e_P_QbXt8U"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DxFol4n0qYD3Of3DJSMdCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DxFol4n0qYD3Of3DJSMdCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "HeIX7YYByh-A-BiyaCtL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U4po32CSkExl1ZPtuJCrxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U4po32CSkExl1ZPtuJCrxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6WcX7YYBO2e_P_QbXNt0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8giK6mKV7HDPF-jB4e6ajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8giK6mKV7HDPF-jB4e6ajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "JeIX7YYByh-A-BiyWSCu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Ts2y4YyapGMgF7J-xZf2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Ts2y4YyapGMgF7J-xZf2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "qNgX7YYBBkbVtX3nd2ng"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rOjpZXDzMuqgXHFTBocx6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rOjpZXDzMuqgXHFTBocx6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "59gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["boIzddYopai9UjphB37nhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["boIzddYopai9UjphB37nhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2tFOqVqvUsAbYZdV5cBjZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2tFOqVqvUsAbYZdV5cBjZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WVvi39LiS0vDLyXeSsVBkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WVvi39LiS0vDLyXeSsVBkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "49gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Vn89WBJR0kfSEtwzji_DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Vn89WBJR0kfSEtwzji_DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9ixj_DfLg90_yfQ28UoVPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9ixj_DfLg90_yfQ28UoVPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "O9gX7YYBBkbVtX3neW4p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dzNbdSn_Zmll2UbzN8G_xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dzNbdSn_Zmll2UbzN8G_xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjtKZoprzXCLbmVAEEoqNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjtKZoprzXCLbmVAEEoqNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "StgX7YYBBkbVtX3neGso"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hjIdYJVrZu9s5d1oY1Nu2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hjIdYJVrZu9s5d1oY1Nu2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "zNgX7YYBBkbVtX3nenK1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VA9pzgeN6ktxH15wu8p4_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "7dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o-13S3KGROj5sAwzFCFlIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o-13S3KGROj5sAwzFCFlIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "zuIX7YYByh-A-Biydzcq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcPyRyUca9zMz9MzDr7aHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcPyRyUca9zMz9MzDr7aHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0AOiOeHK39oqr5eNGKOkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0AOiOeHK39oqr5eNGKOkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "CWcX7YYBO2e_P_QbefLC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RK5QOedYDJN8YhVo9FJwjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RK5QOedYDJN8YhVo9FJwjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "KGgX7YYBO2e_P_Qbtih6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uDeXsyAM1ry2gjp5w7NiBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uDeXsyAM1ry2gjp5w7NiBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MmgX7YYBO2e_P_QbtSOl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jzl9yNFalNrGUBfMA8dCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jzl9yNFalNrGUBfMA8dCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uGgX7YYBO2e_P_QbtiY7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lhRKXGZ_rGWBWtmKBhIK9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lhRKXGZ_rGWBWtmKBhIK9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HOIX7YYByh-A-Biyum09"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tTLMNSROu_QuNHWgUcK-cw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tTLMNSROu_QuNHWgUcK-cw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "KWgX7YYBO2e_P_Qbtih6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mu3LxyO4KAp-wuV_ZLnj9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mu3LxyO4KAp-wuV_ZLnj9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "t2gX7YYBO2e_P_QbtiY7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "1eIX7YYByh-A-Biytmj0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "i-IX7YYByh-A-BiyuGvz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ngVpwVwgO4T6nb-06wRKNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ngVpwVwgO4T6nb-06wRKNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "CGgX7YYBO2e_P_QbtSXs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Cn9LwUauC1J8ZOAWhiijEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Cn9LwUauC1J8ZOAWhiijEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "deIX7YYByh-A-Biyx3Rw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3bvdBbzWBhiwCbUR097jxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3bvdBbzWBhiwCbUR097jxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "C9gX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zEgMCcIzExJibQaME-QTUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zEgMCcIzExJibQaME-QTUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DNgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vSKLEOnt4ZdPD9kAJmGjbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vSKLEOnt4ZdPD9kAJmGjbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DtgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I_HDFrDrvMGFkT8QKDM_1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I_HDFrDrvMGFkT8QKDM_1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_dgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["61D2Ngpext0er1dkiTlWdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["61D2Ngpext0er1dkiTlWdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NmgX7YYBO2e_P_QbxzKv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZgDBfvwbGE_xfAHsOQTl1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZgDBfvwbGE_xfAHsOQTl1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "99gX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ptgX7YYBBkbVtX3nx7cq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S27o98amSiSOrrMpOLWfUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S27o98amSiSOrrMpOLWfUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "9tgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lzkdB1rDpdcMviENXaE3og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lzkdB1rDpdcMviENXaE3og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "-NgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GAokC6Zv-UfUvWotAYqkSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GAokC6Zv-UfUvWotAYqkSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "-WgX7YYBO2e_P_Qbxi0B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9f_l81ae-1ee1EVm4QM8Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9f_l81ae-1ee1EVm4QM8Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9dgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZfHuek4_BlYQGu77SdKXnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZfHuek4_BlYQGu77SdKXnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "tGgX7YYBO2e_P_Qb51bB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PEG6EskooaHoKsVK7C4oiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PEG6EskooaHoKsVK7C4oiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "luIX7YYByh-A-Biy6o0H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J_s4rwypZtS7LrmmQEztWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J_s4rwypZtS7LrmmQEztWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "8NgX7YYBBkbVtX3n6NKY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wl2yvlpS90Ypoy2M-skpww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wl2yvlpS90Ypoy2M-skpww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YdgX7YYBBkbVtX3n5MiB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-wPT7HKHltRvqN1m-PQHbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-wPT7HKHltRvqN1m-PQHbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuIX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7LeOL85PVjOg5Bi-S-b9RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7LeOL85PVjOg5Bi-S-b9RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "A9gX7YYBBkbVtX3n5coI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mnd31Vnx6i_r5WV2hM5bDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mnd31Vnx6i_r5WV2hM5bDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "tGgX7YYBO2e_P_Qb6Vmk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mD3V1vgmmXX17aY1Cc2kog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mD3V1vgmmXX17aY1Cc2kog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "--IX7YYByh-A-Biy5IDD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6xGHh1u34DhHIbK4IY9KBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6xGHh1u34DhHIbK4IY9KBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "AtgX7YYBBkbVtX3n5coI"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nwg53akFiAKZJpHiqCwAbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nwg53akFiAKZJpHiqCwAbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "zGgX7YYBO2e_P_Qb5lIe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kvqyEWe3mfnleSrT6I-tHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kvqyEWe3mfnleSrT6I-tHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "7OIX7YYByh-A-Biy5YKL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["chW5HPktN4b6gYA4Rc8JLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "394233360897966"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["chW5HPktN4b6gYA4Rc8JLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "394233360897966"} {"create": {"_index": "profiling-events-all", "_id": "V-IX7YYByh-A-Biy5oau"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OoPYq5Aw6d1wKTV_c9_UOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OoPYq5Aw6d1wKTV_c9_UOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "C2gX7YYBO2e_P_Qb6Vgq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MT3qrLXJyyFa5mMadoI1ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MT3qrLXJyyFa5mMadoI1ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "peIX7YYByh-A-Biy9ZKm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AbrWqkkHes4LJTZoISq1qQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AbrWqkkHes4LJTZoISq1qQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5NgX7YYBBkbVtX3n-eZU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LdgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e16fjaKgAD3mYYzxm1wovw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e16fjaKgAD3mYYzxm1wovw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P9gX7YYBBkbVtX3n-eUA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Et5sNZhAoszUicKSkeO_ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Et5sNZhAoszUicKSkeO_ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LtgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["An9XjOoTbvCjFLzBdFgpcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["An9XjOoTbvCjFLzBdFgpcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "T9gX7YYBBkbVtX3n-ONq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aNwZEBoTlKLxCLfBZC1w5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aNwZEBoTlKLxCLfBZC1w5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IdgX7YYBBkbVtX3n9Npj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whKjwtCVRYeUJb75GUn0Fw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whKjwtCVRYeUJb75GUn0Fw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "wOIX7YYByh-A-Biy9pUg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0eeGCGwvAUwir03MFPS_Kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0eeGCGwvAUwir03MFPS_Kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MNgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y-972spXlr1Uz9Eo6KX-Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y-972spXlr1Uz9Eo6KX-Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "o2gX7YYBO2e_P_Qb-WKP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ak0vCXdm7bXbIhn8MGGkXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ak0vCXdm7bXbIhn8MGGkXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VtgX7YYBBkbVtX3n9uDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6NkVutVoJ0m5j8aVYyp0Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6NkVutVoJ0m5j8aVYyp0Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "H9gX7YYBBkbVtX3n9Npj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2uW4N0T57kNGJTVG5_1zTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2uW4N0T57kNGJTVG5_1zTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "puIX7YYByh-A-Biy9ZKm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxJxXuPY21qu4ZQy4Vt22Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxJxXuPY21qu4ZQy4Vt22Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "JdgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pMpUaohMW1U4VleTGyqfTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pMpUaohMW1U4VleTGyqfTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "cOIX7YYByh-A-Biy95dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4lZbIrmqX0dcJVBKGnWp9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4lZbIrmqX0dcJVBKGnWp9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "e-IX7YYByh-A-Biy-Jux"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S4QSTs49REr7TSb5qbbUGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S4QSTs49REr7TSb5qbbUGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "pdgX7YYBBkbVtX3n9t6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AuqG1b42cXBbKiNJcLaKpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AuqG1b42cXBbKiNJcLaKpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "6OIX7YYByh-A-Biy9JDf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0pVn3RaIbpVhn8RviFIpJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0pVn3RaIbpVhn8RviFIpJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "c-IX7YYByh-A-Biy95dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "8GgX7YYBO2e_P_Qb9V0w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5Li3P-xnCI7OZMKdo3HZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5Li3P-xnCI7OZMKdo3HZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "keIY7YYByh-A-BiyE6vm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l-jFO6ESsoHoN6gyefmDNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l-jFO6ESsoHoN6gyefmDNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tGylLXpBEK5V82qwwulbVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tGylLXpBEK5V82qwwulbVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "f2gY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PeIY7YYByh-A-BiyGLXe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N8BeXVnVH94z3kcMpdZVRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N8BeXVnVH94z3kcMpdZVRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "c2gY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-WmMHqB8hxsW-_Rm9LtnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-WmMHqB8hxsW-_Rm9LtnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fGgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LO6aOR7Ea3Syr6nMwmmQpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LO6aOR7Ea3Syr6nMwmmQpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hmgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h2gY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ViM-Jm475_B9Vqa7GKjNDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ViM-Jm475_B9Vqa7GKjNDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rtgY7YYBBkbVtX3nF_jI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kKgAz5hOlhhX3Wlk6XRFig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kKgAz5hOlhhX3Wlk6XRFig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "emgY7YYBO2e_P_QbGHpR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gGgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FhJv1Eqg9cSQinz3oYYW7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FhJv1Eqg9cSQinz3oYYW7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "y-IY7YYByh-A-BiyFrB7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RcNPwSZ_RRjUo3KUMQkJwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RcNPwSZ_RRjUo3KUMQkJwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "eWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BRhCpm29bfxo9hoGCffNog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BRhCpm29bfxo9hoGCffNog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "bGgY7YYBO2e_P_QbJn6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jrkadKmUMKJNM1LSCgDP0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jrkadKmUMKJNM1LSCgDP0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IOIY7YYByh-A-BiyJLxn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yyOgLKUZuSQUa5BkL2jvpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yyOgLKUZuSQUa5BkL2jvpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qNkY7YYBBkbVtX3nKAVC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yyo4WtSHD0QUjPwdj4k3Xw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yyo4WtSHD0QUjPwdj4k3Xw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YuIY7YYByh-A-BiyKMoI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GLQz44vVtWQ5ppKiz2gP-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GLQz44vVtWQ5ppKiz2gP-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2-IY7YYByh-A-BiyJ8jR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KmEOZVutONuRJavBSb15QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KmEOZVutONuRJavBSb15QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "j-IY7YYByh-A-BiyJLod"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GV_l_2Wb3JncTPL0Vwsngg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GV_l_2Wb3JncTPL0Vwsngg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "wuIY7YYByh-A-BiyJL_0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dARW4-0nYV7kZ3Ww_-fsnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dARW4-0nYV7kZ3Ww_-fsnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0tkY7YYBBkbVtX3nOAq2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y81csNzyXiJ1pTbECyjzlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y81csNzyXiJ1pTbECyjzlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "09kY7YYBBkbVtX3nOAq2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ium0M6gtUd_sKOi4qCX1xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ium0M6gtUd_sKOi4qCX1xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HNkY7YYBBkbVtX3nNAgf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O0Oa-d1JiNvkWrWHXAez_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O0Oa-d1JiNvkWrWHXAez_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gOIY7YYByh-A-BiyNtki"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ZMz3dJFvpx5F2-aEpHESQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ZMz3dJFvpx5F2-aEpHESQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wmgY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydrSldLsPTdTf2IWl3R-qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydrSldLsPTdTf2IWl3R-qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MeIY7YYByh-A-BiyNNjt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "AWgY7YYBO2e_P_QbN5Bp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["33M_jV1gmHGxTPvzVsOhJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["33M_jV1gmHGxTPvzVsOhJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "uuIY7YYByh-A-BiyONo2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4Cu6oYF8CgThrL_OjB6KKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4Cu6oYF8CgThrL_OjB6KKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "w-IY7YYByh-A-BiyQtx5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PO3q1woza9yi3RpmXDEueA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PO3q1woza9yi3RpmXDEueA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "wGgY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G81V791m7uA9YBPgoQEn8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G81V791m7uA9YBPgoQEn8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "MOIY7YYByh-A-BiyNNjt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jliDtdpQ5AYvFVIEkH2R2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jliDtdpQ5AYvFVIEkH2R2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "8WgY7YYBO2e_P_QbNoxr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oaHIiYNNlfu1QZtM8GPcUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oaHIiYNNlfu1QZtM8GPcUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vOIY7YYByh-A-BiyONo2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UjcwmP94p5_9XdWwQfdoTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UjcwmP94p5_9XdWwQfdoTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gGgY7YYBO2e_P_QbN5Gs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q3gIWoUxhIk-V7r01h-8cQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q3gIWoUxhIk-V7r01h-8cQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "gWgY7YYBO2e_P_QbNYvj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-o3RzId6UYrkAkG0OoSJYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-o3RzId6UYrkAkG0OoSJYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "92gY7YYBO2e_P_QbNYme"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["42JG9a6NRfwi2CO7Z1RPNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["42JG9a6NRfwi2CO7Z1RPNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "SGgY7YYBO2e_P_QbOJVz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v3iq4oJQ3VCG0e1sWoxtkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v3iq4oJQ3VCG0e1sWoxtkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gGgY7YYBO2e_P_QbNYvj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gm1XQ2HBQFDtWIP658EsEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gm1XQ2HBQFDtWIP658EsEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "Z2gY7YYBO2e_P_QbVarR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4atjVCntPFZjlZxUD6MXCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4atjVCntPFZjlZxUD6MXCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "a2gY7YYBO2e_P_QbVq6S"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nMRtZlSwaA-3XiYGlSgMRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nMRtZlSwaA-3XiYGlSgMRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mOIY7YYByh-A-BiyU_DV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "idkY7YYBBkbVtX3nVRw-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Tf-YsisMn-8BkdhwRUXpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Tf-YsisMn-8BkdhwRUXpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "Y-IY7YYByh-A-BiyUu3D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MkzulSfrhjve_NGjxalUxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MkzulSfrhjve_NGjxalUxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "gGgY7YYBO2e_P_QbVqw4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2RyPkubYvOhcCvhjZgdRTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2RyPkubYvOhcCvhjZgdRTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "JdkY7YYBBkbVtX3nUxcK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PLWIfk3kyJVpG6Pe2YW5BQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PLWIfk3kyJVpG6Pe2YW5BQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "meIY7YYByh-A-BiyU_DV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MM2CztTXvV5i9K2i-2RGNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MM2CztTXvV5i9K2i-2RGNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "2mgY7YYBO2e_P_QbZsEV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VgHBJuj56ocTcdUEuVUkvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VgHBJuj56ocTcdUEuVUkvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mtkY7YYBBkbVtX3nZCuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NizhfNviinbrObC9ItpaWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NizhfNviinbrObC9ItpaWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y9kY7YYBBkbVtX3nZSze"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xtPrc0RhZSbX5O68FSRayg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xtPrc0RhZSbX5O68FSRayg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FtkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Iji_zQRXoBblaoaKwHTcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Iji_zQRXoBblaoaKwHTcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mWgY7YYBO2e_P_QbZLxc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eP_FGCwl0PRxWWvmJlwk5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eP_FGCwl0PRxWWvmJlwk5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "12gY7YYBO2e_P_QbcsmS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PkkcFOm60ARum3t1RkmFhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PkkcFOm60ARum3t1RkmFhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ENkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LdughXTyBAtPHlCiLsLIpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LdughXTyBAtPHlCiLsLIpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "E9kY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e-pIZo86wrOcd_F3vppZSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e-pIZo86wrOcd_F3vppZSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MmgY7YYBO2e_P_QbY7vw"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "m2gY7YYBO2e_P_QbZLxc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_eN577uJw5hksIBqBf1iCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_eN577uJw5hksIBqBf1iCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "iWgY7YYBO2e_P_QbZcCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2T6ASeyC2T0swmyL22ngjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2T6ASeyC2T0swmyL22ngjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "2GgY7YYBO2e_P_QbZsEV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wXgj8vfV7ExDQcf7NHp5Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wXgj8vfV7ExDQcf7NHp5Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1WgY7YYBO2e_P_QbcsmS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["743gS6kqzP62ApqBY3aWAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["743gS6kqzP62ApqBY3aWAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "NOIY7YYByh-A-Biycv3l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Oqbb6FakSaKBSmm5UVh16A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Oqbb6FakSaKBSmm5UVh16A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "GNkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["70FXRFUPPXVTyb52_Dovhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["70FXRFUPPXVTyb52_Dovhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6WgY7YYBO2e_P_QbY7m_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFNJ-Y5i5xbWccne1CdTAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFNJ-Y5i5xbWccne1CdTAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "0WgY7YYBO2e_P_QbZb1H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XkN81O9rpvZ7Hq2p_bCXWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XkN81O9rpvZ7Hq2p_bCXWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "iGgY7YYBO2e_P_QbZcCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bbHTlOSio2bcFnLJVCzI_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bbHTlOSio2bcFnLJVCzI_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "AGgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_8VdmeZ5UOnYprIIrYRWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_8VdmeZ5UOnYprIIrYRWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "02gY7YYBO2e_P_QbcsmS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FZB4LrFY55GOwy7SJHFGQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FZB4LrFY55GOwy7SJHFGQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "m2gY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcgTGw0xa6gEK3NEJ5iH1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcgTGw0xa6gEK3NEJ5iH1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "nmgY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jmx0gHeNEF8HBgePt0BNVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jmx0gHeNEF8HBgePt0BNVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "FOIY7YYByh-A-BiyZf0D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s3eG1ITOPVsdH2H5YruQiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s3eG1ITOPVsdH2H5YruQiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "LWgY7YYBO2e_P_QbccJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cyZBUJjFaFOr4hFXJVonyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cyZBUJjFaFOr4hFXJVonyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "J2gY7YYBO2e_P_QbccS0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kHBGm9hT2Ps-15ceIGS3fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kHBGm9hT2Ps-15ceIGS3fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "MuIY7YYByh-A-Biycv3l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MvydvJcdA5Fm40P_1i2ixQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MvydvJcdA5Fm40P_1i2ixQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0mgY7YYBO2e_P_QbZb1H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WgfE3EpDBODOIydfExij_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WgfE3EpDBODOIydfExij_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "UuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z7bOHlSkibuBBI3Vf-N5_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z7bOHlSkibuBBI3Vf-N5_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iv80T_6PcLzWJ9weG26b5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iv80T_6PcLzWJ9weG26b5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YBrq3-KR1ovSakEx4BXycQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YBrq3-KR1ovSakEx4BXycQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-gwAhagkJcxRJ6NcHmc9Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-gwAhagkJcxRJ6NcHmc9Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} {"create": {"_index": "profiling-events-all", "_id": "SeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [3], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [3], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "YeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2bYjKMpMW5W361PJ9SbEqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2bYjKMpMW5W361PJ9SbEqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "XuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8wMbNj2bmC_k-f1sjP1tvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "22781733237518"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8wMbNj2bmC_k-f1sjP1tvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "22781733237518"} {"create": {"_index": "profiling-events-all", "_id": "GeMY7YYByh-A-BiygQrM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5BXyGdP4pSqRCS_nYG5jHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5BXyGdP4pSqRCS_nYG5jHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Q-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EQ-MP_l-CkrAJlJbFI8e3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EQ-MP_l-CkrAJlJbFI8e3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ReMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4qft_3sVVVVKL2SEz3KAyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4qft_3sVVVVKL2SEz3KAyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pc3wbonmXheS0jJ7LgcLWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pc3wbonmXheS0jJ7LgcLWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qc4K52prFTkYQaEkp2a1aA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qc4K52prFTkYQaEkp2a1aA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["riI11_6NUOJGpJKmwVPhYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["riI11_6NUOJGpJKmwVPhYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ya1CUr1oSygfbTjmbb4XLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ya1CUr1oSygfbTjmbb4XLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1eMY7YYByh-A-BiydwlR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GPkYvKamsexuAvXWN9NtXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GPkYvKamsexuAvXWN9NtXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "cmgY7YYBO2e_P_Qbg9Ru"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fWJaqb09QzwUMPXDtHMSXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fWJaqb09QzwUMPXDtHMSXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "Z-MY7YYByh-A-BiyhRJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["32T4OaSlAZyX3gvcGH9I7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["32T4OaSlAZyX3gvcGH9I7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SaK90oQRsfih9wvkMg2Xgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SaK90oQRsfih9wvkMg2Xgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "WOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "MtkY7YYBBkbVtX3nlFIk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6u9I1yH1QSX15dNTqAV9bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6u9I1yH1QSX15dNTqAV9bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "f9kY7YYBBkbVtX3nkU6M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JtidomUfrSQ73J6IJRGkGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JtidomUfrSQ73J6IJRGkGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_uMY7YYByh-A-BiykRT_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pA-dO_FbLIeCPNajC9my7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pA-dO_FbLIeCPNajC9my7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "6GgY7YYBO2e_P_QbkuZr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wYQMafKDTOM5M3m09YsCqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wYQMafKDTOM5M3m09YsCqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "iWgY7YYBO2e_P_Qbk-kz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPOCasATDNT8t_l-saejjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPOCasATDNT8t_l-saejjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "CWgY7YYBO2e_P_QbkOHY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mxeu9v4mR_RhYkEQA098gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mxeu9v4mR_RhYkEQA098gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2tkY7YYBBkbVtX3nkU_F"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_D9JvJUWXzC0H3Nib_YHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_D9JvJUWXzC0H3Nib_YHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NmgY7YYBO2e_P_Qbk-uk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRszVWqkXDpjwjOG8s8zHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRszVWqkXDpjwjOG8s8zHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CGgY7YYBO2e_P_QbkOHY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_7SjIJ79HdCt2_IZxFKFsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_7SjIJ79HdCt2_IZxFKFsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "29kY7YYBBkbVtX3nkU_F"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BAgXx8nEHPgn_EenyoZUug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BAgXx8nEHPgn_EenyoZUug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "NNkY7YYBBkbVtX3nlFIk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IJ27fYwHthmwJsRGiAhneg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IJ27fYwHthmwJsRGiAhneg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9GgY7YYBO2e_P_Qble4x"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8aa5KIF0DFsrJsoVvEfajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8aa5KIF0DFsrJsoVvEfajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "_OMY7YYByh-A-BiykRT_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "F2gY7YYBO2e_P_QbkN5i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BQFGAMPCwBFVLxJFRXAPGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BQFGAMPCwBFVLxJFRXAPGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "CWgY7YYBO2e_P_QblO3v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JdkY7YYBBkbVtX3ns3Wc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HdkY7YYBBkbVtX3nsW5T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["twP61I8BoQSVRAEu87hitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["twP61I8BoQSVRAEu87hitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w-MY7YYByh-A-BiysSvY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bo6NdGV8GXHmalbT9Hz3Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bo6NdGV8GXHmalbT9Hz3Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tdkY7YYBBkbVtX3nsnET"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y5wRxzE9W7SQh2wOeWm08A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y5wRxzE9W7SQh2wOeWm08A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "8dkY7YYBBkbVtX3nsW-T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5BsomDuMI7TNerJ9VXCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5BsomDuMI7TNerJ9VXCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "kOMY7YYByh-A-BiypicL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "6dkY7YYBBkbVtX3nsGgr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbZWbU0S5kd22SAXz7exPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbZWbU0S5kd22SAXz7exPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "cWkY7YYBO2e_P_Qbvw38"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "u9kY7YYBBkbVtX3nwHk9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fN-FycZQoxGhCMzfnhVVLw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fN-FycZQoxGhCMzfnhVVLw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pGkY7YYBO2e_P_QbwxTd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lpK6Grg_nuFwWoAfFimM3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lpK6Grg_nuFwWoAfFimM3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HuMY7YYByh-A-BiyxDwb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hdKC-JUXcZ1pC3Sh2b32Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hdKC-JUXcZ1pC3Sh2b32Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EmkY7YYBO2e_P_QbvwpB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O4mxiScDrXWyuZch_ISgwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "190932526140742"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O4mxiScDrXWyuZch_ISgwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "190932526140742"} {"create": {"_index": "profiling-events-all", "_id": "otkY7YYBBkbVtX3nwHt6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wf7tYPnp7s196C4sU-0Jzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wf7tYPnp7s196C4sU-0Jzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "GuMY7YYByh-A-BiywDft"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g7q97S7Wxm4ynw0Afe3ikw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g7q97S7Wxm4ynw0Afe3ikw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "aeMY7YYByh-A-Biy1UwZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p-Vxhlw_iBQLyGOr_bdBDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p-Vxhlw_iBQLyGOr_bdBDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7tkY7YYBBkbVtX3n05Ok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SMLewOlFeXmKZa6xL_ARDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SMLewOlFeXmKZa6xL_ARDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lOMY7YYByh-A-Biy4lId"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GaCLxWirBhJtu1rdEHoD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GaCLxWirBhJtu1rdEHoD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "otkY7YYBBkbVtX3n3peC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vL8b2HSp2gXZRywmy6vg_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vL8b2HSp2gXZRywmy6vg_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wuMY7YYByh-A-Biy30wN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-7orDytvaM4kAysEKbhD_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-7orDytvaM4kAysEKbhD_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JeMY7YYByh-A-Biy4FGQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CdDJAFAwLQDf0PF-efgD8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CdDJAFAwLQDf0PF-efgD8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "aOMY7YYByh-A-Biy1UwZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IYMwZEdK-ayc3885mQPPQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IYMwZEdK-ayc3885mQPPQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "mtkY7YYBBkbVtX3n35pG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mG9aHndsiNY_nqP4GgB4qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mG9aHndsiNY_nqP4GgB4qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "0NkY7YYBBkbVtX3n7qNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdOVIhl_n9Wje--mxIItNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdOVIhl_n9Wje--mxIItNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "u2kY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fs1_jCyW9_zAyxKO8CT9iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fs1_jCyW9_zAyxKO8CT9iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E-MY7YYByh-A-Biy713T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8mR56EbpVUJgNap7DeaEUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8mR56EbpVUJgNap7DeaEUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1WkY7YYBO2e_P_Qb8jzI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mpnPy7d68UGd_rGkl2xRGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mpnPy7d68UGd_rGkl2xRGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UNkY7YYBBkbVtX3n8Kve"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xUZ2KALaaa1kqxkaDKw_ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xUZ2KALaaa1kqxkaDKw_ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pmkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GRaj-uYSzr92rlMpzMvXrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GRaj-uYSzr92rlMpzMvXrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "oWkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3MSb55knyBJ7ClwjPXRNwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3MSb55knyBJ7ClwjPXRNwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "bmkY7YYBO2e_P_Qb7jUi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["An3C9eVMXyiMNHRbyJ92Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["An3C9eVMXyiMNHRbyJ92Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "F9kY7YYBBkbVtX3n7qav"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0uG7yGrqQSSwb05Hvydocg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0uG7yGrqQSSwb05Hvydocg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "A2kY7YYBO2e_P_Qb7zcM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rrNic4YiyR-5vceCdE4IBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rrNic4YiyR-5vceCdE4IBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "POMY7YYByh-A-Biy71tL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["35FqNK7oEk5oJ-fRh4hptw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["35FqNK7oEk5oJ-fRh4hptw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "PuMY7YYByh-A-Biy71tL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pRkjCHqKoFZiqVbDY-3hgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pRkjCHqKoFZiqVbDY-3hgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ENkY7YYBBkbVtX3n8Kqc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9NG-4Nz_av4xih9kQ0ul7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9NG-4Nz_av4xih9kQ0ul7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LuMY7YYByh-A-Biy8V8f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YEm7NQBrTH5QHQqIE3fDrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YEm7NQBrTH5QHQqIE3fDrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "omkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PicgGG7wbtdmW_0WJKDC-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PicgGG7wbtdmW_0WJKDC-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "pWkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Hi1xqW7gnqMR0vJ_jyI7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Hi1xqW7gnqMR0vJ_jyI7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "uGkY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dwajp9wqywvmsgNufMFSDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dwajp9wqywvmsgNufMFSDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "z9kY7YYBBkbVtX3n7qNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pZUry6bTXYygY6NfqwYQNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pZUry6bTXYygY6NfqwYQNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "FeMY7YYByh-A-Biy713T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4lZbIrmqX0dcJVBKGnWp9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4lZbIrmqX0dcJVBKGnWp9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LWkY7YYBO2e_P_Qb8DhZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uRrKKaf_gbp1De235zmPrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uRrKKaf_gbp1De235zmPrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "UtkY7YYBBkbVtX3n8Kve"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tU6VK5zLihoNeJDRhxPnUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tU6VK5zLihoNeJDRhxPnUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "U9kY7YYBBkbVtX3n8Kve"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_bLJTesE_9jdes5CmhuIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_bLJTesE_9jdes5CmhuIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "02kY7YYBO2e_P_Qb8jzI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "adkY7YYBBkbVtX3n76eU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tWMg8g0Ama4NLtBSkd9DDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tWMg8g0Ama4NLtBSkd9DDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "DGkY7YYBO2e_P_Qb_kKm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qb72Yobg_yLohYI9gpP09w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qb72Yobg_yLohYI9gpP09w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2NkY7YYBBkbVtX3n_7SK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tJWsVLjM_WfOc08-LJ2QNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tJWsVLjM_WfOc08-LJ2QNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QGkZ7YYBO2e_P_QbAUXL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8WkZ7YYBO2e_P_QbAkYL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x6zpGPd4X75Br-x7FtPi9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x6zpGPd4X75Br-x7FtPi9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y2kY7YYBO2e_P_Qb_0PP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzgA-Ra9fc7BJY4Bt8KMwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzgA-Ra9fc7BJY4Bt8KMwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ymkY7YYBO2e_P_Qb_0PP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "N-MZ7YYByh-A-BiyIY5d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KGvPQmnNzLHCdXio5WQKoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KGvPQmnNzLHCdXio5WQKoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-all", "_id": "UuMZ7YYByh-A-BiyIZCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsokQSi3I4rVgRAwb8fhbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsokQSi3I4rVgRAwb8fhbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "I9kZ7YYBBkbVtX3nItGe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9xPGRL1R79V33i_hG1HhlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9xPGRL1R79V33i_hG1HhlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "--MZ7YYByh-A-BiyHoO_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["njwJdQnjALlyqqAczuUyDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["njwJdQnjALlyqqAczuUyDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZeMZ7YYByh-A-BiyIIrY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hAqeyX4eeyZmtR_G1TRong"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hAqeyX4eeyZmtR_G1TRong"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "POMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u_qmAc1-GJOSVHEZfMGXRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "MmkZ7YYBO2e_P_QbH13S"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ytkZ7YYBBkbVtX3nHcyi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E4k8bw63d-M6fpFO-uzDXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E4k8bw63d-M6fpFO-uzDXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "O-MZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OtPO4_Cde7GWru30XAUPmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OtPO4_Cde7GWru30XAUPmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xmkZ7YYBO2e_P_QbH1kG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gnrxxuGE4axnb7TUS1R0kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gnrxxuGE4axnb7TUS1R0kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "R-MZ7YYByh-A-BiyIIcW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o7QpavKpj9xFAwgY9jRDHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o7QpavKpj9xFAwgY9jRDHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QuMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZvwiecS6ape8IUAIjG0SKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZvwiecS6ape8IUAIjG0SKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "x2kZ7YYBO2e_P_QbH1kG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ysNAFKLCXGa-oh7cLstrPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ysNAFKLCXGa-oh7cLstrPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "xOMZ7YYByh-A-BiyH4VG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1zlIQyRrwSjFiz_6jtaVdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1zlIQyRrwSjFiz_6jtaVdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "StkZ7YYBBkbVtX3nHs4s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCS0frHA5BpnX_dWNuYGnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCS0frHA5BpnX_dWNuYGnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "M2kZ7YYBO2e_P_QbH13S"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "S9kZ7YYBBkbVtX3nHs4s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IHmYRt_J6aiZwjhJjkM_cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IHmYRt_J6aiZwjhJjkM_cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "pWkZ7YYBO2e_P_QbIV77"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "-eMZ7YYByh-A-BiyHoO_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yhXdoqpEpMhseJBw06VOcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yhXdoqpEpMhseJBw06VOcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "yNkZ7YYBBkbVtX3nIM9b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "i-MZ7YYByh-A-BiyIYwb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_wGEVT2AG1CIU-Fo31czqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_wGEVT2AG1CIU-Fo31czqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JeMZ7YYByh-A-BiyIpJU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lDXPFryqVEu45-jyL6avaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lDXPFryqVEu45-jyL6avaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "N2kZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K8gQh5zdfmwr_L8d6j_v5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K8gQh5zdfmwr_L8d6j_v5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZmkZ7YYBO2e_P_QbLWYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzEUg3uJDH9y4ttS90mXeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzEUg3uJDH9y4ttS90mXeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OWkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_upsguCu_7bMgt4lbFjaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_upsguCu_7bMgt4lbFjaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OmkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L037eGgcPD3WzV8I4bd-pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L037eGgcPD3WzV8I4bd-pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5dkZ7YYBBkbVtX3nLtZX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vXqajcj1VS3ltzfGYAymZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vXqajcj1VS3ltzfGYAymZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PGkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PcOQ99O4RLe8hzXLIXv3cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PcOQ99O4RLe8hzXLIXv3cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "O2kZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Cdup7ftqfVJjPGHBzmFw9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Cdup7ftqfVJjPGHBzmFw9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "D9kZ7YYBBkbVtX3nLdRG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m6Tpe3Eo4Y_x5AamWL8GLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m6Tpe3Eo4Y_x5AamWL8GLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "F2kZ7YYBO2e_P_QbL2tQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkp5EyZaH9EKC1Tx2EnCYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkp5EyZaH9EKC1Tx2EnCYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "4mUU7YYBO2e_P_QbwKIN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mTY_R13CdFxl1Dzfo0t_sA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mTY_R13CdFxl1Dzfo0t_sA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "898U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qvp6aS0dEuRo-26h2BBtzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qvp6aS0dEuRo-26h2BBtzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "J98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aqT_grJNIBLHd_G0Dg8D7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aqT_grJNIBLHd_G0Dg8D7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Ld8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QYRd432ews7Dx4JLAryfRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QYRd432ews7Dx4JLAryfRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "LdYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxA4A64BqMWXOrNZbvu1iA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxA4A64BqMWXOrNZbvu1iA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nd8U7YYByh-A-BiyusTM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["olxENnVm98xfSUbHZlsIjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["olxENnVm98xfSUbHZlsIjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f6waWCOFDS45u1AgmybjaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f6waWCOFDS45u1AgmybjaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNi8L_o5RGudv-i_EaBkuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNi8L_o5RGudv-i_EaBkuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Bt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bFqi88DUwWkr_8kK2-MSRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bFqi88DUwWkr_8kK2-MSRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Bc2bznRO7nqTExYFV3_HmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Bc2bznRO7nqTExYFV3_HmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Id8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kEXROxnWraXoAAVP6f28xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kEXROxnWraXoAAVP6f28xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Nt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0MR1l5n93T9RL0AOopmz6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0MR1l5n93T9RL0AOopmz6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LNYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1uuXblpY2G2lwZnvrUD5aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1uuXblpY2G2lwZnvrUD5aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Gd8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "HN8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_RxpKSu5Jrbu0E93Q0Uijg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_RxpKSu5Jrbu0E93Q0Uijg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ONYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "w2UU7YYBO2e_P_QbvJmg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihGJ_26t_QqommWWGt2AFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihGJ_26t_QqommWWGt2AFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "3N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPOCasATDNT8t_l-saejjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPOCasATDNT8t_l-saejjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Hd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-jgB9csnI_nQtRWte6ri7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-jgB9csnI_nQtRWte6ri7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "emUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6jaCYXksX4gXZ3wnqIzP_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6jaCYXksX4gXZ3wnqIzP_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "498U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5PHdMxxhN18mCCoCEYhigQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5PHdMxxhN18mCCoCEYhigQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MNYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eBcAbPDQFdqKTTFWbEKpQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eBcAbPDQFdqKTTFWbEKpQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rtUU7YYBBkbVtX3nu_4f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8bW6TIEkYi2BgDqcPdhRTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8bW6TIEkYi2BgDqcPdhRTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "J98U7YYByh-A-Biyu8Z1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nu3lDXuVVBXuKYArc5JOpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nu3lDXuVVBXuKYArc5JOpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9KuldbllYEEo0KaNazsUsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9KuldbllYEEo0KaNazsUsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Dt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZRkXnh8pLDVlUVidYeFDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZRkXnh8pLDVlUVidYeFDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4u9WOOyrWYLdgsjOh9aCUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4u9WOOyrWYLdgsjOh9aCUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "Hd8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2ssOcOjXCCaYX7ZddtppA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2ssOcOjXCCaYX7ZddtppA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "K9YU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4vYC-zQmWI-i2_kgUaJOlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4vYC-zQmWI-i2_kgUaJOlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "KN8U7YYByh-A-Biyu8Z1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0CzIBbH5H33fNR_K-h13Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0CzIBbH5H33fNR_K-h13Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "2N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lq2lfj5xkTFOSbFr4_BQ2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lq2lfj5xkTFOSbFr4_BQ2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dt_oZZ2sQo9aPZAJj8jPTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dt_oZZ2sQo9aPZAJj8jPTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "-N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZKNzQAHHe_cNd3rO-y4iLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZKNzQAHHe_cNd3rO-y4iLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "_98U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tbbZI-xVBmxiBDej_7HL0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tbbZI-xVBmxiBDej_7HL0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Md8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y10qk8LRFU3Juh0Dxf4kmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y10qk8LRFU3Juh0Dxf4kmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "3d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yVp49GPnTVvsmlWLVk3KEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yVp49GPnTVvsmlWLVk3KEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "Fd8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3r97L1FcTYBv0NSBEOY9kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3r97L1FcTYBv0NSBEOY9kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "n98U7YYByh-A-BiyusTM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5oBHen4DGgt6AeseHpHNhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5oBHen4DGgt6AeseHpHNhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "fmUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zl3Lkb99x2SkFZzpGc0tBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zl3Lkb99x2SkFZzpGc0tBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "Td8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wGohEkA_f1FytpRadmhYEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wGohEkA_f1FytpRadmhYEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Yt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k7UKTjtjxgCi_dJkL48hVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k7UKTjtjxgCi_dJkL48hVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jXPLTMNK5Wt6gi7cqc9W_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jXPLTMNK5Wt6gi7cqc9W_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hGUU7YYBO2e_P_Qb38Ek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAJ3qCHOXo3xE1EGVnJuHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAJ3qCHOXo3xE1EGVnJuHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kt8U7YYByh-A-Biy4ewW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6eTapoSsPn6zyk1_cvguaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6eTapoSsPn6zyk1_cvguaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "3t8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u2X_MgQedAiUni8lXoCIrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u2X_MgQedAiUni8lXoCIrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bdYU7YYBBkbVtX3n2BjO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "3N8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s_LM4tNvgy4k7bBRfDcNqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s_LM4tNvgy4k7bBRfDcNqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "a2UU7YYBO2e_P_Qb27nE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hpyllfzpp8_nbwc9QqhNUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hpyllfzpp8_nbwc9QqhNUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Tt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FGp7_YvYovHXJURqI7m4fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FGp7_YvYovHXJURqI7m4fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "a98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3cwgDFIm8qbQUpUKfKmwrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3cwgDFIm8qbQUpUKfKmwrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "et8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UMnm1x59Hw93aRPLKQaavQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UMnm1x59Hw93aRPLKQaavQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "hd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fpy7i-LnOT8PL2nB1iKlIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fpy7i-LnOT8PL2nB1iKlIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "md8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_fL9Sy2i4FwG6fgla2SkUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_fL9Sy2i4FwG6fgla2SkUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "zd8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Z-5rAaOPhdXYQmI34Fo4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Z-5rAaOPhdXYQmI34Fo4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Wt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6WYxVgKRkmzkkIzHH-6U9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6WYxVgKRkmzkkIzHH-6U9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "XN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["48LM-A4BXorXDMlzaCgnhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["48LM-A4BXorXDMlzaCgnhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "nt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6i_SCGQnGma1eU5i0B5EWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6i_SCGQnGma1eU5i0B5EWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "xt8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_PmLUDiMT9Fiy_kfGXHxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_PmLUDiMT9Fiy_kfGXHxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "098U7YYByh-A-Biy2eKG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["injLhEi_92EbuwxraOUUxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["injLhEi_92EbuwxraOUUxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XdYU7YYBBkbVtX3n2hx_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IKyFLy9N9kOhn3GGAEvAMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IKyFLy9N9kOhn3GGAEvAMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "298U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RrLvz0R4S4ONxlxpZkei3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RrLvz0R4S4ONxlxpZkei3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eGUU7YYBO2e_P_Qb3LtA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KtCekK_GfgQ-P7-c_JezYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KtCekK_GfgQ-P7-c_JezYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "U98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["djYzyWbheYppCF6OFaB-rw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["djYzyWbheYppCF6OFaB-rw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spYXnEQIsyd22QzNNaT8Yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spYXnEQIsyd22QzNNaT8Yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q21MZOSvgx9xEGx7DqfZtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q21MZOSvgx9xEGx7DqfZtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ot8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dPivlAC6aaFKRkKmSRwlpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dPivlAC6aaFKRkKmSRwlpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PFbB3COAmbAYRaYoh99KYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PFbB3COAmbAYRaYoh99KYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8tYU7YYBBkbVtX3n3h9C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nyNpeOOTv9ufpl_gGUbV4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nyNpeOOTv9ufpl_gGUbV4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xmUU7YYBO2e_P_Qb38OV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JxEVWjcx9CTh2J891LN34g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JxEVWjcx9CTh2J891LN34g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0d8U7YYByh-A-Biy1-BK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n-PAN0ssaXvJ6kY18i9tog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n-PAN0ssaXvJ6kY18i9tog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "d98U7YYByh-A-Biy3edS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uKWYFiw_KkWlIqqrtXASJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uKWYFiw_KkWlIqqrtXASJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rGUU7YYBO2e_P_Qb3r-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jipDuKi_84DxFQSN76f2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jipDuKi_84DxFQSN76f2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "Lt8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xpOWBBX4t2hmVnFh4nP3jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xpOWBBX4t2hmVnFh4nP3jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "ed8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9JtEJNwxEYbbvpjuHhBBig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9JtEJNwxEYbbvpjuHhBBig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "Ud8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xQF0-oJ8JdkDt-6rMsRLlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xQF0-oJ8JdkDt-6rMsRLlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "oN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UsBNmnLs0et-noTkkUwfYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UsBNmnLs0et-noTkkUwfYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "xWUU7YYBO2e_P_Qb38OV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X2j-ugZRpPjKsN5Ee5wiIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X2j-ugZRpPjKsN5Ee5wiIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "uWUU7YYBO2e_P_Qb4MZd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOL2fCOaJ1jf3dP2xu2v9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOL2fCOaJ1jf3dP2xu2v9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "iGUU7YYBO2e_P_Qb38Ek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jMGP_Z5cZtKhWETiwsHRiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jMGP_Z5cZtKhWETiwsHRiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "0t8U7YYByh-A-Biy2eKG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KcOiTTTgvYGRMXlpLOi98w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KcOiTTTgvYGRMXlpLOi98w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "e98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "z98U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TlylgEpMmjrNXKNFfM0qtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TlylgEpMmjrNXKNFfM0qtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "vGUU7YYBO2e_P_Qb4MZd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "3NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ed2Wt5gOq97H8-8youFpYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ed2Wt5gOq97H8-8youFpYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0OyGL51yTh9zlLSWgdw6NA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0OyGL51yTh9zlLSWgdw6NA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UeaUM7Om2vx-z6s0bBXPMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UeaUM7Om2vx-z6s0bBXPMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "j9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r17pt9-WKBMBu-MRlixjjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r17pt9-WKBMBu-MRlixjjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "p9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["npMsWxmmefd8KwLK_Ds63g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["npMsWxmmefd8KwLK_Ds63g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch10c9ij9u8WCrMXgg26Ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch10c9ij9u8WCrMXgg26Ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ttYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EXarUgAL9HIosZihvCe9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EXarUgAL9HIosZihvCe9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xr5GRqzEWQ1_fZjqV-PZpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xr5GRqzEWQ1_fZjqV-PZpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bYCfW7EaPTh_vnd_DSsC5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bYCfW7EaPTh_vnd_DSsC5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cJw71ZYPzEs9XDrLKegdnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cJw71ZYPzEs9XDrLKegdnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-9YU7YYBBkbVtX3n7TOb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PwiymugfyWZ7JNBkVfJTzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PwiymugfyWZ7JNBkVfJTzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "uNYU7YYBBkbVtX3n7jes"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DJsG1PTKQj3bE1cuaZCjpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DJsG1PTKQj3bE1cuaZCjpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "atYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "dNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihGJ_26t_QqommWWGt2AFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihGJ_26t_QqommWWGt2AFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "f9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bhyCyaPyN3IwoOhomCtSHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bhyCyaPyN3IwoOhomCtSHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "pdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V2lnTrpVtTUaSRDF47D7-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V2lnTrpVtTUaSRDF47D7-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "q9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eg4GNNub3CPns1G5g2R71w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eg4GNNub3CPns1G5g2R71w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "vtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wux22FE_iDeB58rMJzYkuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wux22FE_iDeB58rMJzYkuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "wtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K3Z7Bso8_acxSu6Vxdfbjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K3Z7Bso8_acxSu6Vxdfbjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "AdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o3FJgYr9HoLMDfWyiRLq9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o3FJgYr9HoLMDfWyiRLq9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ltYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YIkBS-ky7rLIF1FVQXh2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YIkBS-ky7rLIF1FVQXh2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "n9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzDNpxQnzmRTQIj87w61bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzDNpxQnzmRTQIj87w61bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "oNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ips6IyoH5NuP1Ttgu1Etow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ips6IyoH5NuP1Ttgu1Etow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "odYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pkyFSPLbfCpCJS0cbldBzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pkyFSPLbfCpCJS0cbldBzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Doitd-dudDRQURc3deY9nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Doitd-dudDRQURc3deY9nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["62xV6FZtZmytFhVdu0rarw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["62xV6FZtZmytFhVdu0rarw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "39YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t3HuJD9NhwbE9maFj9AONw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t3HuJD9NhwbE9maFj9AONw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["759vzPaqX5H2_0qTOKee0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["759vzPaqX5H2_0qTOKee0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ztYU7YYBBkbVtX3n-D_5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rzZCRVK3fR8zM07WhJJirw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rzZCRVK3fR8zM07WhJJirw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "c9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0GXb3m0wsHZfeNuzhQyTYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0GXb3m0wsHZfeNuzhQyTYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "d9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eviuUsW23vOjlBWQv0bv5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eviuUsW23vOjlBWQv0bv5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VfwVZh1ddTJjJNWNT0v7rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VfwVZh1ddTJjJNWNT0v7rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TakHMl9bLHFsMsirzPy3zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TakHMl9bLHFsMsirzPy3zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "l9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fHN81-0bXKBoEo0wx_S7CQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fHN81-0bXKBoEo0wx_S7CQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "09YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZSMUUx94Y3yXU6mhbsNCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZSMUUx94Y3yXU6mhbsNCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "19YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rwPz8ygB6KQKma7nayGgkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rwPz8ygB6KQKma7nayGgkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "29YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Dsg9VMTQ4os1LWYlVehJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Dsg9VMTQ4os1LWYlVehJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dC5N-uFbcqPYrdPkLxaQXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dC5N-uFbcqPYrdPkLxaQXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "o9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CJcaXL-gkWEKk5sisV1pCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CJcaXL-gkWEKk5sisV1pCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "ddYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rmqpLjKhFVehwbUcabYxkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rmqpLjKhFVehwbUcabYxkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "etYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lVm7T9NjlDcvHseuxZtScA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lVm7T9NjlDcvHseuxZtScA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "e9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kWwzipAAQMAm1ZJZNEkFsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kWwzipAAQMAm1ZJZNEkFsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f2RJTDjOG3CtdSv8hOKanw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f2RJTDjOG3CtdSv8hOKanw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "rtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xGXtUwy0WyT4ZsBj5B3XBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xGXtUwy0WyT4ZsBj5B3XBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "v9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HU4NPEh9jq0KD6Ucbp_cWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HU4NPEh9jq0KD6Ucbp_cWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "59YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6BMEwaZdEOxcFFELpK3iqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6BMEwaZdEOxcFFELpK3iqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "89YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5hj4hVJglP1Q0S9uCaavXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5hj4hVJglP1Q0S9uCaavXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WeDK4Wsp82FrbsJvJ81IVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WeDK4Wsp82FrbsJvJ81IVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "P2UU7YYBO2e_P_Qb7cxD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "d-AV7YYByh-A-BiyCBIn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YOAV7YYByh-A-BiyChop"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X-AV7YYByh-A-BiyChop"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DgucPUiNzHSpH364riJw7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DgucPUiNzHSpH364riJw7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5NYV7YYBBkbVtX3nDV0h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CghM7sv0n941Gxqx3t0UfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CghM7sv0n941Gxqx3t0UfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i-AV7YYByh-A-BiyCRYi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YxNEfLJqeU0ck91JlIJDyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YxNEfLJqeU0ck91JlIJDyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "XeAV7YYByh-A-BiyChop"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RZl8t-9rqMQDfV5hnTHtIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RZl8t-9rqMQDfV5hnTHtIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "weAV7YYByh-A-BiyCx2w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-tJlKr_KhSmekGKYSh387Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-tJlKr_KhSmekGKYSh387Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "d-AV7YYByh-A-BiyCRh4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y2rsoafmE6xytYWP5sYCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "66636157595941"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y2rsoafmE6xytYWP5sYCtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "66636157595941"} {"create": {"_index": "profiling-events-all", "_id": "j9YV7YYBBkbVtX3nBk4o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tn-1KeEU3BnmdtG_8ojEnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tn-1KeEU3BnmdtG_8ojEnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DdYV7YYBBkbVtX3nB1HM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wJ7q8DE_vEYNiHBNDFSXUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wJ7q8DE_vEYNiHBNDFSXUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L9YV7YYBBkbVtX3nCFPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JeAV7YYByh-A-BiyCxxq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8OAV7YYByh-A-BiyDB-G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lcGXu2A_kQpLv6e2M4Rs3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lcGXu2A_kQpLv6e2M4Rs3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "leAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydVfMca4pTKtV_nMQvo2kQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydVfMca4pTKtV_nMQvo2kQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_dYV7YYBBkbVtX3nCVTP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xKjCC5yf0r30Yx7ATik86A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xKjCC5yf0r30Yx7ATik86A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7uAV7YYByh-A-BiyDB-G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xGXtUwy0WyT4ZsBj5B3XBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xGXtUwy0WyT4ZsBj5B3XBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "PdYV7YYBBkbVtX3nDFzR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CyRDyPVW9ctHTgi1aqo0EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CyRDyPVW9ctHTgi1aqo0EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "duAV7YYByh-A-BiyCBIn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C0FEuqrCBrJSXy_icrEjSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C0FEuqrCBrJSXy_icrEjSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "dNYV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LOAV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["082Ba9ZuDVL3ONCoRsOt2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["082Ba9ZuDVL3ONCoRsOt2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yOAV7YYByh-A-BiyGSzz"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xmUV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "cWUV7YYBO2e_P_QbG-2M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v78L_ndncKY9XP2euXU8Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v78L_ndncKY9XP2euXU8Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "cNYV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GjKr7NR_B5mtmlpw2-1M7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GjKr7NR_B5mtmlpw2-1M7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "AWUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-tJlKr_KhSmekGKYSh387Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-tJlKr_KhSmekGKYSh387Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xeAV7YYByh-A-BiyGSzz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qktS_v13lC94BidNvaMyEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qktS_v13lC94BidNvaMyEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dtYV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U4MMCuBpnow2QDzcNOq_rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U4MMCuBpnow2QDzcNOq_rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y2UV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EscTPoBu7P-yWjfqYPYrXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EscTPoBu7P-yWjfqYPYrXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "puAV7YYByh-A-BiyHDGA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LuAV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HBL0k7Q3NY1Rzs8CB4mIaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HBL0k7Q3NY1Rzs8CB4mIaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yWUV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M56qbhAN0zSyPqoRnP2tig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M56qbhAN0zSyPqoRnP2tig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "o-AV7YYByh-A-BiyHDGA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "pOAV7YYByh-A-BiyHDGA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DMif4YgVmZnzbZZwItpS7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DMif4YgVmZnzbZZwItpS7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "BGUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mqlqe7dL-IrMXVeazPB5Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mqlqe7dL-IrMXVeazPB5Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "Z2UV7YYBO2e_P_QbGOeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9QQ4r43YuHwpw3ZT9p1xxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9QQ4r43YuHwpw3ZT9p1xxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5-AV7YYByh-A-BiyHC88"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VGkfGlLCT3CZxXjvshlG7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VGkfGlLCT3CZxXjvshlG7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "p-AV7YYByh-A-BiyHDGA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YM8VOmaiYixjkGqh3aYLRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YM8VOmaiYixjkGqh3aYLRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "peAV7YYByh-A-BiyHDGA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "hNYV7YYBBkbVtX3nLYc9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xSNZ9DKX9aJSx1JikG9CmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xSNZ9DKX9aJSx1JikG9CmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "o2YV7YYBO2e_P_QbOABd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOgvcGNEugo-q4Mte_An1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOgvcGNEugo-q4Mte_An1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2NVoBlds2Nc4h_QCDJrDnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2NVoBlds2Nc4h_QCDJrDnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "39YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K0IAThZo_rvcPV0xYOOPXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K0IAThZo_rvcPV0xYOOPXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "aNYV7YYBBkbVtX3nLIUO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nQzW1IRnoVkGXf3RVdA2zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nQzW1IRnoVkGXf3RVdA2zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "cGUV7YYBO2e_P_QbN_2B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uk1ygFuU89LLnNUfPAM8KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uk1ygFuU89LLnNUfPAM8KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "z9YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eu-uDVkqMSOny2oTSdBCFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eu-uDVkqMSOny2oTSdBCFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "F-AV7YYByh-A-BiyNUMG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8THUiHTgWMDGXf1IWeY26Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8THUiHTgWMDGXf1IWeY26Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "zNYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LpjlQ5x3C5y0GS9aUsntg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LpjlQ5x3C5y0GS9aUsntg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "ZGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pvgv_ahPIvTWXkMY-zr13A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pvgv_ahPIvTWXkMY-zr13A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ouAV7YYByh-A-BiyR1Tz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "dGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SKoD-DH2DuktCqfanvYyAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SKoD-DH2DuktCqfanvYyAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "gWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hjl6WiVIjOxuK6ZdXf6w-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hjl6WiVIjOxuK6ZdXf6w-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "gmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbKyofNFaA1HXxSLiY4X4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbKyofNFaA1HXxSLiY4X4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "m2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7M0HNFRDiBlp-s3D7tWjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7M0HNFRDiBlp-s3D7tWjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3LSphn5t1PDGJFGbkaC3PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3LSphn5t1PDGJFGbkaC3PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Z2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "aWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jJFQhyyI67HMRTa7_xT6Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jJFQhyyI67HMRTa7_xT6Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "dWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZV_tr06SpYoUw5FQNiY8zQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZV_tr06SpYoUw5FQNiY8zQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDmVY9Mljfrd1uHcDiDp-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDmVY9Mljfrd1uHcDiDp-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "j2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["19-BIdJDFsbAizxGj9jWhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["19-BIdJDFsbAizxGj9jWhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "kmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "nGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "omYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "YmYV7YYBO2e_P_QbRgy_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mzLwn00dO1KbECLOnE-3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mzLwn00dO1KbECLOnE-3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "gmYV7YYBO2e_P_QbTRNU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2TKowT3Mk2n52YOH2Zj2XA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2TKowT3Mk2n52YOH2Zj2XA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "c2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qmvE4JqUb_c7Db7yXDg5mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qmvE4JqUb_c7Db7yXDg5mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "kGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9j8nawarR-p9di_5gnPsKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9j8nawarR-p9di_5gnPsKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "l2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KmEOZVutONuRJavBSb15QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KmEOZVutONuRJavBSb15QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "1NYV7YYBBkbVtX3nSppo"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xdYV7YYBBkbVtX3nVKlD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8hoWuown2giiSuSwEtqAnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8hoWuown2giiSuSwEtqAnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0eAV7YYByh-A-BiyVFqd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hbFdZ00lApIoSJEOlowBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hbFdZ00lApIoSJEOlowBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RGYV7YYBO2e_P_QbWSCB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T7kTFHjAtS6OtzybnvJ0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T7kTFHjAtS6OtzybnvJ0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KmYV7YYBO2e_P_QbVhYN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3dYV7YYBBkbVtX3nWbXf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t-AV7YYByh-A-BiyVV6i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oSCp9cFxZ1aVa9L0c22cCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oSCp9cFxZ1aVa9L0c22cCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "FmYV7YYBO2e_P_QbVxih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q_0hpIuT4vi1WRoDxA9V3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q_0hpIuT4vi1WRoDxA9V3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "UdYV7YYBBkbVtX3nVa1D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NkkmX71PXT_4RUzWmyda5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NkkmX71PXT_4RUzWmyda5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "J-AV7YYByh-A-BiyWGBd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU-qPn31kCvhNCgJkxCSEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU-qPn31kCvhNCgJkxCSEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WWYV7YYBO2e_P_QbczmF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["codND57fF0ln0PPsgzvoNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["codND57fF0ln0PPsgzvoNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hOAV7YYByh-A-BiydnT4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J2g5fO93ezqUgypiuztojg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J2g5fO93ezqUgypiuztojg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zOAV7YYByh-A-BiydXL_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5NYvRSd87djiQAuRZMHZrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5NYvRSd87djiQAuRZMHZrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GtYV7YYBBkbVtX3ndtBc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zim2W2rYjohz6B9iTayl7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zim2W2rYjohz6B9iTayl7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "xWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C6VUfIIv3MbNvll1xucbVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C6VUfIIv3MbNvll1xucbVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "t2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tTpfeAZeBwNUUR0vm7VdPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tTpfeAZeBwNUUR0vm7VdPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "uWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2CWGwjnZxZvrumi7qK8KzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2CWGwjnZxZvrumi7qK8KzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "lmYV7YYBO2e_P_QbhEb-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bWkQYO8eq_v3XManPn1ThA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bWkQYO8eq_v3XManPn1ThA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "umYV7YYBO2e_P_Qbh0lI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NHR-vq-GiKf-T9dij8d0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NHR-vq-GiKf-T9dij8d0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3NYV7YYBBkbVtX3nieY3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0E7LlamNni9h1zgUjdYD5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0E7LlamNni9h1zgUjdYD5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GdYV7YYBBkbVtX3nhuK9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tXy3kfx8JYVKb1u9vHWM4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tXy3kfx8JYVKb1u9vHWM4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_2YV7YYBO2e_P_Qbg0S7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m3Xj1zX2LizEaWcI0RP5pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m3Xj1zX2LizEaWcI0RP5pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "6tYV7YYBBkbVtX3nh-MJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ExA0qEKb2y-al6iVdwwTBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ExA0qEKb2y-al6iVdwwTBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "1GYV7YYBO2e_P_Qbg0Jm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vk0sA1reerzoGdA4p7qrWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vk0sA1reerzoGdA4p7qrWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "4dYV7YYBBkbVtX3nhd6z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uc3rxkKkk8AS6xhrVwHG8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uc3rxkKkk8AS6xhrVwHG8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "iNYV7YYBBkbVtX3nheD0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9UeR8byKX2vZOFjGKyo1sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9UeR8byKX2vZOFjGKyo1sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "AGYV7YYBO2e_P_Qbg0W7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q2WGGCpyITTBJHm2o0dHlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q2WGGCpyITTBJHm2o0dHlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "CdYV7YYBBkbVtX3nhNrA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L6cJEyVPJDgBEJDXdVk3pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L6cJEyVPJDgBEJDXdVk3pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} {"create": {"_index": "profiling-events-all", "_id": "0dYV7YYBBkbVtX3nl_dj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A3ycyp-L3z38MavKYehVrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A3ycyp-L3z38MavKYehVrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "SNYV7YYBBkbVtX3nmPtF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7JUoTiaPyT_VuWNPYwhf1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7JUoTiaPyT_VuWNPYwhf1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "EeAV7YYByh-A-BiypJYN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R5Cb48qStI1GlPaxKWm-mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R5Cb48qStI1GlPaxKWm-mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "p-AV7YYByh-A-BiypZkc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "emYV7YYBO2e_P_Qbllh-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F42cuc6uAbdZGJ5REI0zrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F42cuc6uAbdZGJ5REI0zrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "f9cV7YYBBkbVtX3npABx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L5PYk_DA5ZMV0OoQVBb0Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L5PYk_DA5ZMV0OoQVBb0Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "2tYV7YYBBkbVtX3nl_UY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3C9vph60V3enG2gCmii1lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3C9vph60V3enG2gCmii1lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "O2YV7YYBO2e_P_Qbsmir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hz3UbtFZA0rYIYn4YRCgtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hz3UbtFZA0rYIYn4YRCgtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XeAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kQLQAgPodGZQIssiOLfgVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kQLQAgPodGZQIssiOLfgVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "atcV7YYBBkbVtX3nsgwF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lE8Ul76Ux_RbEcuXBt93-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lE8Ul76Ux_RbEcuXBt93-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KmYV7YYBO2e_P_QbtnB5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja-NqtogbhFMWHWfFPBr3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja-NqtogbhFMWHWfFPBr3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i9cV7YYBBkbVtX3nuB51"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e6FoyrtZm_iaPN3JoyL3hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e6FoyrtZm_iaPN3JoyL3hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JWYV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GY1eWbgNYGRrYMML_stAMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GY1eWbgNYGRrYMML_stAMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KGYV7YYBO2e_P_QbtnB5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["guvxESb_8YhISaL6-8e6xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["guvxESb_8YhISaL6-8e6xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ftcV7YYBBkbVtX3ntRfi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lX7-zRMdXP98ZhLRX4j9Qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lX7-zRMdXP98ZhLRX4j9Qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "aNcV7YYBBkbVtX3nsgwF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZEifOwXBahtAWgTrRIWHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZEifOwXBahtAWgTrRIWHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "OWYV7YYBO2e_P_Qbsmir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FuGHJWRe5aMwCq833fms7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FuGHJWRe5aMwCq833fms7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "X-AV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0gtJm_g6i_TRRAKd_qakTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0gtJm_g6i_TRRAKd_qakTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "0uAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EhcWlVVBHGJ89wrxw6v8dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EhcWlVVBHGJ89wrxw6v8dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "e9cV7YYBBkbVtX3ntRfi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A8pyJ84eyIC8RCi-Ni9jQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A8pyJ84eyIC8RCi-Ni9jQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "R9cV7YYBBkbVtX3nthkq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HMhRkMthUzKnK3YLiu0DPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HMhRkMthUzKnK3YLiu0DPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "YeAV7YYByh-A-BiytqnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JyUFbnbXVaKa7YEj_4FmBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JyUFbnbXVaKa7YEj_4FmBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ZeAV7YYByh-A-BiytqnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AgP4G3LQ52j-d6F0ClOqXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AgP4G3LQ52j-d6F0ClOqXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "M-AV7YYByh-A-Biyt6sa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pXRClGC06I9e9KXulLXsSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pXRClGC06I9e9KXulLXsSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ydcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "tmYV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Gg373ZQ4MQ2jkh8DEBd7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Gg373ZQ4MQ2jkh8DEBd7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8tcV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iz6TzFT8_J6ff04naDiS0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iz6TzFT8_J6ff04naDiS0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "2tcV7YYBBkbVtX3nsw-Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B2cQtnpQnOanc1V2PQKbyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B2cQtnpQnOanc1V2PQKbyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "JmYV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["obvZcLc8tkTW_PIiHCS5nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["obvZcLc8tkTW_PIiHCS5nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TuAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hUXn7O15YJeI0ULhNiHitQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hUXn7O15YJeI0ULhNiHitQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "UuAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YeAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVfsYo1AUUUdMdlCaFwu-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVfsYo1AUUUdMdlCaFwu-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gWYV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DCRsAykIrZ8I8tHgQy4yA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DCRsAykIrZ8I8tHgQy4yA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "g2YV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JB6F-U_ns7SY5JIwmO_kFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JB6F-U_ns7SY5JIwmO_kFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "KWYV7YYBO2e_P_QbtnB5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NGw-JF-qcO4DHjzt4B2wLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NGw-JF-qcO4DHjzt4B2wLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "uGYV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LMgC_pj236jbZulsolnmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LMgC_pj236jbZulsolnmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "wGYV7YYBO2e_P_Qbx3ol"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tx4eHSc1gmcx0PGWlGwBFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tx4eHSc1gmcx0PGWlGwBFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6NcV7YYBBkbVtX3n0ilU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XTk5UeO5E6dR53Or8LwcYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XTk5UeO5E6dR53Or8LwcYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YeAV7YYByh-A-Biy08WI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f18CNP-wdQ0HCwNNONpc3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f18CNP-wdQ0HCwNNONpc3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EmYV7YYBO2e_P_Qb037f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dJyQsD0pMmiwvo0yyQz-Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dJyQsD0pMmiwvo0yyQz-Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "59cV7YYBBkbVtX3n0ilU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZPTQfDgXmRrvRJctvBjMQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZPTQfDgXmRrvRJctvBjMQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7WYV7YYBO2e_P_Qbxnew"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N7_BJi6ewgXvZf2hE9Zv4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N7_BJi6ewgXvZf2hE9Zv4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "PmYV7YYBO2e_P_Qbxnnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TJIfdSm5tRZeEi6vWzdD9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TJIfdSm5tRZeEi6vWzdD9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "HGYV7YYBO2e_P_Qb0XuE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Z-5rAaOPhdXYQmI34Fo4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Z-5rAaOPhdXYQmI34Fo4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "HmYV7YYBO2e_P_Qb0XuE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wUfvGFMhsPYCiR2iraE6yA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wUfvGFMhsPYCiR2iraE6yA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "I-AV7YYByh-A-Biy0cDF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hgkg_Kmgo0P1NY1bWt3zqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hgkg_Kmgo0P1NY1bWt3zqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Y9cV7YYBBkbVtX3nxSih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pyc1NH6GKoq4eJ7lPXF0sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pyc1NH6GKoq4eJ7lPXF0sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HOAV7YYByh-A-Biy0sIJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mPA9NkH3378cVYxn3yS3sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mPA9NkH3378cVYxn3yS3sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ntcV7YYBBkbVtX3n1C-A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SvIqTeUVvtREJSFJV6baig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SvIqTeUVvtREJSFJV6baig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "f-AV7YYByh-A-Biy0b5D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_8l4R1w8OTMW19KvGaUFKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_8l4R1w8OTMW19KvGaUFKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "P2YV7YYBO2e_P_Qbxnnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXNleeK595fkvNMoEEpulQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXNleeK595fkvNMoEEpulQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "1dcV7YYBBkbVtX3n1C00"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PFH8JfGJ8gxGshJahx5FMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PFH8JfGJ8gxGshJahx5FMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "quAV7YYByh-A-Biy08M-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Iz58o69gDrMyQIJrUSlESQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Iz58o69gDrMyQIJrUSlESQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} {"create": {"_index": "profiling-events-all", "_id": "72YV7YYBO2e_P_Qbxnew"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PbqU-V8nn0Uur1oPUBWRCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PbqU-V8nn0Uur1oPUBWRCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "fuAV7YYByh-A-Biy0b5D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nNcV7YYBBkbVtX3n1C-A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EgKG3D5vsxLZ2SNdnZFPlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EgKG3D5vsxLZ2SNdnZFPlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "aGYV7YYBO2e_P_Qb44h4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D9deM4hA54-jD4QRfBZvyg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D9deM4hA54-jD4QRfBZvyg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TdcV7YYBBkbVtX3n5UgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-4SLKLQoS7j3p5TwUwKzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-4SLKLQoS7j3p5TwUwKzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "52YV7YYBO2e_P_Qb4oar"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Usa9goZrRSpJ79s2Sa1Vog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Usa9goZrRSpJ79s2Sa1Vog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "f9cV7YYBBkbVtX3n4Two"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N4MQ3_wF7ExRpMVmr34tag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N4MQ3_wF7ExRpMVmr34tag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GuAV7YYByh-A-Biy5d7m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5mYV7YYBO2e_P_Qb4oar"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "X-AV7YYByh-A-Biy5t9l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lLjNSon0kMlL_GeOGmhieQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lLjNSon0kMlL_GeOGmhieQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "tNcV7YYBBkbVtX3n4j9s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wl491oxaYlbc9Z3W8DsZgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wl491oxaYlbc9Z3W8DsZgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "v-AV7YYByh-A-Biy4NHi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vMqHPs9EIjuvSlEC4OiiMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vMqHPs9EIjuvSlEC4OiiMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "XuAV7YYByh-A-Biy4dNo"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0RLFnq52wVIAB0sP7d89Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0RLFnq52wVIAB0sP7d89Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "teAV7YYByh-A-Biy4dbn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NwR9aEq_NsygT1I-G4aUWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NwR9aEq_NsygT1I-G4aUWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "IdcV7YYBBkbVtX3n40O7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxb69r-7SoT16KEvWAhaDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uxb69r-7SoT16KEvWAhaDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ydcW7YYBBkbVtX3nBF4I"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uTpiMizzgYQSE0drb2SNgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uTpiMizzgYQSE0drb2SNgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NGYW7YYBO2e_P_QbBq4R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MyElinVLAEAN4F7lmOg8bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MyElinVLAEAN4F7lmOg8bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "4mYV7YYBO2e_P_Qb9p9p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VAqxR_4yWhELwHpSX2G6ng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VAqxR_4yWhELwHpSX2G6ng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "42YV7YYBO2e_P_Qb9p9p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yRFnGrrHGohZX1q__Ac-fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yRFnGrrHGohZX1q__Ac-fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KWYW7YYBO2e_P_QbAabd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HvoR4SCGNKrytsOmcHUMJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HvoR4SCGNKrytsOmcHUMJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SmYW7YYBO2e_P_QbAaSR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F2YW7YYBO2e_P_QbA6id"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bawlMqqRTjOm5tziwkLcwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bawlMqqRTjOm5tziwkLcwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "DtcW7YYBBkbVtX3nA11E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWr06tNuG4WxrmchIC2X3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWr06tNuG4WxrmchIC2X3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "btcV7YYBBkbVtX3n9lYm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iZrZGh0SZx7vPDLJtsMxOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iZrZGh0SZx7vPDLJtsMxOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "6GYW7YYBO2e_P_QbAKAj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["paq17JnBHQOEskwFc5mgww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["paq17JnBHQOEskwFc5mgww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "2uAW7YYByh-A-BiyAu7q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29tkR1iuog5-kDCdzfxqQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29tkR1iuog5-kDCdzfxqQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "HeEW7YYByh-A-BiyFQcS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jWYW7YYBO2e_P_QbFsMV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28KA4slgI2GZmK9Ldp9xgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28KA4slgI2GZmK9Ldp9xgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5eAW7YYByh-A-BiyD_jF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0XhwDDngWLjSP8tIl3SEwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0XhwDDngWLjSP8tIl3SEwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-eAW7YYByh-A-BiyEfoC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A9cW7YYBBkbVtX3nFGcu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jmYW7YYBO2e_P_QbFsMV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZjlOM4tBu1Fp11Fh5nR8-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZjlOM4tBu1Fp11Fh5nR8-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZGYW7YYBO2e_P_QbE72B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_D9JvJUWXzC0H3Nib_YHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_D9JvJUWXzC0H3Nib_YHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KtcW7YYBBkbVtX3nFGl3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUuQ2lSMaZyy3BCVVsDF1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUuQ2lSMaZyy3BCVVsDF1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ImYW7YYBO2e_P_QbFcGw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kxm_Vs_FFH-wRGO9m2XBVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "624455205497100"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kxm_Vs_FFH-wRGO9m2XBVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "624455205497100"} {"create": {"_index": "profiling-events-all", "_id": "gmYW7YYBO2e_P_QbFL_E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XNUe_HujD2dm4YoueIuXlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XNUe_HujD2dm4YoueIuXlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "KdcW7YYBBkbVtX3nFGl3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X4UPDmSUe7qQGbfeloDUvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X4UPDmSUe7qQGbfeloDUvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LGYW7YYBO2e_P_QbELiv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "-uAW7YYByh-A-BiyEfoC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wb8Q-SnAsfDM-7Ktdo9WMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wb8Q-SnAsfDM-7Ktdo9WMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "x9cW7YYBBkbVtX3nFWpl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_IL9L_uv3CfGfQbo7Tbz2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_IL9L_uv3CfGfQbo7Tbz2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "jGYW7YYBO2e_P_QbFsMV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BTbJZwKcIfQ2uNxAXoZhbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BTbJZwKcIfQ2uNxAXoZhbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "QdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lG8h35lC0oRWKY7OgUwEmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lG8h35lC0oRWKY7OgUwEmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DtcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPnyqYXZ1627cOAm3Dt4dA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPnyqYXZ1627cOAm3Dt4dA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} {"create": {"_index": "profiling-events-all", "_id": "2-EW7YYByh-A-BiyIA7i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A9cW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jY6an4TJNzicxfsoO4aEFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jY6an4TJNzicxfsoO4aEFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_9cW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tJrgFzG4FpnL3ryIXlyV6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tJrgFzG4FpnL3ryIXlyV6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "PNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2ywo0l6w7YZhxb-9njWcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2ywo0l6w7YZhxb-9njWcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "PtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0c6MsN22BR9PeSRno8jLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0c6MsN22BR9PeSRno8jLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "QNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "QtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gL8V3iQOuoY8AzmvM83Gdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gL8V3iQOuoY8AzmvM83Gdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "StcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch2MeEpHv6ftyPFPGwDJPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch2MeEpHv6ftyPFPGwDJPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "GmYW7YYBO2e_P_QbJMtU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ri8THh1H50J0zvIysOPcMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ri8THh1H50J0zvIysOPcMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "59cW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lN6iyovFymV6B1GKuV60Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lN6iyovFymV6B1GKuV60Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6NcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T8nh4Uz1hBa2hX7n6l8yLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T8nh4Uz1hBa2hX7n6l8yLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "79cW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cPPlFt_kc7DcftmxWZU-iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cPPlFt_kc7DcftmxWZU-iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "89cW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZEBSFm0BO7Q5NJ0sJOp5pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZEBSFm0BO7Q5NJ0sJOp5pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9tcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3y5nAKYNm-1QPvIH6-wLoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3y5nAKYNm-1QPvIH6-wLoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "99cW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bm8lqh7bD2DbMALavVY57A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bm8lqh7bD2DbMALavVY57A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "-NcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hkSlK3ySEaiDEWQMnnd57g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hkSlK3ySEaiDEWQMnnd57g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "BNcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f-mP1jAp98tb7PtN_trD8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f-mP1jAp98tb7PtN_trD8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7tcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5nMiVDy9d3FyKI4YMMJNGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5nMiVDy9d3FyKI4YMMJNGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "R9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "P9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "d9cW7YYBBkbVtX3nQo-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-TyLNaIus8xYXNJ7qDmYxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-TyLNaIus8xYXNJ7qDmYxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "29cW7YYBBkbVtX3nQo0Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vOAA7I3SI_6CYSE5PLnlzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "393338065829662"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vOAA7I3SI_6CYSE5PLnlzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "393338065829662"} {"create": {"_index": "profiling-events-all", "_id": "eNcW7YYBBkbVtX3nQYxi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LP2s2eN9q4S8yFbcMdo49A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LP2s2eN9q4S8yFbcMdo49A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hWYW7YYBO2e_P_QbRObI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "V-EW7YYByh-A-BiyRTUo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bQwiWS8atv5BrlgvoSejZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bQwiWS8atv5BrlgvoSejZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h-EW7YYByh-A-BiyPiek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2Jekw_GmjkRbs2II8a1AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g2Jekw_GmjkRbs2II8a1AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ReEW7YYByh-A-BiyQSqw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dQoejHiZXRe2q1PMrGN9Aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dQoejHiZXRe2q1PMrGN9Aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WOEW7YYByh-A-BiyRTUo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BgnwfcudspKPFADqFnojuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BgnwfcudspKPFADqFnojuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "8OEW7YYByh-A-BiyQCi1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["woH9c39lhinU4GavawjQuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["woH9c39lhinU4GavawjQuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "zGYW7YYBO2e_P_QbQeID"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RBIYJkbW5-Ss2DSyBgMD1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RBIYJkbW5-Ss2DSyBgMD1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "3dcW7YYBBkbVtX3nQo0Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jbue-qNayNZ_fIEQln1nAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jbue-qNayNZ_fIEQln1nAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "YOEW7YYByh-A-BiyQi3_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5e4O7lBx37gz0fcZIzqtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5e4O7lBx37gz0fcZIzqtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MdcW7YYBBkbVtX3nQItF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bxosazizFV9avn6dlwjxfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bxosazizFV9avn6dlwjxfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "heEW7YYByh-A-BiyPiek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g15iIWCPMhS3lvfL06AkwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g15iIWCPMhS3lvfL06AkwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "7-EW7YYByh-A-BiyQCi1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UlhGaNTjh2CqZiofxpZ5rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UlhGaNTjh2CqZiofxpZ5rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "u2YW7YYBO2e_P_QbPtv2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["i3VVbQEF8y09CAolsSQBvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["i3VVbQEF8y09CAolsSQBvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "t-EW7YYByh-A-BiyRDN8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iMrQPudvsPLod26LuW-2pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iMrQPudvsPLod26LuW-2pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "a9cW7YYBBkbVtX3nT5d5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m55Znt_y0UyjemF1cQhp1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m55Znt_y0UyjemF1cQhp1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5mYW7YYBO2e_P_QbUOsU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VkyfaqGAgzJ1mrE4QyhFNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VkyfaqGAgzJ1mrE4QyhFNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KNcW7YYBBkbVtX3nUJll"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J2g5fO93ezqUgypiuztojg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J2g5fO93ezqUgypiuztojg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jeEW7YYByh-A-BiyUT4z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JK8YqiAWSqqVOym-FM3Bcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JK8YqiAWSqqVOym-FM3Bcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YWYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDY23NPQzaDDr0M473XGdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDY23NPQzaDDr0M473XGdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZGYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZROPXiO1YN29GxsBWCCa4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZROPXiO1YN29GxsBWCCa4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZWYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5K8-gusySTkvMx2KwQOwvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5K8-gusySTkvMx2KwQOwvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CGYW7YYBO2e_P_QbUe_C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XelrCg-bWU72nb6t1bBQsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XelrCg-bWU72nb6t1bBQsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "tuEW7YYByh-A-BiyTzvE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ovZA3MLYLkKXQB_OLfVFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ovZA3MLYLkKXQB_OLfVFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "69cW7YYBBkbVtX3nTpOO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3U1GssMMNyEfwWToQlllWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3U1GssMMNyEfwWToQlllWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "cdcW7YYBBkbVtX3nUpqO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "4NcW7YYBBkbVtX3nY6tx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-4SLKLQoS7j3p5TwUwKzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-4SLKLQoS7j3p5TwUwKzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "l2cW7YYBO2e_P_QbbggN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hp7nnrgj4rg9PepT5KvZxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hp7nnrgj4rg9PepT5KvZxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MmcW7YYBO2e_P_QbYgMK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vg_UUeeyZsIQBEa6Nvh43w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vg_UUeeyZsIQBEa6Nvh43w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_WYW7YYBO2e_P_QbYP9I"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L-ilYQocauuNE095JEhEhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L-ilYQocauuNE095JEhEhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "zmcW7YYBO2e_P_QbYgTe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y4O8D8hxYkhFJTYN-hGU2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y4O8D8hxYkhFJTYN-hGU2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ytcW7YYBBkbVtX3nc7iI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SrDodAnZ9uPT0nyBwub87g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "K-EW7YYByh-A-BiyfmAg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYsp1kqfqOBHCXrvmwLiMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYsp1kqfqOBHCXrvmwLiMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A2cW7YYBO2e_P_QbcRL4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m9ekiTsBlnl55IAShmMs6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m9ekiTsBlnl55IAShmMs6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AGcW7YYBO2e_P_QbgBRD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ6jYHQvenC-oIeTFn_Bnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ6jYHQvenC-oIeTFn_Bnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AWcW7YYBO2e_P_QbgBRD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rOEW7YYByh-A-BiyfV6p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wlmshnU41XskzIJphrG7Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wlmshnU41XskzIJphrG7Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "cWcW7YYBO2e_P_QbfRJk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOXMz1epa3_ioZurkFW7Lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOXMz1epa3_ioZurkFW7Lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "GeEW7YYByh-A-Biyf2a2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "JNcW7YYBBkbVtX3njsRV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UHDtJaJSDV9olnFzaBKrSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UHDtJaJSDV9olnFzaBKrSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "0tcW7YYBBkbVtX3nksnh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bbBiaFslvpreG7iqHkAtng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bbBiaFslvpreG7iqHkAtng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "oWcW7YYBO2e_P_QbkSZ4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7E3wyc4NU7VCrTPNTghNCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7E3wyc4NU7VCrTPNTghNCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EtcW7YYBBkbVtX3nk83N"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cpTq_fYtizjUZY0WgZic9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cpTq_fYtizjUZY0WgZic9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ENcW7YYBBkbVtX3nkcYc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZAocMl5gXpxySXSm5DNBqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZAocMl5gXpxySXSm5DNBqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DeEW7YYByh-A-BiyjXAa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "a2cW7YYBO2e_P_QbkSjL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["frX5XDi_BeSd8ATSu-BzMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["frX5XDi_BeSd8ATSu-BzMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uGcW7YYBO2e_P_QbkymJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FZiQmOxfnnxBneo5kx5hbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FZiQmOxfnnxBneo5kx5hbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DOEW7YYByh-A-BiyjXAa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y8UVXYWOaxAeCcWGwyqDaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y8UVXYWOaxAeCcWGwyqDaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "PuEW7YYByh-A-BiyjnPr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b3E9ZJZY0ClQ35xwbM-URQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b3E9ZJZY0ClQ35xwbM-URQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EdcW7YYBBkbVtX3nkcYc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["422YB3Vi5ylq09ThSitCrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["422YB3Vi5ylq09ThSitCrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "udcW7YYBBkbVtX3nk8tE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TuM-SJ9cGA7C5rVB2mzPbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TuM-SJ9cGA7C5rVB2mzPbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EWcW7YYBO2e_P_QbkCRr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x4JagFwIYKM4hCWjdkk5Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x4JagFwIYKM4hCWjdkk5Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "TWcW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PdcW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fba2v_kCgR0XMDcTdnNyUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fba2v_kCgR0XMDcTdnNyUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xGcW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n73U5dOg61JklJT6WKmxuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n73U5dOg61JklJT6WKmxuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GeEW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wBC8PW2stc2snL3In1dZgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wBC8PW2stc2snL3In1dZgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ItcW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sZEqvMD6I0UREvB3izGzGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sZEqvMD6I0UREvB3izGzGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "o2cW7YYBO2e_P_QboDfC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hqZLEegbkzGMCD8s4XbApA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hqZLEegbkzGMCD8s4XbApA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "z9cW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bgfofmlWcYjAIJ7veGUfFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bgfofmlWcYjAIJ7veGUfFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jNcW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7i3Cx4zEv0tNGbpsMJIrtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7i3Cx4zEv0tNGbpsMJIrtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5OEW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9tcW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EZUfXP4Nc5xC-6yjjFWFXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EZUfXP4Nc5xC-6yjjFWFXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lWcW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIjbpW4afQJ6fKp4bSOkMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIjbpW4afQJ6fKp4bSOkMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HGcW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KlBBeb_BAMwNu68sN8StmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KlBBeb_BAMwNu68sN8StmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FWcW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_u0T-bb-6oGwzLo8Ixk4jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_u0T-bb-6oGwzLo8Ixk4jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "GuEW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mf-R-1299D4bJft-Ookh1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mf-R-1299D4bJft-Ookh1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "l2cW7YYBO2e_P_QbnzSu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XuoRBwH8D9PqSHFLLM0iiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "leEW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["emxPOGrmfJB_o3OYwaU-OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["emxPOGrmfJB_o3OYwaU-OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "l-EW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4r8rDC-HuMcBsJ3v8w5X0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4r8rDC-HuMcBsJ3v8w5X0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jdcW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l7TOnfsZ0Oz_cxAh8t_TMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l7TOnfsZ0Oz_cxAh8t_TMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "P9cW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["scKMoVIbbsXT0ePI0cAHEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["scKMoVIbbsXT0ePI0cAHEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "oOEW7YYByh-A-BiyrYy7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BnraydbvEwL6mkTBVZOVLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BnraydbvEwL6mkTBVZOVLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ZuEW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tfP5YaLMRchIJv1xrJ1yhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tfP5YaLMRchIJv1xrJ1yhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "l2cW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KPRFk1hV8cCqMtGiVI0VUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KPRFk1hV8cCqMtGiVI0VUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "mGcW7YYBO2e_P_QbnzSu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IqqI5g2QWaQwbYuVPZB2iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IqqI5g2QWaQwbYuVPZB2iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "INcW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DbsdNQcghdeUQULeZY1Wfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DbsdNQcghdeUQULeZY1Wfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "4eEW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["93n0e7h5H7aFXvMK8FoA2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["93n0e7h5H7aFXvMK8FoA2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "PNcW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7pCWL6qVpk6cdOVpmC7rqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7pCWL6qVpk6cdOVpmC7rqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ReEW7YYByh-A-BiyrYsf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wHyMmE8SVaekDujzx_Uidg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wHyMmE8SVaekDujzx_Uidg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "gdcW7YYBBkbVtX3nrNrT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fQniEpOBiPV91AqeXKUYaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fQniEpOBiPV91AqeXKUYaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "Q-EW7YYByh-A-BiyrYsf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rdrM64mgJOBslxLrMQ7wSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rdrM64mgJOBslxLrMQ7wSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "QeEW7YYByh-A-BiyrYsf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aTZihWAEPXUldJhLCeEBBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aTZihWAEPXUldJhLCeEBBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "DWcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GQlu4cDmBP0J7ys3CIDFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GQlu4cDmBP0J7ys3CIDFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CGcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7E3wyc4NU7VCrTPNTghNCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7E3wyc4NU7VCrTPNTghNCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EWcW7YYBO2e_P_Qbu0br"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z897RYYv5sVYFZXocfXeZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z897RYYv5sVYFZXocfXeZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fuEW7YYByh-A-BiyvqKW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["57Fil5UcCT4QMA8PK7lldw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["57Fil5UcCT4QMA8PK7lldw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "62cW7YYBO2e_P_QbsUPY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "COEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "vGcW7YYBO2e_P_Qbu0Sm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xOEW7YYByh-A-Biyv6MJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sMrntcfAzsvFpOczgTISXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sMrntcfAzsvFpOczgTISXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "MdcW7YYBBkbVtX3nv-qT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZsYEzt_8lrTbaZDB8kywA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZsYEzt_8lrTbaZDB8kywA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "9uEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["efbR3WWiUOAtCglTIhsrqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["efbR3WWiUOAtCglTIhsrqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "9eEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YABibb_jw0z2mFZJ8rsBIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YABibb_jw0z2mFZJ8rsBIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EmcW7YYBO2e_P_Qbu0br"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tPsVwrBNUc_Ucb0xgpo9Aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tPsVwrBNUc_Ucb0xgpo9Aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FWcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ir6dnl0cXTDA9lqUj6YdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ir6dnl0cXTDA9lqUj6YdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DmcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5L0Vg1E8eRaEol71UFTwGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5L0Vg1E8eRaEol71UFTwGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pNcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7k09_Fy75Q9-PpHdDlKvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7k09_Fy75Q9-PpHdDlKvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "otcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-yIay2Jhm3BbFiMI2RaPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-yIay2Jhm3BbFiMI2RaPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "fOEW7YYByh-A-BiyvqKW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1hW5v3HRKvG-GrmY80R18g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1hW5v3HRKvG-GrmY80R18g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "NtcW7YYBBkbVtX3nveeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q4JtKPdD84sGcDCNyN6nPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "844449768587301"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q4JtKPdD84sGcDCNyN6nPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "844449768587301"} {"create": {"_index": "profiling-events-all", "_id": "dGcW7YYBO2e_P_QbvEm2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MGOK31fQ4tvuxuQh2V8_TA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741972803009497"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MGOK31fQ4tvuxuQh2V8_TA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741972803009497"} {"create": {"_index": "profiling-events-all", "_id": "_OEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6WcW7YYBO2e_P_QbvEco"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["i3VVbQEF8y09CAolsSQBvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["i3VVbQEF8y09CAolsSQBvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "EeEW7YYByh-A-BiyvaHP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3huOpz_dzO1rKry9zYBuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3huOpz_dzO1rKry9zYBuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "DGcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fp3JKxC8Kg-FrE8ZKU_2DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fp3JKxC8Kg-FrE8ZKU_2DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "edcW7YYBBkbVtX3ny_KF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IuEW7YYByh-A-Biyz7ao"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rMpzXndoIcEiY0-GRAGnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rMpzXndoIcEiY0-GRAGnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "meEW7YYByh-A-Biyz7fk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CfSmew3N7q6MPA7XYHOAyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CfSmew3N7q6MPA7XYHOAyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GWcW7YYBO2e_P_QbzVOk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ETxTEgZSbzYjk8XGh4vHbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ETxTEgZSbzYjk8XGh4vHbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "weEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3EdY1eU9gJRJ5-pD1F-9zQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3EdY1eU9gJRJ5-pD1F-9zQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yeEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZRkXnh8pLDVlUVidYeFDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZRkXnh8pLDVlUVidYeFDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "I-EW7YYByh-A-Biyz7ao"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ql-7Y2YW7YrNN7ni_2nHhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ql-7Y2YW7YrNN7ni_2nHhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "DuEW7YYByh-A-BiyzK0R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ORYJnd66MJOP1pD9p0bgYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "845379217314054"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ORYJnd66MJOP1pD9p0bgYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "845379217314054"} {"create": {"_index": "profiling-events-all", "_id": "nOEW7YYByh-A-BiyzK7K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bnUDt83Ym2Sj0RWXP2Cv7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bnUDt83Ym2Sj0RWXP2Cv7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "mtcW7YYBBkbVtX3ny_BB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "wuEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["81PIAyCx2Qr8_sB0OgM2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["81PIAyCx2Qr8_sB0OgM2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "idcW7YYBBkbVtX3nzfVn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PBy4E9YE68R_J8izQs8bgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PBy4E9YE68R_J8izQs8bgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "xNcW7YYBBkbVtX3nz_hr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CAKMNMeNYggEGwOcAHqlpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912668704793740"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CAKMNMeNYggEGwOcAHqlpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912668704793740"} {"create": {"_index": "profiling-events-all", "_id": "XeEW7YYByh-A-Biy38W8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hD5xCbVnNYVsLiq_B3NCDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hD5xCbVnNYVsLiq_B3NCDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SmcW7YYBO2e_P_Qb3V8e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zksj6C38tiqx_uPlkG3-oQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zksj6C38tiqx_uPlkG3-oQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gmcW7YYBO2e_P_Qb4GUL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "odgW7YYBBkbVtX3n4AeL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C3gJbGJLW5qO4iB7Vj3kzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C3gJbGJLW5qO4iB7Vj3kzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "gWcW7YYBO2e_P_Qb4GUL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "veEW7YYByh-A-Biy4MbF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v5EpA4krR2ROLXfbpheNHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "747188141952000"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v5EpA4krR2ROLXfbpheNHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "747188141952000"} {"create": {"_index": "profiling-events-all", "_id": "_uEW7YYByh-A-Biy27u4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-39O5M9xiKV4ss-qyTu_Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-39O5M9xiKV4ss-qyTu_Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "CdgW7YYBBkbVtX3n_BuV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQljazbrYNKb17CR1zcj2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQljazbrYNKb17CR1zcj2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fGcW7YYBO2e_P_Qb_XYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rfbDR_zpgC01-kkTCN3O8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rfbDR_zpgC01-kkTCN3O8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BdgW7YYBBkbVtX3n-xhN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gIiBrAZ3-GkFJ4HVnfTsCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gIiBrAZ3-GkFJ4HVnfTsCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hOEW7YYByh-A-Biy-9YK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cn_9IfzsC8Tr9nDWV2mNig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cn_9IfzsC8Tr9nDWV2mNig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fuEW7YYByh-A-Biy-9iI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pS9eiRRqpJHZ001DHnp6Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pS9eiRRqpJHZ001DHnp6Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lNgW7YYBBkbVtX3n_RyM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JoNW0S9pSsiAmHByB5KnMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JoNW0S9pSsiAmHByB5KnMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "89gW7YYBBkbVtX3n-hQh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fFW4hYJ0SQeKKNYH-nfcDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fFW4hYJ0SQeKKNYH-nfcDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "W-EW7YYByh-A-Biy-9rd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u29RuXTUI9L-Xut890hyuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u29RuXTUI9L-Xut890hyuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "fWcW7YYBO2e_P_Qb_XYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LdgW7YYBBkbVtX3n_R7G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9UOoQ2kKxEY1n0AS8biVEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9UOoQ2kKxEY1n0AS8biVEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8mcW7YYBO2e_P_Qb_nlT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uqbj6xXSR4L1HQjjfr6tw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uqbj6xXSR4L1HQjjfr6tw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "HGcW7YYBO2e_P_Qb_n7b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hIfw2Nju3QIW5tv0qLI6DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hIfw2Nju3QIW5tv0qLI6DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "veEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3blkVUmhYGlb1bNhKqNU-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3blkVUmhYGlb1bNhKqNU-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "tmcW7YYBO2e_P_Qb_nuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQ1fVd58k2fSqjQSJ4y2iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQ1fVd58k2fSqjQSJ4y2iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "DOEW7YYByh-A-Biy_Nwe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kE4sjHZhfkpbgfq0NZt7Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kE4sjHZhfkpbgfq0NZt7Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "DWcW7YYBO2e_P_Qb_XhM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JXC2aswffzFwIASa8HM7TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JXC2aswffzFwIASa8HM7TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "gGcX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BOn5YfVXLbYJVzgO0D8UNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BOn5YfVXLbYJVzgO0D8UNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yeEX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IcP7NfEq0GawQQCHmZWcRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IcP7NfEq0GawQQCHmZWcRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XeEX7YYByh-A-BiyDev8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yaVT1XFUH-nJ3nm8j75Wtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yaVT1XFUH-nJ3nm8j75Wtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pmcX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JoNW0S9pSsiAmHByB5KnMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JoNW0S9pSsiAmHByB5KnMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BNgX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oLIJJ3vFP9iOSdpBALj2Hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oLIJJ3vFP9iOSdpBALj2Hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uGcX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1x1DYtBgANQEqmgJHFNwCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1x1DYtBgANQEqmgJHFNwCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F2cX7YYBO2e_P_QbDI3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "p2cX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3HYswCLIguo6i_KRnM6AYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3HYswCLIguo6i_KRnM6AYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "42cX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_z_VvquVclvHpAURI8mubg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_z_VvquVclvHpAURI8mubg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jeEX7YYByh-A-BiyC-Nk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vUd7LUOlEzT1w32bH1zYbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vUd7LUOlEzT1w32bH1zYbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "yOEX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qUy-ynHNFuJ7a24qIM4sMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qUy-ynHNFuJ7a24qIM4sMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "c-EX7YYByh-A-BiyDedc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "M2cX7YYBO2e_P_QbDZHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c1BBf8_D0Zq_e4sWgAdTEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c1BBf8_D0Zq_e4sWgAdTEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "imcX7YYBO2e_P_QbDpI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gelrHvnNAa48pTzMlBF3sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gelrHvnNAa48pTzMlBF3sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jWcX7YYBO2e_P_QbDpI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["scKMoVIbbsXT0ePI0cAHEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["scKMoVIbbsXT0ePI0cAHEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "omcX7YYBO2e_P_QbCYTC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xh6Pk0lsPTYRl99fICP3qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xh6Pk0lsPTYRl99fICP3qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "vtgX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5GcX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mhlnk07FVj7HY3V21x3-gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mhlnk07FVj7HY3V21x3-gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "cOEX7YYByh-A-BiyDedc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IermM9hSVXhnqyUrzsPYOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IermM9hSVXhnqyUrzsPYOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ceEX7YYByh-A-BiyDedc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gpy3Q2u5ZrnHXb3KmhEpOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gpy3Q2u5ZrnHXb3KmhEpOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "MWcX7YYBO2e_P_QbDZHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tnUJH3O0LYpzeUegdlTfKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tnUJH3O0LYpzeUegdlTfKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5WcX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "UdgX7YYBBkbVtX3nCiNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ddeOs02TklyTU3pmfdTaJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ddeOs02TklyTU3pmfdTaJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xeEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JhEjVrFwxs0Uy2lBICC0hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JhEjVrFwxs0Uy2lBICC0hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fuEX7YYByh-A-BiyG_LF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vPkmrOQ6ZSk1I3l40v9b2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vPkmrOQ6ZSk1I3l40v9b2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xOEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVBTPPapZUXmS1PYAOv-Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVBTPPapZUXmS1PYAOv-Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WGcX7YYBO2e_P_QbHZuf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RTGr7Nm-Ia9juXQJ0VJo4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RTGr7Nm-Ia9juXQJ0VJo4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yGcX7YYBO2e_P_QbHJcK"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "neEX7YYByh-A-BiyKfkD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OeVAEoxBlJnkJrF2AREsYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OeVAEoxBlJnkJrF2AREsYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "utgX7YYBBkbVtX3nHTEc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HKZFXtQtwmpkJ4zu4auFBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HKZFXtQtwmpkJ4zu4auFBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "h2cX7YYBO2e_P_QbOrtJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["adkeyas3W26uFEzRKjKNyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["adkeyas3W26uFEzRKjKNyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QOIX7YYByh-A-BiyPQgM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "idgX7YYBBkbVtX3nPUb7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c7sDL1ZEUDJ12LHKKH-P_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c7sDL1ZEUDJ12LHKKH-P_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "9eIX7YYByh-A-BiyOALn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["asewtAhw0ntqifC47rIgYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["asewtAhw0ntqifC47rIgYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yNgX7YYBBkbVtX3nOTqZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KiwE-vKZHKC3n7ALbEtWxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KiwE-vKZHKC3n7ALbEtWxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IeIX7YYByh-A-BiyPQuP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AOFrzbtSbZoZPfOFvByqkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AOFrzbtSbZoZPfOFvByqkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "a-IX7YYByh-A-BiyOwXV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["67VSY7gMnvXQykqHE0WxBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["67VSY7gMnvXQykqHE0WxBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gOIX7YYByh-A-BiyOAGi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cM0djH2TU0zlpYvTIkqfrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cM0djH2TU0zlpYvTIkqfrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "PNgX7YYBBkbVtX3nPEVT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iiSoTtUS0Kv5axzY5mPeuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iiSoTtUS0Kv5axzY5mPeuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "PdgX7YYBBkbVtX3nPEVT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsV16gz3SHNxrBEt4b7ZuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsV16gz3SHNxrBEt4b7ZuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "tmcX7YYBO2e_P_QbOr_Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rjTw1uwIATCPa-CkaMdjEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rjTw1uwIATCPa-CkaMdjEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "GGcX7YYBO2e_P_QbS8o-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4fZ6VnX9iKRRH_O1UDOxJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4fZ6VnX9iKRRH_O1UDOxJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FeIX7YYByh-A-BiyTBx8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mnK-jqHbwNjcoomJsw59gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mnK-jqHbwNjcoomJsw59gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BuIX7YYByh-A-BiySRTi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LibGknFXAn9fd0n8hPZURw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LibGknFXAn9fd0n8hPZURw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "6uIX7YYByh-A-BiySBCO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n7QrrEicoQGmnUiZzNk-Kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n7QrrEicoQGmnUiZzNk-Kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "itgX7YYBBkbVtX3nSErh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A5T8gkmrKDulci1jhJCJaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A5T8gkmrKDulci1jhJCJaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SGcX7YYBO2e_P_QbTM67"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gZcWJlRvTnEPU2SoN15zhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gZcWJlRvTnEPU2SoN15zhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0tgX7YYBBkbVtX3nTUyD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-y7rTRuLTj8cfBQbfYKy2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-y7rTRuLTj8cfBQbfYKy2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6eIX7YYByh-A-BiySBCO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rv7EKA4ajQNDDg6Um4OsSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rv7EKA4ajQNDDg6Um4OsSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BOIX7YYByh-A-BiySRTi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFcmZLo-GvC7WdK5tCotfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFcmZLo-GvC7WdK5tCotfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DuIX7YYByh-A-BiyShe9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X1aHc6VUJ_Ut6oMpk8MSqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X1aHc6VUJ_Ut6oMpk8MSqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "09gX7YYBBkbVtX3nTUyD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pv_Z9wfk0AjOJ6dIHemB0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pv_Z9wfk0AjOJ6dIHemB0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "dGcX7YYBO2e_P_QbS8t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ui68wyUVCJcxQ5nqpWzN1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ui68wyUVCJcxQ5nqpWzN1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "F2cX7YYBO2e_P_QbTdAJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iWRinu91wuOHnxKN19PJZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iWRinu91wuOHnxKN19PJZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "nWcX7YYBO2e_P_QbSsgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lbp6GnC_9KEun0KEyhcfUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lbp6GnC_9KEun0KEyhcfUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0dgX7YYBBkbVtX3nTUyD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQindYwMtv8QD8UZS8rDBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQindYwMtv8QD8UZS8rDBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "KeIX7YYByh-A-BiyWyN4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vdmPYvdso3nyHwU3P-BxHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vdmPYvdso3nyHwU3P-BxHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "huIX7YYByh-A-BiyWyEq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mDMvsWlBM76O6KXIRi4tEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mDMvsWlBM76O6KXIRi4tEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "t2cX7YYBO2e_P_QbXN28"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-BNILa1SCuDbNciG6XDXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-BNILa1SCuDbNciG6XDXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "hGcX7YYBO2e_P_QbXuFm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FaKFPVNiFZEijjndTiCFKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FaKFPVNiFZEijjndTiCFKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "QmcX7YYBO2e_P_Qbe_U-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FN7nwOP7JnV5VSJaq0yJcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FN7nwOP7JnV5VSJaq0yJcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MrYyGRfOREUeHSMqF3-gkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MrYyGRfOREUeHSMqF3-gkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "69gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WwPr2gilYMTlY-ITJ8otdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WwPr2gilYMTlY-ITJ8otdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["02VDgFkdOKpFXV3fa5Mfsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["02VDgFkdOKpFXV3fa5Mfsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ieam7bsdu72j_HX7vKzkDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ieam7bsdu72j_HX7vKzkDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Q-IX7YYByh-A-BiyeDlq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w4AKEYruYsyRiuNl0wOumw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w4AKEYruYsyRiuNl0wOumw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XeIX7YYByh-A-BiyfDoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W4eaTfNJQRBDVqF5v5x57A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W4eaTfNJQRBDVqF5v5x57A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PtgX7YYBBkbVtX3nenT7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pWn_IRU-j_6Nwh-gfuAqfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pWn_IRU-j_6Nwh-gfuAqfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "tuIX7YYByh-A-BiyfD3Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x92QEPdFkYeW4x8Mit4TyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x92QEPdFkYeW4x8Mit4TyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9mcX7YYBO2e_P_QbeO6k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "9NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ndFy9pak9l2ciS-LEs5_3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ndFy9pak9l2ciS-LEs5_3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LOIX7YYByh-A-BiyhkDE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P6G78bo1y5OAViRPCWI9Fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P6G78bo1y5OAViRPCWI9Fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "4NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lN0VQOhN39IOJVND--OWWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lN0VQOhN39IOJVND--OWWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "4dgX7YYBBkbVtX3ni4Pc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cELS3s9xDUsfqdE_Tc5Ajw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cELS3s9xDUsfqdE_Tc5Ajw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "keIX7YYByh-A-BiyiUZS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5PXpBtV4iL5Ov3ZyHXzrqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5PXpBtV4iL5Ov3ZyHXzrqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lWcX7YYBO2e_P_Qbh_zn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JP-P1F5zqFUSH3g3y80Xwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JP-P1F5zqFUSH3g3y80Xwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "UGcX7YYBO2e_P_QbiP47"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BdKIIeru3ccMM47Vmx2rwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BdKIIeru3ccMM47Vmx2rwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "yNgX7YYBBkbVtX3niHnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fEKLjZ39jYfvxgEfDDOtIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fEKLjZ39jYfvxgEfDDOtIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "3WcX7YYBO2e_P_Qbif8Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kfHsP1mW7mP6jtkOBG2aew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kfHsP1mW7mP6jtkOBG2aew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "QdgX7YYBBkbVtX3ni4AA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wS-0A4EIVIssr7OiOYGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wS-0A4EIVIssr7OiOYGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["myjdfdwRKOn6W5NX1Bn-1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["myjdfdwRKOn6W5NX1Bn-1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "3dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rqpy0rD1vTLq37Y_TBiX2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rqpy0rD1vTLq37Y_TBiX2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "8tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "buIX7YYByh-A-Biyi0uU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPIMyWIkL5MsP1Bo20O32w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPIMyWIkL5MsP1Bo20O32w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "32cX7YYBO2e_P_Qbif8Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V-CMkHxQqgsYZTwaLT0AEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V-CMkHxQqgsYZTwaLT0AEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ZdgX7YYBBkbVtX3npo-z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TtLtDTr58CzaWDA4qlaTmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TtLtDTr58CzaWDA4qlaTmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "stgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S8MXm0YWnV7NY7lXJUOOog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S8MXm0YWnV7NY7lXJUOOog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "s9gX7YYBBkbVtX3np5HZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lxliPpP77L7i9KCpXsSmXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lxliPpP77L7i9KCpXsSmXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "buIX7YYByh-A-BiyqGFv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SLcvmdHWQs_SKMn3hTK4eQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SLcvmdHWQs_SKMn3hTK4eQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ruIX7YYByh-A-Biym1t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zyeCF78Ljkj_liCk-aIaJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zyeCF78Ljkj_liCk-aIaJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "stgX7YYBBkbVtX3nqJPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2XYwJraBsCBYM0BQZOxBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2XYwJraBsCBYM0BQZOxBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XGgX7YYBO2e_P_Qbpxge"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uCc8HSZ3_tVtMqD6Q4eLtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uCc8HSZ3_tVtMqD6Q4eLtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "yOIX7YYByh-A-BiyqF8h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JhzC993A9_3n4z0mG1o_nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JhzC993A9_3n4z0mG1o_nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "o-IX7YYByh-A-BiynF0B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I6emm7QMCp3MTtFFeDRa6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I6emm7QMCp3MTtFFeDRa6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "XmgX7YYBO2e_P_QbnBJG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hGrtOHZgxPff7dF8x4aKsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hGrtOHZgxPff7dF8x4aKsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "X2gX7YYBO2e_P_QbnBJG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T037yNb7uXswtCEJqGAhHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T037yNb7uXswtCEJqGAhHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xmgX7YYBO2e_P_QbnBXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hpyllfzpp8_nbwc9QqhNUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hpyllfzpp8_nbwc9QqhNUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "yGgX7YYBO2e_P_QbnBXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HZ_JeS7wNexqGcIiik5z6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HZ_JeS7wNexqGcIiik5z6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "qtgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUDRhU6l3_2B1svNu-m4OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUDRhU6l3_2B1svNu-m4OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XWgX7YYBO2e_P_QbnBJG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ymCCYcwH_madRuyjsHk5Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ymCCYcwH_madRuyjsHk5Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KWgX7YYBO2e_P_QbnBSM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mNHTiM_I_yOcvPLX_jE4VQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mNHTiM_I_yOcvPLX_jE4VQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b-IX7YYByh-A-BiymlgR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mebu14j6JQPo9D_c1nbUiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mebu14j6JQPo9D_c1nbUiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "H-IX7YYByh-A-Biymlrf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mRqni2HGLC5qImss9JsUdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mRqni2HGLC5qImss9JsUdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X2gX7YYBO2e_P_Qbpxge"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iNwZltOIYGIMPbPaWa-8GQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iNwZltOIYGIMPbPaWa-8GQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "sdgX7YYBBkbVtX3nqJPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rSr_eB05hnLSYA4C6q23LQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rSr_eB05hnLSYA4C6q23LQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "pOIX7YYByh-A-BiynF0B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "X-IX7YYByh-A-Biypl4D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AXSC7Ug5s-HSwYDMXe0_bQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AXSC7Ug5s-HSwYDMXe0_bQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ItgX7YYBBkbVtX3nmo13"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["slEp0H_n8NXap1EwAwcqUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["slEp0H_n8NXap1EwAwcqUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "mmgX7YYBO2e_P_QbphZT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["69ND-pArCGiPGEzmwahftg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["69ND-pArCGiPGEzmwahftg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "r-IX7YYByh-A-Biym1t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xzw5iVxHgzqrkDAJ9WwC1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xzw5iVxHgzqrkDAJ9WwC1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "q9gX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b24SYdtkbKZNKbXUua9QEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b24SYdtkbKZNKbXUua9QEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "TdgX7YYBBkbVtX3nuKdo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gtgX7YYBBkbVtX3nt6R3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OFYcBHyJD4Mumr7Mh7SCEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OFYcBHyJD4Mumr7Mh7SCEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "n-IX7YYByh-A-Biyum7A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nNScNuSTrpa5-8cxBl8OiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nNScNuSTrpa5-8cxBl8OiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MWgX7YYBO2e_P_QbtSOl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ELGYYB5yptbs2J-FWT6xOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ELGYYB5yptbs2J-FWT6xOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B2gX7YYBO2e_P_QbtSXs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["APioPA6bDIYwGq2IbkrzMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["APioPA6bDIYwGq2IbkrzMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LNgX7YYBBkbVtX3nua7-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["olGExzlNnh_tZyTGOfUK4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["olGExzlNnh_tZyTGOfUK4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HOIX7YYByh-A-BiyuGot"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L5h2Klu_Zrlmt_s7mEC_fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L5h2Klu_Zrlmt_s7mEC_fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "udgX7YYBBkbVtX3nuKip"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DnkK77oJ5OEGMcCjv0REHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DnkK77oJ5OEGMcCjv0REHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "e9gX7YYBBkbVtX3nuay9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tSmYASezZL-7l2EICLQFkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tSmYASezZL-7l2EICLQFkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "OtgX7YYBBkbVtX3nu7Ec"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mpjzTZhXzUC8aYg4OfeyGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mpjzTZhXzUC8aYg4OfeyGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "YNgX7YYBBkbVtX3ntqGz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pp4DRoolWQ68gC0mJ3Fd4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pp4DRoolWQ68gC0mJ3Fd4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "t2gX7YYBO2e_P_Qbtynt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ycogR2C1hH5eXGjaW9oPeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ycogR2C1hH5eXGjaW9oPeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YdgX7YYBBkbVtX3ntqGz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4T9YWo_AUvk6BjQe3pJjFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4T9YWo_AUvk6BjQe3pJjFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "6NgX7YYBBkbVtX3nt6W3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HTwNGIG-KZHMdnRtNZB5Xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HTwNGIG-KZHMdnRtNZB5Xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "m2gX7YYBO2e_P_QbuSt5"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnWo91SdXvpnkjVUdYM0rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnWo91SdXvpnkjVUdYM0rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "nGgX7YYBO2e_P_QbuSt5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pHFN0qaDz6OHVNs6LDyfew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pHFN0qaDz6OHVNs6LDyfew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "oOIX7YYByh-A-Biyum7A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0KW_YB1xCjsP8IMGIq3y3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0KW_YB1xCjsP8IMGIq3y3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "jOIX7YYByh-A-BiyuGvz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pgb9JoAUQxoSCvdXn7xEkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pgb9JoAUQxoSCvdXn7xEkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xtgX7YYBBkbVtX3nuq9-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RsVBVY52cTTp5FCtYm6r4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RsVBVY52cTTp5FCtYm6r4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "heIX7YYByh-A-BiyxXHD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NHMC3YByz5PUd8-9hJAYRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NHMC3YByz5PUd8-9hJAYRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_tgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BdgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qIbdzZ8tB4OMM3huzZH7DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qIbdzZ8tB4OMM3huzZH7DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "idgX7YYBBkbVtX3nybor"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KxMpHytF7kd-rarpM4w5Fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KxMpHytF7kd-rarpM4w5Fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5dgX7YYBBkbVtX3nyr5a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Et5sNZhAoszUicKSkeO_ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Et5sNZhAoszUicKSkeO_ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AtgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GvHv11zqYBw9Vq56J4GXwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GvHv11zqYBw9Vq56J4GXwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nGgX7YYBO2e_P_Qbyjma"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AdgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w_qNnBzYsJ58t8YL4ziO_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w_qNnBzYsJ58t8YL4ziO_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "_NgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["za9zfvytpYavwLxYksfHEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "BtgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vIzLPwNwvusWrF-AAIfmqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vIzLPwNwvusWrF-AAIfmqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "LeIX7YYByh-A-BiyyXdn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kaySmFbhcfXoELn4EdRoeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kaySmFbhcfXoELn4EdRoeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "BNgX7YYBBkbVtX3n5coI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bYOa3wUBO_fHKJp5WGlbmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bYOa3wUBO_fHKJp5WGlbmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "G2gX7YYBO2e_P_Qb51U6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fzu_bSULZtVzZW5-F-QApQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fzu_bSULZtVzZW5-F-QApQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "V9gX7YYBBkbVtX3n5c3Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5oS51f0z7QyabKQzOu2RYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5oS51f0z7QyabKQzOu2RYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BuIX7YYByh-A-Biy5oju"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PgTdyJ7xx8fAccK2NtQowQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PgTdyJ7xx8fAccK2NtQowQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lOIX7YYByh-A-Biy6IkE"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6oJOlriSsxoHCj15KtT0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6oJOlriSsxoHCj15KtT0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9dgX7YYBBkbVtX3n6tVt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DU3pAewxbbLyFetX4pmrVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DU3pAewxbbLyFetX4pmrVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "99gX7YYBBkbVtX3n6tVt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hyfp1vfnvOTkKMqmQpUQgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hyfp1vfnvOTkKMqmQpUQgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "aNgX7YYBBkbVtX3n6dRk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EyPwNPIcZxBA4fL24xAk6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EyPwNPIcZxBA4fL24xAk6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "tuIX7YYByh-A-Biy5oRq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agxJ4qtH42heXKOg02CiKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agxJ4qtH42heXKOg02CiKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} {"create": {"_index": "profiling-events-all", "_id": "r9gX7YYBBkbVtX3n5ctL"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "adgX7YYBBkbVtX3n6dRk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WXat65Sd_FSb3q_O_39_OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WXat65Sd_FSb3q_O_39_OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "U9gX7YYBBkbVtX3n9uDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KF5ebpo2QysWAQEhhpRt1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KF5ebpo2QysWAQEhhpRt1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JtgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M8PQiFj2pfLsH3aiHHnZSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M8PQiFj2pfLsH3aiHHnZSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9eIX7YYByh-A-Biy95jl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6iO15jj0vZmOpf_lsLTSaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6iO15jj0vZmOpf_lsLTSaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pGgX7YYBO2e_P_Qb-WKP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNnxxsIO5dQ0UMCgK8G4sA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNnxxsIO5dQ0UMCgK8G4sA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6uIX7YYByh-A-Biy9JDf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZxBtZLIjgWi7iyuWzr-iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZxBtZLIjgWi7iyuWzr-iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8mgX7YYBO2e_P_Qb9V0w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7UUpVBUGNxt4Ms0zx7knOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7UUpVBUGNxt4Ms0zx7knOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ptgX7YYBBkbVtX3n9t6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z1ah3dkQRTcpWCEydc1lfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ceIX7YYByh-A-Biy95dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ess3oHhLNEi0m2JAz0_5DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ess3oHhLNEi0m2JAz0_5DQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qdgX7YYBBkbVtX3n9Ngk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["khaMzqn4jk0qmytmlLpK9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["khaMzqn4jk0qmytmlLpK9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "rNgX7YYBBkbVtX3n9Ngk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pt09Zvf__ya1eNp2CoZNVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pt09Zvf__ya1eNp2CoZNVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6-IX7YYByh-A-Biy9JDf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HQ-3JYSn-GrIHbyFQdzdAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HQ-3JYSn-GrIHbyFQdzdAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "qOIX7YYByh-A-Biy9ZKm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EK7FG-N0XT8ybJhJIv-IHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EK7FG-N0XT8ybJhJIv-IHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ROIX7YYByh-A-Biy9ZTm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qa_k6A40Yydkz_UFy6Z1_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qa_k6A40Yydkz_UFy6Z1_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "KdgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "K9gX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IO7igLUjHuSwhRGut0RlMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IO7igLUjHuSwhRGut0RlMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VNgX7YYBBkbVtX3n9uDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BPbVSqBHjmwe-nD9qiLIFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BPbVSqBHjmwe-nD9qiLIFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VdgX7YYBBkbVtX3n9uDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GpWKihnKGLWcQ6H8XP4Cdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GpWKihnKGLWcQ6H8XP4Cdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5tgX7YYBBkbVtX3n9-Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2aKzwMjw6-zxCFb-O-8vkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2aKzwMjw6-zxCFb-O-8vkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "UNgX7YYBBkbVtX3n-ONq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rhx-kyzSwrzzbVrVZ_XCyg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rhx-kyzSwrzzbVrVZ_XCyg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "eeIX7YYByh-A-Biy-Jux"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pf-Heq8cDrQxCCAzjzKdYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pf-Heq8cDrQxCCAzjzKdYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "R-IX7YYByh-A-Biy9ZTm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GabuPwfXa70OHQ02xp-bDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GabuPwfXa70OHQ02xp-bDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "8WgX7YYBO2e_P_Qb9V0w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ETlNSw_6vrTcv4HXKtYlKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ETlNSw_6vrTcv4HXKtYlKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "p-IX7YYByh-A-Biy9ZKm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNwCIBshkIMvUtAdsIyUXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNwCIBshkIMvUtAdsIyUXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} {"create": {"_index": "profiling-events-all", "_id": "YWgX7YYBO2e_P_Qb91-r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yEoFm5-7Ri3zCUzV2d985Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yEoFm5-7Ri3zCUzV2d985Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TdgX7YYBBkbVtX3n-ONq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2MnN4qpG46YaqtSyYvWn0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2MnN4qpG46YaqtSyYvWn0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "euIX7YYByh-A-Biy-Jux"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JLHzPwzEV5rRN9RuEzoMPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JLHzPwzEV5rRN9RuEzoMPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5dgX7YYBBkbVtX3n-eZU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1tQ4s1_uuQscYA1lUCsKxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1tQ4s1_uuQscYA1lUCsKxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ItgX7YYBBkbVtX3n9Npj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_FNO79a5C7cHLejvYdQS3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_FNO79a5C7cHLejvYdQS3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "TtgX7YYBBkbVtX3n-ONq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epmApGucYGosaiNqgwoK4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epmApGucYGosaiNqgwoK4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "KNgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "i9gY7YYBBkbVtX3nFvdC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "iGgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7NvWJH6Vo-unjJv4pudacA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7NvWJH6Vo-unjJv4pudacA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFJedgXKyIDWLxlCPDwfQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFJedgXKyIDWLxlCPDwfQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bmgY7YYBO2e_P_QbFHAh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rHXidrM7meN_QI4wKNJ_Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rHXidrM7meN_QI4wKNJ_Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "d2gY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "32gY7YYBO2e_P_QbGHgM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K9LDD5AZV4XmqBf_IoPXlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K9LDD5AZV4XmqBf_IoPXlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L-IY7YYByh-A-BiyE6qn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-BJw7BDfkkLGBaeu3mTtJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-BJw7BDfkkLGBaeu3mTtJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "cWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qy63CZwa2X4_cMyWGg3_fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qy63CZwa2X4_cMyWGg3_fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "GGgY7YYBO2e_P_QbFXK-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ps4eXe9gyP0W0fZFsyk6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ps4eXe9gyP0W0fZFsyk6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} {"create": {"_index": "profiling-events-all", "_id": "--IY7YYByh-A-BiyF7M-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GXfd-67U3kWd07TLbZUnXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GXfd-67U3kWd07TLbZUnXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "dGgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7rYDHDMd68AxfAV4sQDwaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7rYDHDMd68AxfAV4sQDwaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "udkY7YYBBkbVtX3nJgIl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1uIY7YYByh-A-BiyJ8jR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rMpzXndoIcEiY0-GRAGnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rMpzXndoIcEiY0-GRAGnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2uIY7YYByh-A-BiyJ8jR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sczC8NZ4ijZkOgrx_9LW8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sczC8NZ4ijZkOgrx_9LW8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VWgY7YYBO2e_P_QbI30D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5XCCyw1sj3cwxEJr3r3pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5XCCyw1sj3cwxEJr3r3pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "muIY7YYByh-A-BiyI7hA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pixjUDno8EQPnhCn1ap_SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pixjUDno8EQPnhCn1ap_SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Y-IY7YYByh-A-BiyKMoI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b-IY7YYByh-A-BiyJsRk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "N-IY7YYByh-A-BiyKMx-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-77tqpe6hKMIrwkkHYgFFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-77tqpe6hKMIrwkkHYgFFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "xeIY7YYByh-A-BiyJL_0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QbcK_gbMTYuvwl_FoMECaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QbcK_gbMTYuvwl_FoMECaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "K9kY7YYBBkbVtX3nJQGU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MaChZnl-rNfGFkTRfl_iA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MaChZnl-rNfGFkTRfl_iA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "6-IY7YYByh-A-BiyJsXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VoEfCO4MusB_VsJY8Q8iKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VoEfCO4MusB_VsJY8Q8iKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "Z-IY7YYByh-A-BiyJ8dT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HYzllkhJBtq1_HQGHScByA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HYzllkhJBtq1_HQGHScByA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "G9kY7YYBBkbVtX3nNAgf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l02Tu6tzBSE8eTIc8ew_RQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l02Tu6tzBSE8eTIc8ew_RQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wWgY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SUZ7CA7sE1ISr8T76gz0pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SUZ7CA7sE1ISr8T76gz0pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9mgY7YYBO2e_P_QbNYme"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "guIY7YYByh-A-BiyNtki"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j3fPwb3ArZvn7Z7I5e1B3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j3fPwb3ArZvn7Z7I5e1B3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "w2gY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "u-IY7YYByh-A-BiyONo2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D_W7lWj5puMsPDS-FaHcww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D_W7lWj5puMsPDS-FaHcww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "HdkY7YYBBkbVtX3nNAgf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nb8JnNS4P-YTnEEC4c_iFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nb8JnNS4P-YTnEEC4c_iFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} {"create": {"_index": "profiling-events-all", "_id": "1uIY7YYByh-A-BiyNNZi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5mBEpaSnzHuLLvoimyigA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5mBEpaSnzHuLLvoimyigA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KmgY7YYBO2e_P_QbNYgr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a5-obKRYvJiw7VDkIkKBqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a5-obKRYvJiw7VDkIkKBqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xGgY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_NBZhL-VXv-Q6LRQmOUyNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_NBZhL-VXv-Q6LRQmOUyNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8mgY7YYBO2e_P_QbNoxr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xQ24QHkRlAYtTjdJ6daIzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xQ24QHkRlAYtTjdJ6daIzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SWgY7YYBO2e_P_QbOJVz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Apktul3cj6NpEbhKyTyUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Apktul3cj6NpEbhKyTyUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GeIY7YYByh-A-BiyQttA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uPfQx41sGpWXSF6wjd1f8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uPfQx41sGpWXSF6wjd1f8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xWgY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1eIY7YYByh-A-BiyNNZi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aCYKHhebiJ9jpDR5JQOfqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aCYKHhebiJ9jpDR5JQOfqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "dtkY7YYBBkbVtX3nNgnQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UZVNKPpjh_ukvtZTR8SkKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UZVNKPpjh_ukvtZTR8SkKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "XdkY7YYBBkbVtX3nQwsG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9LOuDoi5esCx_k0gisuwsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9LOuDoi5esCx_k0gisuwsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "xeIY7YYByh-A-BiyQtx5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["97LYjag2U--dwlPedhKFsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["97LYjag2U--dwlPedhKFsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "umgY7YYBO2e_P_QbV7Dp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kz2jgY6jyGc0z6njue6m7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kz2jgY6jyGc0z6njue6m7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5NkY7YYBBkbVtX3nVBpn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0UeOgQYKC7zcrsZ5ZxtGIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0UeOgQYKC7zcrsZ5ZxtGIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P9kY7YYBBkbVtX3nUhWB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XecKyHj9SENWU--AxSqY-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XecKyHj9SENWU--AxSqY-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hNkY7YYBBkbVtX3nVyEZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uHTQGYxyOTYnqxyDJdTj2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uHTQGYxyOTYnqxyDJdTj2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "69kY7YYBBkbVtX3nUxhO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcrgeMf65aey4TtBdOxT8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcrgeMf65aey4TtBdOxT8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "veIY7YYByh-A-BiyU-6O"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mfdo9N1VvIQt_V4_UKPs_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mfdo9N1VvIQt_V4_UKPs_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "KGgY7YYBO2e_P_QbZb97"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pl4gIyM-ZJ0uHaZ1UHaSeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pl4gIyM-ZJ0uHaZ1UHaSeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-all", "_id": "DdkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AN24Ay2FFm6R_uskGlbDvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AN24Ay2FFm6R_uskGlbDvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "K2gY7YYBO2e_P_QbZb97"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5ddNwsq9oH1jvVfHsja-nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5ddNwsq9oH1jvVfHsja-nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "K9kY7YYBBkbVtX3ncS0m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2L91SyJLMmUa3HpOOsZXbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2L91SyJLMmUa3HpOOsZXbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LdkY7YYBBkbVtX3ncS0m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tT2A_bCpClAyR0rNtUXbYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tT2A_bCpClAyR0rNtUXbYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "U9kY7YYBBkbVtX3nZCoi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zUInPt-LD-WhkBhzuV68pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zUInPt-LD-WhkBhzuV68pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "VNkY7YYBBkbVtX3nZCoi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qChF1b-F4ZHuRR8vMI_y5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qChF1b-F4ZHuRR8vMI_y5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5uIY7YYByh-A-BiyZPvI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bMR3cLNESeD0GrHLhW-oYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bMR3cLNESeD0GrHLhW-oYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "oGgY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HQFX_6tqgj5N1dAyiK5_bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HQFX_6tqgj5N1dAyiK5_bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KGgY7YYBO2e_P_QbccS0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cO9htV71m7HNbsVXnS974g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cO9htV71m7HNbsVXnS974g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1GgY7YYBO2e_P_QbcsmS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-iLOmSM0bOvxtv9W5h6VXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-iLOmSM0bOvxtv9W5h6VXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "n2gY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6GgY7YYBO2e_P_QbY7m_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2p1e_ByfhPk84t_WqyZsxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2p1e_ByfhPk84t_WqyZsxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "m9kY7YYBBkbVtX3nZCuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bV7P2mLAotscMMHsjhqrPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bV7P2mLAotscMMHsjhqrPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "nNkY7YYBBkbVtX3nZCuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LtZuBt3l0YoQy2lVtYcCNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LtZuBt3l0YoQy2lVtYcCNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "i2gY7YYBO2e_P_QbZcCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5e4O7lBx37gz0fcZIzqtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5e4O7lBx37gz0fcZIzqtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "zNkY7YYBBkbVtX3nZSze"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pczYn9bA4SlIUvF6oLM4Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pczYn9bA4SlIUvF6oLM4Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ztkY7YYBBkbVtX3nZSze"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SWnMQRi9b6sdpiIXZr2J-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SWnMQRi9b6sdpiIXZr2J-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "L9kY7YYBBkbVtX3ncS0m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ng7Kn6I7osQY62ITDyHvMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ng7Kn6I7osQY62ITDyHvMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "AmgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ducvofyrwFEKpejeDz6ZEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ducvofyrwFEKpejeDz6ZEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "nGgY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x40XXgRvsC7DRm_EiCrdLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x40XXgRvsC7DRm_EiCrdLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "nWgY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["39C59YAEETP8fgJkvRsitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["39C59YAEETP8fgJkvRsitg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "FNkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pCGmghmQMI3e0JNfZpz8pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pCGmghmQMI3e0JNfZpz8pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "2mgY7YYBO2e_P_QbcsdE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ESbYg3aZAaH86uOl-CijNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ESbYg3aZAaH86uOl-CijNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "UdkY7YYBBkbVtX3nZCoi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP6GIsw4ofWcnUGlBduuVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP6GIsw4ofWcnUGlBduuVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "22gY7YYBO2e_P_QbcsdE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Em3twIsXXSYKKY9obJCgGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Em3twIsXXSYKKY9obJCgGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "omgY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gAG1HMhOOlK-azE89-mC-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gAG1HMhOOlK-azE89-mC-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "DtkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFK_K9iyGPWt-RXiomayjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFK_K9iyGPWt-RXiomayjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "EuIY7YYByh-A-BiyZf0D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mu3LxyO4KAp-wuV_ZLnj9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mu3LxyO4KAp-wuV_ZLnj9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "5-IY7YYByh-A-BiyZPvI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wkNFgmgAHEeZwvM9ZbPDWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wkNFgmgAHEeZwvM9ZbPDWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "HdkY7YYBBkbVtX3ngUGP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eU8ia1QG3CJz_p9ALXHslA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eU8ia1QG3CJz_p9ALXHslA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ROMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hbFdZ00lApIoSJEOlowBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hbFdZ00lApIoSJEOlowBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-WgY7YYBO2e_P_Qbhdgp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2oY1qTWIwDNkXOF0PagdBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2oY1qTWIwDNkXOF0PagdBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "R-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EhmE1DErCIwAQObQqIf2Fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EhmE1DErCIwAQObQqIf2Fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "U-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EG9jBmB3lh64ME0jyCkfBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EG9jBmB3lh64ME0jyCkfBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3eMY7YYByh-A-BiyhBDs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NZH6uNdKRM0PBr1CbdPUbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NZH6uNdKRM0PBr1CbdPUbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4Fi4uj8_8j0Q94aQJWSzrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4Fi4uj8_8j0Q94aQJWSzrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epUUcKloArUaO4HmSd6-0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["epUUcKloArUaO4HmSd6-0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "TWgY7YYBO2e_P_QbgNH_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_eN577uJw5hksIBqBf1iCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_eN577uJw5hksIBqBf1iCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "T-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tDdgTWNJfepwn2J7xetthA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tDdgTWNJfepwn2J7xetthA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UOeWC8fAGloWbHEYVuqcaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UOeWC8fAGloWbHEYVuqcaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "RuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JF-bEOX20dA6FrlaQoCfyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JF-bEOX20dA6FrlaQoCfyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "lWgY7YYBO2e_P_QbkeIU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I5V2d7T-ngpDaQd5S4eJBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I5V2d7T-ngpDaQd5S4eJBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "vuMY7YYByh-A-Biykxdq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D17V2ZvopmhLBd7dZ3Y1BA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D17V2ZvopmhLBd7dZ3Y1BA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "N2gY7YYBO2e_P_Qbk-uk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k1TJV5J17869y08LRTIrbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k1TJV5J17869y08LRTIrbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "GtkY7YYBBkbVtX3nlFSh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "GWgY7YYBO2e_P_QbkN5i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UL06CNiVyxEFpIouFPRx3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UL06CNiVyxEFpIouFPRx3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6mgY7YYBO2e_P_QbkuQ0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k6Ao0lNSN-mIGSeq85ATfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k6Ao0lNSN-mIGSeq85ATfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "K2gY7YYBO2e_P_Qbk-gA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2j8VUArr_b9AewT6WEQL_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2j8VUArr_b9AewT6WEQL_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8mgY7YYBO2e_P_Qble4x"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qBgjikC2bNo6FIg5CEWcww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qBgjikC2bNo6FIg5CEWcww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "aeMY7YYByh-A-Biykhac"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gbjQ3Y9_diPygyamcLKI4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gbjQ3Y9_diPygyamcLKI4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "k2gY7YYBO2e_P_QbkeIU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "fdkY7YYBBkbVtX3nkU6M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vdmPYvdso3nyHwU3P-BxHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vdmPYvdso3nyHwU3P-BxHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "y2gY7YYBO2e_P_QbkeNU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yVg35eksppyHad0lI1eXKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yVg35eksppyHad0lI1eXKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ftkY7YYBBkbVtX3nkU6M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1o1T1TIStxTZj-e2WTNkwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1o1T1TIStxTZj-e2WTNkwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "GGgY7YYBO2e_P_QbkN5i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QkZ15erAXl4_xxP-OSkyww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QkZ15erAXl4_xxP-OSkyww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "M9kY7YYBBkbVtX3nlFIk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qsczIwhDc4-8PRo_Si-xGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qsczIwhDc4-8PRo_Si-xGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "j-MY7YYByh-A-BiypicL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8QWlvovygGFcfE-e_CiKgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ttkY7YYBBkbVtX3nsnET"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v33eHkvIcwAV8Kdb6vad5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v33eHkvIcwAV8Kdb6vad5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "itkY7YYBBkbVtX3nsnOa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-E3XQIukGGWcLnxv-RKlXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-E3XQIukGGWcLnxv-RKlXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "luMY7YYByh-A-BiysCjE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["thKn-WCpUEzIzuV2FdOJxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["thKn-WCpUEzIzuV2FdOJxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "MuMY7YYByh-A-BiysSoN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jUHt5y4xbMsVQ2NY3U5mxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jUHt5y4xbMsVQ2NY3U5mxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "TtkY7YYBBkbVtX3npWjG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "MeMY7YYByh-A-BiysSoN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ty1oaaASyN0P9ADJQRlYBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ty1oaaASyN0P9ADJQRlYBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "BdkY7YYBBkbVtX3nsGt1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rI5YB_X-7tV_Ivns4mBUGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rI5YB_X-7tV_Ivns4mBUGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "m-MY7YYByh-A-Biysi1P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xWkY7YYBO2e_P_QbxBjA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BKLrtLNoy5ML86CG9IkqrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BKLrtLNoy5ML86CG9IkqrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "o9kY7YYBBkbVtX3nwHt6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["raJRlHaX6wCTMJ3KEat5OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["raJRlHaX6wCTMJ3KEat5OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} {"create": {"_index": "profiling-events-all", "_id": "cGkY7YYBO2e_P_Qbvw38"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZa8a_Yg4q8eagHioqnf0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZa8a_Yg4q8eagHioqnf0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8GkY7YYBO2e_P_QbwhLW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Msb5zTh45ugl6Rm8uu8cBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Msb5zTh45ugl6Rm8uu8cBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "vNkY7YYBBkbVtX3nwHk9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iV-DwRGSNVWP28e5KWrgaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iV-DwRGSNVWP28e5KWrgaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "seMY7YYByh-A-BiywTh8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_0gKV2ZisrZ7u9Pxy3RvUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_0gKV2ZisrZ7u9Pxy3RvUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ZuMY7YYByh-A-BiywjpO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V3E8ZRD3c2NXGI6JDwoBvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V3E8ZRD3c2NXGI6JDwoBvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "D9kY7YYBBkbVtX3nw4Mt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iErvH58Jze4Jx0cV_htakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iErvH58Jze4Jx0cV_htakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "_NkY7YYBBkbVtX3nw4SF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3iF9md1hhA5lL3Jz0Fzo3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3iF9md1hhA5lL3Jz0Fzo3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "o9kY7YYBBkbVtX3n3peC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["etP9qf40owgaYF723aNtWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["etP9qf40owgaYF723aNtWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XdkY7YYBBkbVtX3n4Z_Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fMEGhVur8bO2mv1boqOVuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fMEGhVur8bO2mv1boqOVuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "m2kY7YYBO2e_P_Qb3yiB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OWYvN4elc_j6L7TTAYFNTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OWYvN4elc_j6L7TTAYFNTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mdkY7YYBBkbVtX3n35pG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w8DNqhKpZMDXQeydz4OxrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w8DNqhKpZMDXQeydz4OxrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AmkY7YYBO2e_P_Qb4CpI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AwxnuopW8nP4PdF2hl0d_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AwxnuopW8nP4PdF2hl0d_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2WkY7YYBO2e_P_Qb4S1Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hOMY7YYByh-A-Biy4E8H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c2STw5Dy59fyAI6ZtoR41g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c2STw5Dy59fyAI6ZtoR41g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "8WkY7YYBO2e_P_Qb8z_u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BGkY7YYBO2e_P_Qb7zcM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CUGk5Rcar1NXFwPiGIQVJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CUGk5Rcar1NXFwPiGIQVJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EdkY7YYBBkbVtX3n8Kqc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2tnlnNGd85PzXoftF65LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2tnlnNGd85PzXoftF65LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RmkY7YYBO2e_P_Qb8Tn-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4nXxkGYVgHbeGTI3oHepdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4nXxkGYVgHbeGTI3oHepdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0dkY7YYBBkbVtX3n7qNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wu5Ui6X1wYCeANyAsyHaFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wu5Ui6X1wYCeANyAsyHaFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "atkY7YYBBkbVtX3n76eU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Is6WKpq1rPgjROiY1ySbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Is6WKpq1rPgjROiY1ySbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LmkY7YYBO2e_P_Qb8DhZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nf56AYpKmfrZuf4mkXNDvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nf56AYpKmfrZuf4mkXNDvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EtkY7YYBBkbVtX3n8Kqc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RS8ti051V-zPvOk5r6Viqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RS8ti051V-zPvOk5r6Viqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "auMY7YYByh-A-Biy8WB6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q4P-bQgCFNgZRLoUhZxNlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q4P-bQgCFNgZRLoUhZxNlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "fNkY7YYBBkbVtX3n8ay8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5oIGtzRMmg2pbiJ9Og8Hog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5oIGtzRMmg2pbiJ9Og8Hog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ftkY7YYBBkbVtX3n8ay8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1GkY7YYBO2e_P_Qb8jzI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jIUkkqlhs_xaucQSfOkxdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jIUkkqlhs_xaucQSfOkxdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "vGkY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vFkcrQtWCVTfQjjlGu2S_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vFkcrQtWCVTfQjjlGu2S_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "l9kY7YYBBkbVtX3n861d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NWbNOvcI2-WO4U1Dw4kVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NWbNOvcI2-WO4U1Dw4kVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "mNkY7YYBBkbVtX3n861d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bjA-twM-arP4DofwAmuiCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bjA-twM-arP4DofwAmuiCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "kdkY7YYBBkbVtX3n86-p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3cJve6GcDJQsWrYAyQs7-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3cJve6GcDJQsWrYAyQs7-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LeMY7YYByh-A-Biy8V8f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsyWJacYf-Fc3uMhBCP2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vsyWJacYf-Fc3uMhBCP2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "nmkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "pGkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NLOhgxL61Nf_cs-ijqpzdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NLOhgxL61Nf_cs-ijqpzdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "uWkY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "8mkY7YYBO2e_P_Qb8z_u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Dj-O9jQmIE1I6VOFUdq8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Dj-O9jQmIE1I6VOFUdq8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "p2kY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9enMqDCvmYDJCBOe5Nxb-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9enMqDCvmYDJCBOe5Nxb-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "n2kY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j29xU2xGn45BT-0GH_GSjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j29xU2xGn45BT-0GH_GSjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nNkZ7YYBBkbVtX3nAbtG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sNkZ7YYBBkbVtX3nAb2C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CWudsqc4YRwwO2UAdX1LxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CWudsqc4YRwwO2UAdX1LxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9NkZ7YYBBkbVtX3nALZa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QWkZ7YYBO2e_P_QbAUXL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["62iN7sDVKIBNfUxfmQdZvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["62iN7sDVKIBNfUxfmQdZvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-dkZ7YYBBkbVtX3nAr6P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hA0aNZ7KwEp2Q70t1DnO8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hA0aNZ7KwEp2Q70t1DnO8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LeMZ7YYByh-A-BiyA20X"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_DXIIIPmnKg43Vr19XmVJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_DXIIIPmnKg43Vr19XmVJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "LuMZ7YYByh-A-BiyA20X"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "0GkZ7YYBO2e_P_QbA0mU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "OtkY7YYBBkbVtX3n_rPy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q7XAR2zqlv3Nkd1rHK-fsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q7XAR2zqlv3Nkd1rHK-fsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "jtkZ7YYBBkbVtX3nALic"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "4OMZ7YYByh-A-BiyA25V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z1K4WqC6eykbHpG2pCP39Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z1K4WqC6eykbHpG2pCP39Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JuMZ7YYByh-A-BiyIpJU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bmkZ7YYBO2e_P_QbImDZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5wAfaxsqFHmGRlT9DISZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5wAfaxsqFHmGRlT9DISZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "k2kZ7YYBO2e_P_QbHVZZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AG_6FvO14ax3UdwVieto8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AG_6FvO14ax3UdwVieto8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ydkZ7YYBBkbVtX3nIM9b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WDhZREc9K0ZHjA0h4NDJhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WDhZREc9K0ZHjA0h4NDJhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "R2kZ7YYBO2e_P_QbHVji"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ofBPwIIjkbSYwPiBTq9MQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ofBPwIIjkbSYwPiBTq9MQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OeMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3gm_ZN3iF76tH1hRk5YEPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3gm_ZN3iF76tH1hRk5YEPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-uMZ7YYByh-A-BiyHoO_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n8J0DIbyYxslBat-_GGskw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n8J0DIbyYxslBat-_GGskw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PeMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3wY3n6ZuFWe08ye_NO9bMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3wY3n6ZuFWe08ye_NO9bMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "QeMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5XgTaHt-dAo8vDgnzZy0dA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5XgTaHt-dAo8vDgnzZy0dA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "1OMZ7YYByh-A-BiyIIiV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOL7hTlazWG39CR6gZV56w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOL7hTlazWG39CR6gZV56w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "o2kZ7YYBO2e_P_QbIV77"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_AVql7KXMLg1O-JULbNgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_AVql7KXMLg1O-JULbNgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "JOMZ7YYByh-A-BiyIpJU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qD_J237PVcJWQeJzWEaj4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qD_J237PVcJWQeJzWEaj4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "b2kZ7YYBO2e_P_QbImDZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Os-4RhVkjeRwXnMgi8sCPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Os-4RhVkjeRwXnMgi8sCPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "NuMZ7YYByh-A-BiyIY5d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["caBHj7BnRD7P-V0_GNLChg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["caBHj7BnRD7P-V0_GNLChg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "A2kZ7YYBO2e_P_QbHVUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jsv-D1yBcc_Oezz_dC64fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jsv-D1yBcc_Oezz_dC64fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OuMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2tnlnNGd85PzXoftF65LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2tnlnNGd85PzXoftF65LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lGkZ7YYBO2e_P_QbHVZZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaQK44tICLO4ljAwiqTd8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaQK44tICLO4ljAwiqTd8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "ydkZ7YYBBkbVtX3nHcyi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yj80EbH9E-W_Q5ntbWTS-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yj80EbH9E-W_Q5ntbWTS-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "JNkZ7YYBBkbVtX3nItGe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DyoYwzb-7gOf1fSN1_ar0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DyoYwzb-7gOf1fSN1_ar0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "AmkZ7YYBO2e_P_QbHVUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZnqYkX8N3cXlE52NykkcUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "190932526140742"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZnqYkX8N3cXlE52NykkcUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "190932526140742"} {"create": {"_index": "profiling-events-all", "_id": "lGkZ7YYBO2e_P_QbMHDd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PaAhGTZhdMpehXmqOre8TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PaAhGTZhdMpehXmqOre8TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "S2kZ7YYBO2e_P_QbMXJP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnrTN3oNHBWQmiPNUfJdZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnrTN3oNHBWQmiPNUfJdZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PmkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rHXidrM7meN_QI4wKNJ_Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rHXidrM7meN_QI4wKNJ_Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NmkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iUnfb2tG1dlb7pOItjVV-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iUnfb2tG1dlb7pOItjVV-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "a2kZ7YYBO2e_P_QbL2zC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["akH3OgREzKvOjMJueUVNqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["akH3OgREzKvOjMJueUVNqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "D-MZ7YYByh-A-BiyMJmk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_qVPV2mnUzJdVUKzin8SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_qVPV2mnUzJdVUKzin8SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "NWkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "_t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u15yh22vXEfk9m8DTVYjBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u15yh22vXEfk9m8DTVYjBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "I98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ewzp1cQUgnGEhjhPWmAuYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ewzp1cQUgnGEhjhPWmAuYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fGUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9pkEB28Fu_-6ScaHtKhZrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9pkEB28Fu_-6ScaHtKhZrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "598U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0KTuXhL53Ud6Yv9U2lJ-uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0KTuXhL53Ud6Yv9U2lJ-uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "798U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zI9JvucnvxyxLZyzixdcpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zI9JvucnvxyxLZyzixdcpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Ct8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wPMMl0ctYrNZIQpMrKFAHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wPMMl0ctYrNZIQpMrKFAHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "M98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QCh5sHgROUyQz6UM1BRbJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QCh5sHgROUyQz6UM1BRbJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DGUU7YYBO2e_P_Qbvp4b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Q9YU7YYBBkbVtX3nuwHT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "e98U7YYByh-A-Biyvcco"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ijzowidHYpe6-vKJVUy8nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ijzowidHYpe6-vKJVUy8nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "eGUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "DN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eqelKqbeHiTw1Jlw68liwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eqelKqbeHiTw1Jlw68liwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Gt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sWUvdmC1yhMffRymX3J_5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sWUvdmC1yhMffRymX3J_5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "2d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5P_TKhYwLk13iojfv7McxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5P_TKhYwLk13iojfv7McxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bmEkpLRxJcoyFpwchxMFpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bmEkpLRxJcoyFpwchxMFpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nbSM4WjFbrCVZaZdfPqVTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nbSM4WjFbrCVZaZdfPqVTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ON8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V3FpOTUk7je6uP4mlCqmkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V3FpOTUk7je6uP4mlCqmkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LtYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iu2XyIi4FVCIJrDGecefmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iu2XyIi4FVCIJrDGecefmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "d2UU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["81I56QjbyDYSIFcetHM2Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["81I56QjbyDYSIFcetHM2Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "D98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bo6NdGV8GXHmalbT9Hz3Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bo6NdGV8GXHmalbT9Hz3Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Nd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_zNN2R6gCnlCmrGYYAK4_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_zNN2R6gCnlCmrGYYAK4_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NNYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EYN09YTQJzILDrRVzDD1TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EYN09YTQJzILDrRVzDD1TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ut8U7YYByh-A-Biyyc_A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BHEo7QGhrwJZN1gfWBJvpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BHEo7QGhrwJZN1gfWBJvpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "f2UU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B70CGF1Zyq8tOnSlg6wrvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B70CGF1Zyq8tOnSlg6wrvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "198U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GSSm9zDDOmvcEwNipfzOIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GSSm9zDDOmvcEwNipfzOIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dYH5SpsYYEui3Y6WMr108A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dYH5SpsYYEui3Y6WMr108A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Bd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fapb056I2fVdN7OPGzRPEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fapb056I2fVdN7OPGzRPEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Ft8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [3], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [3], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "IN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XJXvcRJSTv0fetUhCxNrHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XJXvcRJSTv0fetUhCxNrHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "F98U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dSWElYBhPnO8A6dUDGPqZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dSWElYBhPnO8A6dUDGPqZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4d8U7YYByh-A-BiyyM0P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7W9hedogOVjTIlC7EFh1sA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7W9hedogOVjTIlC7EFh1sA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "N9YU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-wKVKRY5ojzt2TA4h8awoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-wKVKRY5ojzt2TA4h8awoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "e2UU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uFGWThWg3zgxDL3xxQAwYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uFGWThWg3zgxDL3xxQAwYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "nt8U7YYByh-A-BiyusTM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OGDDHXE1D8GrXVwGCFBYtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OGDDHXE1D8GrXVwGCFBYtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "9t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "_N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yfHwdMgNnjjToBF0X-5h8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yfHwdMgNnjjToBF0X-5h8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "FWUU7YYBO2e_P_QbvZyb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_YaGHyTQP12JNwJEIiAfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_YaGHyTQP12JNwJEIiAfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "42UU7YYBO2e_P_QbwKIN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UHAJjYcGydH1wr7a4b6sRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UHAJjYcGydH1wr7a4b6sRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "7t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F2xDW96B93CXTxJUyPdwXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F2xDW96B93CXTxJUyPdwXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "9N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MWvxOIZDGq4hR0RiTlBjWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MWvxOIZDGq4hR0RiTlBjWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "G98U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oXw1ozfUuFf-QgxGHiD6zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oXw1ozfUuFf-QgxGHiD6zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "1N8U7YYByh-A-Biy2eKG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_4atfXLBslE1IWQAx5zAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R_4atfXLBslE1IWQAx5zAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "d98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iLTslrGORIyXKfkvn0rVCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iLTslrGORIyXKfkvn0rVCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Ld8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFeV1BctdgGmKhHEdAax5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFeV1BctdgGmKhHEdAax5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FvVELYKd9mRXwxXR-cNS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FvVELYKd9mRXwxXR-cNS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dd8U7YYByh-A-Biy3edS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UyfEXTPwOxcT1_g30PZ4bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UyfEXTPwOxcT1_g30PZ4bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yNYU7YYBBkbVtX3n1xTG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5QxSgtn_YPXxJ3jCeAVHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5QxSgtn_YPXxJ3jCeAVHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} {"create": {"_index": "profiling-events-all", "_id": "cNYU7YYBBkbVtX3n2BjO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnHzRqCV09wC0f_3_YNq7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MnHzRqCV09wC0f_3_YNq7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "0d8U7YYByh-A-Biy2eKG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eIiWRPbXZKuww0eQLj2S1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eIiWRPbXZKuww0eQLj2S1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "WtYU7YYBBkbVtX3n2hx_"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_j480Qg9v5TNK0lQGeFwAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_j480Qg9v5TNK0lQGeFwAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "k9YU7YYBBkbVtX3n2h7w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8AqERkmGja0aVhFHauF_yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8AqERkmGja0aVhFHauF_yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "1d8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "V98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8vj8M1UtdEZK08xJh31zdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8vj8M1UtdEZK08xJh31zdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Xt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7ppDlnMd3xFqbVJBXCzI3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7ppDlnMd3xFqbVJBXCzI3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Y98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GSPW9ejYGoryJizaJVvA_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GSPW9ejYGoryJizaJVvA_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ad8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ct8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2eTVIIJLC47DBl1ATsTawg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2eTVIIJLC47DBl1ATsTawg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "fN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PYBUfV4nZR3PAgyIKhIwDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PYBUfV4nZR3PAgyIKhIwDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "r98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Faz4zgOreVW0ypNL1btnNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Faz4zgOreVW0ypNL1btnNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "I9YU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZ-D2nB7GiIQ9IS_G8xApA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZ-D2nB7GiIQ9IS_G8xApA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ktYU7YYBBkbVtX3n2h7w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DsctT-_nwdHL3iCwXEsMsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DsctT-_nwdHL3iCwXEsMsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0t8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bihc1FtLJWO9OKz_9ub0mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bihc1FtLJWO9OKz_9ub0mw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "62UU7YYBO2e_P_Qb3L3V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PWgvP1t7oB9ALOV1YcECow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PWgvP1t7oB9ALOV1YcECow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Vd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S64TDAyJLSWTBaPN1VT2qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S64TDAyJLSWTBaPN1VT2qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CM3wI_wNpbRDHBz8scMkcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CM3wI_wNpbRDHBz8scMkcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k9vLKRFLFVoj2RZU6JVbBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k9vLKRFLFVoj2RZU6JVbBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9GEtZihsTZTLRmEEEBupXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9GEtZihsTZTLRmEEEBupXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "g98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rBcz46AS-WiFNbV2tPSIUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rBcz46AS-WiFNbV2tPSIUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KaCen5lChBQlFEf5iOW4fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KaCen5lChBQlFEf5iOW4fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0d8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3PFZFr9561-fHgGNeWX0Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3PFZFr9561-fHgGNeWX0Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "x9YU7YYBBkbVtX3n1xTG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eGK7xB80q6I8iYxZSFEE1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eGK7xB80q6I8iYxZSFEE1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IdYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2BsosH4qrHldN8GgKmd2MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2BsosH4qrHldN8GgKmd2MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7GUU7YYBO2e_P_Qb3L3V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bCqkpCznSihZhI5AqtWxgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bCqkpCznSihZhI5AqtWxgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ed8U7YYByh-A-Biy3edS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mbOVGyx5XatnK0SRKgRKUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mbOVGyx5XatnK0SRKgRKUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FSB68hM0SvGoIwFSJoj9uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FSB68hM0SvGoIwFSJoj9uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jvc_WE7B1F8hMVB_gxFucA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jvc_WE7B1F8hMVB_gxFucA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cdYU7YYBBkbVtX3n2BjO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SnDCdYihCB3VPX-yxBkTjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SnDCdYihCB3VPX-yxBkTjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "2N8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JrMhbMTHmXqZZAz4xxL86g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JrMhbMTHmXqZZAz4xxL86g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "kN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LGVygn6s_9pflNC3YeaZkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LGVygn6s_9pflNC3YeaZkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "aWUU7YYBO2e_P_Qb27nE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lxizPGq-ZlOtsos_BMUvJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lxizPGq-ZlOtsos_BMUvJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "Yd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VaGG1TY-seWlRMIXhOJNsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "503617803902968"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VaGG1TY-seWlRMIXhOJNsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "503617803902968"} {"create": {"_index": "profiling-events-all", "_id": "l98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sJC4CV2eRcloTSQEGQH29Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sJC4CV2eRcloTSQEGQH29Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6QhkBtx7gvljSIZUeTjHcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6QhkBtx7gvljSIZUeTjHcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} {"create": {"_index": "profiling-events-all", "_id": "XtYU7YYBBkbVtX3n2hx_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["laLiuCpHKHuOVWhZk5dt9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["laLiuCpHKHuOVWhZk5dt9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "kdYU7YYBBkbVtX3n2h7w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LEWayJpRjxQq29QdLgh_nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LEWayJpRjxQq29QdLgh_nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "yd8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yZrBKxKhbw4I5T2D2ia0Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yZrBKxKhbw4I5T2D2ia0Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "kd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7FDPiYnztHLh8lvlMFRyXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7FDPiYnztHLh8lvlMFRyXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "a9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VaLam_KQiz8POCW3aoer2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VaLam_KQiz8POCW3aoer2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "iNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWKX-ZC2lOv6w1ALdddfTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWKX-ZC2lOv6w1ALdddfTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mknpTKgXrVcTrP0y-Vu4pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mknpTKgXrVcTrP0y-Vu4pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zthyKuXsoA4KkwsS0PTltg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zthyKuXsoA4KkwsS0PTltg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SOHLJ-nmGdCO3sK7plOv2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SOHLJ-nmGdCO3sK7plOv2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fq5gtNQfWd1unM0EZse4Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fq5gtNQfWd1unM0EZse4Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AP03oXWkrXH194nvbfv1DA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AP03oXWkrXH194nvbfv1DA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "cdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H5q2tXxLE1d4iFM0ZRc45w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H5q2tXxLE1d4iFM0ZRc45w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "gdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I5V2d7T-ngpDaQd5S4eJBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I5V2d7T-ngpDaQd5S4eJBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "idYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L9mej-PTu4SZGfpi083-2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L9mej-PTu4SZGfpi083-2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "jNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Nr_8hMt7lL3ObaXhoWtKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Nr_8hMt7lL3ObaXhoWtKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "vdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B_1gf1EYUxn7EVvh7093Dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B_1gf1EYUxn7EVvh7093Dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "wdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UOcKR6g40j0qNVOcPsBj4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UOcKR6g40j0qNVOcPsBj4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["87tmMz7dkdhga3ssbWBSBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["87tmMz7dkdhga3ssbWBSBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "79YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JoGJrCEQ34-StmPNyR5q3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JoGJrCEQ34-StmPNyR5q3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "WdYU7YYBBkbVtX3n7DJm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uh-jwsuxuUYFlAJ62euRwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uh-jwsuxuUYFlAJ62euRwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Cd8U7YYByh-A-Biy7P_G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbcOgGVzXu_Kl1MHENboNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbcOgGVzXu_Kl1MHENboNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "qtYU7YYBBkbVtX3n9jvb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cxsXzrG-rWhSkAffaeLL8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "g9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BfRadBAJgVIPCs4sRWRCsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BfRadBAJgVIPCs4sRWRCsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "69YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LrLWZ5bevl0fyb8pVLrxUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LrLWZ5bevl0fyb8pVLrxUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "yNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jBxx8BsBrE-wbyWADe34bQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jBxx8BsBrE-wbyWADe34bQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "dtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mxx8ugWwWszTb7eJBegR_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mxx8ugWwWszTb7eJBegR_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IngmyqAhSupCs-_uuEXPtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IngmyqAhSupCs-_uuEXPtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FkuuK33tmpaBP6yN588PoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FkuuK33tmpaBP6yN588PoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l-yGtc7ewbZgVN8gK10pTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l-yGtc7ewbZgVN8gK10pTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IVe1z8n3OgBSXs9ZgYQTCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IVe1z8n3OgBSXs9ZgYQTCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IWUU7YYBO2e_P_Qb7s4B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fRgvmW6OzWfmn-P-AvBcnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fRgvmW6OzWfmn-P-AvBcnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ftyi07t9eEe9-e4ojBNJbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ftyi07t9eEe9-e4ojBNJbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nn_ySIHBlBRC_S__9EYUQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nn_ySIHBlBRC_S__9EYUQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ndYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihAOVqchKA5mXlZP4M1IsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihAOVqchKA5mXlZP4M1IsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "r9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XDv5HwoOhhJwuGtzx9aiqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XDv5HwoOhhJwuGtzx9aiqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PEL3jW6ozwuPYGESMGBbbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PEL3jW6ozwuPYGESMGBbbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JCe68q68pLemWJDxvGUezA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JCe68q68pLemWJDxvGUezA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["64Ux2oNdDZBBedmvlh2Jwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["64Ux2oNdDZBBedmvlh2Jwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gJQSsgSapsp92rjPF1WFiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gJQSsgSapsp92rjPF1WFiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "ntYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uchiuLFbH0NhckqiyJoDow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uchiuLFbH0NhckqiyJoDow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "qNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Dqb1JZa6QuOKypgO1FUIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Dqb1JZa6QuOKypgO1FUIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ytYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TrAEEkzHCQIrkyMsb-wF4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TrAEEkzHCQIrkyMsb-wF4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mef8b6Ms_KB9BmHs5fEaQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mef8b6Ms_KB9BmHs5fEaQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "8dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MMNf5Flp1WG-AiF7Q4RJ_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MMNf5Flp1WG-AiF7Q4RJ_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "QuAU7YYByh-A-Biy9wH-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P_F4N85n6ygrRQ1ObfKSJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P_F4N85n6ygrRQ1ObfKSJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "s9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fgalSE7MGf2Y-ZRC0Xb24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fgalSE7MGf2Y-ZRC0Xb24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "wNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gf9T-ToUcXZ1fopG8bUaCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gf9T-ToUcXZ1fopG8bUaCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "UWUU7YYBO2e_P_Qb-NFi"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "meAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5QdX4ICkFiBy7z35tqsMeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5QdX4ICkFiBy7z35tqsMeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "R2UV7YYBO2e_P_QbDOFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gkXyNFDJDBzOyUtceexiUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gkXyNFDJDBzOyUtceexiUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZuAV7YYByh-A-BiyCBR8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aMeqW0QxLpn1TpYZf4XBMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "p9YV7YYBBkbVtX3nClae"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5herarFi58uky2CNY5OarQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "SGUV7YYBO2e_P_QbDOFC"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K3Z7Bso8_acxSu6Vxdfbjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K3Z7Bso8_acxSu6Vxdfbjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "FeAV7YYByh-A-BiyBxBv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GQHZ33M2r9CWZs0ylfnGiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GQHZ33M2r9CWZs0ylfnGiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DtYV7YYBBkbVtX3nB1HM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kOsAFOokw3TMOocYazB7hA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kOsAFOokw3TMOocYazB7hA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LNYV7YYBBkbVtX3nCFPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SmKjrD7wsCsyGVvgvupg3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SmKjrD7wsCsyGVvgvupg3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "duAV7YYByh-A-BiyCRh4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Org4Ct_UK1CJypQlyzN79g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Org4Ct_UK1CJypQlyzN79g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XuAV7YYByh-A-BiyChop"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NJLtoy2AIp4OF7edUsA8Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NJLtoy2AIp4OF7edUsA8Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qNYV7YYBBkbVtX3nClae"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fmIQ76zzVZ9EWAQ55W78zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fmIQ76zzVZ9EWAQ55W78zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "l-AV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EM9AISJikuWZSi4uSs5f_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EM9AISJikuWZSi4uSs5f_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GOAV7YYByh-A-BiyBxBv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WGa9qjf8wQqVaf6Gdp-z_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WGa9qjf8wQqVaf6Gdp-z_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qdYV7YYBBkbVtX3nClae"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rp13JE-7UWo9J1PFiierKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rp13JE-7UWo9J1PFiierKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "luAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8YnDoiutnFkCKfHN27NSuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8YnDoiutnFkCKfHN27NSuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "ZOAV7YYByh-A-BiyCBR8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLtkTN9H0P9GQGUpxzaGrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLtkTN9H0P9GQGUpxzaGrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gtYV7YYBBkbVtX3nC1gN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YtVvdG2-K8RntFVkz8aZsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YtVvdG2-K8RntFVkz8aZsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "muAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dOsagAt-XXDxs5XGCBbstQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dOsagAt-XXDxs5XGCBbstQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "OtYV7YYBBkbVtX3nC1r7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SHi_az7OQcBjeyPt41wowA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "95381405781962"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SHi_az7OQcBjeyPt41wowA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "95381405781962"} {"create": {"_index": "profiling-events-all", "_id": "ctYV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L7NiiM2JcpyLYptGtnS-lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L7NiiM2JcpyLYptGtnS-lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aGUV7YYBO2e_P_QbGOeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yG5lvQdywRfDEpDAMlDFjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yG5lvQdywRfDEpDAMlDFjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zGUV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yc_2GTJ_IVPE7f4u8QXDeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yc_2GTJ_IVPE7f4u8QXDeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "ztYV7YYBBkbVtX3nFmPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BWUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F6v4xvUd6G7lflKiKXtLLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F6v4xvUd6G7lflKiKXtLLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xOAV7YYByh-A-BiyGCg0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "L-AV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g5lILDW4r2wlyzbt5lq7Iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g5lILDW4r2wlyzbt5lq7Iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BmUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MUbC0p7FbajtleTdDiK2wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MUbC0p7FbajtleTdDiK2wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "xOAV7YYByh-A-BiyGSzz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bsoNX49ITduR-HMxcIbsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-bsoNX49ITduR-HMxcIbsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zdYV7YYBBkbVtX3nFmPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JKuwq_wY-m6F_YLLdvsE3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JKuwq_wY-m6F_YLLdvsE3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3tYV7YYBBkbVtX3nF2Us"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "rGUV7YYBO2e_P_QbF-XY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["99BgVVChjb4P4hAXrcSmGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["99BgVVChjb4P4hAXrcSmGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "A2UV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oiCF7gS8wBa3SfipWqWdgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oiCF7gS8wBa3SfipWqWdgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "yNYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BOn5YfVXLbYJVzgO0D8UNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BOn5YfVXLbYJVzgO0D8UNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9IJobkCHFBtPShwAqokpow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9IJobkCHFBtPShwAqokpow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "iuAV7YYByh-A-BiyNUTr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qb72Yobg_yLohYI9gpP09w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qb72Yobg_yLohYI9gpP09w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mIzahuxkrhduKlDufHRVKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mIzahuxkrhduKlDufHRVKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOKIXPSdx-N8wuoQB9U_bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pOKIXPSdx-N8wuoQB9U_bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wtYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XaUnPPtgxfYR4iOYVLS0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XaUnPPtgxfYR4iOYVLS0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "w9YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ejCsmFBHwcIycmn6V3r8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ejCsmFBHwcIycmn6V3r8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "3tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kAevgyPrUYMi1qMg2RT9YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kAevgyPrUYMi1qMg2RT9YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6OAV7YYByh-A-BiyNkXi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wfVS4y4D58OSyaXvZj-XtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wfVS4y4D58OSyaXvZj-XtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "w-AV7YYByh-A-BiyN0ck"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z4iiv5UxRhQpx3JPtDse_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z4iiv5UxRhQpx3JPtDse_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "FeAV7YYByh-A-BiyK0C1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qwzw6oIfyawdflY_bB-eDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qwzw6oIfyawdflY_bB-eDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "x9YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z0qb5lnO7jV9ZiYyxxUpVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z0qb5lnO7jV9ZiYyxxUpVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "0dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sIn36_6lhKQc_bEzQgq03g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sIn36_6lhKQc_bEzQgq03g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "5tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8E3vSloXP4dGqDQFAfS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8E3vSloXP4dGqDQFAfS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "d2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zs0wdHAUro9OZHb7uDVC0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zs0wdHAUro9OZHb7uDVC0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "f2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nZ8fRP549U0JQTjsBAy_jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nZ8fRP549U0JQTjsBAy_jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WeAV7YYByh-A-BiySFbi"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4YktLuYieY_qIn0-Svbtbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4YktLuYieY_qIn0-Svbtbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ANYV7YYBBkbVtX3nSp3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5yQFzmK6rVAYH_IWw9mY4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5yQFzmK6rVAYH_IWw9mY4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "TuAV7YYByh-A-BiyRE-k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OlDB0giXI1NsaTgwfP9dqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OlDB0giXI1NsaTgwfP9dqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "cWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96zUk00wJUkz6pqWJ4UVBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96zUk00wJUkz6pqWJ4UVBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "eGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n7QBBvONqlianWpauyZWrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n7QBBvONqlianWpauyZWrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "bmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sSznj8DVFmJrz2nQyuMvVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sSznj8DVFmJrz2nQyuMvVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "mmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LUbJKRt2QZB4MM480Ex81g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LUbJKRt2QZB4MM480Ex81g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "kWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n0VugIogSoCuJazNruqmpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n0VugIogSoCuJazNruqmpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WD-Hox2mUf33ggVA1pZW3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WD-Hox2mUf33ggVA1pZW3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Y2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0P5ZzCLXHvPtrKtxiUuFPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0P5ZzCLXHvPtrKtxiUuFPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "dmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "eWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "iWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dpoKTK9LU4hKKEDZqArQ-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dpoKTK9LU4hKKEDZqArQ-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "lmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uf9UVmqLr0wj8jkOytmlgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uf9UVmqLr0wj8jkOytmlgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1WYV7YYBO2e_P_QbSQ9v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "3dYV7YYBBkbVtX3nTKQS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["56T0aIwgwSEUNL-7riuYkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["56T0aIwgwSEUNL-7riuYkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xtYV7YYBBkbVtX3nVKlD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIUTQnmo7hmDvvAn77UZ1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIUTQnmo7hmDvvAn77UZ1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "U9YV7YYBBkbVtX3nVa1D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["puIsGFT9D9ie7OaAMWkigA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["puIsGFT9D9ie7OaAMWkigA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UtYV7YYBBkbVtX3nVa1D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mAgmPEf7EXxW53hQ-sKjJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mAgmPEf7EXxW53hQ-sKjJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "7tYV7YYBBkbVtX3nVq52"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JT_5OseDCbBwbh6-ei601g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JT_5OseDCbBwbh6-ei601g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7dYV7YYBBkbVtX3nVq52"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GU1_UWZYiKrKpJ3S0rF8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GU1_UWZYiKrKpJ3S0rF8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F2YV7YYBO2e_P_QbVxih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QBmlTSly2COGQg4tFf4YgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QBmlTSly2COGQg4tFf4YgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OWYV7YYBO2e_P_QbWBy9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TwXoXWu14wnNism8hup1ig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TwXoXWu14wnNism8hup1ig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-tYV7YYBBkbVtX3nW7rm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uV6WbBNOuHvs6QDcFyIEHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uV6WbBNOuHvs6QDcFyIEHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-2YV7YYBO2e_P_QbWR0q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JDfH1YIww9Sqd-S_w7NU1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JDfH1YIww9Sqd-S_w7NU1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RNYV7YYBBkbVtX3nWrnf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["deEhfqa-GWkx1wr4iMz1nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["deEhfqa-GWkx1wr4iMz1nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "uOAV7YYByh-A-BiyWmE2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y7-fVN4a3INYDwPmaOS0Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y7-fVN4a3INYDwPmaOS0Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "mdYV7YYBBkbVtX3nWreQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ENrq2_MBwld_ERQVMIbQlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ENrq2_MBwld_ERQVMIbQlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "KeAV7YYByh-A-BiyWGBd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a7q9y6bl0FIQxuLqZqANIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a7q9y6bl0FIQxuLqZqANIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "mNYV7YYBBkbVtX3nWreQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "B2YV7YYBO2e_P_QbWyI8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AUziHZelmRAq_ogGLsAghQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AUziHZelmRAq_ogGLsAghQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "4GYV7YYBO2e_P_QbWyOI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["07tFiGQvKlKEn8Vy4W9Sog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["07tFiGQvKlKEn8Vy4W9Sog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9-AV7YYByh-A-BiyVFzv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFK8oAGV_jR__SZWaQoiWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFK8oAGV_jR__SZWaQoiWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "-OAV7YYByh-A-BiyVFzv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MErld6wdBGLR2ab9ZWtH2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MErld6wdBGLR2ab9ZWtH2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "uOAV7YYByh-A-BiyVV6i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "NtYV7YYBBkbVtX3ndcxm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPAXeu9JRh62VS0TzctJEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPAXeu9JRh62VS0TzctJEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wmYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rh6dkbq8WqrY7XSMixfetg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rh6dkbq8WqrY7XSMixfetg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TtYV7YYBBkbVtX3ndtKj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Wa8MgBNSJuWvg6Zve7ROA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Wa8MgBNSJuWvg6Zve7ROA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RX6MWdoFei8k1kwyhzfnHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RX6MWdoFei8k1kwyhzfnHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6YWns3NF2PVmevxSMrfdSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6YWns3NF2PVmevxSMrfdSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vmYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WwFWfRAxe9vNEiy3LvcKPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WwFWfRAxe9vNEiy3LvcKPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iweYdmdhgZ2TexEvbTHmRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iweYdmdhgZ2TexEvbTHmRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "heAV7YYByh-A-Biyd3Y6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3tj55kewRVSqh_hbiqeHsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3tj55kewRVSqh_hbiqeHsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XeAV7YYByh-A-BiyaW45"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JKuwq_wY-m6F_YLLdvsE3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JKuwq_wY-m6F_YLLdvsE3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "G-AV7YYByh-A-BiydHGD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6GIXZB_oqJtK4ZOCyzjV1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6GIXZB_oqJtK4ZOCyzjV1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "VWYV7YYBO2e_P_Qbdz58"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lrXQOYdtT3nAkaFRyN7-0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lrXQOYdtT3nAkaFRyN7-0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "q9YV7YYBBkbVtX3naMS4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SJqbNgrSxDdoOACHB93N4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SJqbNgrSxDdoOACHB93N4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "wGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fkclrml2poKZRsRiP2tUBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fkclrml2poKZRsRiP2tUBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "xGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AwchOulsOERDFXbfKPcBMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AwchOulsOERDFXbfKPcBMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "yGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "c-AV7YYByh-A-BiyhoV2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4qaZ4j35u_YBqQ5HnDB_oQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4qaZ4j35u_YBqQ5HnDB_oQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7OAV7YYByh-A-BiyiIYF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ymje1CajexZF5hJ1bAbTlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ymje1CajexZF5hJ1bAbTlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VOAV7YYByh-A-BiyiIjx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["99dRlWUAlFNw4L5T7yQdfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["99dRlWUAlFNw4L5T7yQdfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QOAV7YYByh-A-BiyhIRV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CaYO4egGBij97PHY37LF3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CaYO4egGBij97PHY37LF3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CtYV7YYBBkbVtX3nhNrA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oihk6v8OvTDdD6N0NY6YVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oihk6v8OvTDdD6N0NY6YVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UWYV7YYBO2e_P_Qbhkg5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jP--MF88HszSEEHJkdZMeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jP--MF88HszSEEHJkdZMeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QWYV7YYBO2e_P_QbiE9B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BDlisnvqa1LLQOmq1q0Eow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BDlisnvqa1LLQOmq1q0Eow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3dYV7YYBBkbVtX3nieY3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0BKf-9CBUYklPmi5iCM1rw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0BKf-9CBUYklPmi5iCM1rw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TGYV7YYBO2e_P_Qbh029"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NyrWdA_BddZBmB7vWwrYvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NyrWdA_BddZBmB7vWwrYvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "f-AV7YYByh-A-BiyhIIL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7_opwU1mFxT0XU3A2dlAxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7_opwU1mFxT0XU3A2dlAxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "SNYV7YYBBkbVtX3niOWG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nZ2BKRv9gSdaoFxQ-TzvPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nZ2BKRv9gSdaoFxQ-TzvPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "TWYV7YYBO2e_P_Qbh029"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E6UMSC7GLe9jd7t1ot1_kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E6UMSC7GLe9jd7t1ot1_kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "U-AV7YYByh-A-BiyiIjx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eakp9OVIhBxsZNnrdfGKYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eakp9OVIhBxsZNnrdfGKYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "fuAV7YYByh-A-Biyo5MJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RhMJrUxrd57e6G7g2-PKcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RhMJrUxrd57e6G7g2-PKcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "R2YV7YYBO2e_P_Qbol5k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9dYV7YYBBkbVtX3nmPyP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MLI30dzAv_XVLHnFXWzCzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MLI30dzAv_XVLHnFXWzCzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zmYV7YYBO2e_P_QbmV0u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GNcV7YYBBkbVtX3npQJl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aLyOgMQu19TF5wLalqlvBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "707202532850088"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aLyOgMQu19TF5wLalqlvBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "707202532850088"} {"create": {"_index": "profiling-events-all", "_id": "NtYV7YYBBkbVtX3nlvIq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["btxpNnU_e8R601EfodEE5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["btxpNnU_e8R601EfodEE5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0NYV7YYBBkbVtX3nl_dj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0pVn3RaIbpVhn8RviFIpJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0pVn3RaIbpVhn8RviFIpJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "b2YV7YYBO2e_P_Qbo2DE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "adcV7YYBBkbVtX3nsgwF"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFeV1BctdgGmKhHEdAax5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFeV1BctdgGmKhHEdAax5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MeAV7YYByh-A-Biyt6sa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fUnBrD_WzBp45WRRoNXPpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fUnBrD_WzBp45WRRoNXPpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VeAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uDNzqwFHdWL1Gt4wJdSyGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uDNzqwFHdWL1Gt4wJdSyGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B9cV7YYBBkbVtX3ntRYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VRt6q5F3ckt_c8O1gwmSjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VRt6q5F3ckt_c8O1gwmSjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2dcV7YYBBkbVtX3nsw-Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PQ297jfrM7aOAB4-C3MH-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PQ297jfrM7aOAB4-C3MH-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "I2YV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o02UcgaTacPmYjOwwPOCJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o02UcgaTacPmYjOwwPOCJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UeAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R8qQ1EkUatykSwAEdlZfRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R8qQ1EkUatykSwAEdlZfRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VNcV7YYBBkbVtX3nsg5Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nON9RmBx4umF5B_Of_VNaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nON9RmBx4umF5B_Of_VNaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "2NcV7YYBBkbVtX3nsw-Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ExUCp1oD4V2746Bz2cZdiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ExUCp1oD4V2746Bz2cZdiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9NcV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ojISZd3oQrHFv15BTiVAhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ojISZd3oQrHFv15BTiVAhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VOAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pczYn9bA4SlIUvF6oLM4Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pczYn9bA4SlIUvF6oLM4Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "WeAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g-gvaAwuroQsfSOFcGq40g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g-gvaAwuroQsfSOFcGq40g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "YOAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-uvhrHdtYBwaSTwW97ffvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-uvhrHdtYBwaSTwW97ffvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "CNcV7YYBBkbVtX3ntRYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fp8z5vGvSD0i9Ni8-EZ6jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fp8z5vGvSD0i9Ni8-EZ6jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Y-AV7YYByh-A-BiytqnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["48ZFJTNDYJpyOFN3X2WN0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["48ZFJTNDYJpyOFN3X2WN0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6dcV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I8foXFGiFUjsvoBc2tLNSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I8foXFGiFUjsvoBc2tLNSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "yNcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "q2YV7YYBO2e_P_QbtW5b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NNgMPk_Aq5xW7b1t7OKA5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NNgMPk_Aq5xW7b1t7OKA5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "V9cV7YYBBkbVtX3nsg5Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d31EKO2VF5LonJxrOu4FNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d31EKO2VF5LonJxrOu4FNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "0OAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9eJFc1RqWTK4Nh5sHxlOdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9eJFc1RqWTK4Nh5sHxlOdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "SNcV7YYBBkbVtX3nthkq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dZVhEMwoIzMGD6Fthzhnhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dZVhEMwoIzMGD6Fthzhnhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "iNcV7YYBBkbVtX3ntBPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8giK6mKV7HDPF-jB4e6ajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8giK6mKV7HDPF-jB4e6ajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "iNcV7YYBBkbVtX3nuB51"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LKEaCr3J8DRAWmFUoWCNBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LKEaCr3J8DRAWmFUoWCNBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "fNcV7YYBBkbVtX3ntRfi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "4mYV7YYBO2e_P_Qbsmn3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KOMN7HDuAGD1N2A7P0t7vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KOMN7HDuAGD1N2A7P0t7vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "QGYV7YYBO2e_P_Qbxnnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wlrbhXKEUrmfjLJYUMrELQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wlrbhXKEUrmfjLJYUMrELQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-OAV7YYByh-A-Biyxbvb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1uwRNa4999k1DkBivtvQiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1uwRNa4999k1DkBivtvQiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "p-AV7YYByh-A-Biy08M-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E53CaCO64IW70sjHWzGHVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E53CaCO64IW70sjHWzGHVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "D2YV7YYBO2e_P_Qb037f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mcFH-Ijp7M4Pm2g7nfowcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mcFH-Ijp7M4Pm2g7nfowcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kmYV7YYBO2e_P_QbxXYP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wZyq92-n1mREdjg_zgtpMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wZyq92-n1mREdjg_zgtpMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "wWYV7YYBO2e_P_Qbx3ol"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DBkis1--lWH-VTcyB6uwYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DBkis1--lWH-VTcyB6uwYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "gOAV7YYByh-A-Biy0b5D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acVe45FNWlYgmO4nWQHN3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acVe45FNWlYgmO4nWQHN3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "qeAV7YYByh-A-Biy08M-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihGJ_26t_QqommWWGt2AFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ihGJ_26t_QqommWWGt2AFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "qOAV7YYByh-A-Biy08M-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WJw6-5iVNJ-4mcsircvR6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WJw6-5iVNJ-4mcsircvR6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "ZdcV7YYBBkbVtX3nxSih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ_bArkiIPRSWu3KZBADQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ_bArkiIPRSWu3KZBADQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L-AV7YYByh-A-Biyxr4p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YYYi3p87Qv-d-cNhljrsYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YYYi3p87Qv-d-cNhljrsYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nGYV7YYBO2e_P_Qb0nzr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZGUB8nd8sv9Or-VM0Kjm3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZGUB8nd8sv9Or-VM0Kjm3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-eAV7YYByh-A-Biyxbvb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UcSfB9O7oaCsfgTNqnRSmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UcSfB9O7oaCsfgTNqnRSmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QuAV7YYByh-A-Biy1MfN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pUot7h45U8B9b9S1T5stzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pUot7h45U8B9b9S1T5stzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7mYV7YYBO2e_P_Qbxnew"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QT_0k1qSEqNIoe2v3zsJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QT_0k1qSEqNIoe2v3zsJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Y9cV7YYBBkbVtX3n0iyh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IuR7KAGunHdUgixJ44Kh9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IuR7KAGunHdUgixJ44Kh9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ZNcV7YYBBkbVtX3n0iyh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h78omCSCOG8EoQ0xkchTYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h78omCSCOG8EoQ0xkchTYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "BNcV7YYBBkbVtX3nxSdX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8mRMx1StXr0LAO6ji3Y2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8mRMx1StXr0LAO6ji3Y2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "JOAV7YYByh-A-Biy0cDF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "JeAV7YYByh-A-Biy0cDF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UpG4HUjCnzDBM_w7fbVK2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "aNcV7YYBBkbVtX3n5EbR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vB8t8x7qJssFpC43ts9N9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vB8t8x7qJssFpC43ts9N9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XuAV7YYByh-A-Biy5t9l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pOAV7YYByh-A-Biy5dxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v3ZqEBwfD7Kct76Q2Ha5ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v3ZqEBwfD7Kct76Q2Ha5ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tuAV7YYByh-A-Biy4dbn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0e7BwxOo44d7lCUy997IJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0e7BwxOo44d7lCUy997IJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "INcV7YYBBkbVtX3n40O7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jx5ziVarO0rH_UBySTUCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "wdcV7YYBBkbVtX3n5EQ_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1azdmus8MOaCZZsOVGC4Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1azdmus8MOaCZZsOVGC4Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gNcV7YYBBkbVtX3n4Two"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UDdkGUkVb5eesXaBvqvyqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UDdkGUkVb5eesXaBvqvyqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "s9cV7YYBBkbVtX3n4j9s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyslIhEYrdCY7Y2kR4LC4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyslIhEYrdCY7Y2kR4LC4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "YOAV7YYByh-A-Biy4dNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LZvFVTJOMfo7RHR7D2PEUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LZvFVTJOMfo7RHR7D2PEUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ntcV7YYBBkbVtX3n40Er"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wMYpNYJGdYQOQzp2QFWVOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wMYpNYJGdYQOQzp2QFWVOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "JdcV7YYBBkbVtX3n4j4u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q-SfQ_r9EJdGkmFMOGPAZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q-SfQ_r9EJdGkmFMOGPAZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "z9cW7YYBBkbVtX3nAVhI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acGHnAm6JFFvJ2ZoZKt_fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acGHnAm6JFFvJ2ZoZKt_fg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-WYW7YYBO2e_P_QbBKlb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fMEGhVur8bO2mv1boqOVuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fMEGhVur8bO2mv1boqOVuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ytcW7YYBBkbVtX3nBWBe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gXR6EGOsoWtrSlWApDMCzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gXR6EGOsoWtrSlWApDMCzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b9cV7YYBBkbVtX3n9lYm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRG5hVGVXLYVZ0h02g0udQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRG5hVGVXLYVZ0h02g0udQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "r-AW7YYByh-A-BiyBPC2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2tnlnNGd85PzXoftF65LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L2tnlnNGd85PzXoftF65LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "S2YW7YYBO2e_P_QbAaSR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-riZP-fh7uXaUsCqBO2ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-riZP-fh7uXaUsCqBO2ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "y9cW7YYBBkbVtX3nBWBe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EVbkX_ePnzMrnOl-TBl5FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EVbkX_ePnzMrnOl-TBl5FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-all", "_id": "k-AW7YYByh-A-BiyBfKz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SGWXcHhJ-3eFNiySrhglHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SGWXcHhJ-3eFNiySrhglHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "juAW7YYByh-A-BiyEf7z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cqS65a_0vS0KD1oFWfGPCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cqS65a_0vS0KD1oFWfGPCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "j-AW7YYByh-A-BiyEf7z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hMnXoL28a6WRFVFuXnlcrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hMnXoL28a6WRFVFuXnlcrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nOEW7YYByh-A-BiyEgDd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j6Z5oRx4O63IFM67ZJuuJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j6Z5oRx4O63IFM67ZJuuJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LWYW7YYBO2e_P_QbELiv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mUFADSo1xxMWcv8DSPuI8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mUFADSo1xxMWcv8DSPuI8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BNcW7YYBBkbVtX3nFGcu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vwNl340FtK4II3OTHfAxDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vwNl340FtK4II3OTHfAxDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4-AW7YYByh-A-BiyD_jF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zt84rjIRj6I8L5VSa3HBpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zt84rjIRj6I8L5VSa3HBpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "edcW7YYBBkbVtX3nEmVM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gKIIWZ-RBY_pDJxZsnm0jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gKIIWZ-RBY_pDJxZsnm0jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "neEW7YYByh-A-BiyEgDd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HOEW7YYByh-A-BiyFQcS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7-AW7YYByh-A-BiyEfxY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JqJc9Tw8mUc7OkItUIvw5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JqJc9Tw8mUc7OkItUIvw5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "gWYW7YYBO2e_P_QbFL_E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t1xfFBeH5Fl1K12J5A31pQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "g2YW7YYBO2e_P_QbFL_E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q1sP74JQ43bJB5q4cKtRIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q1sP74JQ43bJB5q4cKtRIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "qmYW7YYBO2e_P_QbEruU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "89cW7YYBBkbVtX3nEGNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MNoOASNIU68SUFgbeLW58A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MNoOASNIU68SUFgbeLW58A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "GWYW7YYBO2e_P_QbEbqq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z_XfhBGlE3Xx8UElIIjuaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z_XfhBGlE3Xx8UElIIjuaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9dcW7YYBBkbVtX3nEGNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EEeUn8j0Iub4lrEKoW-8Cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "179675615145181"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EEeUn8j0Iub4lrEKoW-8Cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "179675615145181"} {"create": {"_index": "profiling-events-all", "_id": "etcW7YYBBkbVtX3nEmVM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["720bYtIjXZ0s4au9mJ3ENA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["720bYtIjXZ0s4au9mJ3ENA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nuEW7YYByh-A-BiyEgDd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9jPK4ekFswUlDMrLg6xAug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9jPK4ekFswUlDMrLg6xAug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "buEW7YYByh-A-BiyEwXO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NYCnn7inzK2gAPHma58uQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NYCnn7inzK2gAPHma58uQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "IWYW7YYBO2e_P_QbFcGw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S58C5t85-Y0vir0VJHn3sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S58C5t85-Y0vir0VJHn3sQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "7uAW7YYByh-A-BiyEfxY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jQg_3Bmo6e2S1O85p_SEZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jQg_3Bmo6e2S1O85p_SEZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "CNcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ch3muNdb91l8mJnrRw326w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ch3muNdb91l8mJnrRw326w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nqNBcpTJOJBcaLx3xSaIng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nqNBcpTJOJBcaLx3xSaIng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E9cW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PaaTApbUCVYoJdVKOnUBcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PaaTApbUCVYoJdVKOnUBcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ymYW7YYBO2e_P_QbJczE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bgzswmC99T0GXpCWQr9U_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bgzswmC99T0GXpCWQr9U_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5NcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t4n19LeK4zvHCEOuBRHoDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t4n19LeK4zvHCEOuBRHoDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "BtcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "DNcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TUWMTxzeES_T8-svE5R5CA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TUWMTxzeES_T8-svE5R5CA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "C9cW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lJAXkKqZmAGFZNufVun9jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lJAXkKqZmAGFZNufVun9jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "ueEW7YYByh-A-BiyIRAu"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "PdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5RtcWDMSyG5oMUK9N5k8yg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5RtcWDMSyG5oMUK9N5k8yg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "k-EW7YYByh-A-BiyQzHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7oGs945PXyHA3K9GcsRw1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7oGs945PXyHA3K9GcsRw1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1dcW7YYBBkbVtX3nRZBy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JWmxFWMO8LHVPPV9p1GcnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JWmxFWMO8LHVPPV9p1GcnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "d9cW7YYBBkbVtX3nQYxi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RTGr7Nm-Ia9juXQJ0VJo4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RTGr7Nm-Ia9juXQJ0VJo4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aGYW7YYBO2e_P_QbROQw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vcIsDLwygnNOUzkldgQe7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vcIsDLwygnNOUzkldgQe7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yOEW7YYByh-A-BiyQitl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dtcW7YYBBkbVtX3nQo-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["746hwfph0Dw2g_3bePUkEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["746hwfph0Dw2g_3bePUkEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hmYW7YYBO2e_P_QbRObI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D3VUcyuPxGSWdwpH9VSPOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D3VUcyuPxGSWdwpH9VSPOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuEW7YYByh-A-BiyRTUo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mqr5kDewzIwNjBe2t0vTXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mqr5kDewzIwNjBe2t0vTXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1tcW7YYBBkbVtX3nRZBy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPqtJ9Ffu3wDNg6koFPX5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPqtJ9Ffu3wDNg6koFPX5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Q-EW7YYByh-A-BiyQSqw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["osSW2qt9a8zRx0nR8PECjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["osSW2qt9a8zRx0nR8PECjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "hGYW7YYBO2e_P_QbP91J"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f6Keqe1sXyk36jAJ3WN1sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f6Keqe1sXyk36jAJ3WN1sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "hWYW7YYBO2e_P_QbP91J"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f7riNXLCE7Lya9tYdhWvxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f7riNXLCE7Lya9tYdhWvxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "Z2YW7YYBO2e_P_QbROQw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bYFx-SR9JjDh3LNKYdmEBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bYFx-SR9JjDh3LNKYdmEBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "y2YW7YYBO2e_P_QbQeID"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "X-EW7YYByh-A-BiyQi3_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I9TiskxOBE6uewdlBEfbaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I9TiskxOBE6uewdlBEfbaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "YeEW7YYByh-A-BiyQi3_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "MuEW7YYByh-A-BiyTjpF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PR0G3Br-iqix1uCUZkKS_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PR0G3Br-iqix1uCUZkKS_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "T2YW7YYBO2e_P_QbUe1z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IePXD1TmrKr2VUEUp0lyYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IePXD1TmrKr2VUEUp0lyYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CWYW7YYBO2e_P_QbUe_C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9kcejzSJCXOEAAMTuFifhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9kcejzSJCXOEAAMTuFifhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7mYW7YYBO2e_P_QbVPdI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oAhRUpu5Nxvud8PhxnY97g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oAhRUpu5Nxvud8PhxnY97g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NOEW7YYByh-A-BiyTjpF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vyzbd-n47muGD1CcY51iSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vyzbd-n47muGD1CcY51iSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KdcW7YYBBkbVtX3nUJll"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7biARfQSIKGkOMBE8K3ifw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7biARfQSIKGkOMBE8K3ifw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jOEW7YYByh-A-BiyUT4z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["00uTEq02DOt2grmBxWEFtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["00uTEq02DOt2grmBxWEFtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aGYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Et5sNZhAoszUicKSkeO_ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Et5sNZhAoszUicKSkeO_ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bdcW7YYBBkbVtX3nU53R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A4PiDpik1xNqn-sMYyun1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A4PiDpik1xNqn-sMYyun1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "atcW7YYBBkbVtX3nT5d5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PwiymugfyWZ7JNBkVfJTzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PwiymugfyWZ7JNBkVfJTzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BeEW7YYByh-A-BiyUkAI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sd-ZaAvLHLrrMbq7MjTuQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sd-ZaAvLHLrrMbq7MjTuQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "K2YW7YYBO2e_P_QbUvPO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kuJBVypeLq1V0jSA-wxI4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kuJBVypeLq1V0jSA-wxI4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "C9cW7YYBBkbVtX3nU5wV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FpeKkethPGO1uv-qrij4uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FpeKkethPGO1uv-qrij4uQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "_mYW7YYBO2e_P_QbU_RV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OYGXc31yJI5bR-H2iNSwHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OYGXc31yJI5bR-H2iNSwHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "b9cW7YYBBkbVtX3nUpqO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6bno3KY4YPf5Yv8-TeyIMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6bno3KY4YPf5Yv8-TeyIMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-all", "_id": "RmYW7YYBO2e_P_QbT-o1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AGYW7YYBO2e_P_QbU_VV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["92cNuiuQKW3x7lS40O9Vmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["92cNuiuQKW3x7lS40O9Vmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b9cW7YYBBkbVtX3nU53R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O3n8w8bc5rIZeq-nJR67uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O3n8w8bc5rIZeq-nJR67uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gGYW7YYBO2e_P_QbU_aU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-KIwcLrlxoEZYnIYFs7xDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-KIwcLrlxoEZYnIYFs7xDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "YGYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nnMQdtf0-TCma7GTQu1BbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nnMQdtf0-TCma7GTQu1BbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "aWYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E4d9ZgpjdjB2MMbdIa-vAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E4d9ZgpjdjB2MMbdIa-vAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "juEW7YYByh-A-BiyUT4z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "utcW7YYBBkbVtX3nVJ4L"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sj1IWeYK2LXaE0gPl1F28Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sj1IWeYK2LXaE0gPl1F28Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "JuEW7YYByh-A-Biybk1R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rlf-Kh1cYYNXH3i3_B9teA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rlf-Kh1cYYNXH3i3_B9teA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "69cW7YYBBkbVtX3nYKLY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "htcW7YYBBkbVtX3nYaQt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wfCTZ14vNLwvsq17Hq3glw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wfCTZ14vNLwvsq17Hq3glw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BuEW7YYByh-A-BiyZEso"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFcmZLo-GvC7WdK5tCotfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YFcmZLo-GvC7WdK5tCotfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qOEW7YYByh-A-BiybUuC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a4F_DzJWoeWMlYJL40d_JA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a4F_DzJWoeWMlYJL40d_JA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w-EW7YYByh-A-Biyclr3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kfr7TPEfSe6g4pC6WtyOxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kfr7TPEfSe6g4pC6WtyOxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "AmcW7YYBO2e_P_QbcRL4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["INv4Z_GTWxG4wGfSHZU1vQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["INv4Z_GTWxG4wGfSHZU1vQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jNcW7YYBBkbVtX3nf735"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qtJZCZ940TmjMXNEWgVXDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qtJZCZ940TmjMXNEWgVXDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FOEW7YYByh-A-Biyclc2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GOEW7YYByh-A-Biyf2a2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xaL3njYZVA5XbbnR4zdhGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xaL3njYZVA5XbbnR4zdhGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "DtcW7YYBBkbVtX3ncrd2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NEr5ZXND311_OYMu-NMslw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NEr5ZXND311_OYMu-NMslw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7eEW7YYByh-A-BiyfVwi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fIYzG8NwYtgLi_ZHHyU4xA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fIYzG8NwYtgLi_ZHHyU4xA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "E-EW7YYByh-A-Biyf2M6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "omcW7YYBO2e_P_QbkSZ4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MbW3yiCJFvhva0WqbvTBag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MbW3yiCJFvhva0WqbvTBag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "I9cW7YYBBkbVtX3njsRV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PeEW7YYByh-A-BiyjnPr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n9MMvRTyGAAOhcwC8K3gwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n9MMvRTyGAAOhcwC8K3gwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X-EW7YYByh-A-Biyj3ZQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P4UuSsDKCXWF3oh3R7oZEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P4UuSsDKCXWF3oh3R7oZEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ReEW7YYByh-A-BiykHvM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2i-Edq18smF6rac2fCPmsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2i-Edq18smF6rac2fCPmsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "oGcW7YYBO2e_P_QbkSZ4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0NRB9624MZLIKmkHE-1rew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0NRB9624MZLIKmkHE-1rew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bGcW7YYBO2e_P_QbkSjL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ECFdcptasOGGQlul_TP0kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ECFdcptasOGGQlul_TP0kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "umcW7YYBO2e_P_QbjiAN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5PRmsP6A6H0WlT5JRWzu5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5PRmsP6A6H0WlT5JRWzu5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P-EW7YYByh-A-BiyjnPr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ztcW7YYBBkbVtX3nkscq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAORKE733KRegC2qyifuEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAORKE733KRegC2qyifuEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "umcW7YYBO2e_P_QbkymJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkrlPnVIce6tMBVHgicCGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkrlPnVIce6tMBVHgicCGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "suEW7YYByh-A-BiyjXFu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OwYjh2SJYfmFVJG36Cn3Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OwYjh2SJYfmFVJG36Cn3Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "Y2cW7YYBO2e_P_QbjiKj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BBtaj0c5fzDAqxomou5uMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "551319322047447"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BBtaj0c5fzDAqxomou5uMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "551319322047447"} {"create": {"_index": "profiling-events-all", "_id": "JdcW7YYBBkbVtX3njsRV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KEKnZX1SRSUSzJoangOL-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KEKnZX1SRSUSzJoangOL-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ZGcW7YYBO2e_P_QbjiKj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iXZcf6LHfVLaFOybaknpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "u2cW7YYBO2e_P_QbjiAN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7AxSTJksslsjaD4JN8OKPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7AxSTJksslsjaD4JN8OKPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "F-EW7YYByh-A-Biyj3ir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcg4yzcU6w_vTsKTk-8RpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcg4yzcU6w_vTsKTk-8RpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "_uEW7YYByh-A-BiyknyA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "E9cW7YYBBkbVtX3nk83N"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BBKNI3Uum2tvcePLaUHnIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BBKNI3Uum2tvcePLaUHnIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "mWcW7YYBO2e_P_QbnzSu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0VAqWGznmKP6FXK4zqZ7QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0VAqWGznmKP6FXK4zqZ7QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "T2cW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PDs7NfSLItAy9yFZzNUo4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PDs7NfSLItAy9yFZzNUo4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8tcW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kuEW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WXO05qRm-L5_dLDd84meuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WXO05qRm-L5_dLDd84meuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "luEW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ofbkGVhqHskFPiKv2X28g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ofbkGVhqHskFPiKv2X28g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "oWcW7YYBO2e_P_QboDfC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-KfBhnJmEvuvAwkRvUGBJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-KfBhnJmEvuvAwkRvUGBJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4-EW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l3DRS0zpqMRRIvCRvx_gnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l3DRS0zpqMRRIvCRvx_gnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5GcW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "x2cW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tvv0GKho3Y3Jvs5JVJ4HCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tvv0GKho3Y3Jvs5JVJ4HCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "k2cW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_ZJuN4Q3XfLWBtpXFkmHgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_ZJuN4Q3XfLWBtpXFkmHgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EWcW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-W1It0TVP9HlUzppA_nUmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-W1It0TVP9HlUzppA_nUmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EmcW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6OaUumRb8P6q4GlOGK0eGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6OaUumRb8P6q4GlOGK0eGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "IdcW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["10lGPWMVE-wMY3Dd5VnXkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["10lGPWMVE-wMY3Dd5VnXkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "I9cW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f7aWCPaXdIyFkeOjGCsGbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f7aWCPaXdIyFkeOjGCsGbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "kNcW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MIjdoSZWUGoqrMkmoKBGPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MIjdoSZWUGoqrMkmoKBGPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5mcW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IQdfawGZrkhBWNm1MuU4ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IQdfawGZrkhBWNm1MuU4ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "PtcW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ewqZUIOmU9Ti-nJquCY7dQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ewqZUIOmU9Ti-nJquCY7dQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "xWcW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NEZCSjz-TkZF0o0U1xZQKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NEZCSjz-TkZF0o0U1xZQKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "oeEW7YYByh-A-BiyrYy7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uBbKBM9HPWXmF7lgBbqn7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uBbKBM9HPWXmF7lgBbqn7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ouEW7YYByh-A-BiyrYy7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFlLbHihvaUX7uvWAN_dUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFlLbHihvaUX7uvWAN_dUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "o-EW7YYByh-A-BiyrYy7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "lmcW7YYBO2e_P_QbnzSu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7jhdqicGJ85l6MMpJ5h4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7jhdqicGJ85l6MMpJ5h4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "H9cW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kHZvNVXXuZ4FaC6U3PxZfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kHZvNVXXuZ4FaC6U3PxZfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "zNcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ay1JvUpYidc_jtVVQh5xJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ay1JvUpYidc_jtVVQh5xJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "w2cW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-EkvDjA94Zr1iIohgty7mQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-EkvDjA94Zr1iIohgty7mQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9dcW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K83bjAjxHCy932bC6uK3sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K83bjAjxHCy932bC6uK3sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "aOEW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mHT6jAWpCslZAdClcFLweg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mHT6jAWpCslZAdClcFLweg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ROEW7YYByh-A-BiyrYsf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHZe7xzP_hYpSdyN0kQ8tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHZe7xzP_hYpSdyN0kQ8tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "aeEW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XMBO6mK2eUEy_2zoVmK7iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XMBO6mK2eUEy_2zoVmK7iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "ymcW7YYBO2e_P_QbvkoZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4SkGmkKhl-y6jJi976f71g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4SkGmkKhl-y6jJi976f71g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "D2cW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oxzBhk8Wo4MNtv46Cf01yQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oxzBhk8Wo4MNtv46Cf01yQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AeEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dn6DDmlkkMhcrqy-oKH_Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dn6DDmlkkMhcrqy-oKH_Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sOEW7YYByh-A-BiyvZ85"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3MlZ2duzbecHR9Swq4KrIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3MlZ2duzbecHR9Swq4KrIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9-EW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["alVDVSUDsOePavwUi2Uc4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["alVDVSUDsOePavwUi2Uc4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "-OEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "_eEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4Cu6oYF8CgThrL_OjB6KKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4Cu6oYF8CgThrL_OjB6KKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "o9cW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jcBkdPt8EyvUbg8R86Rk3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jcBkdPt8EyvUbg8R86Rk3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "G-EW7YYByh-A-Biyv6XQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I6emm7QMCp3MTtFFeDRa6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["I6emm7QMCp3MTtFFeDRa6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "XuEW7YYByh-A-BiywKYJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "8-EW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5-br3efshNyjcSWox2NvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S5-br3efshNyjcSWox2NvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AuEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Hmjwbizys6J1V2OuvGqAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Hmjwbizys6J1V2OuvGqAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "GuEW7YYByh-A-Biyv6XQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ERZHMw9hepZtP1YDjwtr1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ERZHMw9hepZtP1YDjwtr1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "N9cW7YYBBkbVtX3nveeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MP07z4BgOJ1bvy0UuehdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MP07z4BgOJ1bvy0UuehdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-all", "_id": "c2cW7YYBO2e_P_Qb0FnB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GQlu4cDmBP0J7ys3CIDFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3GQlu4cDmBP0J7ys3CIDFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t-EW7YYByh-A-BiyzrTg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M4rbTRbrX0yQkCxBeFA9NQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M4rbTRbrX0yQkCxBeFA9NQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xOEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jGJYBKwxppFBRbCx_fEt5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jGJYBKwxppFBRbCx_fEt5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mdcW7YYBBkbVtX3ny_BB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pr_aC7V9ziezcWkTX9r7wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pr_aC7V9ziezcWkTX9r7wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xuEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7ofbCHl8qRy2q41G8_s7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7ofbCHl8qRy2q41G8_s7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EOEW7YYByh-A-Biy0Llz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tEf9Ie5yokrYlSGE7DLxmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tEf9Ie5yokrYlSGE7DLxmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F-EW7YYByh-A-BiyzbAL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AoxNz9Y_PEGGL6UFqTd8NA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AoxNz9Y_PEGGL6UFqTd8NA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "IeEW7YYByh-A-Biyz7ao"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YDo1NT9KzNVeSq1G9W3WWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YDo1NT9KzNVeSq1G9W3WWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "d9cW7YYBBkbVtX3ny_KF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NFliTc5RbA2S_WmSY2-5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4NFliTc5RbA2S_WmSY2-5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "eNcW7YYBBkbVtX3ny_KF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UugzqeTQ92pW0pcPdsUFNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UugzqeTQ92pW0pcPdsUFNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "v-EW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TwmBGtDgORQiem0fqXxYUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TwmBGtDgORQiem0fqXxYUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "o2cW7YYBO2e_P_Qb0Fc4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VGkfGlLCT3CZxXjvshlG7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VGkfGlLCT3CZxXjvshlG7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "x-EW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9PHIiDKAKQbdjZhfXOPI4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9PHIiDKAKQbdjZhfXOPI4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "mOEW7YYByh-A-Biyz7fk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hy11GM4V5rJ1R_KKBReCYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hy11GM4V5rJ1R_KKBReCYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "J2cW7YYBO2e_P_Qbzlaj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c6o4JEm_SHCSlbGrmocvXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "tuEW7YYByh-A-BiyzrTg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["66C1_zZgLoWX2sy4ZYxW1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "513527008281347"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["66C1_zZgLoWX2sy4ZYxW1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "513527008281347"} {"create": {"_index": "profiling-events-all", "_id": "bWcW7YYBO2e_P_Qb3mI-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t9HIF4XCgjQvhopK4zB1OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t9HIF4XCgjQvhopK4zB1OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "oOEW7YYByh-A-Biy38Nt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dYb17oP8aNL5n3jF96PoUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dYb17oP8aNL5n3jF96PoUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LmcW7YYBO2e_P_Qb3mTL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7LpnWklabUetOIUe4VKNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7LpnWklabUetOIUe4VKNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "tuEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DR99mqPw14HhCOTiH_yCjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DR99mqPw14HhCOTiH_yCjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uOEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dl4T3akeu1eU8F-sCfOUww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dl4T3akeu1eU8F-sCfOUww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uuEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s-ueWdcoRW3U3Wl2s7q1wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s-ueWdcoRW3U3Wl2s7q1wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gtgW7YYBBkbVtX3n-haV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q1W8n5Cn-ifIaAP9BQF_Hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q1W8n5Cn-ifIaAP9BQF_Hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "g9gW7YYBBkbVtX3n-haV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UBsDigUiCriOBIKnOe9n2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UBsDigUiCriOBIKnOe9n2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3OEW7YYByh-A-Biy-tTN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w-GsWvuRbgtEQFjz7G6cAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w-GsWvuRbgtEQFjz7G6cAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wmcW7YYBO2e_P_Qb_4GC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["haUW2KmBYJrtqWFKN3ox5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["haUW2KmBYJrtqWFKN3ox5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8tgW7YYBBkbVtX3n-hQh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "02cW7YYBO2e_P_Qb_38l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mfuqRLZYclnGs_5tl5SKFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mfuqRLZYclnGs_5tl5SKFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wOEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aYIIEFIIPBZMufRatARTng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aYIIEFIIPBZMufRatARTng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "R2cW7YYBO2e_P_Qb-nVZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zqEaDpKRyJAOpyXtzl9UkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zqEaDpKRyJAOpyXtzl9UkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "f-EW7YYByh-A-Biy-9iI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKq1so1oRdQrNuV0NoX8eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKq1so1oRdQrNuV0NoX8eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "zOEW7YYByh-A-Biy_N3T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Jy_fLEQmYV8Uv4CPynhtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Jy_fLEQmYV8Uv4CPynhtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "G2cW7YYBO2e_P_Qb_n7b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8O4Oo3VCILgT6pGMxLQiaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8O4Oo3VCILgT6pGMxLQiaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "u-EW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iL4-l2lLaZN3l2HTgJnKig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iL4-l2lLaZN3l2HTgJnKig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "1GcW7YYBO2e_P_Qb_38l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "AeEX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acpHxpMx1lbCfcQ7t4BGwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acpHxpMx1lbCfcQ7t4BGwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pOEX7YYByh-A-BiyDuyF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N2sJHNv2xDtI8Fug5gaP7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N2sJHNv2xDtI8Fug5gaP7w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "M2cX7YYBO2e_P_QbCoY2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jP--MF88HszSEEHJkdZMeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jP--MF88HszSEEHJkdZMeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gmcX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xs7bKuwpf1rgdNVeL5Z1tA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xs7bKuwpf1rgdNVeL5Z1tA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MmcX7YYBO2e_P_QbCoY2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O456hrPREziYCrquwnUdNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O456hrPREziYCrquwnUdNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "SuEX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ffqI7X0j8c4Op_Y9dHk8Vg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ffqI7X0j8c4Op_Y9dHk8Vg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "AdgX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7bAi2ETLik8HmJW4q-mO5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7bAi2ETLik8HmJW4q-mO5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "A9gX7YYBBkbVtX3nCyUt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ra9c-heZALvJmUxSmzUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ra9c-heZALvJmUxSmzUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "iWcX7YYBO2e_P_QbDpI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UUY2L_ithWPFsPGJM4Kw3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UUY2L_ithWPFsPGJM4Kw3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "MWcX7YYBO2e_P_QbCoY2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["udcCD1ZwYlzlR2BrHqM6qQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["udcCD1ZwYlzlR2BrHqM6qQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4GcX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NiLAkn8PMf6fUuAyKn54rw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NiLAkn8PMf6fUuAyKn54rw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "FmcX7YYBO2e_P_QbDI3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OqL1jazxhGNp3BmuN0BL6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OqL1jazxhGNp3BmuN0BL6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5mcX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q1_7VQVZ9B_m5nqjto1Vhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q1_7VQVZ9B_m5nqjto1Vhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "52cX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YAxRX4VggixxxzGwx37hxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YAxRX4VggixxxzGwx37hxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "NGcX7YYBO2e_P_QbDZHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HVutYQOiJQEOoaSuVpBJXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HVutYQOiJQEOoaSuVpBJXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "p-EX7YYByh-A-BiyDuyF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIcwjcTUxYrOZlE8A754rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIcwjcTUxYrOZlE8A754rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "vNgX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zii4wg2T59k_VWZoCJQUDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zii4wg2T59k_VWZoCJQUDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "4mcX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KwiR_SttfPlB9Vl4LoTdOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KwiR_SttfPlB9Vl4LoTdOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5GcX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "JmcX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_isMSxwsOfQpJC0IwuP96g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_isMSxwsOfQpJC0IwuP96g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "A-EX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["88SBwyQrj_3EBC_tr5p_Vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["88SBwyQrj_3EBC_tr5p_Vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "E2cX7YYBO2e_P_QbDI3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7Vtiv4jrMy8dqqN7pIRDXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7Vtiv4jrMy8dqqN7pIRDXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "cuEX7YYByh-A-BiyDedc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kbEUN-QOQOIjM5mfj2ILfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kbEUN-QOQOIjM5mfj2ILfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "XuEX7YYByh-A-BiyDev8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rm376MXTBGWCRQJ58nODcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rm376MXTBGWCRQJ58nODcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "FWcX7YYBO2e_P_QbDI3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "jGcX7YYBO2e_P_QbDpI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8j8JNdpbtu6ZzDCgLiiQag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8j8JNdpbtu6ZzDCgLiiQag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "v-EX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7Y6G7QHAuKl9wgxs-phIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U7Y6G7QHAuKl9wgxs-phIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eNgX7YYBBkbVtX3nKTOX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DrFuJie5559qDcG8I2bFVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DrFuJie5559qDcG8I2bFVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yOEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PeEX7YYByh-A-BiyHvlf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JZACgiJEVY_n0sywfrDQWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JZACgiJEVY_n0sywfrDQWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hNgX7YYBBkbVtX3nGiz5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7EgL5MlvlPFHn8CzMa-nAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7EgL5MlvlPFHn8CzMa-nAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "duEX7YYByh-A-BiyKv0g"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja9MBlCW9JbhLw8tshjLeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ja9MBlCW9JbhLw8tshjLeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vOEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Bpc8eToJHZimyCogMpwGRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Bpc8eToJHZimyCogMpwGRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "WuEX7YYByh-A-BiyHPRK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jygufO1z6CmSlXFVDFIyOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jygufO1z6CmSlXFVDFIyOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "vuEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UN3BthZs3VjmxILWgSyPNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UN3BthZs3VjmxILWgSyPNw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "_dgX7YYBBkbVtX3nGy2K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "wuEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t7wSwGaTC42K8TqyqrWQ8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t7wSwGaTC42K8TqyqrWQ8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "QeIX7YYByh-A-BiyOQRi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pa7eV1ClIoEc0MOWrL7aYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pa7eV1ClIoEc0MOWrL7aYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HmcX7YYBO2e_P_QbPMHK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5wAfaxsqFHmGRlT9DISZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5wAfaxsqFHmGRlT9DISZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UWcX7YYBO2e_P_QbOr2M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jgUXsxTIbIDkQmcG83gUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jgUXsxTIbIDkQmcG83gUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "YdgX7YYBBkbVtX3nO0KT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsE2Ss7VQy9Y1xUvJ14HPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsE2Ss7VQy9Y1xUvJ14HPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "zNgX7YYBBkbVtX3nPEMb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gd9Zti7g9VaXgPNM2AMUpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gd9Zti7g9VaXgPNM2AMUpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "IOIX7YYByh-A-BiyPQuP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PVeP7XzJjOmZ2Pz05AHQcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PVeP7XzJjOmZ2Pz05AHQcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "H2cX7YYBO2e_P_QbPMHK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GhO-Q7jxmabu9xQq_grssg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GhO-Q7jxmabu9xQq_grssg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "iGcX7YYBO2e_P_QbOrtJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["37H1sSWP9fHHtDykTwvxJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["37H1sSWP9fHHtDykTwvxJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t2cX7YYBO2e_P_QbOr_Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zdgX7YYBBkbVtX3nPEMb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EKi5VuydurxKNxIla0x28A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EKi5VuydurxKNxIla0x28A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "GtgX7YYBBkbVtX3nOTzV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNENHeLd1blNIM02vY64Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNENHeLd1blNIM02vY64Gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "L9gX7YYBBkbVtX3nOz8i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nK-VQLHeSCyigrjH5wLGZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nK-VQLHeSCyigrjH5wLGZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "AmcX7YYBO2e_P_QbObof"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["plpBd6vKoF_CiIU1pw2x5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["plpBd6vKoF_CiIU1pw2x5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "luIX7YYByh-A-BiySRIf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQljazbrYNKb17CR1zcj2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQljazbrYNKb17CR1zcj2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X-IX7YYByh-A-BiyShVm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Cia-tEvT-RYth24Bv6xiew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Cia-tEvT-RYth24Bv6xiew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9mcX7YYBO2e_P_QbS8zy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vSb0Ydm_vV2aKQF-Jm54LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vSb0Ydm_vV2aKQF-Jm54LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6mcX7YYBO2e_P_QbSMNE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MvjeD28P3dNyT_MVzGvfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8MvjeD28P3dNyT_MVzGvfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "idgX7YYBBkbVtX3nSErh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TQY_WRO1qymUg5IQJijndw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TQY_WRO1qymUg5IQJijndw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "udgX7YYBBkbVtX3nSkv7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BD6RKw99uF2og061lVltVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BD6RKw99uF2og061lVltVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dWcX7YYBO2e_P_QbTdFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["btPiCrGC5QuwreUh6KDqzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["btPiCrGC5QuwreUh6KDqzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-WcX7YYBO2e_P_QbScad"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nobPGa726Uz_QIRAEzxZhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nobPGa726Uz_QIRAEzxZhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FmcX7YYBO2e_P_QbTdAJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "J9gX7YYBBkbVtX3nTU6_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["We4ZzWo4Sdy3cRIrD2Ba6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["We4ZzWo4Sdy3cRIrD2Ba6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YOIX7YYByh-A-BiySxi3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HkfH8phILjoSDOJDy-1TVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HkfH8phILjoSDOJDy-1TVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "62cX7YYBO2e_P_QbSMNE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JtgX7YYBBkbVtX3nTU6_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "tmcX7YYBO2e_P_QbXN28"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lFR0pO4Wu14TGU5YNpr5mA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lFR0pO4Wu14TGU5YNpr5mA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UmcX7YYBO2e_P_QbWtrI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ypK8YAnhyIQbCIxguF3ZYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ypK8YAnhyIQbCIxguF3ZYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kuIX7YYByh-A-BiyXSZb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jOIX7YYByh-A-BiyWR5r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6DnsHbkP7LCj2ghN7B7D9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6DnsHbkP7LCj2ghN7B7D9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "n2cX7YYBO2e_P_QbXt8U"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fqlDalQnR0z4CfFMV3Mv9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fqlDalQnR0z4CfFMV3Mv9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "huIX7YYByh-A-BiyXSiy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "j-IX7YYByh-A-BiyZymA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nG3ZbKid0T9JiPA-g3DZAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nG3ZbKid0T9JiPA-g3DZAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "nWcX7YYBO2e_P_QbaOMC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m8UxqcMGCNBvKBluS5X8zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m8UxqcMGCNBvKBluS5X8zA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "7dgX7YYBBkbVtX3nXVoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z-xVIHcDRK95_cuEpNrf-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "4tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xXZDG1IWCIku2BjFkk_Ekw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xXZDG1IWCIku2BjFkk_Ekw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AtiTcD0o7LzpBiQNMhP0Vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AtiTcD0o7LzpBiQNMhP0Vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d0uyKUSw4GUejkSAuddDbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d0uyKUSw4GUejkSAuddDbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "79gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynw1R90P5jqjjO7FNW192w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LNgX7YYBBkbVtX3nenF5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O7pMotA2VuO21v-RSvDOAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O7pMotA2VuO21v-RSvDOAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-NgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EJns5ogzEJmEGiD1xX0ydA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EJns5ogzEJmEGiD1xX0ydA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4eIX7YYByh-A-BiyfDtb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4J0058RPoWpK37V_WzN4IQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4J0058RPoWpK37V_WzN4IQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} {"create": {"_index": "profiling-events-all", "_id": "kdgX7YYBBkbVtX3ne3fS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZKZ1FR707KOqJJQD503DPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZKZ1FR707KOqJJQD503DPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Y2cX7YYBO2e_P_QbefBx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Lcmvo890HG8Y4rQbXwRxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Lcmvo890HG8Y4rQbXwRxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R7BFs04CJH6GNBGB3gnfow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R7BFs04CJH6GNBGB3gnfow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "8tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W3Xg5UYDFZ6S6yXmGwJPvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W3Xg5UYDFZ6S6yXmGwJPvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "59gX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8CftQWwP45Az974XyC6KVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8CftQWwP45Az974XyC6KVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MeIX7YYByh-A-Biyikgc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9mRMBlsO9IKvKzWL8LqfoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9mRMBlsO9IKvKzWL8LqfoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tGgX7YYBO2e_P_QbigJT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3T5Y-Jcmq5ISgDLlSLnByg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3T5Y-Jcmq5ISgDLlSLnByg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ecEckjK5VzUg46drS_Qbqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ecEckjK5VzUg46drS_Qbqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LeIX7YYByh-A-BiyhkDE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CVtNM3kFgea788jNz-19vQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CVtNM3kFgea788jNz-19vQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "UWcX7YYBO2e_P_QbiP47"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tSCnGC8MSIDk_S0oLclfKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tSCnGC8MSIDk_S0oLclfKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "3mcX7YYBO2e_P_Qbif8Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wO6E0aI_adazJwV1nEPvow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wO6E0aI_adazJwV1nEPvow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "6tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_OQKwd7_zKSX8IYHdhu4OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "702806677431836"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_OQKwd7_zKSX8IYHdhu4OA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "702806677431836"} {"create": {"_index": "profiling-events-all", "_id": "YGcX7YYBO2e_P_Qbh_kU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HVutYQOiJQEOoaSuVpBJXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HVutYQOiJQEOoaSuVpBJXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "UmcX7YYBO2e_P_QbiP47"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ubMKKKyUhNm18UW6QrHRVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ubMKKKyUhNm18UW6QrHRVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MOIX7YYByh-A-Biyikgc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3XmH7C16CjfWPwwqHrVw7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3XmH7C16CjfWPwwqHrVw7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "seIX7YYByh-A-BiyikmM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MRPIiqkdVFL_ATcyw44gkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MRPIiqkdVFL_ATcyw44gkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VdgX7YYBBkbVtX3nin7J"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f0fqU4EglvDX7hh3PMNsxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f0fqU4EglvDX7hh3PMNsxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "QtgX7YYBBkbVtX3ni4AA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MlcjX8Vw9YA9B7H2WMfzTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MlcjX8Vw9YA9B7H2WMfzTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YgLJzuU9kxRA_szpHYnDhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YgLJzuU9kxRA_szpHYnDhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWQpEA25V7CPZz_CbRPwlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWQpEA25V7CPZz_CbRPwlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q9GZUSBL9TB0CdE5vyfEsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q9GZUSBL9TB0CdE5vyfEsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "79gX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0R9Tk_AwuvgNuleyrD0E-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0R9Tk_AwuvgNuleyrD0E-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "49gX7YYBBkbVtX3ni4Pc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1OSkSfHh0FyowliERICfKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1OSkSfHh0FyowliERICfKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7GgX7YYBO2e_P_QbjANs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BJCmF0rYAGSsMmkTDz_IDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BJCmF0rYAGSsMmkTDz_IDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "z9gX7YYBBkbVtX3niXvV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFiegCzfTyYQuNCyI5Zg9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFiegCzfTyYQuNCyI5Zg9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "QNgX7YYBBkbVtX3ni4AA"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "4tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fkFINeCBMiTJSpNxRsOdew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fkFINeCBMiTJSpNxRsOdew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "E-IX7YYByh-A-BiyjE0n"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZSiw25_awwy7RlclMxL6hA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZSiw25_awwy7RlclMxL6hA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ztgX7YYBBkbVtX3niXvV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CAKC2o2NYggEGwOb_Ja6EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "162746825496619"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CAKC2o2NYggEGwOb_Ja6EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "162746825496619"} {"create": {"_index": "profiling-events-all", "_id": "7mgX7YYBO2e_P_Qbpxl-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-4SLKLQoS7j3p5TwUwKzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7-4SLKLQoS7j3p5TwUwKzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8WgX7YYBO2e_P_Qbpxl-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HeIX7YYByh-A-Biymlrf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EXarUgAL9HIosZihvCe9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EXarUgAL9HIosZihvCe9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tNgX7YYBBkbVtX3nqJPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ONg1QQM5k43WL1XUwy52Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ONg1QQM5k43WL1XUwy52Hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "peIX7YYByh-A-BiynF0B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nZ8fRP549U0JQTjsBAy_jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nZ8fRP549U0JQTjsBAy_jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tdgX7YYBBkbVtX3np5HZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eZ0CyfLsOPhGDflPO5HbqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eZ0CyfLsOPhGDflPO5HbqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "571288167487052"} {"create": {"_index": "profiling-events-all", "_id": "reIX7YYByh-A-Biym1t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v5SF2iml9eZuaSs1mX-aSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v5SF2iml9eZuaSs1mX-aSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xGgX7YYBO2e_P_QbnBXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EszYJlG3uJtSxM3Y3d7bAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EszYJlG3uJtSxM3Y3d7bAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "tdgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qBcAPztwjs-VUIEIDjbDtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qBcAPztwjs-VUIEIDjbDtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xWgX7YYBO2e_P_QbnBXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_xmAQQzI2PQtnY3j6S3cOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_xmAQQzI2PQtnY3j6S3cOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nGgX7YYBO2e_P_QbphZT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BJWhTnZbfJWxo1Rlx1U4ZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BJWhTnZbfJWxo1Rlx1U4ZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tNgX7YYBBkbVtX3np5HZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JR8_DdEIpI8T7Ny-5nFoag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JR8_DdEIpI8T7Ny-5nFoag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yuIX7YYByh-A-BiyqF8h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cOIX7YYByh-A-BiyqGFv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yjN3QcXIO7ZJpjPqQPEBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yjN3QcXIO7ZJpjPqQPEBbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tNgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QDgIPJ6K1Rf5OSw95yEFHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QDgIPJ6K1Rf5OSw95yEFHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "XWgX7YYBO2e_P_Qbpxge"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Ea6eul6ZytqY8xapNMsbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Ea6eul6ZytqY8xapNMsbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "m2gX7YYBO2e_P_QbphZT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LZbycaXCeyBUz4EBiDGWbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LZbycaXCeyBUz4EBiDGWbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "yeIX7YYByh-A-BiyqF8h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s-PQaLTLtULIOEGxMYfLJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s-PQaLTLtULIOEGxMYfLJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "e2gX7YYBO2e_P_QbqRsl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyslIhEYrdCY7Y2kR4LC4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyslIhEYrdCY7Y2kR4LC4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "HuIX7YYByh-A-Biymlrf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HcHMtbSJVvIGBxeKT7TjXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HcHMtbSJVvIGBxeKT7TjXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "YOIX7YYByh-A-Biypl4D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nmIx6kUsvWkiJwjLxLculQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nmIx6kUsvWkiJwjLxLculQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "cOIX7YYByh-A-BiymlgR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbZWbU0S5kd22SAXz7exPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbZWbU0S5kd22SAXz7exPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "sNgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fhWOfzRaLRg1Sw58iH7CwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fhWOfzRaLRg1Sw58iH7CwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "beIX7YYByh-A-BiymlgR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "J2gX7YYBO2e_P_QbnBSM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WgfE3EpDBODOIydfExij_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WgfE3EpDBODOIydfExij_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "g9gX7YYBBkbVtX3nt6R3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["94o6mawNwOW6nwyr1PAIHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["94o6mawNwOW6nwyr1PAIHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TNgX7YYBBkbVtX3nuKdo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3z_uYJyhnmGT6PJSN17I-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3z_uYJyhnmGT6PJSN17I-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "G-IX7YYByh-A-Biyum09"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GD_7fNw_aMjvomJCJdhcmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GD_7fNw_aMjvomJCJdhcmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6NgX7YYBBkbVtX3nuao5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6dgX7YYBBkbVtX3nt6W3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mhlnk07FVj7HY3V21x3-gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mhlnk07FVj7HY3V21x3-gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7tgX7YYBBkbVtX3nt6I3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "uNgX7YYBBkbVtX3nuKip"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_VzxKSgG_e2BNdUl-pfPBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6dgX7YYBBkbVtX3nuao5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "q2gX7YYBO2e_P_QbxzPs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BOn5YfVXLbYJVzgO0D8UNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BOn5YfVXLbYJVzgO0D8UNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "89gX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m2ytJBFkfHdhX1fCXv-xiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m2ytJBFkfHdhX1fCXv-xiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BNgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8i6BiToPKTJjFIRVyGCmXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8i6BiToPKTJjFIRVyGCmXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "O2gX7YYBO2e_P_QbyTjm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6gUB2Vi4QgxQPdYRzsxAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6gUB2Vi4QgxQPdYRzsxAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A9gX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zeIX7YYByh-A-BiyyHV8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vthGd_F8mbZA2w12Nf4aww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vthGd_F8mbZA2w12Nf4aww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_9gX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y1JTMrOxhzScpzOLDM9hCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y1JTMrOxhzScpzOLDM9hCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-9gX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PNk5D3ntxPXuIHkBkun6-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PNk5D3ntxPXuIHkBkun6-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ANgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ET7o_hXX2lHnkNRaSpVTBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ET7o_hXX2lHnkNRaSpVTBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CdgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jMkr2A9XqdtOl_G9IEsnSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jMkr2A9XqdtOl_G9IEsnSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DdgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bGgX7YYBO2e_P_Qbxi-y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H6aVRP1XZGxW_TNZpAozgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H6aVRP1XZGxW_TNZpAozgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-all", "_id": "B9gX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QD2PazKHor-pbdbD3PDPLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QD2PazKHor-pbdbD3PDPLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "CNgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZAomE2KxhcS4gmu1C8JgWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZAomE2KxhcS4gmu1C8JgWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "VtgX7YYBBkbVtX3n5c3Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-G7AAW8oqT97cLlLLBEEKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-G7AAW8oqT97cLlLLBEEKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "NOIX7YYByh-A-Biy6Ivf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9lObyrii2tZY3D8d0ZEhiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9lObyrii2tZY3D8d0ZEhiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "M-IX7YYByh-A-Biy6Ivf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeZUkY-05pj9hvZA8tv9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeZUkY-05pj9hvZA8tv9Zg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "leIX7YYByh-A-Biy6o0H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DV6JRqrUjDUvu9f2B4lulg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DV6JRqrUjDUvu9f2B4lulg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_OIX7YYByh-A-Biy5IDD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GQY3N3qZRu8haCsdxq571g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GQY3N3qZRu8haCsdxq571g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y2gX7YYBO2e_P_Qb5lIe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2qSXnyvIGQkSNpP5wPgdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2qSXnyvIGQkSNpP5wPgdA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} {"create": {"_index": "profiling-events-all", "_id": "LdgX7YYBBkbVtX3n6NFW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "79gX7YYBBkbVtX3n6NKY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jrwbv8YUisyxPyCibkR_Xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jrwbv8YUisyxPyCibkR_Xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "CmgX7YYBO2e_P_Qb6Vgq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x1lBt1LJeSjGFaVggB_aRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x1lBt1LJeSjGFaVggB_aRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "GmgX7YYBO2e_P_Qb51U6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QNP7PZqJy6OGXmZc1fiP_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QNP7PZqJy6OGXmZc1fiP_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6-IX7YYByh-A-Biy5YKL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TtbiDhEltHmoi7DhhlF0WQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TtbiDhEltHmoi7DhhlF0WQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "s2gX7YYBO2e_P_Qb51bB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MM2CztTXvV5i9K2i-2RGNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MM2CztTXvV5i9K2i-2RGNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "EtgX7YYBBkbVtX3n589_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8xpc2h22mHPdvmOlcypPfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8xpc2h22mHPdvmOlcypPfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "KtgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UXyZCc79Dkgss9nEK0VutQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UXyZCc79Dkgss9nEK0VutQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2mgX7YYBO2e_P_Qb-GAl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g5T7QK05XvJ6l-R8ldhwpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g5T7QK05XvJ6l-R8ldhwpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "9uIX7YYByh-A-Biy95jl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pBOutl9bHW2BqNx2HBhaPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pBOutl9bHW2BqNx2HBhaPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RuIX7YYByh-A-Biy9ZTm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_7eGxy7JatY66SnXVDAow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_7eGxy7JatY66SnXVDAow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PtgX7YYBBkbVtX3n-eUA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aVn8RcB-QxhkQWDJX_CUMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t9gX7YYBBkbVtX3n9dtq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ocpdZMlRpRzCexEbOVmPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ocpdZMlRpRzCexEbOVmPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ReIX7YYByh-A-Biy9ZTm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e-T0iWff_O-Pyy2T4tpPxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e-T0iWff_O-Pyy2T4tpPxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "cuIX7YYByh-A-Biy95dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vUd7LUOlEzT1w32bH1zYbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vUd7LUOlEzT1w32bH1zYbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Y2gX7YYBO2e_P_Qb91-r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UxGwXndIKBYsvQXlfr8jlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UxGwXndIKBYsvQXlfr8jlA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "QdgX7YYBBkbVtX3n-eUA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "pWgX7YYBO2e_P_Qb-WKP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MdgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2qcLJwksgGzDh4MhNtDMFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2qcLJwksgGzDh4MhNtDMFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "J9gX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s1HOikX7RhbcXlSxKwqqDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s1HOikX7RhbcXlSxKwqqDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LNgX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-j0lF4sF4jza_a1DeuTz0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-j0lF4sF4jza_a1DeuTz0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YmgX7YYBO2e_P_Qb91-r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jlpJ01FQ_CCdjG0s8LBgOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jlpJ01FQ_CCdjG0s8LBgOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9-IX7YYByh-A-Biy95jl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0mXxveDvgmyZUQk8txqhkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0mXxveDvgmyZUQk8txqhkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "59gX7YYBBkbVtX3n-eZU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AP5Eq7B7RisKC973OjTPaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AP5Eq7B7RisKC973OjTPaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "a2gX7YYBO2e_P_Qb9Fyf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ffFMdQwpHORekV2ieknMEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ffFMdQwpHORekV2ieknMEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "tdgX7YYBBkbVtX3n9dtq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r2afFjMCaKrL1sh73z1hIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r2afFjMCaKrL1sh73z1hIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ttgX7YYBBkbVtX3n9dtq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pp5lsGmp-JSx0DYM6KPKrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pp5lsGmp-JSx0DYM6KPKrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "5tgX7YYBBkbVtX3n-eZU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TohoLCipAaXWyBdT-2T3rQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TohoLCipAaXWyBdT-2T3rQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "hGgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V9tSD5BQ3ATDkE3Dz2odow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V9tSD5BQ3ATDkE3Dz2odow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w-IY7YYByh-A-BiyE6dj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nNScNuSTrpa5-8cxBl8OiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nNScNuSTrpa5-8cxBl8OiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eGgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WSjAZWkrBfhyqCpr7c2wpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WSjAZWkrBfhyqCpr7c2wpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4eIY7YYByh-A-BiyFKxe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuPwjyefoJQ1lu-T5igwEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuPwjyefoJQ1lu-T5igwEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "e2gY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Orgm72EfMt-zO2dZAN4-xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Orgm72EfMt-zO2dZAN4-xQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "O9gY7YYBBkbVtX3nGPqd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bDEpUjfMy73JDN93aU2Y1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bDEpUjfMy73JDN93aU2Y1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3mgY7YYBO2e_P_QbFnT2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_9qdqX3M61Erctug7dsAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_9qdqX3M61Erctug7dsAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "fWgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "juIY7YYByh-A-BiyJLod"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O3Tz5fQYZgLM16jx54ZxgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O3Tz5fQYZgLM16jx54ZxgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2eIY7YYByh-A-BiyJ8jR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PeUu-CO8oH7bDpiXW5er7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PeUu-CO8oH7bDpiXW5er7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "weIY7YYByh-A-BiyJL2w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAORKE733KRegC2qyifuEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAORKE733KRegC2qyifuEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuIY7YYByh-A-BiyJcFE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "1dgY7YYBBkbVtX3nI__a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jo8tWR_pkEX_X8H988Y6bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jo8tWR_pkEX_X8H988Y6bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "wuIY7YYByh-A-BiyJL2w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kNJuZj4X_lXclYreYCLjug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kNJuZj4X_lXclYreYCLjug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "a2gY7YYBO2e_P_QbJn6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "t9gY7YYBBkbVtX3nI_yO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "KdkY7YYBBkbVtX3nJwQY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YtXvff3EKZwAqNYCPh_G8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YtXvff3EKZwAqNYCPh_G8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "KtkY7YYBBkbVtX3nJwQY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ai5oW8uVA4tEGajwsXvwJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ai5oW8uVA4tEGajwsXvwJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "OOIY7YYByh-A-BiyKMx-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tni8OBdz6TpUMf5YmlBpkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tni8OBdz6TpUMf5YmlBpkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "1NkY7YYBBkbVtX3nOAq2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GwKVzdGAgri-K--qj0BBVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GwKVzdGAgri-K--qj0BBVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "omgY7YYBO2e_P_QbNIap"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G8rfpCwclnmPxDW_rTu3XA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G8rfpCwclnmPxDW_rTu3XA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pGgY7YYBO2e_P_QbNIap"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zb66k8tNN3fufwrfwPNRpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zb66k8tNN3fufwrfwPNRpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XGgY7YYBO2e_P_QbN44i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T3wubxICfJSY8FNVzGkEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T3wubxICfJSY8FNVzGkEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "W9kY7YYBBkbVtX3nQwsG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnaxKNXR9iQyPdmTTWbz9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnaxKNXR9iQyPdmTTWbz9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "f2gY7YYBO2e_P_QbN5Gs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "R2gY7YYBO2e_P_QbOJVz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3wY3n6ZuFWe08ye_NO9bMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3wY3n6ZuFWe08ye_NO9bMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "KWgY7YYBO2e_P_QbNYgr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fQiJSfBrHoS3h5i-nuWFRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fQiJSfBrHoS3h5i-nuWFRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-GgY7YYBO2e_P_QbN5Lt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bo6NdGV8GXHmalbT9Hz3Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bo6NdGV8GXHmalbT9Hz3Eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XWgY7YYBO2e_P_QbN44i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EkobH195Wur44OeENgdTnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EkobH195Wur44OeENgdTnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "fmgY7YYBO2e_P_QbN5Gs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t_MhSbU2giVaKBznPcc-Nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t_MhSbU2giVaKBznPcc-Nw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "ddkY7YYBBkbVtX3nNgnQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x8C8r_joS9eFrngYSfAD9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x8C8r_joS9eFrngYSfAD9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "u9kY7YYBBkbVtX3nVh_a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8mfKeyebNB7GEjVrotPPKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8mfKeyebNB7GEjVrotPPKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vuIY7YYByh-A-BiyU-6O"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IcP7NfEq0GawQQCHmZWcRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IcP7NfEq0GawQQCHmZWcRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gWgY7YYBO2e_P_QbVqw4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["21VADknXj310Vq9ESNjcWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["21VADknXj310Vq9ESNjcWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3OIY7YYByh-A-BiyUejm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1-IY7YYByh-A-BiyUuo6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3N5lLpTOYxG1gCT2yPAYAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3N5lLpTOYxG1gCT2yPAYAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HNkY7YYBBkbVtX3nVyNb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uuIY7YYByh-A-BiyV_WZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PtkY7YYBBkbVtX3nUhWB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KTolfZraJkKDWNdjvBwe_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KTolfZraJkKDWNdjvBwe_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "ueIY7YYByh-A-BiyV_WZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bredr3OvHQiC2uo7mFYNAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bredr3OvHQiC2uo7mFYNAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "fuIY7YYByh-A-BiyVPIV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6TvPSjgriSXKOKa1IM7e3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6TvPSjgriSXKOKa1IM7e3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0GgY7YYBO2e_P_QbVKit"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iDXH9KGzXeYmvEE7qpFOKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "641828620625480"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iDXH9KGzXeYmvEE7qpFOKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "641828620625480"} {"create": {"_index": "profiling-events-all", "_id": "oWgY7YYBO2e_P_QbY7hd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QY705UJL2QEaImwzck5kDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QY705UJL2QEaImwzck5kDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2WgY7YYBO2e_P_QbcsdE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vv8H2tPfs7d9zjnnJhB0rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vv8H2tPfs7d9zjnnJhB0rA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F9kY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Tt9tabVrOQsSQg8A1kRaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Tt9tabVrOQsSQg8A1kRaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L2gY7YYBO2e_P_QbY7vw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fYmJVY6gIzPkbudFgeXtGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fYmJVY6gIzPkbudFgeXtGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E-IY7YYByh-A-BiyZf0D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qj4-KfGxeYDC4jIbY_qMxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qj4-KfGxeYDC4jIbY_qMxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2WgY7YYBO2e_P_QbZsEV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PBMBNfpl26clo2_eg37Few"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PBMBNfpl26clo2_eg37Few"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LNkY7YYBBkbVtX3ncS0m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EtkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IbZEnp9wozgTuaJYdK6lSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IbZEnp9wozgTuaJYdK6lSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "FdkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JrCc1ySWaMhrJB1f8olPDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JrCc1ySWaMhrJB1f8olPDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ytkY7YYBBkbVtX3nZSze"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fNOV0V-zSZCXeYqmr986ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "3GgY7YYBO2e_P_QbcsdE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WTK3WD91wf76sAd6PBxIoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WTK3WD91wf76sAd6PBxIoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6OIY7YYByh-A-BiyZPvI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u4YpFJ3r3BxuJCj_h-nNzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u4YpFJ3r3BxuJCj_h-nNzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6eIY7YYByh-A-BiyZPvI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Spa1oEv_GaWrLXNIiVxEEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Spa1oEv_GaWrLXNIiVxEEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "02gY7YYBO2e_P_QbZb1H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lB1Y7h-GI8V7NnJH4cEyWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lB1Y7h-GI8V7NnJH4cEyWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "1mgY7YYBO2e_P_QbcsmS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vHlfPkBgdyUjCnS7-7m_jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vHlfPkBgdyUjCnS7-7m_jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "D9kY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyaOwrYy-y6_MqiyYC7KDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyaOwrYy-y6_MqiyYC7KDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "EeIY7YYByh-A-BiyZf0D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XOEcWEB-1gR5VA6Y_JnK8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XOEcWEB-1gR5VA6Y_JnK8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "3WgY7YYBO2e_P_QbcsdE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_dTR833ucc6p1Tdsdvx5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_dTR833ucc6p1Tdsdvx5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "MGgY7YYBO2e_P_QbY7vw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "BGgY7YYBO2e_P_QbgtPz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "S-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "WeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bNNll9gtsumikBQkeP5zYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bNNll9gtsumikBQkeP5zYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0dkY7YYBBkbVtX3ngkWp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AyDqByyWIoIiVixwsKuo2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AyDqByyWIoIiVixwsKuo2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "veMY7YYByh-A-Biygwur"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K_CAUyuD7NBkhK5Ltc_feQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K_CAUyuD7NBkhK5Ltc_feQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "W-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yU87pg-Ch2E9K6GDZMg_og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "0OMY7YYByh-A-BiyhA5n"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9T2neRyvBAjupi4KvqMEkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9T2neRyvBAjupi4KvqMEkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "HdkY7YYBBkbVtX3ngkRm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dDG6jf6GCHoU_y56rXJY_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dDG6jf6GCHoU_y56rXJY_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "Y-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uoJuSy3Naz5PMjayntwpXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uoJuSy3Naz5PMjayntwpXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "eOMY7YYByh-A-BiylBpj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YlwuTI9pMmyVbwg2FaXibA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YlwuTI9pMmyVbwg2FaXibA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3NkY7YYBBkbVtX3nkU_F"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L3YM-JzWQGZBl6Hww0qX5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L3YM-JzWQGZBl6Hww0qX5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "eeMY7YYByh-A-BiylBpj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yXWhC6PUKwhiRKybc8XJKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yXWhC6PUKwhiRKybc8XJKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "CmgY7YYBO2e_P_QblO3v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HcQSqbXhDJcv-dVT41RQ6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HcQSqbXhDJcv-dVT41RQ6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xGgY7YYBO2e_P_QbkN-c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_eMY7YYByh-A-BiykRT_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "G9kY7YYBBkbVtX3nlFSh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7bsINl9DvTXKKGeGoWTVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7bsINl9DvTXKKGeGoWTVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HNkY7YYBBkbVtX3nlFSh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bTwDM-MjmPXB3OC1RQFixw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bTwDM-MjmPXB3OC1RQFixw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lGgY7YYBO2e_P_QbkeIU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2uW4N0T57kNGJTVG5_1zTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2uW4N0T57kNGJTVG5_1zTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ymgY7YYBO2e_P_QbkeNU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HsiX5vdQunBNkC7dzQqFjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HsiX5vdQunBNkC7dzQqFjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "A9kY7YYBBkbVtX3nklHO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SwjluL3-fAPsYBuygjQN9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SwjluL3-fAPsYBuygjQN9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "veMY7YYByh-A-Biykxdq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ju1Y8z3O5D2e-y3UCauHLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ju1Y8z3O5D2e-y3UCauHLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "FOMY7YYByh-A-Biykxnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xekeer0c5o0XNQ05adBIWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xekeer0c5o0XNQ05adBIWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "CmgY7YYBO2e_P_QbkOHY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jqtNscAcTM6hZXRdTqaMBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jqtNscAcTM6hZXRdTqaMBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "imgY7YYBO2e_P_Qbk-kz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJHpr4fLpWoSKqg-aUPBfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJHpr4fLpWoSKqg-aUPBfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "82gY7YYBO2e_P_Qble4x"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fL44zZ44BPFnGW549J-v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fL44zZ44BPFnGW549J-v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "BNkY7YYBBkbVtX3nklHO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A-XVDxnDwU8uNV8SIPsqYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A-XVDxnDwU8uNV8SIPsqYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "l-MY7YYByh-A-BiysCjE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hfDMNuzdjFAGAQxLAUumIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hfDMNuzdjFAGAQxLAUumIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nOMY7YYByh-A-Biysi1P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Oxe_oPi543aZz0lTOU2m-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Oxe_oPi543aZz0lTOU2m-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xOMY7YYByh-A-BiysSvY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SSybZ3-qjl-24IZIjWrutg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SSybZ3-qjl-24IZIjWrutg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xeMY7YYByh-A-BiysSvY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HylmGygzkKByc907Hb1zHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t9kY7YYBBkbVtX3nsnET"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C7vQBGljPqDLZ1glBrcWiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C7vQBGljPqDLZ1glBrcWiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wtkY7YYBBkbVtX3ns3bd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29_6ZAb70CIp_EG0wp7_NQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29_6ZAb70CIp_EG0wp7_NQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4WgY7YYBO2e_P_QbpfyH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ow1D1C0phScv22K7VxLC2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ow1D1C0phScv22K7VxLC2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4GgY7YYBO2e_P_QbpfyH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4u9WOOyrWYLdgsjOh9aCUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4u9WOOyrWYLdgsjOh9aCUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "T9kY7YYBBkbVtX3npWjG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["djLrau8jrfBziaCPclZ3HQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["djLrau8jrfBziaCPclZ3HQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "w9kY7YYBBkbVtX3ns3bd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WimOxYeSVVOX0wF3HCZkxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WimOxYeSVVOX0wF3HCZkxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "t9kY7YYBBkbVtX3npWUb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["It0gQVHszUoDuF9_NGkXWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["It0gQVHszUoDuF9_NGkXWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "aWgY7YYBO2e_P_Qbr_2i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3hucOiuiT_BuF65onWDmkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3hucOiuiT_BuF65onWDmkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "BNkY7YYBBkbVtX3nsGt1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ccuRE7rvGbylFOE2-Vdm-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ccuRE7rvGbylFOE2-Vdm-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "f2kY7YYBO2e_P_QbsgDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nK-VQLHeSCyigrjH5wLGZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nK-VQLHeSCyigrjH5wLGZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "iNkY7YYBBkbVtX3nsnOa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fR8gXe-G1OoCrBfWymUTjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fR8gXe-G1OoCrBfWymUTjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "hGkY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A88fFaIU_zwiD_q-n14PHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A88fFaIU_zwiD_q-n14PHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "tdkY7YYBBkbVtX3npWUb"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IsqdVnLNhl2x75Zl1gQDqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IsqdVnLNhl2x75Zl1gQDqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "amgY7YYBO2e_P_Qbr_2i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E6KBzvcDthx6uaZz_Stfkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E6KBzvcDthx6uaZz_Stfkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "HNkY7YYBBkbVtX3nsW5T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RK5QOedYDJN8YhVo9FJwjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RK5QOedYDJN8YhVo9FJwjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JtkY7YYBBkbVtX3ns3Wc"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "oOMY7YYByh-A-BiyvzV7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4BeOljMY7zmWSgDKbEbSZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4BeOljMY7zmWSgDKbEbSZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xGkY7YYBO2e_P_QbxBjA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GG472XEEVdeqeiwioTPVvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GG472XEEVdeqeiwioTPVvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OdkY7YYBBkbVtX3nwoGT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6Xxk_y7umqFVq79tKmttkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6Xxk_y7umqFVq79tKmttkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bNkY7YYBBkbVtX3nwH2y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKJhqpG2dRoKsO5mlCpMbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKJhqpG2dRoKsO5mlCpMbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "hmkY7YYBO2e_P_QbxBZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-_1XfHbZNvE_DNOZ6TgYMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-_1XfHbZNvE_DNOZ6TgYMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "H-MY7YYByh-A-BiyxDwb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EMABXmd9W1xztmohmhT4jw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EMABXmd9W1xztmohmhT4jw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "bdkY7YYBBkbVtX3nwH2y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9o9XxmdKp5kr_ASSIejj0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9o9XxmdKp5kr_ASSIejj0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "H2kY7YYBO2e_P_QbwQ-_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6s327-MJ8tXADNCpUFJLTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6s327-MJ8tXADNCpUFJLTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "72kY7YYBO2e_P_QbwhLW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w2cA5pFSzeyVJ9Di06ODVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w2cA5pFSzeyVJ9Di06ODVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "-9kY7YYBBkbVtX3nw4SF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6OMY7YYByh-A-Biy1EpB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RtkY7YYBBkbVtX3n3pnE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Iji_zQRXoBblaoaKwHTcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Iji_zQRXoBblaoaKwHTcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A2kY7YYBO2e_P_Qb4CpI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oNARuCgevgyxtAjFL2xZeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "U2kY7YYBO2e_P_Qb1CiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "fNkY7YYBBkbVtX3n1JXT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HAu5rAU21OgIaT7rvuQn1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HAu5rAU21OgIaT7rvuQn1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6eMY7YYByh-A-Biy1EpB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "V9kY7YYBBkbVtX3n4ZwS"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "b2kY7YYBO2e_P_Qb7jUi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FOMY7YYByh-A-Biy713T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xefg2tu-dTR7fu4bq6TlVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xefg2tu-dTR7fu4bq6TlVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UdkY7YYBBkbVtX3n8Kve"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SKoD-DH2DuktCqfanvYyAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SKoD-DH2DuktCqfanvYyAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BWkY7YYBO2e_P_Qb7zcM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U9D_YJUEsrwBEswWxHC35w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U9D_YJUEsrwBEswWxHC35w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0NkY7YYBBkbVtX3n8KgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ztkY7YYBBkbVtX3n8KgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SzEyh5UKxIErIGMjrb6UXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SzEyh5UKxIErIGMjrb6UXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "L-MY7YYByh-A-Biy8V8f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mXTwlU3Af7Gcl1McbGUk9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mXTwlU3Af7Gcl1McbGUk9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Z-MY7YYByh-A-Biy8WB6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x5-D1ZwWartfLwFKR52-CA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x5-D1ZwWartfLwFKR52-CA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "aOMY7YYByh-A-Biy8WB6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t-flxrF7p2MCZlWnBJdilQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t-flxrF7p2MCZlWnBJdilQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "fdkY7YYBBkbVtX3n8ay8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lo6X16n0sxmFnHCmqvzqeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lo6X16n0sxmFnHCmqvzqeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "oGkY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "q-MY7YYByh-A-Biy8mGJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SVxJAjIDjZrrbvA9qjRyTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SVxJAjIDjZrrbvA9qjRyTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "vWkY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MLRa2nw0cHalfHkTovbapg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MLRa2nw0cHalfHkTovbapg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ltkY7YYBBkbVtX3n861d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mi0B0tB9mjIesaGe45FXYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mi0B0tB9mjIesaGe45FXYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "quMY7YYByh-A-Biy8mGJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YQv8Jjxrz6pIHbJnxDZTDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YQv8Jjxrz6pIHbJnxDZTDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "bWkY7YYBO2e_P_Qb7jUi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E1Ij_aNOjEdQHLl7MQgu9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "8GkZ7YYBO2e_P_QbAkYL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y9yOjEX9YsHot-nonRkNzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y9yOjEX9YsHot-nonRkNzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NuMY7YYByh-A-Biy_mUb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xe1hJxuA7dGkCnSYKpXyog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xe1hJxuA7dGkCnSYKpXyog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aOMZ7YYByh-A-BiyAGoT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XGi0gq3X0lbtkz60bv_FjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XGi0gq3X0lbtkz60bv_FjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "uOMY7YYByh-A-Biy_mZe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l6meUfZ5uC9_p8a-MC7XWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "190932526140742"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l6meUfZ5uC9_p8a-MC7XWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "190932526140742"} {"create": {"_index": "profiling-events-all", "_id": "sWkZ7YYBO2e_P_QbH1uL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7kg8w388m41akTi9Kihyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7kg8w388m41akTi9Kihyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZOMZ7YYByh-A-BiyIIrY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dqkoz37L8v8ZGKH2dg08IA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dqkoz37L8v8ZGKH2dg08IA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PuMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yCJBwrMAMfpAui_lf-8LYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yCJBwrMAMfpAui_lf-8LYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P-MZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H6bVVoHSSJLF6qLyyJlYew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H6bVVoHSSJLF6qLyyJlYew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sGkZ7YYBO2e_P_QbH1uL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5kK1-2HMVYa08NL2RAHTaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5kK1-2HMVYa08NL2RAHTaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0uMZ7YYByh-A-BiyIIiV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kjbzs3pi_uYSkjO5yzNzAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kjbzs3pi_uYSkjO5yzNzAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SGkZ7YYBO2e_P_QbHVji"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jmaxos_Fbss7GX1ulltAuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jmaxos_Fbss7GX1ulltAuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "xeMZ7YYByh-A-BiyH4VG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2W434choOPZkVODgIFHPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G2W434choOPZkVODgIFHPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "AWkZ7YYBO2e_P_QbHVUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iMWox14mMY2b1SuNGxsCtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iMWox14mMY2b1SuNGxsCtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "SOMZ7YYByh-A-BiyIIcW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TCI-U8WcxrkkRuvWag0ygQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pGkZ7YYBO2e_P_QbIV77"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SdkZ7YYBBkbVtX3nHs4s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6RQSqI_DYo_fU-yUjLfiPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6RQSqI_DYo_fU-yUjLfiPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "QOMZ7YYByh-A-BiyHoJs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UjSnJkMJp-ScD3GTE7GQCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UjSnJkMJp-ScD3GTE7GQCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "cGkZ7YYBO2e_P_QbImDZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pf3D6JGFbbrRUgOJ18HuJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pf3D6JGFbbrRUgOJ18HuJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "SWkZ7YYBO2e_P_QbHVji"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xuMZ7YYByh-A-BiyH4VG"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "f2kZ7YYBO2e_P_QbLGOi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XWIVrcPaz7dnfTJVzRuiVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XWIVrcPaz7dnfTJVzRuiVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OGkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GkfSwJU4VtNnyHQkcXbLQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GkfSwJU4VtNnyHQkcXbLQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B9kZ7YYBBkbVtX3nMNxw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uog7BBtBaBoHv7gkfaKdng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uog7BBtBaBoHv7gkfaKdng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TdkZ7YYBBkbVtX3nLtnW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["U-SQw_Ej849fFrBkcLqfHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "i2kZ7YYBO2e_P_QbL2kZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cRfPb25MGbKffPO7zvRoZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cRfPb25MGbKffPO7zvRoZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "g9kZ7YYBBkbVtX3nL9qN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dECtgLMdnRfSCldLDKs55Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dECtgLMdnRfSCldLDKs55Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "O9kZ7YYBBkbVtX3nMd2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["76S40x9sTPqO8zAoNyL8dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["76S40x9sTPqO8zAoNyL8dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "-GkZ7YYBO2e_P_QbLGTb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xGUU7YYBO2e_P_QbvJmg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ScSKYoSOs8nhVq0muD3gAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ScSKYoSOs8nhVq0muD3gAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ntYU7YYBBkbVtX3nvwQZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TtJhJ3zPw--x6TQzyvUSEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TtJhJ3zPw--x6TQzyvUSEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4WUU7YYBO2e_P_QbwKIN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e9NUfgKKbs-MiLMXsDbEjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e9NUfgKKbs-MiLMXsDbEjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Dd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OMaWFUMaKo-t8HBZbSb7rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OMaWFUMaKo-t8HBZbSb7rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Et8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iBwxinLolVoovC6Eh145Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iBwxinLolVoovC6Eh145Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "n9YU7YYBBkbVtX3nvwQZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Va3LK8uFodhrLyRtybcuhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Va3LK8uFodhrLyRtybcuhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Jd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sN_SI69IRP_CTM4xM4ZnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sN_SI69IRP_CTM4xM4ZnyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L9YU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n0M5HPUARuaG-cOZx59FHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n0M5HPUARuaG-cOZx59FHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FmUU7YYBO2e_P_QbvZyb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u26YAXespQsbQjR7YsAYzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u26YAXespQsbQjR7YsAYzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "298U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CryaWZekzG3MnYg7CufHdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CryaWZekzG3MnYg7CufHdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Kd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "K98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9pmIhrtRRi4GPUMxg8HjSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9pmIhrtRRi4GPUMxg8HjSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "N98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sEaRb-2yXJZGrLeypPMviA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sEaRb-2yXJZGrLeypPMviA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "lGUU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLT0UoH40niOjk-XVme7dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dLT0UoH40niOjk-XVme7dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "fN8U7YYByh-A-Biyvcco"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJNVQg8pccfKm6nYhuiPHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJNVQg8pccfKm6nYhuiPHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "At8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LbIdVoxeM1glCVhs3QD2Rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LbIdVoxeM1glCVhs3QD2Rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZMsyLjDeZJ1X8UROk68yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZMsyLjDeZJ1X8UROk68yw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjWTEl1JczrCDNWdjCq0IQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjWTEl1JczrCDNWdjCq0IQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MdYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["19eOj7rW2BMrJQCjgPGRMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["19eOj7rW2BMrJQCjgPGRMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NdYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F4DUvBkuKPYx1hCGNzwitQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F4DUvBkuKPYx1hCGNzwitQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cyR0VPenXOOdQcTb8oa6LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cyR0VPenXOOdQcTb8oa6LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "C98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdoI0rjd901gurnJCTWJVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdoI0rjd901gurnJCTWJVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "k2UU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1tUySHOH0tYebEIwqtbzkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1tUySHOH0tYebEIwqtbzkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "M9YU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wWjEk4V-ocnXQQZfOB5PAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t76-b4gm5U3oB29oJeJYQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t76-b4gm5U3oB29oJeJYQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "xWUU7YYBO2e_P_QbvJmg"}} -{"Stacktrace.count": [3], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vUd7LUOlEzT1w32bH1zYbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [3], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vUd7LUOlEzT1w32bH1zYbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "698U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pg9BpRl7fhNFrbhldfBoVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pg9BpRl7fhNFrbhldfBoVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "998U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3sCEoFWbinM5GJ3BUzAdHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3sCEoFWbinM5GJ3BUzAdHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Ad8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hJqYLUumz9zXvS_kxlOwXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hJqYLUumz9zXvS_kxlOwXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "FN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FXSDb1LRkJ6lyn8itNo02Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FXSDb1LRkJ6lyn8itNo02Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Fd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gni4FNEytoNXn1VB81DlJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gni4FNEytoNXn1VB81DlJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "F98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uX6Z-_eBVfWUQHo7dxxgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uX6Z-_eBVfWUQHo7dxxgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BnraydbvEwL6mkTBVZOVLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BnraydbvEwL6mkTBVZOVLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Mt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QdOz4wxFUC8pDwHEAxZcWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QdOz4wxFUC8pDwHEAxZcWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MtYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SRve3dTLPRl1qAhVYZQKgw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "W9YU7YYBBkbVtX3nyQUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dVb-MiyMMGjQnN4CNy5W_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dVb-MiyMMGjQnN4CNy5W_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "RNYU7YYBBkbVtX3nuwHT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DLW1J3k1lahctYuhwA129g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "398U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gl4D81F4_LquzSzjpshT5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gl4D81F4_LquzSzjpshT5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "Cd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K83sxCaQdi1aUMB4CY2c2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K83sxCaQdi1aUMB4CY2c2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "H98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyhq7VnU7oy4urORTsa4ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyhq7VnU7oy4urORTsa4ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "Lt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YVVpCpnnAN7nqi22_B110A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YVVpCpnnAN7nqi22_B110A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "XNYU7YYBBkbVtX3nyQUE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nOTOWE8V9a_XoRYtELkH0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nOTOWE8V9a_XoRYtELkH0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "C2UU7YYBO2e_P_Qbvp4b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-r8cbHNOP2N9259mbYQNyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-r8cbHNOP2N9259mbYQNyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "Ed8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28flplgbX9OoTxrrq9LhNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28flplgbX9OoTxrrq9LhNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "Z2UU7YYBO2e_P_Qb27nE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ccyeq1IpEdYyyzMGVkI9Ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ccyeq1IpEdYyyzMGVkI9Ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Z98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YRYK-waaBK93YQxC-u6FpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YRYK-waaBK93YQxC-u6FpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Dncz0Y_So0i0vXWTX7iycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ft8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Y6gZg8K9awv8z0hisYsKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Y6gZg8K9awv8z0hisYsKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7jgrYi9xWKJVjQJiteksdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7jgrYi9xWKJVjQJiteksdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9oafXxRRgws5bQkcIU2Q0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9oafXxRRgws5bQkcIU2Q0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "z98U7YYByh-A-Biy1-BK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wh31IgccB0bo8SH5BOevTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wh31IgccB0bo8SH5BOevTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XNYU7YYBBkbVtX3n2hx_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "zN8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k1TJV5J17869y08LRTIrbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k1TJV5J17869y08LRTIrbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "amUU7YYBO2e_P_Qb27nE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["33M_jV1gmHGxTPvzVsOhJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["33M_jV1gmHGxTPvzVsOhJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "6mUU7YYBO2e_P_Qb3L3V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gRdGxn1bfMhp02lCqIS5Kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gRdGxn1bfMhp02lCqIS5Kw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "W98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A41aW57rPqkbdBRy4L9pwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A41aW57rPqkbdBRy4L9pwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Xd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mk9zCNEY-hYZnvZiO93Kbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mk9zCNEY-hYZnvZiO93Kbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ZN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ypQufrPd-vWE7YGaekcTfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ypQufrPd-vWE7YGaekcTfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "fd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sd-ZaAvLHLrrMbq7MjTuQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sd-ZaAvLHLrrMbq7MjTuQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "pd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "8NYU7YYBBkbVtX3n3h9C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xPHwmFt4fvCxveu9JS8ZxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xPHwmFt4fvCxveu9JS8ZxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "8dYU7YYBBkbVtX3n3h9C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["C8RiWN9GOAWu10jfv-Iilw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "hWUU7YYBO2e_P_Qb38Ek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v78L_ndncKY9XP2euXU8Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v78L_ndncKY9XP2euXU8Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "hmUU7YYBO2e_P_Qb38Ek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbcOgGVzXu_Kl1MHENboNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbcOgGVzXu_Kl1MHENboNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "K98U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4KYdSxEIpyB6jl1rRIzYcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4KYdSxEIpyB6jl1rRIzYcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "u2UU7YYBO2e_P_Qb4MZd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9zCLbc3pKhchwVlW_zTLBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9zCLbc3pKhchwVlW_zTLBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "eN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFZWod3ShZzIho6L40kyaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eFZWod3ShZzIho6L40kyaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "UN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jdzvLx5l7JUNfi9LmdMqdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jdzvLx5l7JUNfi9LmdMqdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "xdYU7YYBBkbVtX3n1xTG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0_0t2poX7i0kjQvasvtSEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0_0t2poX7i0kjQvasvtSEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vtYU7YYBBkbVtX3n2BY6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tk-Rn8r6-wqzqI-bfiAJ7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tk-Rn8r6-wqzqI-bfiAJ7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "W9YU7YYBBkbVtX3n2hx_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LcLD9Ru4GLxHGOxhmBbPug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LcLD9Ru4GLxHGOxhmBbPug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7mUU7YYBO2e_P_Qb3L3V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IEh2TcuBJ50L6QBQgKo1LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IEh2TcuBJ50L6QBQgKo1LA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "id8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m77yCqhJC-TV7tjIyUjLSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m77yCqhJC-TV7tjIyUjLSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nixBByAIlNzP6S-DgkxohA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nixBByAIlNzP6S-DgkxohA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SdkBKb56HsioGRxWHq-7TA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SdkBKb56HsioGRxWHq-7TA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w2UU7YYBO2e_P_Qb38OV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FbKeTovw8ZZ-HdSWJ6n-2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FbKeTovw8ZZ-HdSWJ6n-2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vdYU7YYBBkbVtX3n2BY6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NyRf8XlP9WjHePtKdUVEyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NyRf8XlP9WjHePtKdUVEyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "INYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FfJoEeFsWI41bsMAEfnR5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FfJoEeFsWI41bsMAEfnR5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wN8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AzjRpwmenH1G04gO5z-8Hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AzjRpwmenH1G04gO5z-8Hw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xt8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0WZjWPo0ZsPZ-DIkV3gJrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0WZjWPo0ZsPZ-DIkV3gJrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dWUU7YYBO2e_P_Qb3LtA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bgzswmC99T0GXpCWQr9U_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bgzswmC99T0GXpCWQr9U_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "T98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["it1M7ufrxHsYyi2peFanww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "it8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0aQ-GR8dzIcLY-JHg_Ltg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0aQ-GR8dzIcLY-JHg_Ltg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "n98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acgoKBxSl7O4j7VOkIDurg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["acgoKBxSl7O4j7VOkIDurg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "kN8U7YYByh-A-Biy4ewW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RV64TmaQgxBDavQQ3RlpBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RV64TmaQgxBDavQQ3RlpBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "vNYU7YYBBkbVtX3n2BY6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tl8-uLg-sk_bVEVT-WQP4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tl8-uLg-sk_bVEVT-WQP4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "aGUU7YYBO2e_P_Qb27nE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ImaxZCdsAx2D2FVy0fxyGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ImaxZCdsAx2D2FVy0fxyGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "eWUU7YYBO2e_P_Qb3LtA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1XV7unux6YV2hG1GouNtCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1XV7unux6YV2hG1GouNtCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Ut8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EO1X9bMgoknGN8tYEcbt6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EO1X9bMgoknGN8tYEcbt6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1d8U7YYByh-A-Biy2eKG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g3jLco5iklv9rjHlmxCtzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g3jLco5iklv9rjHlmxCtzQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "u9YU7YYBBkbVtX3n2BY6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AREehA9nDZJasQeEH6svQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AREehA9nDZJasQeEH6svQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "JNYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v1g3luunQaTy0sgJ7RCzFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v1g3luunQaTy0sgJ7RCzFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WtYU7YYBBkbVtX3n4CG5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OlH3AeYcsXnih5MNDHAYTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OlH3AeYcsXnih5MNDHAYTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "d2UU7YYBO2e_P_Qb3LtA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "7WUU7YYBO2e_P_Qb3L3V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xAB5rWSdYiNfzgIdJBEyIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xAB5rWSdYiNfzgIdJBEyIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "at8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kvLeDZxzbcRlBxqtjei7Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kvLeDZxzbcRlBxqtjei7Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ld8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SFu0TnwDMO-fagKeZiwsbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SFu0TnwDMO-fagKeZiwsbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "zt8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O3Wm5BKcHbAbJgVLxR44SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O3Wm5BKcHbAbJgVLxR44SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "qN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XifASgxXQbagp8rNDbQOHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XifASgxXQbagp8rNDbQOHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "907195111575642"} {"create": {"_index": "profiling-events-all", "_id": "jd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "1N8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "umUU7YYBO2e_P_Qb4MZd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruch9eRlQqOnJ3ZVNLKC2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "kd8U7YYByh-A-Biy4ewW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-D2Xan0xr7Iyy5r8CY20RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-D2Xan0xr7Iyy5r8CY20RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "8NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "utYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zYVMGADUhvH0MNK-_5jLUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zYVMGADUhvH0MNK-_5jLUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "k9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ne60Rh_KLhugEPI_VMwIQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ne60Rh_KLhugEPI_VMwIQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kFwQt40kRkAJhq_qjy2boQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kFwQt40kRkAJhq_qjy2boQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "htYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UnZlpcKdTEmf9Jf7wgc4Wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UnZlpcKdTEmf9Jf7wgc4Wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "sdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wYQMafKDTOM5M3m09YsCqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wYQMafKDTOM5M3m09YsCqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "u9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FMEbu46FVTF9FY-0Ogn2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FMEbu46FVTF9FY-0Ogn2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "x9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rjNVMj90Vubz91AMpodGGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rjNVMj90Vubz91AMpodGGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "z9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T_m8wOPYHgqUseziTFic-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T_m8wOPYHgqUseziTFic-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "3tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xkw543uTXeeuNcRX3BWzOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xkw543uTXeeuNcRX3BWzOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "4NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [3], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [3], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "49YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnpinE4u8LaMWLZMBdXuZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VnpinE4u8LaMWLZMBdXuZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "5dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JYl32o-03G4ABrH8cW9MlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "9NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wOaaLLn26MWCq1Ch7gi66A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wOaaLLn26MWCq1Ch7gi66A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "1dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jliDtdpQ5AYvFVIEkH2R2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jliDtdpQ5AYvFVIEkH2R2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "qdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OHN-NCR9tXyaSbIcRGyJXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OHN-NCR9tXyaSbIcRGyJXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "ctYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbnTibb7iUG5Z59b5ewlIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UbnTibb7iUG5Z59b5ewlIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lBjHl88ojwoksS7PDXJBaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lBjHl88ojwoksS7PDXJBaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xe7_zbD7BhFg8NiRYVvMrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xe7_zbD7BhFg8NiRYVvMrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TBeuzjOZRDdI9h9tZfZZsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TBeuzjOZRDdI9h9tZfZZsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sLM1wIlvdF1g5AqGWS2w3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sLM1wIlvdF1g5AqGWS2w3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BdYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cyFXzJO3EyfOUXZc5VIG_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cyFXzJO3EyfOUXZc5VIG_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-9YU7YYBBkbVtX3n-UTI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YiY7TtFv0EXQiZMyJynqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3YiY7TtFv0EXQiZMyJynqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ptYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9hCoRxPXk-6CzoYUlUSWOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9hCoRxPXk-6CzoYUlUSWOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MlT_4DHKrs5Ys4kSTL31fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MlT_4DHKrs5Ys4kSTL31fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rlbPtxuicfhcv7QXNW6CDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rlbPtxuicfhcv7QXNW6CDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHUuby7f7Ki-mhiDAG_3SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHUuby7f7Ki-mhiDAG_3SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L9YU7YYBBkbVtX3n9jmK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M-oVh-FVYE_OigiSXRD1tA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M-oVh-FVYE_OigiSXRD1tA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "btYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-f-8xMNzAVnOWhCPzAg7Cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-f-8xMNzAVnOWhCPzAg7Cg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "6dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aRGWrc208dGoT33fGEbwGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aRGWrc208dGoT33fGEbwGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "hNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CAMqWYZA5nX1ba5rg42kvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CAMqWYZA5nX1ba5rg42kvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "m9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XAkh0cI6mI0TEjgeMQjJRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XAkh0cI6mI0TEjgeMQjJRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "rdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PkV0kGs2CSg2biD88r4Y4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PkV0kGs2CSg2biD88r4Y4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "stYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NobRtV28TztWPphel-_oog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NobRtV28TztWPphel-_oog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "tdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mY-rd4h7J7uWaPfvMpVGGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mY-rd4h7J7uWaPfvMpVGGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "udYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP1YbEn43FveSGHDAeyzEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP1YbEn43FveSGHDAeyzEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "xNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5QZMmKE4g5NoBX6HRV7SWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5QZMmKE4g5NoBX6HRV7SWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "99YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lKZJwz7nYfsG1OMpqNS4TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lKZJwz7nYfsG1OMpqNS4TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "-9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pzPHOZ2KMa2AZ8PFjN6JMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pzPHOZ2KMa2AZ8PFjN6JMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "CNYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynjt3bgLGnY61oQESibEHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynjt3bgLGnY61oQESibEHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "I98U7YYByh-A-Biy7P0G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ycc5WIDSFgbOYKJJPEnKPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ycc5WIDSFgbOYKJJPEnKPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7dYU7YYBBkbVtX3n7jVX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yzwq46KsVTwibTlrmeJDug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yzwq46KsVTwibTlrmeJDug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LWUU7YYBO2e_P_Qb79AO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xgQ5fvtkK4YCunRGORxAiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xgQ5fvtkK4YCunRGORxAiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ydYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2BrtK-7vEpxtyRvA-Da53w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2BrtK-7vEpxtyRvA-Da53w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "EGUV7YYBO2e_P_QbBuCR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hMnXoL28a6WRFVFuXnlcrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hMnXoL28a6WRFVFuXnlcrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DNYV7YYBBkbVtX3nB1HM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jw0LVPxt2I2Zcn1jBHHTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3jw0LVPxt2I2Zcn1jBHHTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "deAV7YYByh-A-BiyCRh4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rW61i2rZH66wSQyYlV4AKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rW61i2rZH66wSQyYlV4AKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jOAV7YYByh-A-BiyCRYi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l3unjN-Nom23dUWou6f5fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l3unjN-Nom23dUWou6f5fA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "59YV7YYBBkbVtX3nDV0h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zKpNTqz8S7smmzjSBvFLmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zKpNTqz8S7smmzjSBvFLmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jeAV7YYByh-A-BiyCRYi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_v5AZnkwJNNAFHCBYTKsLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_v5AZnkwJNNAFHCBYTKsLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "juAV7YYByh-A-BiyCRYi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B-XLpCbHVWJllSfmbTHDIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B-XLpCbHVWJllSfmbTHDIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "D9YV7YYBBkbVtX3nB1HM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["--ixTzVal287MaHIkMjGPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["--ixTzVal287MaHIkMjGPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LtYV7YYBBkbVtX3nCFPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gtt2kNKLBYrjW8ZF3asaVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gtt2kNKLBYrjW8ZF3asaVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JOAV7YYByh-A-BiyCxxq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wc2YJ7FTpO6RxJmi8R3V5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wc2YJ7FTpO6RxJmi8R3V5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RmUV7YYBO2e_P_QbDOFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j6b-IQfEVBkMZQup2Hh2og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j6b-IQfEVBkMZQup2Hh2og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mOAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mSsTbj23GsDgy2L_ys-j9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mSsTbj23GsDgy2L_ys-j9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZeAV7YYByh-A-BiyCBR8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFCYP0M0hh6g3AUrorpNSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XFCYP0M0hh6g3AUrorpNSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "eOAV7YYByh-A-BiyCBIn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NLuoJVh7KKlp7vUyDXbc5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NLuoJVh7KKlp7vUyDXbc5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "g9YV7YYBBkbVtX3nC1gN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kv93tElXuO9W5qREarSlDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kv93tElXuO9W5qREarSlDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "wuAV7YYByh-A-BiyCx2w"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XEVDehOwFGRzuyg-wdytUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XEVDehOwFGRzuyg-wdytUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "QNYV7YYBBkbVtX3nDFzR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RakQwb6TZGrlrD1xg1MS9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RakQwb6TZGrlrD1xg1MS9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "Z-AV7YYByh-A-BiyCBR8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KcOiTTTgvYGRMXlpLOi98w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KcOiTTTgvYGRMXlpLOi98w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "7eAV7YYByh-A-BiyDB-G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zlmxsTTPMJDp5d_OFnqBkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zlmxsTTPMJDp5d_OFnqBkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xuAV7YYByh-A-BiyGSzz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["86mdF-KM7NXo0RUNJiei_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["86mdF-KM7NXo0RUNJiei_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ddYV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SQYzaRy22h79zcc3oYHt2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SQYzaRy22h79zcc3oYHt2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ZmUV7YYBO2e_P_QbGOeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eIiWRPbXZKuww0eQLj2S1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eIiWRPbXZKuww0eQLj2S1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "x-AV7YYByh-A-BiyGSzz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BjjctUOzAXO89YV2nk2Q4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BjjctUOzAXO89YV2nk2Q4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ymUV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Icjoo1-DazyjO-tC_2ln0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Icjoo1-DazyjO-tC_2ln0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "AmUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ntU9lIOS2on6fT6gjqaLpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ntU9lIOS2on6fT6gjqaLpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "CGUV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9KEwJQyYCkGUHgQfZ4zuUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9KEwJQyYCkGUHgQfZ4zuUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "cdYV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1_2jb7w-6SaVosGj92Tp3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1_2jb7w-6SaVosGj92Tp3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "xdYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKeJv4UECTJRoO7tbd8ieA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKeJv4UECTJRoO7tbd8ieA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "49YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EqC6GrxSGBqn7Se_QkfBvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EqC6GrxSGBqn7Se_QkfBvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xWUV7YYBO2e_P_QbLPlx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MZ2ns561NqM1CIUtwsXKqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MZ2ns561NqM1CIUtwsXKqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wNYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hf9wGp5TNFiImJfF3zrljg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hf9wGp5TNFiImJfF3zrljg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "xtYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n1K87iiownRMy9p9EhnxaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n1K87iiownRMy9p9EhnxaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "2dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["is-GrshzXGfvyrs7C84YDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["is-GrshzXGfvyrs7C84YDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "2tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cGm2F3NETfQrvkp4OdpTFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cGm2F3NETfQrvkp4OdpTFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "3NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hD87Iq0SHAPj8Fv9uEQOUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hD87Iq0SHAPj8Fv9uEQOUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "IOAV7YYByh-A-BiyLELp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BpKdezWYGYGHxKuRbIhA6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BpKdezWYGYGHxKuRbIhA6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "T9YV7YYBBkbVtX3nKoD0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W_4xBcIc_f_s9tU-JNOc3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W_4xBcIc_f_s9tU-JNOc3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "4tYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["to88lQuatgOtWyhP8T7OMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["to88lQuatgOtWyhP8T7OMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "zdYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P3ialIWlnxijlxjtEz_ZOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P3ialIWlnxijlxjtEz_ZOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "ztYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GlIWXDdurIjEanwPccKsgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GlIWXDdurIjEanwPccKsgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "MWUV7YYBO2e_P_QbOP8d"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yI2e6HYAfhTSJaxYuulCOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yI2e6HYAfhTSJaxYuulCOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "jWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nO9wgb1CAloL4EZkWArlWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nO9wgb1CAloL4EZkWArlWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lUwPl7LdcSnsIUgnw1ojfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lUwPl7LdcSnsIUgnw1ojfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1dYV7YYBBkbVtX3nS6Gk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eh1qER1qLyoMW0w6ZkEkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Eh1qER1qLyoMW0w6ZkEkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "oWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8J86z5adi7zJtjuGctS5Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8J86z5adi7zJtjuGctS5Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "fWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHXfHm0BU3ZDtLvmt4EIRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHXfHm0BU3ZDtLvmt4EIRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "g2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t6ufu4-IpYRs7bFVfd4NLw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t6ufu4-IpYRs7bFVfd4NLw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-WmMHqB8hxsW-_Rm9LtnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-WmMHqB8hxsW-_Rm9LtnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "amYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QqagX4SxhMaRMlzq_9N22A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QqagX4SxhMaRMlzq_9N22A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "a2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pDo331a23FHFPXYKG9i3EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pDo331a23FHFPXYKG9i3EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "bGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["It7LZ_q9NvTlZJoCJvT4UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["It7LZ_q9NvTlZJoCJvT4UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "emYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3vt8oTjSVhChgy2VdDHcdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3vt8oTjSVhChgy2VdDHcdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "nWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIcwjcTUxYrOZlE8A754rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NIcwjcTUxYrOZlE8A754rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "S9YV7YYBBkbVtX3nR5Yn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rt3hWtoW0qQnxFuClIgRWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Rt3hWtoW0qQnxFuClIgRWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "RtYV7YYBBkbVtX3nR5mR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a1Wk2s-gPeQ3RACuvlpgIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a1Wk2s-gPeQ3RACuvlpgIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "dtYV7YYBBkbVtX3nS587"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FAfemJcT6DP2ZiUG7J8bPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FAfemJcT6DP2ZiUG7J8bPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ZmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oPgWgw_HJ-7hbpa6_4Pqeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oPgWgw_HJ-7hbpa6_4Pqeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "iGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nay4Cu7cpfWvHwjKfzebpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nay4Cu7cpfWvHwjKfzebpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "imYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aHwd23m95kbO5iH430mBgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aHwd23m95kbO5iH430mBgA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "19YV7YYBBkbVtX3nTKb2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xVGi1i7nlJYkT__QgtZrJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xVGi1i7nlJYkT__QgtZrJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "YmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mtj0z4-Jv9LSHprcHM_y6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mtj0z4-Jv9LSHprcHM_y6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "e2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LpjlQ5x3C5y0GS9aUsntg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LpjlQ5x3C5y0GS9aUsntg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "mWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AFNGR4OlXqTo-8_xvYFKBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AFNGR4OlXqTo-8_xvYFKBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "udYV7YYBBkbVtX3nRZKx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YHlz2_RUb_dJDnbIGfEi0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "K2YV7YYBO2e_P_QbVhYN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yrneF8Y5HnZdPRsa0iSPNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yrneF8Y5HnZdPRsa0iSPNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Q9YV7YYBBkbVtX3nWrnf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oCsk-dy_lD0KOZfnzHxYTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oCsk-dy_lD0KOZfnzHxYTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4WYV7YYBO2e_P_QbWyOI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOgvcGNEugo-q4Mte_An1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iOgvcGNEugo-q4Mte_An1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-dYV7YYBBkbVtX3nW7rm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nNScNuSTrpa5-8cxBl8OiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nNScNuSTrpa5-8cxBl8OiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "N2YV7YYBO2e_P_QbWBy9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S4WvNNBIMRmvCXXO42eZaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S4WvNNBIMRmvCXXO42eZaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "StYV7YYBBkbVtX3nVrHh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQ1fVd58k2fSqjQSJ4y2iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQ1fVd58k2fSqjQSJ4y2iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "S9YV7YYBBkbVtX3nVrHh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q8Eu40FpZPClw51Nc5Z0VQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q8Eu40FpZPClw51Nc5Z0VQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "F2YV7YYBO2e_P_QbWBoF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "yWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NRT6b-EmSsUKrT0-0ibcag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NRT6b-EmSsUKrT0-0ibcag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vWYV7YYBO2e_P_Qbajgv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-C8OkGycyUxmY2Oeulo77A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-C8OkGycyUxmY2Oeulo77A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JdYV7YYBBkbVtX3ndc64"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOiM2iaG3zJbqgtGW26o0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOiM2iaG3zJbqgtGW26o0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GIXoZcc-rO2_QJqWdyhQCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GIXoZcc-rO2_QJqWdyhQCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "z2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5uwo_M9Ua17W7BQNlGpxyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5uwo_M9Ua17W7BQNlGpxyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "s2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GdQO73uJU5ltMBM9sQEM4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GdQO73uJU5ltMBM9sQEM4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "v2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b_jCta422d6xvVpqh75ZGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b_jCta422d6xvVpqh75ZGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xmYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Xt8BX3Fxv0nw4SPWrSguQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Xt8BX3Fxv0nw4SPWrSguQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zmYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["toPZwdg4nGX0bw501hsszg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JeAV7YYByh-A-BiydG8z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6bno3KY4YPf5Yv8-TeyIMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6bno3KY4YPf5Yv8-TeyIMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-all", "_id": "zGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c09KLYvfDtrLePqGFrZGFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c09KLYvfDtrLePqGFrZGFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "ymYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["apJrIWswhYE4MqQ6C-GbSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["apJrIWswhYE4MqQ6C-GbSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0GYV7YYBO2e_P_QbdDzG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfmPxWX0umuPnDn2aoiurQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qfmPxWX0umuPnDn2aoiurQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "7eAV7YYByh-A-BiyiIYF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8bQaxjHqOXy8jFaY6w3jpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8bQaxjHqOXy8jFaY6w3jpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LtYV7YYBBkbVtX3nhd1U"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n0VugIogSoCuJazNruqmpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n0VugIogSoCuJazNruqmpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TGYV7YYBO2e_P_Qbh0uD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fn2Ai3DCNmO1q3hi2Wb60Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fn2Ai3DCNmO1q3hi2Wb60Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "SdYV7YYBBkbVtX3niOWG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u4n3YuffBdoPIiHaB1UC0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u4n3YuffBdoPIiHaB1UC0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "148877361383403"} {"create": {"_index": "profiling-events-all", "_id": "RGYV7YYBO2e_P_QbmFzZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruy3OOd-IyG1ZkwpGQGBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ruy3OOd-IyG1ZkwpGQGBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eWYV7YYBO2e_P_Qbllh-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZCa7vDPVTmdjv0FBSHomYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZCa7vDPVTmdjv0FBSHomYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TWYV7YYBO2e_P_Qbl1q1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "kdYV7YYBBkbVtX3nmPkA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L9SYu-N9HWWrCeEAOt9YiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L9SYu-N9HWWrCeEAOt9YiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "cGYV7YYBO2e_P_Qbo2DE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "EuAV7YYByh-A-BiypJjC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fvee1-CYSv6CWV-rI4TxkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fvee1-CYSv6CWV-rI4TxkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "t2YV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3KY9CUj1lI4EPyAmsjiKpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3KY9CUj1lI4EPyAmsjiKpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-all", "_id": "BdcV7YYBBkbVtX3ntRYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ql87_TD7x_m3wC-TEuP9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ql87_TD7x_m3wC-TEuP9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CdcV7YYBBkbVtX3ntRYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["czdRcblS6ivfa0r3vBCxww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["czdRcblS6ivfa0r3vBCxww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tGYV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nfuQzK4dMvkwCIn4oK0vJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BtcV7YYBBkbVtX3ntRYQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0DLtHxiVxElcFIXMT-PNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZOAV7YYByh-A-BiytqnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cjNpri0ftTdS6gywMlEj6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cjNpri0ftTdS6gywMlEj6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XOAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zpy6U6QwlCQnvibG2K7Iuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zpy6U6QwlCQnvibG2K7Iuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "idcV7YYBBkbVtX3nuB51"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h3J3yP8dE1Gp9C8Y2fBhOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h3J3yP8dE1Gp9C8Y2fBhOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "42YV7YYBO2e_P_Qbsmn3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O5lLlWeDVPBzxcQgxCRocQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O5lLlWeDVPBzxcQgxCRocQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "gmYV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pdAduE6Gutq4FbdcnP42xA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pdAduE6Gutq4FbdcnP42xA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hGYV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OGCNe0J99A_EC_qmplbVRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OGCNe0J99A_EC_qmplbVRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "U-AV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f34iYGJZ4-vox7c1m40S5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f34iYGJZ4-vox7c1m40S5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "etcV7YYBBkbVtX3ntRfi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2kXwUSA3PTDw3kyQaaCEMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2kXwUSA3PTDw3kyQaaCEMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "K2YV7YYBO2e_P_QbtnB5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YxRH1n6rM_I4hLiGtKmVMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YxRH1n6rM_I4hLiGtKmVMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "69cV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ml7vM_TA_xUcAexnu4FJAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ml7vM_TA_xUcAexnu4FJAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ytcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jNdyoypr6XrrbEBsVS2_Xw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jNdyoypr6XrrbEBsVS2_Xw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "zdcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Lcmvo890HG8Y4rQbXwRxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Lcmvo890HG8Y4rQbXwRxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jNcV7YYBBkbVtX3nuB51"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kk6lQFGFmE5-o8l9P-PnVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kk6lQFGFmE5-o8l9P-PnVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ImYV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CCqMRHd4WGpx3xij440EOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CCqMRHd4WGpx3xij440EOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "6NcV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5BZrfAQrKtaiW6I35J5iBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5BZrfAQrKtaiW6I35J5iBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "89cV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [4], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [4], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9dcV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9tcV7YYBBkbVtX3ntBEr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "UOAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qi_HV2s76U-q22eSjxJyyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qi_HV2s76U-q22eSjxJyyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WOAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6qn8dRThwMb4sKyHdsYIBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6qn8dRThwMb4sKyHdsYIBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "i9cV7YYBBkbVtX3ntBPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hITXE_D420gqVob6jyPWhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hITXE_D420gqVob6jyPWhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "0-AV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NZjfatQVxHkimbXA3s51kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NZjfatQVxHkimbXA3s51kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "MuAV7YYByh-A-Biyt6sa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5zce_f4N45Itu5RhOF9CQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5zce_f4N45Itu5RhOF9CQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "NOAV7YYByh-A-Biyt6sa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oswz9Ug-CA3h0V4jS_UMxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oswz9Ug-CA3h0V4jS_UMxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "OmYV7YYBO2e_P_Qbsmir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sjgFFvTDTMQOTVFeiCq8hQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sjgFFvTDTMQOTVFeiCq8hQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "WuAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V6pL6H57Sh06W9eadqo0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V6pL6H57Sh06W9eadqo0ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "itcV7YYBBkbVtX3ntBPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bc6HNlC8Sl7niDN9L84HCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bc6HNlC8Sl7niDN9L84HCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "rGYV7YYBO2e_P_QbtW5b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DBbDg0Db98hCosBBvxLmXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DBbDg0Db98hCosBBvxLmXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "0tcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "v2YV7YYBO2e_P_Qbx3ol"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ci1aB63L8kECW29ygL8YPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ci1aB63L8kECW29ygL8YPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EWYV7YYBO2e_P_Qb037f"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rtcV7YYBBkbVtX3nxil1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lE8Ul76Ux_RbEcuXBt93-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lE8Ul76Ux_RbEcuXBt93-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kGYV7YYBO2e_P_QbxXYP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AjN_kWkUNJ8KmZKfGtKOWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AjN_kWkUNJ8KmZKfGtKOWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rdcV7YYBBkbVtX3nxil1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k1TJV5J17869y08LRTIrbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k1TJV5J17869y08LRTIrbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "X-AV7YYByh-A-Biy08WI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E8q5-T4I0EEq3oPd2J28VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E8q5-T4I0EEq3oPd2J28VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ndcV7YYBBkbVtX3n1C-A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NUICzvay5gqiM1JCIDYjDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NUICzvay5gqiM1JCIDYjDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "HWYV7YYBO2e_P_Qb0XuE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_2Gi4xXPiktjMQVPnPo6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_2Gi4xXPiktjMQVPnPo6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MOAV7YYByh-A-Biyxr4p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MeAV7YYByh-A-Biyxr4p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "feAV7YYByh-A-Biy0b5D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vfg07z_Uc57UwdNH4Rkz_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "845379217314054"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vfg07z_Uc57UwdNH4Rkz_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "845379217314054"} {"create": {"_index": "profiling-events-all", "_id": "BdcV7YYBBkbVtX3nxSdX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RK5QOedYDJN8YhVo9FJwjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RK5QOedYDJN8YhVo9FJwjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "ZNcV7YYBBkbVtX3nxSih"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Of21tDBsawVNvxkGbr6swA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Of21tDBsawVNvxkGbr6swA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nmYV7YYBO2e_P_Qb0nzr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RiwkHW7QG1cujKZJxeET4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RiwkHW7QG1cujKZJxeET4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "hmYV7YYBO2e_P_Qb5Ymt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["unr_AT5uIzeOxUG_JOGaZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["unr_AT5uIzeOxUG_JOGaZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "-WYV7YYBO2e_P_Qb5ooi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B9mdhsnY5y_-MapRECAsRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B9mdhsnY5y_-MapRECAsRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ftcV7YYBBkbVtX3n4Two"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IqfRn2aVThGWJpMyYAGTnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IqfRn2aVThGWJpMyYAGTnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wtcV7YYBBkbVtX3n5EQ_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n021nAexdd_F-L49egYEAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n021nAexdd_F-L49egYEAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JNcV7YYBBkbVtX3n4j4u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxgOBo6uq7uvbQcqbAhQEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxgOBo6uq7uvbQcqbAhQEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "aeAV7YYByh-A-Biy5NuM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4uPdEWYsvs4SFw1vIspTAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4uPdEWYsvs4SFw1vIspTAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "vuAV7YYByh-A-Biy4NHi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r6N0xoA2ZW8gX4-YxbnuYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r6N0xoA2ZW8gX4-YxbnuYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "peAV7YYByh-A-Biy5dxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jthhLIqVB5doOdOhwJAhFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jthhLIqVB5doOdOhwJAhFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "G-AV7YYByh-A-Biy5d7m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cfo59YpRKB0q5iQSQJ-VYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cfo59YpRKB0q5iQSQJ-VYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "n9cV7YYBBkbVtX3n40Er"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ICTXUEt57m3Mefmfzh3iUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "314337526876654"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ICTXUEt57m3Mefmfzh3iUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "314337526876654"} {"create": {"_index": "profiling-events-all", "_id": "auAV7YYByh-A-Biy5NuM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M-2CQZEnf6BMvQazkJUZsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M-2CQZEnf6BMvQazkJUZsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "BuAV7YYByh-A-Biy4dWo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P5qpfZ9QS6f9dFQXQ-RQug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P5qpfZ9QS6f9dFQXQ-RQug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "DdcW7YYBBkbVtX3nA11E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L-P4L1LyUnq2IHmuakdfLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "28424007785283"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L-P4L1LyUnq2IHmuakdfLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "28424007785283"} {"create": {"_index": "profiling-events-all", "_id": "79cW7YYBBkbVtX3nAFa0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gD23Bd8kEVujP5Tq3uPRhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gD23Bd8kEVujP5Tq3uPRhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "nmYW7YYBO2e_P_QbAKJr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V1p0cyagFSh4bcZOqHE1xA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V1p0cyagFSh4bcZOqHE1xA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VGYW7YYBO2e_P_QbBawR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5pUaZNDVlK5DFtb06wgQqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5pUaZNDVlK5DFtb06wgQqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LtcW7YYBBkbVtX3nAls_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ryXlsOyzYLGsXWk1fBrhYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ryXlsOyzYLGsXWk1fBrhYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "meAW7YYByh-A-BiyAuya"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rkgBP4sIA5dHtR_5QDvD0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rkgBP4sIA5dHtR_5QDvD0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} {"create": {"_index": "profiling-events-all", "_id": "-mYW7YYBO2e_P_QbBKlb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KYi5gWT8f3sgP5wTTol1PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KYi5gWT8f3sgP5wTTol1PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "n2YW7YYBO2e_P_QbAKJr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ww_KG3DMJJ4ZQFU4V6lPqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ww_KG3DMJJ4ZQFU4V6lPqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ztcW7YYBBkbVtX3nAVhI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Wc8cD-xlBFleqsp-xbM4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Wc8cD-xlBFleqsp-xbM4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LdcW7YYBBkbVtX3nAls_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lQB8mTpjqxNXboFLF_ikTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lQB8mTpjqxNXboFLF_ikTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "2-AW7YYByh-A-BiyAu7q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wKd3bA5CzFfK_BkLhBHABw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wKd3bA5CzFfK_BkLhBHABw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "52YW7YYBO2e_P_QbAKAj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "8OAW7YYByh-A-BiyEfxY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GITcXcM5OZJEsFYPj2RnOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GITcXcM5OZJEsFYPj2RnOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Y2YW7YYBO2e_P_QbE72B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQ1nyOGbOtedL7gx4fO8XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQ1nyOGbOtedL7gx4fO8XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GGYW7YYBO2e_P_QbEbqq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vyzbd-n47muGD1CcY51iSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Vyzbd-n47muGD1CcY51iSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qWYW7YYBO2e_P_QbEruU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7ut68c_tuuoqFzX7ruLfoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7ut68c_tuuoqFzX7ruLfoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GmYW7YYBO2e_P_QbEbqq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["odxDK_3zvNwVZ9HE8UBEtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["odxDK_3zvNwVZ9HE8UBEtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b-EW7YYByh-A-BiyEwXO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wx1SQ999fU4_Vx8sVoOw-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wx1SQ999fU4_Vx8sVoOw-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5OAW7YYByh-A-BiyD_jF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rv7vsqjWP8SoKG0Qu1ylfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rv7vsqjWP8SoKG0Qu1ylfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "q2YW7YYBO2e_P_QbEruU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uPfQx41sGpWXSF6wjd1f8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uPfQx41sGpWXSF6wjd1f8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GuEW7YYByh-A-BiyEwMv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UwLGDv_kxs9iZbW8xcSUiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UwLGDv_kxs9iZbW8xcSUiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xtcW7YYBBkbVtX3nFWpl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["en13QzIrjXnNEN-2tQMMJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["en13QzIrjXnNEN-2tQMMJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "eNcW7YYBBkbVtX3nEmVM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "kNcW7YYBBkbVtX3nFmxw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DAQHjXtThJ1eaHLevfklmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DAQHjXtThJ1eaHLevfklmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "K9cW7YYBBkbVtX3nFGl3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ig2MzPdh_XK7em8mWoJag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Ig2MzPdh_XK7em8mWoJag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "xdcW7YYBBkbVtX3nFWpl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n5uPBDEBKL0cPVfOG1jyTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n5uPBDEBKL0cPVfOG1jyTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "j9cW7YYBBkbVtX3nFmxw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "ANcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oKjEqCTMwkPftp0JIk3zEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oKjEqCTMwkPftp0JIk3zEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9dcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["akR0ajfGkZj2z5CmqvQfvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["akR0ajfGkZj2z5CmqvQfvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWQBAJ7ncYkjoV_tk9YLSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWQBAJ7ncYkjoV_tk9YLSg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_tcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T2adJ0VUBNmWRoosWSssPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T2adJ0VUBNmWRoosWSssPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "9eEW7YYByh-A-BiyIxQ6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6OaUumRb8P6q4GlOGK0eGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6OaUumRb8P6q4GlOGK0eGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Q9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "T9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3tO3AhnrBAiBOTlaDL412Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3tO3AhnrBAiBOTlaDL412Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "UtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LTTi8ZBWlyKqRGwjukTflA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LTTi8ZBWlyKqRGwjukTflA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6dcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eHad6fXlNZDDIqYPS_NA_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eHad6fXlNZDDIqYPS_NA_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7NcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0j1wiZ2zi9t7EenFUwZ_Qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0j1wiZ2zi9t7EenFUwZ_Qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8tcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PkUlg8ipxB6y2tnHWbLlxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PkUlg8ipxB6y2tnHWbLlxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "-9cW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["muSA4-3A5tqLjcddDaeDBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["muSA4-3A5tqLjcddDaeDBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "_NcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-G51CEezafd_J98dgV5JgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-G51CEezafd_J98dgV5JgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "_dcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T5Cn4ZcI85w-SXFfrytVPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T5Cn4ZcI85w-SXFfrytVPg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "BdcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UoCOFE0Ha3XaxXZbhILYDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UoCOFE0Ha3XaxXZbhILYDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "B9cW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uQC4_VLdsRdPOY_eYCLgyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uQC4_VLdsRdPOY_eYCLgyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "CdcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lQw85LEW4DpLukB4K3A6Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lQw85LEW4DpLukB4K3A6Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "CtcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E0PbyjdVN-U5rJIxbRMmXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E0PbyjdVN-U5rJIxbRMmXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "DdcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fn0ZK2-ZIUvfytO-QISHyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fn0ZK2-ZIUvfytO-QISHyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EtcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["05r6Ccnm9nLiv5rAFI61BA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["05r6Ccnm9nLiv5rAFI61BA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "RNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZpjYqKFeza_P-0E6-9HQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "D9cW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TpDEzWoy6rEMZYVF9eYCTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TpDEzWoy6rEMZYVF9eYCTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TtcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "5tcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynwp47PusWaUtQGudVhz4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ynwp47PusWaUtQGudVhz4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "A9cW7YYBBkbVtX3nIHCT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tq6Q7NqNkBok1R0-y_UPeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tq6Q7NqNkBok1R0-y_UPeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "sWYW7YYBO2e_P_QbP-Dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHeythk0HZH6YmI9vQ5rRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHeythk0HZH6YmI9vQ5rRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "8eEW7YYByh-A-BiyQy5a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fay02h057oipAap2CpcvzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fay02h057oipAap2CpcvzA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ROEW7YYByh-A-BiyQSqw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rh6dkbq8WqrY7XSMixfetg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rh6dkbq8WqrY7XSMixfetg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B2YW7YYBO2e_P_QbP9-R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bciyx_9NZlf5osbnTw9ncg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bciyx_9NZlf5osbnTw9ncg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CGYW7YYBO2e_P_QbP9-R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7755LNVrLbBmdM_Bp6XCxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7755LNVrLbBmdM_Bp6XCxQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h2YW7YYBO2e_P_QbRObI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "r2YW7YYBO2e_P_QbP-Dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x_C9pMCGlnFGRtyqng1scA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x_C9pMCGlnFGRtyqng1scA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "ddcW7YYBBkbVtX3nQo-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jlfx0CU2a7x4kprrtwg64Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jlfx0CU2a7x4kprrtwg64Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "lOEW7YYByh-A-BiyQzHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JkCyXgxX7cEcni0HaLkYUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JkCyXgxX7cEcni0HaLkYUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "umYW7YYBO2e_P_QbPtv2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BN1QyTRWZUfNNPVd_m-f5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BN1QyTRWZUfNNPVd_m-f5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "kuEW7YYByh-A-BiyQzHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GXIswAHFAGNbG7pCJXH-2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GXIswAHFAGNbG7pCJXH-2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "huEW7YYByh-A-BiyPiek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "zWYW7YYBO2e_P_QbQeID"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ppE0asB2Tjvm1WVJwx6bDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ppE0asB2Tjvm1WVJwx6bDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "5WYW7YYBO2e_P_QbUOsU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yqNwMmnv9h-z0-i604hZXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yqNwMmnv9h-z0-i604hZXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UGYW7YYBO2e_P_QbUe1z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-JEJOChfUhn_oksa05rgbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-JEJOChfUhn_oksa05rgbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Z2YW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2IwLSNJXYCXB5L0gWZQiOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2IwLSNJXYCXB5L0gWZQiOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "M-EW7YYByh-A-BiyTjpF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8e2eKxLMr45T-uq51LWiRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8e2eKxLMr45T-uq51LWiRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TmYW7YYBO2e_P_QbUe1z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["trg5FG0_Dj2SBeJw5MOwoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["trg5FG0_Dj2SBeJw5MOwoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "72YW7YYBO2e_P_QbVPdI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LdtY_NIqhiA3emudSaygtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LdtY_NIqhiA3emudSaygtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B2YW7YYBO2e_P_QbUe_C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j7OoU3oXSY3AFf-whF_CWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j7OoU3oXSY3AFf-whF_CWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5GYW7YYBO2e_P_QbUOsU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VXEIZNsetkTnWe5kx41b7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VXEIZNsetkTnWe5kx41b7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "B-EW7YYByh-A-BiyUkAI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H6SfMk8pl8KEOh7Msy9oRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H6SfMk8pl8KEOh7Msy9oRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "YmYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YiqN1eguj9w9NAvkPJxo-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YiqN1eguj9w9NAvkPJxo-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "vNcW7YYBBkbVtX3nVJ4L"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t5iMV2bxdd31FJyizPOYCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t5iMV2bxdd31FJyizPOYCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "HOEW7YYByh-A-BiyUD3c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mAZtQho57bjyTMlusRjj_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mAZtQho57bjyTMlusRjj_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bNcW7YYBBkbVtX3nT5d5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K9LDD5AZV4XmqBf_IoPXlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K9LDD5AZV4XmqBf_IoPXlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "R2YW7YYBO2e_P_QbT-o1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wi6N3XBGb2fuENnxnEyLLw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wi6N3XBGb2fuENnxnEyLLw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "KtcW7YYBBkbVtX3nUJll"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w_y9NqchzoBrOm_UtCMj_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w_y9NqchzoBrOm_UtCMj_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "xdcW7YYBBkbVtX3nTpXo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wGWkKDGzXSSBbLkENAOIkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wGWkKDGzXSSBbLkENAOIkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "HeEW7YYByh-A-BiyUD3c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "u9cW7YYBBkbVtX3nVJ4L"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ifPg0tbOeATgXu54GVLHjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ifPg0tbOeATgXu54GVLHjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "beEW7YYByh-A-BiyX0Zg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y9yOjEX9YsHot-nonRkNzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y9yOjEX9YsHot-nonRkNzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "RtcW7YYBBkbVtX3nX6G1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AidY4CO5JNQB7gWz7IQBaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AidY4CO5JNQB7gWz7IQBaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Z9cW7YYBBkbVtX3nYajE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whMMD-Ig4w265V1dioRGrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whMMD-Ig4w265V1dioRGrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "AWcW7YYBO2e_P_QbbQfF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gO3h9sz_Pp88e_MYvoWQhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gO3h9sz_Pp88e_MYvoWQhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "5eEW7YYByh-A-Biycli0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ql87_TD7x_m3wC-TEuP9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ql87_TD7x_m3wC-TEuP9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i9cW7YYBBkbVtX3nf735"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKhp4VrunMdJZkEZm9IkqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iKhp4VrunMdJZkEZm9IkqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FeEW7YYByh-A-Biyclc2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zrel0O8Eu19Ixn8b1A7RrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zrel0O8Eu19Ixn8b1A7RrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cmcW7YYBO2e_P_QbfRJk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V8737ugipSYB_laFotiYpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V8737ugipSYB_laFotiYpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mOEW7YYByh-A-Biyf2R6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rrfjqA-Dh9U3GF1qMdtRQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rrfjqA-Dh9U3GF1qMdtRQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "G9cW7YYBBkbVtX3nfryk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RWHF3OwT21IPak-nIUzKKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RWHF3OwT21IPak-nIUzKKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jWcW7YYBO2e_P_QbgBWH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ic2LVsuiX2BndjGY3s8emQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ic2LVsuiX2BndjGY3s8emQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PdcW7YYBBkbVtX3nfbnk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q0wzD6Wsaoym2okQ8aY53w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q0wzD6Wsaoym2okQ8aY53w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "GtcW7YYBBkbVtX3nfryk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Xg3Upyi105Wyx-NTECB2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Xg3Upyi105Wyx-NTECB2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "RuEW7YYByh-A-BiykHvM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzu7roeVjuX8DIGpBc0otA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "0dcW7YYBBkbVtX3nksnh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mTpUz_E1PPzj0HR92ABMpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mTpUz_E1PPzj0HR92ABMpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FuEW7YYByh-A-Biyj3ir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WBHQM4NBGzoZVLedZma7Ig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WBHQM4NBGzoZVLedZma7Ig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "neEW7YYByh-A-BiykHkK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["txUH5yrbhq1IXgpWcJMBxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["txUH5yrbhq1IXgpWcJMBxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yeEW7YYByh-A-BiyjG3E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ul5WCOLuBGGX66Anz_J-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ul5WCOLuBGGX66Anz_J-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XuEW7YYByh-A-Biyj3ZQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uaiZKrXoT8VGJLZeMjATrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uaiZKrXoT8VGJLZeMjATrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nOEW7YYByh-A-BiykHkK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SFVwB25B3ZOzmrFYMtl7jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SFVwB25B3ZOzmrFYMtl7jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_OEW7YYByh-A-BiyknyA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-HAzLlWL3fwYJPxGXqYsZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-HAzLlWL3fwYJPxGXqYsZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "yuEW7YYByh-A-BiyjG3E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aRl5mcquqOzq3HPlHFumow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aRl5mcquqOzq3HPlHFumow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "yOEW7YYByh-A-BiyjG3E"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HCpiBj8BD8aEJWFEDHXgqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HCpiBj8BD8aEJWFEDHXgqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "zdcW7YYBBkbVtX3nkscq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dF3lN3ea4am_7tDjMTNP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6WcW7YYBO2e_P_QbjR7F"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TEYPXE1OBEat8qmRauzsiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TEYPXE1OBEat8qmRauzsiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "4uEW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wDcbOGXjyzRDEWJtXUJ7rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wDcbOGXjyzRDEWJtXUJ7rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "TGcW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iVK1cbIgag654ehUa-HUNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iVK1cbIgag654ehUa-HUNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QuEW7YYByh-A-BiyrYsf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["86WQcXcUxaHfJUCEplsqoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["86WQcXcUxaHfJUCEplsqoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UGcW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fHrd9ZU73jKyeFVMnONXJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fHrd9ZU73jKyeFVMnONXJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F-EW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bQ285knnYYuMww0WgMbI2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bQ285knnYYuMww0WgMbI2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GGcW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVY8nef_n-I9Q52QhyCFfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVY8nef_n-I9Q52QhyCFfQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zdcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nBUbTpmi8j18IFjmOSwgBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nBUbTpmi8j18IFjmOSwgBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lGcW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nq7kSiChsqLqIUaoOI5SGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nq7kSiChsqLqIUaoOI5SGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "FGcW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5T5ZLDbv0qRIOWXAecPJiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5T5ZLDbv0qRIOWXAecPJiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "k-EW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XaUnPPtgxfYR4iOYVLS0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XaUnPPtgxfYR4iOYVLS0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "lOEW7YYByh-A-Biyn4n1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ND1k9rOosEcGzLPWnPdgVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ND1k9rOosEcGzLPWnPdgVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "oGcW7YYBO2e_P_QboDfC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jtcW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["txZXHAJurNaMIlI0kux2YA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["txZXHAJurNaMIlI0kux2YA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "j9cW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tVG5BmNboq64Jjq3eLhTAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tVG5BmNboq64Jjq3eLhTAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "V9cW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h0lEtzKJzcNxepmOT3KRtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h0lEtzKJzcNxepmOT3KRtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5eEW7YYByh-A-BiyoYqh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SFn-AE4FVjnPbzGVfeaMqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SFn-AE4FVjnPbzGVfeaMqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "42cW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5WcW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EZ2YsSTqh3amiqmt5jrW4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EZ2YsSTqh3amiqmt5jrW4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "S2cW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxLFvg4n6uQItdMk3gw_xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AxLFvg4n6uQItdMk3gw_xg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "O9cW7YYBBkbVtX3nrNlH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZUvWAqmAXt-dgxjo_MjchA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZUvWAqmAXt-dgxjo_MjchA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hdcW7YYBBkbVtX3nrNrT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZKNzQAHHe_cNd3rO-y4iLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZKNzQAHHe_cNd3rO-y4iLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9NcW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sl8hikPZI3gmfoj4I-xFMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sl8hikPZI3gmfoj4I-xFMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "GmcW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZcpIqjO8GbOGxvXYAifsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZcpIqjO8GbOGxvXYAifsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "pOEW7YYByh-A-BiyrYy7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["naLB5wSXMG6mCbQGVr-m2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["naLB5wSXMG6mCbQGVr-m2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ZeEW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4I-ntmDjAgUXJfwbuBJNjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4I-ntmDjAgUXJfwbuBJNjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "Z-EW7YYByh-A-Biyro4M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6US40l27xGVk9xU0Gj_K9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6US40l27xGVk9xU0Gj_K9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TmcW7YYBO2e_P_QbrDkF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lWGBthO0cXLKT_wGxBJl5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lWGBthO0cXLKT_wGxBJl5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6mcW7YYBO2e_P_QbsUPY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Va3LK8uFodhrLyRtybcuhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Va3LK8uFodhrLyRtybcuhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9OEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["531_Sc4IW-g1NnLnDZ_hAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["531_Sc4IW-g1NnLnDZ_hAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8uEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cAz0gk7brP4PWna-bhJGIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cAz0gk7brP4PWna-bhJGIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A-EW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K-Ytw62_KLFXRAkcUu6qRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K-Ytw62_KLFXRAkcUu6qRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BuEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OlDB0giXI1NsaTgwfP9dqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OlDB0giXI1NsaTgwfP9dqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "qOEW7YYByh-A-Biyspx4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hnpa0jil7FRY5KNbXbXjyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hnpa0jil7FRY5KNbXbXjyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "7-EW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "--EW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["41RJH9BALozcwHa5Gm2tSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["41RJH9BALozcwHa5Gm2tSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "AOEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cgbUcRDTpEjDrsHsz7--9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "844449768587301"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cgbUcRDTpEjDrsHsz7--9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "844449768587301"} {"create": {"_index": "profiling-events-all", "_id": "69gX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UsU0osaCNAmSunlpUc3Ozg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UsU0osaCNAmSunlpUc3Ozg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9WcX7YYBO2e_P_Qbh_pU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hpl7qJJwhIXHDYYdvHuzLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Hpl7qJJwhIXHDYYdvHuzLg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kuIX7YYByh-A-BiyiUZS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xqMKJtcmKXbh2cms887n-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xqMKJtcmKXbh2cms887n-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lGcX7YYBO2e_P_Qbh_zn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l_LfIb1A5Uh6akK6C3GVnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l_LfIb1A5Uh6akK6C3GVnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "5OIX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_AVql7KXMLg1O-JULbNgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_AVql7KXMLg1O-JULbNgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "VNgX7YYBBkbVtX3nin7J"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9i0d0snq2LSo5WLubtd6_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9i0d0snq2LSo5WLubtd6_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "V2gX7YYBO2e_P_QbiQGT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eX5L_3abHLPWPQF-_snJfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eX5L_3abHLPWPQF-_snJfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4tgX7YYBBkbVtX3ni4Pc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HnIE-9MlTVx0Ab-mshynaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HnIE-9MlTVx0Ab-mshynaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7WgX7YYBO2e_P_QbjANs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CWAxO1Icd_0_-O3aV3iUhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CWAxO1Icd_0_-O3aV3iUhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "k2cX7YYBO2e_P_Qbh_zn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ze3jQXtgwNkR6O4a_Nqg1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ze3jQXtgwNkR6O4a_Nqg1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5uIX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5tbIu8B2wKAudkUXTqytHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5tbIu8B2wKAudkUXTqytHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "0NgX7YYBBkbVtX3niXvV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zeLqMJxxpT2jsR6Xt4zqGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "L-IX7YYByh-A-Biyikgc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O9ef8dRV4l_MugfvQ0rYYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["O9ef8dRV4l_MugfvQ0rYYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "sOIX7YYByh-A-BiyikmM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7Swiq0tk_yociUJzvIr0Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7Swiq0tk_yociUJzvIr0Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3dzkAiyt1YVI-og1A_HKMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3dzkAiyt1YVI-og1A_HKMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJzVoueJRM62h7Ahq8M6SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJzVoueJRM62h7Ahq8M6SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "bOIX7YYByh-A-Biyi0uU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BeIW539VCuG8AbY5zkzibA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BeIW539VCuG8AbY5zkzibA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7eIX7YYByh-A-Biyh0Kg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pzJd5yzlB5VRPpGvz2d_9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pzJd5yzlB5VRPpGvz2d_9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WGgX7YYBO2e_P_QbiQGT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XC5SywLBeX1PQ5gC8i2e8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XC5SywLBeX1PQ5gC8i2e8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "U9gX7YYBBkbVtX3nin7J"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RGoTgpaa0vsxWtWSGraFrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RGoTgpaa0vsxWtWSGraFrA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "3NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dZVhEMwoIzMGD6Fthzhnhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dZVhEMwoIzMGD6Fthzhnhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "39gX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Egb8M192QRouZ1YPjNwqmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Egb8M192QRouZ1YPjNwqmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7mgX7YYBO2e_P_QbjANs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HFDZtj7y0Bw2k96K0Shk-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HFDZtj7y0Bw2k96K0Shk-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "8eIX7YYByh-A-BiyqWJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rNgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y9X7gkveuiKIarXoPu9Pow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y9X7gkveuiKIarXoPu9Pow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YeIX7YYByh-A-Biypl4D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wv08CwSYSbgcSoEXkrZnIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Wv08CwSYSbgcSoEXkrZnIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9GgX7YYBO2e_P_QbmxAt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_3hLenFHyAFyb6H7VmWWGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_3hLenFHyAFyb6H7VmWWGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "9WgX7YYBO2e_P_QbmxAt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Os-4RhVkjeRwXnMgi8sCPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Os-4RhVkjeRwXnMgi8sCPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "r9gX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w_gVd-AvWZmQ3WMy6t6XAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w_gVd-AvWZmQ3WMy6t6XAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "72gX7YYBO2e_P_Qbpxl-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hdc8gR_Y8kDXnRgAlQGfhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hdc8gR_Y8kDXnRgAlQGfhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "8GgX7YYBO2e_P_Qbpxl-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ttgX7YYBBkbVtX3np5HZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3QVerrpALkFsA-z-U___AA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3QVerrpALkFsA-z-U___AA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "emgX7YYBO2e_P_QbqRsl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g4wOsBXMok0GNueh82GdWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g4wOsBXMok0GNueh82GdWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "JtgX7YYBBkbVtX3nqZao"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["avHlGvNfTVzeaAgsVgxB6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["avHlGvNfTVzeaAgsVgxB6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ouIX7YYByh-A-BiynF0B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TuPnIZnhjIAYjeiVxPyaLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TuPnIZnhjIAYjeiVxPyaLQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "711845992008202"} {"create": {"_index": "profiling-events-all", "_id": "buIX7YYByh-A-BiymlgR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mIwFBWh1v3kx8u1FeFlbIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mIwFBWh1v3kx8u1FeFlbIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "seIX7YYByh-A-Biym1t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R1wuCeOIa20fh-d5eRRVFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["R1wuCeOIa20fh-d5eRRVFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZtgX7YYBBkbVtX3npo-z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["92cNuiuQKW3x7lS40O9Vmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["92cNuiuQKW3x7lS40O9Vmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b-IX7YYByh-A-BiyqGFv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-9_uQoUPE9EW73Ys_J5m3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-9_uQoUPE9EW73Ys_J5m3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9mgX7YYBO2e_P_QbmxAt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIxtnf4ZytccTyNG23fGog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AIxtnf4ZytccTyNG23fGog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "s9gX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qpqVuz6fzTFpU75L4AxuKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qpqVuz6fzTFpU75L4AxuKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "Z9gX7YYBBkbVtX3npo-z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["slgUvSTKxzBwaU847WWWjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["slgUvSTKxzBwaU847WWWjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "y-IX7YYByh-A-BiyqF8h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pxt9G8AauuDa281-G4uTWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "844449768587301"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pxt9G8AauuDa281-G4uTWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "844449768587301"} {"create": {"_index": "profiling-events-all", "_id": "KmgX7YYBO2e_P_QbnBSM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nWgX7YYBO2e_P_QbphZT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "8uIX7YYByh-A-BiyqWJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Q6w1HKYFAJALkbhmH-RHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Q6w1HKYFAJALkbhmH-RHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "G98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOolLKwTF6c0fdaMu4zrpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zOolLKwTF6c0fdaMu4zrpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5lorII3BQFhJxreg2edqjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5lorII3BQFhJxreg2edqjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "898U7YYByh-A-BiyusJq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdV0WYTy4PSs_A4me7FKqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AdV0WYTy4PSs_A4me7FKqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "-t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9UpR2U-Z66umiXz9ZVDmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9UpR2U-Z66umiXz9ZVDmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "lmUU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UutkxbmCiH9flxeQtiJBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UutkxbmCiH9flxeQtiJBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "It8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zhkr7FPwkPtUyVPXWQDkzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zhkr7FPwkPtUyVPXWQDkzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "F2UU7YYBO2e_P_QbvZyb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9N8U7YYByh-A-BiyusJq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0-Jsd5mQCWnt_-lPVIShHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0-Jsd5mQCWnt_-lPVIShHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "r9UU7YYBBkbVtX3nu_4f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8vj8M1UtdEZK08xJh31zdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8vj8M1UtdEZK08xJh31zdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "Id8U7YYByh-A-Biyv8mc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnBTQbU2oNTyfQ4d69ZrwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnBTQbU2oNTyfQ4d69ZrwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "-d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XgUh4zP6_HxjUL-1XhJT2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XgUh4zP6_HxjUL-1XhJT2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "A98U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jedq9pa2af0dW7deMw2jFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jedq9pa2af0dW7deMw2jFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lncubcdiqHSYqwQrDvrkCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lncubcdiqHSYqwQrDvrkCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "CN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4muA3nXQ7wRP5Hb8eGEGZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4muA3nXQ7wRP5Hb8eGEGZg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ntYU7YYBBkbVtX3nvAI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ieFm4DhmWNYMrTtIZLOTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4ieFm4DhmWNYMrTtIZLOTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fd8U7YYByh-A-Biyvcco"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzDNpxQnzmRTQIj87w61bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JzDNpxQnzmRTQIj87w61bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1tBKNWCTouiyLWmoA4fnhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1tBKNWCTouiyLWmoA4fnhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IxM2ggcVNlY2O-JpYzPXTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IxM2ggcVNlY2O-JpYzPXTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Kt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ijh2g5A8fvLXjeEqDoDNpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ijh2g5A8fvLXjeEqDoDNpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GN8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KaHxjoiTM1eQ6lx1DMgvTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KaHxjoiTM1eQ6lx1DMgvTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "l2UU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YZ6XTwSTsk_RRpTARdTTcg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YZ6XTwSTsk_RRpTARdTTcg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-98U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K9LDD5AZV4XmqBf_IoPXlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K9LDD5AZV4XmqBf_IoPXlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4N8U7YYByh-A-BiyyM0P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8Bx6QFlsny3BVfw-E8xnEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8Bx6QFlsny3BVfw-E8xnEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "u98U7YYByh-A-Biyyc_A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aAPXh-Ln7dsyIue7-chOWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aAPXh-Ln7dsyIue7-chOWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NtYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PDpZZR8qlUndvJSVZUQGfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PDpZZR8qlUndvJSVZUQGfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Gd8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_3rOpJzGPS7tGvMhQ90uyg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_3rOpJzGPS7tGvMhQ90uyg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "ndYU7YYBBkbVtX3nvAI_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BYfLgwssJN01WD8jqeu3Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BYfLgwssJN01WD8jqeu3Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "eWUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mjmL0Xb5ExHKk3gY3SfF8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mjmL0Xb5ExHKk3gY3SfF8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "IN8U7YYByh-A-Biyv8mc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y8na3ceZxIiFeB38FaoyuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["y8na3ceZxIiFeB38FaoyuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HRl0p2QhnLJg3zvMHmkZqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HRl0p2QhnLJg3zvMHmkZqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6t8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FN29r4iQqyKpKryFAHklNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FN29r4iQqyKpKryFAHklNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5QOVChXLsrqENbKSsGj8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5QOVChXLsrqENbKSsGj8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8N8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["an9gjQnke-IYWAGoKUs5KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["an9gjQnke-IYWAGoKUs5KQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j_ZkdluVAC4IXHAbI6Pmjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j_ZkdluVAC4IXHAbI6Pmjw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Ht8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XQeY0u1F2xnHmZQvstPXhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XQeY0u1F2xnHmZQvstPXhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Jt8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uIMM5HqMkglfbJ18Ml0GlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uIMM5HqMkglfbJ18Ml0GlQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Ht8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0TcViARYA_NarblNdiqUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g0TcViARYA_NarblNdiqUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "lWUU7YYBO2e_P_QbyKOD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OxVykxLrjAY-XgNQtErYDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OxVykxLrjAY-XgNQtErYDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "KtYU7YYBBkbVtX3nygdO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQfZAh-DQHDVJDhrdQQeqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UQfZAh-DQHDVJDhrdQQeqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "DWUU7YYBO2e_P_Qbvp4b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whLFrJ0C3L5ID9FEmIKmhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["whLFrJ0C3L5ID9FEmIKmhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYTaEZ21SPKDnDxndpockQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FYTaEZ21SPKDnDxndpockQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "AN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyslIhEYrdCY7Y2kR4LC4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hyslIhEYrdCY7Y2kR4LC4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "Kd8U7YYByh-A-Biyu8Z1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "LN8U7YYByh-A-BiywMu7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mwUpd0imVLBffXq6CKbujA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mwUpd0imVLBffXq6CKbujA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "6d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NQzAAYItWlUR8Wx0iQghsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NQzAAYItWlUR8Wx0iQghsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "398U7YYByh-A-BiyyM0P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RLske_-faZ7wKdYb3WXphQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nt8U7YYByh-A-Biyy9Ef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "fWUU7YYBO2e_P_QbvqCh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PVlyLKXyb8x0uLNYAPexSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PVlyLKXyb8x0uLNYAPexSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "9d8U7YYByh-A-BiywMq7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7I_OssEt4qZsJxTrqNd4gQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7I_OssEt4qZsJxTrqNd4gQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "Ft8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8h10fs0ddiOcVgnyW4Tl_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8h10fs0ddiOcVgnyW4Tl_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "Gt8U7YYByh-A-Biyx8yq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jsGoGFJd_KwHDVlL9hbdSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014803"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jsGoGFJd_KwHDVlL9hbdSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "55734071622694"} {"create": {"_index": "profiling-events-all", "_id": "0t8U7YYByh-A-Biy1-BK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["67AU8cgiv2OiIR5ejtdmRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["67AU8cgiv2OiIR5ejtdmRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Vt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQ1nyOGbOtedL7gx4fO8XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zQ1nyOGbOtedL7gx4fO8XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BDM5FGocWkrUljGSyVLiPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BDM5FGocWkrUljGSyVLiPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "iN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cW8t-wBFVbXPMN_YH8nydw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cW8t-wBFVbXPMN_YH8nydw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j6Z5oRx4O63IFM67ZJuuJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j6Z5oRx4O63IFM67ZJuuJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0t8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28hHkhN7jPc2yLRpJAYfPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["28hHkhN7jPc2yLRpJAYfPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "x2UU7YYBO2e_P_Qb38OV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nWUjPDlBGs10DeEAyhYVTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b9YU7YYBBkbVtX3n2BjO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Xd2WZFqlgKVx01Ohrr1dQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Xd2WZFqlgKVx01Ohrr1dQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Zt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["beuv2u9oMMhwQHihFlStkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["beuv2u9oMMhwQHihFlStkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rmUU7YYBO2e_P_Qb3r-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bNNll9gtsumikBQkeP5zYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bNNll9gtsumikBQkeP5zYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0N8U7YYByh-A-Biy1-BK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K3Z7Bso8_acxSu6Vxdfbjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K3Z7Bso8_acxSu6Vxdfbjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "wt8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HcQSqbXhDJcv-dVT41RQ6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HcQSqbXhDJcv-dVT41RQ6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "zd8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EruDr_ih7XLGuzv_u8mEQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EruDr_ih7XLGuzv_u8mEQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "0N8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWfVfitdsTIFX4dhe6CakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xWfVfitdsTIFX4dhe6CakA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "72UU7YYBO2e_P_Qb3L3V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H2vgTPpm8BMcHhsujCAFSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H2vgTPpm8BMcHhsujCAFSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "WN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mdw3BiJM92OOtEHXgQMjkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mdw3BiJM92OOtEHXgQMjkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "aN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-tJlKr_KhSmekGKYSh387Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-tJlKr_KhSmekGKYSh387Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "bN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2bYjKMpMW5W361PJ9SbEqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2bYjKMpMW5W361PJ9SbEqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "cd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XmdrI-QkL3G1KMx-UT00Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XmdrI-QkL3G1KMx-UT00Dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "c98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3wmGM9d6YoSoIyBMvtxpaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3wmGM9d6YoSoIyBMvtxpaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ht8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7lZc2qqwTOxuwAsl_tPb5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7lZc2qqwTOxuwAsl_tPb5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "jN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qDosqYNWqMjeDR-l1Za_TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qDosqYNWqMjeDR-l1Za_TQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "kt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5hG8KKglQrQ3G7KSXA2QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["j5hG8KKglQrQ3G7KSXA2QQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "od8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Uz66tx2I5JTSXA6CNdimw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-Uz66tx2I5JTSXA6CNdimw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "89YU7YYBBkbVtX3n3h9C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Nr_8hMt7lL3ObaXhoWtKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_Nr_8hMt7lL3ObaXhoWtKw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "9NYU7YYBBkbVtX3n3h9C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzPOzTEXdQzPan7rC__T_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzPOzTEXdQzPan7rC__T_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "r2UU7YYBO2e_P_Qb3r-v"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uWH1YJMiRNhCnBrl6NfCMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "h2UU7YYBO2e_P_Qb38Ek"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydmNLaPNVcV_2d5DkMu7ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ydmNLaPNVcV_2d5DkMu7ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "LN8U7YYByh-A-Biy3-v-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G81V791m7uA9YBPgoQEn8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G81V791m7uA9YBPgoQEn8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "V9YU7YYBBkbVtX3n4CG5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uh-jwsuxuUYFlAJ62euRwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uh-jwsuxuUYFlAJ62euRwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "btYU7YYBBkbVtX3n2BjO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcGz4984lW_7ADxWvMJl6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcGz4984lW_7ADxWvMJl6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "cN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ilIjnGLprZJBf-XmwKk7UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ilIjnGLprZJBf-XmwKk7UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "lNYU7YYBBkbVtX3n2h7w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["799eAdJjPYE03w7zg0dmIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["799eAdJjPYE03w7zg0dmIQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "xtYU7YYBBkbVtX3n1xTG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TQ4Z-jiPS9ERtxr-dNMVPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TQ4Z-jiPS9ERtxr-dNMVPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ItYU7YYBBkbVtX3n2hoO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4f9KZiG-idTZu0O-sRt4aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4f9KZiG-idTZu0O-sRt4aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wd8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z00iShXdXFb3_lRNuX4ZQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Z00iShXdXFb3_lRNuX4ZQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xd8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NXFmJwy8bX4T3TBtUWzk4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NXFmJwy8bX4T3TBtUWzk4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dt8U7YYByh-A-Biy3edS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rG147l1B0EGMuLS3fy86lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rG147l1B0EGMuLS3fy86lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ACT7gU2GPCwMpgWEOyi5HQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ACT7gU2GPCwMpgWEOyi5HQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aUWb9UKO7qICsUy_ccfdaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aUWb9UKO7qICsUy_ccfdaQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "o98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["val5lb3yDclirfA_QdK7Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["val5lb3yDclirfA_QdK7Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vWUU7YYBO2e_P_Qb4MZd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96g1R53V5QyPuXTUHSgw4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["96g1R53V5QyPuXTUHSgw4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WdYU7YYBBkbVtX3n4CG5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6IN4ndcB5qmSJYNzvpVbgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6IN4ndcB5qmSJYNzvpVbgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Wd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2WgjTSIQCP6U6Q-JjUia1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2WgjTSIQCP6U6Q-JjUia1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WJtwE6C4KDOaEo17zelbiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WJtwE6C4KDOaEo17zelbiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "m98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1W94d88mnm9x39d54400ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1W94d88mnm9x39d54400ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yt8U7YYByh-A-Biy3enR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kqSFQUO3VSzNPTrQP20mfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kqSFQUO3VSzNPTrQP20mfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rWUU7YYBO2e_P_Qb3r-v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7RMgnGzd9pjT-Nh8jG3zbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7RMgnGzd9pjT-Nh8jG3zbw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "lN8U7YYByh-A-Biy4ewW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vSHgan70C0hkYZy36mxqBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vSHgan70C0hkYZy36mxqBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "yN8U7YYByh-A-Biy2-RY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F_APHoeVxOWNqwDMoBgBUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["F_APHoeVxOWNqwDMoBgBUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "eN8U7YYByh-A-Biy3edS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eZEQXM7WYfQLn99tFhWnyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eZEQXM7WYfQLn99tFhWnyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "k98U7YYByh-A-Biy4ewW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ROD9hyXKyG1xyIp3eNp24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ROD9hyXKyG1xyIp3eNp24A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "b98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Y5-o0gkUhbrP54-KmzBRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Y5-o0gkUhbrP54-KmzBRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "k98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jxx94k5bF0AyU24TvMCnFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jxx94k5bF0AyU24TvMCnFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WNYU7YYBBkbVtX3n4CG5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kCi3XJtF81OLZhjrXcqzHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kCi3XJtF81OLZhjrXcqzHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "098U7YYByh-A-Biy1-BK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JnSKFHek1VX4hQrcBvK6Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "dmUU7YYBO2e_P_Qb3LtA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wVxfeW31BKBlFSOTuEq2vg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wVxfeW31BKBlFSOTuEq2vg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "Zd8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ryfu5a--oA0HxtDhUCtdpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ryfu5a--oA0HxtDhUCtdpg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "j98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsjTmLeWgJZGEXKE2bGYPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nsjTmLeWgJZGEXKE2bGYPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "lt8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zLdPokHD2Z2SVrMjPVZbgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zLdPokHD2Z2SVrMjPVZbgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "xGUU7YYBO2e_P_Qb38OV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HSqoNRZZIrgV8Hc05ks5og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HSqoNRZZIrgV8Hc05ks5og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "f98U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y3XyMSK9tPI3_U0zY2ps0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y3XyMSK9tPI3_U0zY2ps0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "rN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yPsp3gldnYluQE1Il8N2GA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yPsp3gldnYluQE1Il8N2GA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "pN8U7YYByh-A-Biy3enQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GzMkMHSbJB6nV1XM7_SYKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014810"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GzMkMHSbJB6nV1XM7_SYKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "428437761470762"} {"create": {"_index": "profiling-events-all", "_id": "7dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WvJZCAk7gVxCX3Q5TFv5cQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WvJZCAk7gVxCX3Q5TFv5cQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pXACL2-jPjXQBG18kGP3iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pXACL2-jPjXQBG18kGP3iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ftYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zyPUjDErN9KDQ5m99X0sAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zyPUjDErN9KDQ5m99X0sAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h9YU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ePjKQtLllV_2B6Oq3TJ35Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ePjKQtLllV_2B6Oq3TJ35Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2KQ1xLodxTiqHmDQYXbmJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2KQ1xLodxTiqHmDQYXbmJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "V9YU7YYBBkbVtX3n-UE3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vBuMYbV0eX1rnNuqJvyNWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vBuMYbV0eX1rnNuqJvyNWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NtYU7YYBBkbVtX3n-UN7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tnEGCyYPY-9Dy4jeOy-iBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tnEGCyYPY-9Dy4jeOy-iBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "It8U7YYByh-A-Biy6_ul"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAgkJzf4StR0guQvtrxwfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UAgkJzf4StR0guQvtrxwfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qtYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fZs8SW_-AMd6brkAp6VfIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fZs8SW_-AMd6brkAp6VfIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "edYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZsYEzt_8lrTbaZDB8kywA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZsYEzt_8lrTbaZDB8kywA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "itYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DWfScUV2_2OCeYx4zWNovQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "mNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b9bN4B0y0HQxr-zG0AhOUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b9bN4B0y0HQxr-zG0AhOUQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "otYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IoJlfiMVuBuG6DfHS2d0IA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IoJlfiMVuBuG6DfHS2d0IA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "uNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-PWxSiTmjhtgtcqWr-cUtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-PWxSiTmjhtgtcqWr-cUtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "zNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SjE2Ni6CAQyLI_0LOuh-XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SjE2Ni6CAQyLI_0LOuh-XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "zdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Sx2U9dpgshF4QL4T5gZ6MQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Sx2U9dpgshF4QL4T5gZ6MQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ztYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["40e3Tg8yjxNKy4P6BuWO-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["40e3Tg8yjxNKy4P6BuWO-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "2NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WNKWldEDEQV0rRa3BHpz0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WNKWldEDEQV0rRa3BHpz0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "3dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iJXWguOQwBM8DxQXGl57jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iJXWguOQwBM8DxQXGl57jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "-NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hLX1mOZZ4FB_ggjZCD1W-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hLX1mOZZ4FB_ggjZCD1W-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D9uEZQLKh57x4BtzNglutA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D9uEZQLKh57x4BtzNglutA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "0dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ViC03sje9QXvOY-ekeiy4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ViC03sje9QXvOY-ekeiy4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "i98U7YYByh-A-Biy9_8o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFwqiF8mGnNqqISBiOraPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFwqiF8mGnNqqISBiOraPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OoP9hAiZoGUDEMy64jET7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OoP9hAiZoGUDEMy64jET7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aFWcqgahvwzy1xUY69A0Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aFWcqgahvwzy1xUY69A0Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHgSIcaSuS6XNpC67kiXTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GHgSIcaSuS6XNpC67kiXTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z6VzC1_Xu_H2TiDjzjPdmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z6VzC1_Xu_H2TiDjzjPdmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ANYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lo1FK0cOT4jUm2Qr6L-02w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lo1FK0cOT4jUm2Qr6L-02w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lNYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-B1OuisGq94rIWOaaG-QZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-B1OuisGq94rIWOaaG-QZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IDJuAvr20i1MdkeJctVLcg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IDJuAvr20i1MdkeJctVLcg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4dYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PKfrUz68RAX4mdNriJ73lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PKfrUz68RAX4mdNriJ73lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dRBdj6fqBaNFs9qEBG9D-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dRBdj6fqBaNFs9qEBG9D-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ktYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y8nvfrvenFH8tjnsQqRmig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y8nvfrvenFH8tjnsQqRmig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "BmUU7YYBO2e_P_Qb-NOw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VQ0HdY-PXTuyjNIK6sm3RQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VQ0HdY-PXTuyjNIK6sm3RQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "bdYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ju1Y8z3O5D2e-y3UCauHLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ju1Y8z3O5D2e-y3UCauHLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6NYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["66YyzrRAaK1eflQF_FbcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["66YyzrRAaK1eflQF_FbcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "-tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LA1QfR7E7BQq2NnqmNTjFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LA1QfR7E7BQq2NnqmNTjFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "AtYU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kks8edTwYqrUkhTSOKMQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kks8edTwYqrUkhTSOKMQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "B9YU7YYBBkbVtX3n9z6H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["amHFhXVq-o4KXgR6R1r-Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["amHFhXVq-o4KXgR6R1r-Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ldYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "_tYU7YYBBkbVtX3n9z2H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LpjlQ5x3C5y0GS9aUsntg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014816"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LpjlQ5x3C5y0GS9aUsntg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "jdYV7YYBBkbVtX3nBk4o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JI7mG0vgGSTA2uia9-1jSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JI7mG0vgGSTA2uia9-1jSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "q-AV7YYByh-A-BiyBg34"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["boIzddYopai9UjphB37nhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["boIzddYopai9UjphB37nhQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PtYV7YYBBkbVtX3nDFzR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YEq1JWlDmlwTpiy46PgDqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YEq1JWlDmlwTpiy46PgDqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "m-AV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EiP_fqJ5eainxyo48aQcOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EiP_fqJ5eainxyo48aQcOQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dOAV7YYByh-A-BiyCRh4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r1HvPKUhWfo1c_dGIcqb1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r1HvPKUhWfo1c_dGIcqb1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kNYV7YYBBkbVtX3nBk4o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fdWGYIeNIIrvl5yNTWQFCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fdWGYIeNIIrvl5yNTWQFCA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "DmUV7YYBO2e_P_QbBuCR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BhvORIoUEUvqKKPPz94jvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "F-AV7YYByh-A-BiyBxBv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wUfvGFMhsPYCiR2iraE6yA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wUfvGFMhsPYCiR2iraE6yA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "_NYV7YYBBkbVtX3nCVTP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PwiymugfyWZ7JNBkVfJTzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PwiymugfyWZ7JNBkVfJTzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "gdYV7YYBBkbVtX3nC1gN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fhL78fB6ht38oYP9R7oGng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fhL78fB6ht38oYP9R7oGng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "7-AV7YYByh-A-BiyDB-G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "5tYV7YYBBkbVtX3nDV0h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T_m8wOPYHgqUseziTFic-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T_m8wOPYHgqUseziTFic-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "nOAV7YYByh-A-BiyDSFn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZvW02WsFdrDb2uuQD6AqIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZvW02WsFdrDb2uuQD6AqIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "JuAV7YYByh-A-BiyCxxq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lfg5-StCmQRLLhdxDtBcFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Lfg5-StCmQRLLhdxDtBcFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OdYV7YYBBkbVtX3nC1r7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lAXP_bDkVl41XjIqstP9YA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lAXP_bDkVl41XjIqstP9YA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P9YV7YYBBkbVtX3nDFzR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFOpEBwKNMGOTnHzKtdQnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pFOpEBwKNMGOTnHzKtdQnQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5dYV7YYBBkbVtX3nDV0h"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1StoQg7J8aPbo68sE2e2FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1StoQg7J8aPbo68sE2e2FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FuAV7YYByh-A-BiyBxBv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7dZ7-R85Uk0iMtgooj6v_Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_tYV7YYBBkbVtX3nCVTP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o02UcgaTacPmYjOwwPOCJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o02UcgaTacPmYjOwwPOCJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "quAV7YYByh-A-BiyBg34"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNwCIBshkIMvUtAdsIyUXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RNwCIBshkIMvUtAdsIyUXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} {"create": {"_index": "profiling-events-all", "_id": "jtYV7YYBBkbVtX3nBk4o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P_F4N85n6ygrRQ1ObfKSJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P_F4N85n6ygrRQ1ObfKSJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LdYV7YYBBkbVtX3nCFPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VrBz5ulfwdPTqnMaGIpcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VrBz5ulfwdPTqnMaGIpcBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "qtYV7YYBBkbVtX3nClae"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MWfc397MJFynjmcnyAtS1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MWfc397MJFynjmcnyAtS1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gNYV7YYBBkbVtX3nC1gN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XB2RBcspSf2rnOrxNGF7Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XB2RBcspSf2rnOrxNGF7Mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "w-AV7YYByh-A-BiyCx2w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP1YbEn43FveSGHDAeyzEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aP1YbEn43FveSGHDAeyzEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "O9YV7YYBBkbVtX3nC1r7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SsB_u-p6-kmstFPsLe9XQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SsB_u-p6-kmstFPsLe9XQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "D2UV7YYBO2e_P_QbBuCR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DU2gNL0dqNkZNb3bXXutHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DU2gNL0dqNkZNb3bXXutHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "rOAV7YYByh-A-BiyBg34"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TNDnc_BbX-bZvBLeZE3IhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014821"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TNDnc_BbX-bZvBLeZE3IhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "NGUV7YYBO2e_P_QbGelA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hSPX2ocR_Ka7dmSG_0BvUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hSPX2ocR_Ka7dmSG_0BvUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MeAV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8T064malfbI6yltLIiW-8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8T064malfbI6yltLIiW-8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "c9YV7YYBBkbVtX3nFmEP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RacUKMIrwwT1y_qD2hDfpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RacUKMIrwwT1y_qD2hDfpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LeAV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pDq0KLS_CVlPqSda6JpGZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pDq0KLS_CVlPqSda6JpGZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "B2UV7YYBO2e_P_QbF-SC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OLDYY6fj7GShTOkVXzydtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "28424007785283"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OLDYY6fj7GShTOkVXzydtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "28424007785283"} {"create": {"_index": "profiling-events-all", "_id": "MOAV7YYByh-A-BiyHDPM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UlF3sWH6N3gcr5OTBdjCWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UlF3sWH6N3gcr5OTBdjCWg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZWUV7YYBO2e_P_QbGOeB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch1MSb9N9bTihIUdmamkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ch1MSb9N9bTihIUdmamkLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yGUV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uVz8NwCzYiroPS8zol4cYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uVz8NwCzYiroPS8zol4cYQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "x2UV7YYBO2e_P_QbGuqP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["26eMbIowZ7RFzGdD2uFyMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014826"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["26eMbIowZ7RFzGdD2uFyMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "wdYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H2NuFpd57ieo26ztmYwFIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H2NuFpd57ieo26ztmYwFIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PUKA7gaaH9QtcEUOhnkXBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PUKA7gaaH9QtcEUOhnkXBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "19YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LadRZ9nrWUWtpCeBiU-rCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LadRZ9nrWUWtpCeBiU-rCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pGUV7YYBO2e_P_QbNfql"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AqW4O0lT_YcKP7qw6bzS2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AqW4O0lT_YcKP7qw6bzS2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "140556023255829"} {"create": {"_index": "profiling-events-all", "_id": "3dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7WB2ChzRmsP63O7cEov2qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7WB2ChzRmsP63O7cEov2qw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "kGYV7YYBO2e_P_QbOALl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["53PCQ4S8hGae7xDUxkptJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["53PCQ4S8hGae7xDUxkptJg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "ydYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yLwY_MOsydDU7XEbyC_1EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yLwY_MOsydDU7XEbyC_1EQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y9YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qVW6zqZFUDf4jmIJtsdFjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qVW6zqZFUDf4jmIJtsdFjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "5dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_NYV7YYBBkbVtX3nN4vV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zwhp9WeyhfaKSBb1ToTrGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zwhp9WeyhfaKSBb1ToTrGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "09YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f0fqU4EglvDX7hh3PMNsxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["f0fqU4EglvDX7hh3PMNsxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u6l9MuBKqlPDG0c4BbLwSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["u6l9MuBKqlPDG0c4BbLwSw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "2NYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5ViqVqqhAWPiT5DHT3ocA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5ViqVqqhAWPiT5DHT3ocA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "-9YV7YYBBkbVtX3nNolA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4dYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0JrvqOGSV5sSkPZVHmV-1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0JrvqOGSV5sSkPZVHmV-1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "v9YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XI0a5uYy8WGcbycZNNF9pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XI0a5uYy8WGcbycZNNF9pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "xNYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TaW85eSCND8M8sXCtd--5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TaW85eSCND8M8sXCtd--5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ytYV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "29YV7YYBBkbVtX3nK4JW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "-GUV7YYBO2e_P_QbNvuJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pNtMkp20SCCEh-TxrA7W_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pNtMkp20SCCEh-TxrA7W_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7-AV7YYByh-A-BiyOEia"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iAyna-aTAn1PsVqMhzzlmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "UdYV7YYBBkbVtX3nNYhc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TNDnc_BbX-bZvBLeZE3IhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014832"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TNDnc_BbX-bZvBLeZE3IhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "V-AV7YYByh-A-BiyRFH8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_4SSZ-fDRU6dq-MfFWxOng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_4SSZ-fDRU6dq-MfFWxOng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "bWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bX4y30xaW5TGzCNkEXdvXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bX4y30xaW5TGzCNkEXdvXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lBfh2StrmCD3agR-LjX8jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lBfh2StrmCD3agR-LjX8jA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "otYV7YYBBkbVtX3nRpQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LiSJgYaS_IuBq_4ymnNLrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LiSJgYaS_IuBq_4ymnNLrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "vWYV7YYBO2e_P_QbSRHb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RSnmXvhjCRc5PWjsdxAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RSnmXvhjCRc5PWjsdxAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "b2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hicFPnqcgI-QATM_d1RRhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hicFPnqcgI-QATM_d1RRhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_zNN2R6gCnlCmrGYYAK4_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_zNN2R6gCnlCmrGYYAK4_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5uAV7YYByh-A-BiyTFeK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wg4weiqhx4cQSZpZOkpJ7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wg4weiqhx4cQSZpZOkpJ7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "aGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YtaoZwIzTcxhkVhNaJ4ybg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YtaoZwIzTcxhkVhNaJ4ybg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "cmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a3rA5HVT6PyGXCEVq07mnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a3rA5HVT6PyGXCEVq07mnw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-P0Vso9aXjNnbKarUe02Qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-P0Vso9aXjNnbKarUe02Qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "jGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9i4bcor3bCmUHnFwLkINw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9i4bcor3bCmUHnFwLkINw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "oGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NnEd2rdWIzQh3lzvczhnrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NnEd2rdWIzQh3lzvczhnrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EuAV7YYByh-A-BiyRlNc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9be6nEEjsc2_ia_Ui8XKOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9be6nEEjsc2_ia_Ui8XKOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "FmYV7YYBO2e_P_QbSA5P"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_CuNkg8IjplIBsYDC3MqZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_CuNkg8IjplIBsYDC3MqZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "fmYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OJQp-pLhhKtUabxqe1o08Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OJQp-pLhhKtUabxqe1o08Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "k2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7I7fZ1n3NXikDC-SAVTDhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7I7fZ1n3NXikDC-SAVTDhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ZWYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CFNczi5jgqdp9YJbvPCa9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CFNczi5jgqdp9YJbvPCa9g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "gGYV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fFdYACc4s_4eKR1XWC7l8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fFdYACc4s_4eKR1XWC7l8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "i2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-KuDdfusItDSRJVlZWeuIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-KuDdfusItDSRJVlZWeuIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "n2YV7YYBO2e_P_QbRQpV"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014837"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "9uAV7YYByh-A-BiyVFzv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JrtGgsej_ZvMvypTCZY9mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JrtGgsej_ZvMvypTCZY9mg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KOAV7YYByh-A-BiyWGBd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YoeXFhmTkr3_l-fmzIYzFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YoeXFhmTkr3_l-fmzIYzFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Q2YV7YYBO2e_P_QbWSCB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJPjpnk4438S9AxhBdL7Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJPjpnk4438S9AxhBdL7Og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BmYV7YYBO2e_P_QbWyI8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DU1r9dRWaA73O7zRX_V2Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DU1r9dRWaA73O7zRX_V2Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xNYV7YYBBkbVtX3nVKlD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h9g7rZTa7cbOgmdfpCroqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h9g7rZTa7cbOgmdfpCroqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-mYV7YYBO2e_P_QbWR0q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SpBP0MEtp9HLwHtyNGRBfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SpBP0MEtp9HLwHtyNGRBfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0OAV7YYByh-A-BiyVFqd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b782ua3hr4B0mzdz6X7qIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b782ua3hr4B0mzdz6X7qIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GGYV7YYBO2e_P_QbWBoF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Sz3zcn_jRruHSw5ug1i1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Sz3zcn_jRruHSw5ug1i1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ueAV7YYByh-A-BiyWmE2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Sfb50IP_djC3GCW2v6RYMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Sfb50IP_djC3GCW2v6RYMA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Z9YV7YYBBkbVtX3nV7Q-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dYYgiy_hJlJmGi14KCzeng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dYYgiy_hJlJmGi14KCzeng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "aNYV7YYBBkbVtX3nV7Q-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z5-B-mtdUNg5G8Toj1uZ9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z5-B-mtdUNg5G8Toj1uZ9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "CGYV7YYBO2e_P_QbWyI8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["shWMJLfbuiijw_CV7zD8MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["shWMJLfbuiijw_CV7zD8MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "3tYV7YYBBkbVtX3nWbXf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q-t0WQ1QPG_465StwGrNQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q-t0WQ1QPG_465StwGrNQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "OGYV7YYBO2e_P_QbWBy9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VhuOZfIjfkTp0PdE7E7l0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VhuOZfIjfkTp0PdE7E7l0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "GWYV7YYBO2e_P_QbWBoF"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014842"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jphq2mADJdPqQSMJRmqCfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "vWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A998Aw2wlvaHmpTDQoJVWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A998Aw2wlvaHmpTDQoJVWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zWYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eYJ3KKpVqQxoShfKUyVbPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eYJ3KKpVqQxoShfKUyVbPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gNYV7YYBBkbVtX3ndcoT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_DergfmxCZYVsT8aG5xQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_DergfmxCZYVsT8aG5xQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0WYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["21VADknXj310Vq9ESNjcWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["21VADknXj310Vq9ESNjcWw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YmYV7YYBO2e_P_QbaTbR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8tq3ImbcqHoL1eNze2XmnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8tq3ImbcqHoL1eNze2XmnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "umYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7izJ-LV-AEcodCtu0-YXBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7izJ-LV-AEcodCtu0-YXBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HNYV7YYBBkbVtX3nasl7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zy8I_mLxkUqRNobY73aLCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2XkIPoT1RGScJv7HcNexyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2XkIPoT1RGScJv7HcNexyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "0GYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_U__gC97iLDLPRg-7bXig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H_U__gC97iLDLPRg-7bXig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "EGYV7YYBO2e_P_Qbczvj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h15-mwdtFf1_Tp_C0u_H4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h15-mwdtFf1_Tp_C0u_H4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "uGYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c_YW_y8Erj_86DJCOJ5hiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c_YW_y8Erj_86DJCOJ5hiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "u2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-sR7gULJJ6wMZ9ZddAv4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-sR7gULJJ6wMZ9ZddAv4g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "x2YV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oe_nHyIGjMEfD9kwUevsMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oe_nHyIGjMEfD9kwUevsMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "TNYV7YYBBkbVtX3naMf3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["itGspDeyDyUECCgRGfTEng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["itGspDeyDyUECCgRGfTEng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "tmYV7YYBO2e_P_QbaTSA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014848"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JOAV7YYByh-A-Biyg4Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LC6mVf6FPr_kqWjuiJLTRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LC6mVf6FPr_kqWjuiJLTRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gOAV7YYByh-A-BiyhIIL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0mM0lZYG8GYmeCMXMbETOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0mM0lZYG8GYmeCMXMbETOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "S2YV7YYBO2e_P_Qbh0uD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVUJWL9ZnL1p_YLKqzUSFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OVUJWL9ZnL1p_YLKqzUSFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P-AV7YYByh-A-BiyhIRV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Va3LK8uFodhrLyRtybcuhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Va3LK8uFodhrLyRtybcuhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "I-AV7YYByh-A-Biyg4Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5hO63TnTaHm6rWDJ9tLlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o5hO63TnTaHm6rWDJ9tLlg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "u2YV7YYBO2e_P_Qbh0lI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LdYV7YYBBkbVtX3nhd1U"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CEu7QDQCpna6AMKIewlkmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CEu7QDQCpna6AMKIewlkmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-all", "_id": "cuAV7YYByh-A-BiyhoV2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QIUi7r0lX0T7lggo-V8-5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QIUi7r0lX0T7lggo-V8-5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "l2YV7YYBO2e_P_QbhEb-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vTYOA5_SnOZSwiGON798-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vTYOA5_SnOZSwiGON798-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "6dYV7YYBBkbVtX3nh-MJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n1Ma-6R1TuGNeT2qh3rpLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014854"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n1Ma-6R1TuGNeT2qh3rpLA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "WdYV7YYBBkbVtX3nov24"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZfWmwYaJIIOUGCRBPlr6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZfWmwYaJIIOUGCRBPlr6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BtYV7YYBBkbVtX3no_91"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_cwyhujbNFnjVbOtCoyQwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_cwyhujbNFnjVbOtCoyQwg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "9tYV7YYBBkbVtX3nlvPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OD4R30IpW0nvOt_G6qR8Lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014860"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OD4R30IpW0nvOt_G6qR8Lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0eAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p0Pia_VKvNIpcjOrg5PBFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p0Pia_VKvNIpcjOrg5PBFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1eAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PbvkExxuXir8i2DmyuUgsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PbvkExxuXir8i2DmyuUgsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tWYV7YYBO2e_P_QbuHEg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NkZgcPyvanvZxrwD91jJQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NkZgcPyvanvZxrwD91jJQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "N2YV7YYBO2e_P_Qbsmir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r1HvPKUhWfo1c_dGIcqb1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r1HvPKUhWfo1c_dGIcqb1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "T-AV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZPTQfDgXmRrvRJctvBjMQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZPTQfDgXmRrvRJctvBjMQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VjZoTTtVYbpedfOtHXez9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VjZoTTtVYbpedfOtHXez9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "V-AV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0i9UUQjQ7yqS-rcTUC_StQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0i9UUQjQ7yqS-rcTUC_StQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4WYV7YYBO2e_P_Qbsmn3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kIp3c6lhOmVwD-TdMXCwzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kIp3c6lhOmVwD-TdMXCwzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1OAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X27L9SIqnKudu5fjFY2qfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X27L9SIqnKudu5fjFY2qfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JGYV7YYBO2e_P_Qbs23k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9PStVBMpTyifWDjuM_1F9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9PStVBMpTyifWDjuM_1F9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rmYV7YYBO2e_P_QbtW5b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUuQ2lSMaZyy3BCVVsDF1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dUuQ2lSMaZyy3BCVVsDF1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fdcV7YYBBkbVtX3ntRfi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pq-E12uy1vBeK4HeCjUqKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pq-E12uy1vBeK4HeCjUqKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SdcV7YYBBkbVtX3nthkq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjtKZoprzXCLbmVAEEoqNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QjtKZoprzXCLbmVAEEoqNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "zNcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vF_VQLSiCrZAF-ltqCX4Kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vF_VQLSiCrZAF-ltqCX4Kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Z9cV7YYBBkbVtX3nsgwF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GqY4_lTSGhEb3OIQFssjmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GqY4_lTSGhEb3OIQFssjmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VtcV7YYBBkbVtX3nsg5Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B3eCfffgWDywlQf98in5qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B3eCfffgWDywlQf98in5qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4GYV7YYBO2e_P_Qbsmn3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sizNQvpwrsYG1iwjQh48UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "XuAV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oWsHJQGRgZYwHwaOMaB07w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oWsHJQGRgZYwHwaOMaB07w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "rWYV7YYBO2e_P_QbtW5b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jPOL2BCon4p4v0UyqfsXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5jPOL2BCon4p4v0UyqfsXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "1uAV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8eRxSiE_6KOXeGPJZDEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8eRxSiE_6KOXeGPJZDEAg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "RdcV7YYBBkbVtX3nthkq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["me0oRgVcR_uBmJ_kCe-VfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["me0oRgVcR_uBmJ_kCe-VfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LGYV7YYBO2e_P_QbtnB5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YLcFpzDQdtlqJCOlCEyl9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YLcFpzDQdtlqJCOlCEyl9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6tcV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3h9kpMSl0a0kJr7xr2rPxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3h9kpMSl0a0kJr7xr2rPxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "y9cV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "z9cV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MxaBJ5vAlZJbFL1ZFA-tNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MxaBJ5vAlZJbFL1ZFA-tNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "09cV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lX6_U4PFMyyAKyc5PyNFEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lX6_U4PFMyyAKyc5PyNFEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VdcV7YYBBkbVtX3nsg5Y"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1m76_MQ1CKkUeXqbKRoHZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1m76_MQ1CKkUeXqbKRoHZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "OGYV7YYBO2e_P_Qbsmir"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9ECosqumaYYOVTFlJRp6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9ECosqumaYYOVTFlJRp6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "gGYV7YYBO2e_P_Qbs2tB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9BGZHTzs6oj_j1YiF-r26w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9BGZHTzs6oj_j1YiF-r26w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YuAV7YYByh-A-BiytqnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPIMyWIkL5MsP1Bo20O32w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IPIMyWIkL5MsP1Bo20O32w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "MOAV7YYByh-A-Biyt6sa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JHoQWviQB3DglItLg3z8Dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JHoQWviQB3DglItLg3z8Dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "59cV7YYBBkbVtX3ntxpc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8UN7adkU7fT1ZBcxBGzmmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8UN7adkU7fT1ZBcxBGzmmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ztcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["niPAiGls6k32DnDasicdew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["niPAiGls6k32DnDasicdew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "itcV7YYBBkbVtX3nuB51"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yuDdBF0iwQiPnskDDqWYwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yuDdBF0iwQiPnskDDqWYwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "19cV7YYBBkbVtX3nsw-Q"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x1DopX-Mm-f8qb0DCkjPyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x1DopX-Mm-f8qb0DCkjPyA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "RtcV7YYBBkbVtX3nthkq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_9EUaNCl3IuE7tIxwFYMuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "0dcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XMBO6mK2eUEy_2zoVmK7iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XMBO6mK2eUEy_2zoVmK7iw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "W-AV7YYByh-A-BiytKZu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["860tvNw0EZMCDcPC0s5-KA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["860tvNw0EZMCDcPC0s5-KA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "0NcV7YYBBkbVtX3ntxyk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W24Y25ivMwuM7NhKCx2-SQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "1-AV7YYByh-A-Biytaeh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8P7h-qet3p603NxSf5JWTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8P7h-qet3p603NxSf5JWTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "5GYV7YYBO2e_P_Qbsmn3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0GDclsv_fsyemmV-JwlFeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0GDclsv_fsyemmV-JwlFeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "idcV7YYBBkbVtX3ntBPF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ASErGV7Ep5Qa_hvKdrg1Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014866"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ASErGV7Ep5Qa_hvKdrg1Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "8GYV7YYBO2e_P_Qbxnew"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CL5weeVaATD-2rEA3Y4f8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CL5weeVaATD-2rEA3Y4f8Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "A9cV7YYBBkbVtX3nxSdX"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-vLpK350nikq7KFGmYwazg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-vLpK350nikq7KFGmYwazg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wmYV7YYBO2e_P_Qbx3ol"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Aq5HJRyvf0tzNYqtMdi3JQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Aq5HJRyvf0tzNYqtMdi3JQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "j2YV7YYBO2e_P_QbxXYP"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EijkmEEZKl52rGWO7h0aXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "EGYV7YYBO2e_P_Qb037f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["avHlGvNfTVzeaAgsVgxB6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["avHlGvNfTVzeaAgsVgxB6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "1NcV7YYBBkbVtX3n1C00"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EOO-biRc_oXEIgdrmE3Yfg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "-uAV7YYByh-A-Biyxbvb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x_f2mxVuf-0C8zGyqNgR_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x_f2mxVuf-0C8zGyqNgR_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "H2YV7YYBO2e_P_Qb0XuE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ykMVLYSfkbON4cDYPX_Bug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ykMVLYSfkbON4cDYPX_Bug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GuAV7YYByh-A-Biy0sIJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yY8YFryVNm9WZVvrCSeuww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yY8YFryVNm9WZVvrCSeuww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5tcV7YYBBkbVtX3n0ilU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b656ShIEq9w_RkKDz2WXsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b656ShIEq9w_RkKDz2WXsw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "09cV7YYBBkbVtX3n1C00"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tALP4PSq-pTzVgkPI2BjaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tALP4PSq-pTzVgkPI2BjaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kWYV7YYBO2e_P_QbxXYP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eyk6myVZ88WlljRICe8V4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eyk6myVZ88WlljRICe8V4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "r9cV7YYBBkbVtX3nxil1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EA7jmP4TdABUc9EMMeGdDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EA7jmP4TdABUc9EMMeGdDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sNcV7YYBBkbVtX3nxil1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mkFUdRqA_r18Eg2eao8QGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mkFUdRqA_r18Eg2eao8QGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZdcV7YYBBkbVtX3n0iyh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TVIGjcT1LXA0y6eT2GUjVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TVIGjcT1LXA0y6eT2GUjVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nWYV7YYBO2e_P_Qb0nzr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UHKEygcq3rVrnRHn0tQvCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UHKEygcq3rVrnRHn0tQvCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "n2YV7YYBO2e_P_Qb0nzr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1deqhHF4DdD8sVl-g6p7dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1deqhHF4DdD8sVl-g6p7dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "YOAV7YYByh-A-Biy08WI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4KnU6IH3mkqGAC1cm7wZtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4KnU6IH3mkqGAC1cm7wZtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "G-AV7YYByh-A-Biy0sIJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2eEjs7_9575hZerEBB52yg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014872"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2eEjs7_9575hZerEBB52yg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "K-AV7YYByh-A-Biy4tjs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXgAgM2hMcqzn0fnoAoP0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXgAgM2hMcqzn0fnoAoP0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "seAV7YYByh-A-Biy49n9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h3J3yP8dE1Gp9C8Y2fBhOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["h3J3yP8dE1Gp9C8Y2fBhOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Z9cV7YYBBkbVtX3n5EbR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Iji_zQRXoBblaoaKwHTcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Iji_zQRXoBblaoaKwHTcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-GYV7YYBO2e_P_Qb5ooi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S-j_I9z7LfR6_TFzt2st2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S-j_I9z7LfR6_TFzt2st2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "h2YV7YYBO2e_P_Qb5Ymt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5c1QuSeaLbMocVTvYRIwcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5c1QuSeaLbMocVTvYRIwcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "suAV7YYByh-A-Biy49n9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7xwMOd4RJ8Ot1XrcX4r_8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7xwMOd4RJ8Ot1XrcX4r_8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TNcV7YYBBkbVtX3n5UgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "X-AV7YYByh-A-Biy4dNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rFs-0q-mW95c43NFrT9uBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014878"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rFs-0q-mW95c43NFrT9uBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "_OAW7YYByh-A-BiyAekA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JVsUuzDWhnoUag5GJcXYzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JVsUuzDWhnoUag5GJcXYzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8NcW7YYBBkbVtX3nAFa0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["peEoWSsNziqM-hGC6EpouA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["peEoWSsNziqM-hGC6EpouA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KGYW7YYBO2e_P_QbAabd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NxGiaShnOfbsdncn_w2-8w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "muAW7YYByh-A-BiyAuya"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fqiKGqkA5IoNaeD9flVx0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fqiKGqkA5IoNaeD9flVx0Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FmYW7YYBO2e_P_QbA6id"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DrB5T-9pXds_Mi6uJBhFEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DrB5T-9pXds_Mi6uJBhFEQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sOAW7YYByh-A-BiyBPC2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fdt-C6H2QksCac6R9wTd_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fdt-C6H2QksCac6R9wTd_w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kuAW7YYByh-A-BiyBfKz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8y9d1sq41lKVZ2hOEtOWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a8y9d1sq41lKVZ2hOEtOWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-all", "_id": "U2YW7YYBO2e_P_QbBawR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0k6wELJt8HNaoJlSxjp1OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0k6wELJt8HNaoJlSxjp1OQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "ytcW7YYBBkbVtX3nBF4I"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cdhfW3Az6hl2kyXX_PlbPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "641828620625480"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014884"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cdhfW3Az6hl2kyXX_PlbPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "641828620625480"} {"create": {"_index": "profiling-events-all", "_id": "8GYW7YYBO2e_P_QbELUW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k2c8JznJ4XJJ2wtl1jvs7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k2c8JznJ4XJJ2wtl1jvs7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cOEW7YYByh-A-BiyEwXO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x7E_WMpPyNR6UoR6jD_ztQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZWYW7YYBO2e_P_QbE72B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H0WY9BQOdRjXYQkYwkFdgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H0WY9BQOdRjXYQkYwkFdgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "72YW7YYBO2e_P_QbELUW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kOAW7YYByh-A-BiyEf7z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P0mdBgw13J_O1CukthEqMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P0mdBgw13J_O1CukthEqMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "kdcW7YYBBkbVtX3nFmxw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4_rg67KP9ZbtiP0CeHeyFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4_rg67KP9ZbtiP0CeHeyFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "BdcW7YYBBkbVtX3nFGcu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QvlUdkP4T0hZxSt3gSdllg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QvlUdkP4T0hZxSt3gSdllg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "IGYW7YYBO2e_P_QbFcGw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QWHewGzkmPtQ4MCGkNndoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QWHewGzkmPtQ4MCGkNndoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "9NcW7YYBBkbVtX3nEGNk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6RXKJU69zgwLTvkyKxffdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6RXKJU69zgwLTvkyKxffdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "K2YW7YYBO2e_P_QbELiv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3ORtBaUkgpJHtPNW2d8ZAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3ORtBaUkgpJHtPNW2d8ZAQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "GeEW7YYByh-A-BiyEwMv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FxTaDizS0-OeBLILGTyzyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FxTaDizS0-OeBLILGTyzyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "G-EW7YYByh-A-BiyFQcS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014890"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "RdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m4xUM11zLI-btfCgwf6IbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m4xUM11zLI-btfCgwf6IbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "69cW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mdeRbDX2AaZ19IQrZUW2Vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mdeRbDX2AaZ19IQrZUW2Vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CROrpVXcmXQOxuX7oY29og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CROrpVXcmXQOxuX7oY29og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6tcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RWz2cEGgcNrcd3eaLVUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["b2RWz2cEGgcNrcd3eaLVUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8dcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hp7nnrgj4rg9PepT5KvZxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hp7nnrgj4rg9PepT5KvZxg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ENcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hsZRqI3yAbmj0WUJqK3N6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hsZRqI3yAbmj0WUJqK3N6Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qDjdfIDXYeKpMzfOZsKweg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qDjdfIDXYeKpMzfOZsKweg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UeEW7YYByh-A-BiyIhPk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kl-ygAMUUp50SfchyrhbRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Kl-ygAMUUp50SfchyrhbRg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "O9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NiVW0V5NwxAo0iHOds4ZXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NiVW0V5NwxAo0iHOds4ZXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "SNcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ADkra47La3HpwdD_ixkQqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ADkra47La3HpwdD_ixkQqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "SdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrKlAuh0mNP-abBix0Hafg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrKlAuh0mNP-abBix0Hafg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5dcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pN5IOT_VxO3_wUIBhsiEIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pN5IOT_VxO3_wUIBhsiEIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8NcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7fLjmESQgzutRqqKhKAIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7fLjmESQgzutRqqKhKAIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9NcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bI0z5pYH2KJIm4IMFjDYGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bI0z5pYH2KJIm4IMFjDYGQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "-dcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cG6xy387-SzKYkUtR_Jn3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cG6xy387-SzKYkUtR_Jn3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "AtcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VcxRLLsRsRxgG1o3M1zeCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VcxRLLsRsRxgG1o3M1zeCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EdcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "SdcW7YYBBkbVtX3nJn4S"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QhuAxDp-mAXxSlQCTHCDJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QhuAxDp-mAXxSlQCTHCDJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "49cW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iwupaAsbz59XDygi08k4AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iwupaAsbz59XDygi08k4AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "7dcW7YYBBkbVtX3nJHi9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ldIz4_ZIf6SyO1TJCB_KFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ldIz4_ZIf6SyO1TJCB_KFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "-tcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9tg9pzqKdnfA2ABqB6tHtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9tg9pzqKdnfA2ABqB6tHtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "AdcW7YYBBkbVtX3nJXsP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mzoBdlEKKFJr7cg7JTbkCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mzoBdlEKKFJr7cg7JTbkCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "TdcW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jR2WafQ5aT4KiR_9VxLM1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jR2WafQ5aT4KiR_9VxLM1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "S9cW7YYBBkbVtX3nI3eQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xricnf20K4kRE1JxfxLKUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xricnf20K4kRE1JxfxLKUA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "9tcW7YYBBkbVtX3nJXoP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TliOfrc4IBAVNwcIQStW3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TliOfrc4IBAVNwcIQStW3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "aeEW7YYByh-A-BiyJRZt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3uWWKodcRyYR91PnFWenuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014895"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3uWWKodcRyYR91PnFWenuQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "MtcW7YYBBkbVtX3nQItF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sdYsXSOFq3ZV5V3ZTd5OZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sdYsXSOFq3ZV5V3ZTd5OZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "M9cW7YYBBkbVtX3nQItF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CROrpVXcmXQOxuX7oY29og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CROrpVXcmXQOxuX7oY29og"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "edcW7YYBBkbVtX3nQYxi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AePsFsEWIAKcD6i5fTcKwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AePsFsEWIAKcD6i5fTcKwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8eEW7YYByh-A-BiyQCi1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hz7cJPgHH18BeBTdm_DtkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hz7cJPgHH18BeBTdm_DtkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "562164997202330"} {"create": {"_index": "profiling-events-all", "_id": "8uEW7YYByh-A-BiyQy5a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WL6Cc06d288zx9ELZZqz5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WL6Cc06d288zx9ELZZqz5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aWYW7YYBO2e_P_QbROQw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-FgHfu9tQhYTgpwF5irr9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-FgHfu9tQhYTgpwF5irr9w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "sGYW7YYBO2e_P_QbP-Dm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7uqXjdMn8cKJH0c7LBBRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["s7uqXjdMn8cKJH0c7LBBRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "3NcW7YYBBkbVtX3nQo0Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VjM0wOnf3jCPBA8dcfHmfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VjM0wOnf3jCPBA8dcfHmfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "x-EW7YYByh-A-BiyQitl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r4lcZDimr4HL3ZJBoS4zaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r4lcZDimr4HL3ZJBoS4zaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "uOEW7YYByh-A-BiyRDN8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZVHdt4rRKbUdxnZTJm-T_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014901"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZVHdt4rRKbUdxnZTJm-T_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "X2YW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TSyLQAdR8wymfWchXZ62Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TSyLQAdR8wymfWchXZ62Ew"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6tcW7YYBBkbVtX3nTpOO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FPsM-hxiPAxa6Tn5oevNoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FPsM-hxiPAxa6Tn5oevNoQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "teEW7YYByh-A-BiyTzvE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M3p5yPCVtJT8g9hbQp71SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["M3p5yPCVtJT8g9hbQp71SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZmYW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ujmw3KBSZEY5_4s7Myq2pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ujmw3KBSZEY5_4s7Myq2pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cNcW7YYBBkbVtX3nUpqO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OmeWJ7pAymYhWRrAnd1xrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OmeWJ7pAymYhWRrAnd1xrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CtcW7YYBBkbVtX3nU5wV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wDMn1VGkg3lC6D8rQjVrvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wDMn1VGkg3lC6D8rQjVrvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6dcW7YYBBkbVtX3nTpOO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aUhFzDZcHwff0_YJWeg6vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aUhFzDZcHwff0_YJWeg6vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BuEW7YYByh-A-BiyUkAI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XpxK_Q-DP0fSfpiLzuOV7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XpxK_Q-DP0fSfpiLzuOV7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "KmYW7YYBO2e_P_QbUvPO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fphxs_kpB5neRcVNyqbyvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fphxs_kpB5neRcVNyqbyvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "LGYW7YYBO2e_P_QbUvPO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yGIeKLHM9G7RRknI-piiTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yGIeKLHM9G7RRknI-piiTA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "gWYW7YYBO2e_P_QbU_aU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["weC-mDxEvnrRd8m5lrSC_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["weC-mDxEvnrRd8m5lrSC_g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "x9cW7YYBBkbVtX3nTpXo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oF-wY_acT328qrZ3ugnkzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oF-wY_acT328qrZ3ugnkzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xtcW7YYBBkbVtX3nTpXo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5dotPkeOMgRPYfdERquW0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5dotPkeOMgRPYfdERquW0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SGYW7YYBO2e_P_QbT-o1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "Y2YW7YYBO2e_P_QbUvBL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QicAohYElZnDluXcclQ5ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QicAohYElZnDluXcclQ5ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "btcW7YYBBkbVtX3nU53R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "t-EW7YYByh-A-BiyTzvE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["18dt2YlDI5SQuyr5uDM2hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["18dt2YlDI5SQuyr5uDM2hg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "_2YW7YYBO2e_P_QbU_RV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zWnar1cv2OPLTAmuUX5okA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zWnar1cv2OPLTAmuUX5okA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "8GYW7YYBO2e_P_QbVPdI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XGi0gq3X0lbtkz60bv_FjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014906"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XGi0gq3X0lbtkz60bv_FjA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "nWcW7YYBO2e_P_QbYAGS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gmcW7YYBO2e_P_QbYwYk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sb2Ufhkj-HCEBpI7dzePDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014911"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sb2Ufhkj-HCEBpI7dzePDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "5OEW7YYByh-A-Biycli0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UuDeBu8oU2omluou-0a1Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UuDeBu8oU2omluou-0a1Ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "q-EW7YYByh-A-BiyfmHu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["462hK3NQJ12J9biGtouuGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["462hK3NQJ12J9biGtouuGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "FOEW7YYByh-A-Biyf2M6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PlesHQMkaxUDV_hg8gFhww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PlesHQMkaxUDV_hg8gFhww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WuEW7YYByh-A-Biyc1w8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8m9XmKLa72WdntoQwSY0tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8m9XmKLa72WdntoQwSY0tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "jGcW7YYBO2e_P_QbgBWH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NCSbO-OFxqpqVTMmflnoIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NCSbO-OFxqpqVTMmflnoIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "reEW7YYByh-A-BiyfV6p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RmHwYLknd0bV48qFH9eQ6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RmHwYLknd0bV48qFH9eQ6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ptcW7YYBBkbVtX3nfrpi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GG0F2L0nSA_B6ZW5v6LB3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GG0F2L0nSA_B6ZW5v6LB3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "168532957631583"} {"create": {"_index": "profiling-events-all", "_id": "p9cW7YYBBkbVtX3nfrpi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DAkV_97hJaROs8HKSG_NUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DAkV_97hJaROs8HKSG_NUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "l-EW7YYByh-A-Biyf2R6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPqqmDZLLGF6_pzrJ1s5lA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sPqqmDZLLGF6_pzrJ1s5lA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "DdcW7YYBBkbVtX3ncrd2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014916"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "ROEW7YYByh-A-BiykHvM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wJMcWNAb5ql2fkVg8DVb0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wJMcWNAb5ql2fkVg8DVb0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EGcW7YYBO2e_P_QbkCRr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-o7jkJLtD4xHchcLgzNuuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-o7jkJLtD4xHchcLgzNuuA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "6GcW7YYBO2e_P_QbjR7F"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K8gQh5zdfmwr_L8d6j_v5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["K8gQh5zdfmwr_L8d6j_v5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vGcW7YYBO2e_P_QbjiAN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g4J6Jl239ZcoU2ZSj7-fag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g4J6Jl239ZcoU2ZSj7-fag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "09cW7YYBBkbVtX3nksnh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IzpuUVv759Abltk8UjLULQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IzpuUVv759Abltk8UjLULQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ZWcW7YYBO2e_P_QbjiKj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IkFQJGH6hdklKpjmMwHToQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IkFQJGH6hdklKpjmMwHToQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "seEW7YYByh-A-BiyjXFu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ay1JvUpYidc_jtVVQh5xJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ay1JvUpYidc_jtVVQh5xJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "sOEW7YYByh-A-BiyjXFu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kpyhs2kTFJc98nncsIEGzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kpyhs2kTFJc98nncsIEGzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "52cW7YYBO2e_P_QbjR7F"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6uN-YY_i1gvVmqACLDXQMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "_eEW7YYByh-A-BiyknyA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fLHZEyltMzzscfMhon-Tzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fLHZEyltMzzscfMhon-Tzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "uNcW7YYBBkbVtX3nk8tE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MqCIDPuPM-mrPy2Wr4E0pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MqCIDPuPM-mrPy2Wr4E0pg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "utcW7YYBBkbVtX3nk8tE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6XkFhPi9lM3BiwzJEIoaIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6XkFhPi9lM3BiwzJEIoaIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "uWcW7YYBO2e_P_QbkymJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "FNcW7YYBBkbVtX3nk83N"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N6yoC5MEhf-Plh-uBAaDFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N6yoC5MEhf-Plh-uBAaDFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "DuEW7YYByh-A-BiyjXAa"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RM52I8qJK_HFvsZhTonctg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "D-EW7YYByh-A-BiyjXAa"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014921"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["A37WFlc27IDax1__xu-KJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "JNcW7YYBBkbVtX3noNM2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4pxTtEE_f9QsF_FY1lgc-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4pxTtEE_f9QsF_FY1lgc-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "omcW7YYBO2e_P_QboDfC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eXATor8dtVm3LPIO_GNaZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eXATor8dtVm3LPIO_GNaZA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kdcW7YYBBkbVtX3nodQJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NRT6b-EmSsUKrT0-0ibcag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NRT6b-EmSsUKrT0-0ibcag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0dcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "52cW7YYBO2e_P_Qbojgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z8goRTVMaTzMkIP86WRVRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z8goRTVMaTzMkIP86WRVRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "lmcW7YYBO2e_P_QbnjHj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E2cW7YYBO2e_P_QbnzMm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cn2EpQqWCVem8DknXDkNXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cn2EpQqWCVem8DknXDkNXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "G-EW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p0NFUe2QgjtWEWYDSLMm1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p0NFUe2QgjtWEWYDSLMm1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "mmcW7YYBO2e_P_QbnzSu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UwSPv3v3mJV5n8bvEwP9ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UwSPv3v3mJV5n8bvEwP9ww"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "F2cW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ade_-rYDOOWEqYEPYyknBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ade_-rYDOOWEqYEPYyknBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "GWcW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E518XUc1CtUggz7KTKp7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["E518XUc1CtUggz7KTKp7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "G2cW7YYBO2e_P_QboDaE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rtLWsf0bQDHrSMWDW9YU3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "WdcW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3zYROBVu24JPj2x-xW0ysA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3zYROBVu24JPj2x-xW0ysA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "WtcW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uqwRXN4Yq9WZrlMPcUG5Yg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uqwRXN4Yq9WZrlMPcUG5Yg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "0NcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dKz6wRYxk5b-EmykX6Tcqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dKz6wRYxk5b-EmykX6Tcqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "xmcW7YYBO2e_P_QbrDqK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Xgi2WyDfYTM06WuIqjfkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3Xgi2WyDfYTM06WuIqjfkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "gtcW7YYBBkbVtX3nrNrT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FjtKztgbAQPS6bJqFyRkYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "hNcW7YYBBkbVtX3nrNrT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g7rfCmzBd1WanveypHmAqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g7rfCmzBd1WanveypHmAqA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "8dcW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W0j_klLnHW1yyhF4U8DXiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W0j_klLnHW1yyhF4U8DXiA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "VtcW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["43Mh5txMzJNoI6svI0SbQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["43Mh5txMzJNoI6svI0SbQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WNcW7YYBBkbVtX3noddV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N6dB94SEYMjCukJ9TS8bDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N6dB94SEYMjCukJ9TS8bDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ztcW7YYBBkbVtX3nodjp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPeOIoXE8SwG5uuXQoAI6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OPeOIoXE8SwG5uuXQoAI6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "g9cW7YYBBkbVtX3nrNrT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LcYkQAM-vgmPtnOsNMORSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LcYkQAM-vgmPtnOsNMORSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "89cW7YYBBkbVtX3nrdtn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7irlEx7CVauqLLMLkiu9aA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7irlEx7CVauqLLMLkiu9aA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "GOEW7YYByh-A-Biyn4hj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q7XAR2zqlv3Nkd1rHK-fsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q7XAR2zqlv3Nkd1rHK-fsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "n2cW7YYBO2e_P_QboDfC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014927"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "FmcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5ssUCcghlPpbufn_FI7lhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5ssUCcghlPpbufn_FI7lhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_uEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m7HYxR39MOi2F5s3SuKENw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m7HYxR39MOi2F5s3SuKENw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B-EW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6VlRZTvCAGEjKAJI9WErGg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "E2cW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QB20QHI7TlFL4JvuFhH_6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QB20QHI7TlFL4JvuFhH_6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-uEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Nu4JYvGvXl5CW_RB7l_vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0Nu4JYvGvXl5CW_RB7l_vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "_-EW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NUICzvay5gqiM1JCIDYjDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NUICzvay5gqiM1JCIDYjDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "BOEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-vSsOv3oIeGq1jnkLewmKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-vSsOv3oIeGq1jnkLewmKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "8OEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NWTYSV7vGDryRONnCUqo1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NWTYSV7vGDryRONnCUqo1A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "c2cW7YYBO2e_P_QbvEm2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q67ZvLIlucofkIvus5w-GQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["q67ZvLIlucofkIvus5w-GQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EGcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gq5ri62azb2HgYO44ajr9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gq5ri62azb2HgYO44ajr9Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FGcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XxoiLx9HpNbK-YWzICyumQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XxoiLx9HpNbK-YWzICyumQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "f-EW7YYByh-A-BiyvqKW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7ofbCHl8qRy2q41G8_s7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e7ofbCHl8qRy2q41G8_s7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BeEW7YYByh-A-Biyspsk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yTAi1Yo0NZNgWpXZUjzOrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yTAi1Yo0NZNgWpXZUjzOrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "XeEW7YYByh-A-BiywKYJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XL31E2Uzdrei76bGcaLiXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XL31E2Uzdrei76bGcaLiXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "BmcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g-xXoA0lL9IYRJcrTwtAWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g-xXoA0lL9IYRJcrTwtAWA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "gOEW7YYByh-A-BiyvqKW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vQXtdmIzgIVlhx1gewz_RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vQXtdmIzgIVlhx1gewz_RA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} {"create": {"_index": "profiling-events-all", "_id": "8eEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5bQcQ0KEBggKnhUPDGb0jQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "pdcW7YYBBkbVtX3nv-hH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZ5KGLHdU9j4VxTqgQfhhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bZ5KGLHdU9j4VxTqgQfhhg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "-eEW7YYByh-A-Biyspok"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LMgC_pj236jbZulsolnmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6LMgC_pj236jbZulsolnmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "CmcW7YYBO2e_P_QbvkxZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yg3TaXRMJTka-oF2wrTuxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yg3TaXRMJTka-oF2wrTuxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-all", "_id": "fGcW7YYBO2e_P_Qbvk3S"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ECM4wduGn2SgkCpNnIpPqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014932"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ECM4wduGn2SgkCpNnIpPqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741865085146651"} {"create": {"_index": "profiling-events-all", "_id": "7NcW7YYBBkbVtX3nzPOJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ii5fUg--kGCwh43V7vfk4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ii5fUg--kGCwh43V7vfk4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xeEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nd9rMkTYCiObUWdQEYWSwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Nd9rMkTYCiObUWdQEYWSwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "omcW7YYBO2e_P_Qb0Fc4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ri5pW0t6s5lXro7RV78vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2Ri5pW0t6s5lXro7RV78vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B9cW7YYBBkbVtX3nz_cq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_BHWrMWBlVU6-0DD2Kvl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "muEW7YYByh-A-Biyz7fk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zk1KOzfHoLWnIpBzzSfmpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zk1KOzfHoLWnIpBzzSfmpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DeEW7YYByh-A-BiyzK0R"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1dmpJ55uvZdOMq_7dLN78g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1dmpJ55uvZdOMq_7dLN78g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w-EW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oFFMBClb7YDKbss0-D49Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oFFMBClb7YDKbss0-D49Bg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GmcW7YYBO2e_P_QbzVOk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lXS9jaakohlJ8WgrZlMjbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lXS9jaakohlJ8WgrZlMjbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "itcW7YYBBkbVtX3nzfVn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0RtFA4NAxhPCgHCcZm471A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0RtFA4NAxhPCgHCcZm471A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MOEW7YYByh-A-BiyzrNj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tvUUMUcodTkJ0m6RggT6bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tvUUMUcodTkJ0m6RggT6bA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "GeEW7YYByh-A-BiyzbAL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MDX9xkXZ6YV1jVI86ZVY2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MDX9xkXZ6YV1jVI86ZVY2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "neEW7YYByh-A-BiyzK7K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-ggHg8zNX_Qs55mykGTE7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-ggHg8zNX_Qs55mykGTE7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "556968501734974"} {"create": {"_index": "profiling-events-all", "_id": "CdcW7YYBBkbVtX3nz_cq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CBlNVd4rPCO2FdA0l90MKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CBlNVd4rPCO2FdA0l90MKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "iNcW7YYBBkbVtX3nzfVn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p5nXo4mBd3u0s_59PDRZvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["p5nXo4mBd3u0s_59PDRZvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "L-EW7YYByh-A-BiyzrNj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["idsijnsI-3EEcw8J1DhUvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["idsijnsI-3EEcw8J1DhUvg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "D-EW7YYByh-A-Biy0Llz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W1X66sQmZo7R_inBU4PeQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["W1X66sQmZo7R_inBU4PeQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "y-EW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mhCENzeJsRypXPr7NLjqVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mhCENzeJsRypXPr7NLjqVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "lmcW7YYBO2e_P_QbzFFQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9ROJ1260u7kvs85ZsQXWJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9ROJ1260u7kvs85ZsQXWJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "GGcW7YYBO2e_P_QbzVOk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hgbYFeQR5UbL1ILQeJXsrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hgbYFeQR5UbL1ILQeJXsrg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "yOEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S05AC4-RVY5vRimCgolcQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S05AC4-RVY5vRimCgolcQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "mGcW7YYBO2e_P_QbzFFQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nV3Fn_RzzKrNcUUuAsluvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nV3Fn_RzzKrNcUUuAsluvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "zOEW7YYByh-A-BiyzbHq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcg4yzcU6w_vTsKTk-8RpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcg4yzcU6w_vTsKTk-8RpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "omcW7YYBO2e_P_QbzlQs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29tkR1iuog5-kDCdzfxqQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["29tkR1iuog5-kDCdzfxqQw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "o2cW7YYBO2e_P_QbzlQs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VVLBSGTuYWH3O356lNUySg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014938"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VVLBSGTuYWH3O356lNUySg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "g-EW7YYByh-A-Biy3cBp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dC0hqK0ytFANzaVY5y-X0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dC0hqK0ytFANzaVY5y-X0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "A2cW7YYBO2e_P_Qb6mbH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FrtmF-TX0N6XSfQgrjNNSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FrtmF-TX0N6XSfQgrjNNSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "otgW7YYBBkbVtX3n4AeL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LU5M-Y2vAZAPnKMCmcDaJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LU5M-Y2vAZAPnKMCmcDaJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "313646706170047"} {"create": {"_index": "profiling-events-all", "_id": "u2cW7YYBO2e_P_Qb3WD9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BNgW7YYBBkbVtX3n4AZK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vvJfyuw2NkjY_OhqTi4dyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vvJfyuw2NkjY_OhqTi4dyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "3tgW7YYBBkbVtX3n3QKs"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t3YPrWrDzJFDnReQ7K0ZIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t3YPrWrDzJFDnReQ7K0ZIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "O-EW7YYByh-A-Biy3sKF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GUmcuPH6_akbAJCgr_HMZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GUmcuPH6_akbAJCgr_HMZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "SdgW7YYBBkbVtX3n3wQf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RCyrcGCWUG81ALfuR1iT2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RCyrcGCWUG81ALfuR1iT2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "BGcW7YYBO2e_P_Qb6mbH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e3lcOyxzIAx9GClHCrbUDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e3lcOyxzIAx9GClHCrbUDA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ReEW7YYByh-A-Biy6seD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uc3rxkKkk8AS6xhrVwHG8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uc3rxkKkk8AS6xhrVwHG8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "hWcW7YYBO2e_P_Qb3F3b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AnYjvsg56TcxE6xD0wQ5qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014943"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AnYjvsg56TcxE6xD0wQ5qA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "RmcW7YYBO2e_P_Qb-nVZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HQfdRiN_i6nsUpr-osGkTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HQfdRiN_i6nsUpr-osGkTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "CtgW7YYBBkbVtX3n_BuV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z2emazyAu13iRamH5lmUoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z2emazyAu13iRamH5lmUoA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "g-EW7YYByh-A-Biy-9YK"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DBpdnhabztGdbOuXyBjkeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DBpdnhabztGdbOuXyBjkeg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "vuEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbxQOwOeVRIDcuzZmln_AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JbxQOwOeVRIDcuzZmln_AQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BtgW7YYBBkbVtX3n-xhN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lGYnfjmvwXki2C5OKuIGdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lGYnfjmvwXki2C5OKuIGdg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i9gW7YYBBkbVtX3n_Blb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uTJCeaCRI3Z-859bdSx3XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uTJCeaCRI3Z-859bdSx3XQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "t-EW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9CvZpABHsErJ2oaka4jO4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9CvZpABHsErJ2oaka4jO4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "DeEW7YYByh-A-Biy_Nwe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1RCMO4Rht0Tyq-ucg22Gag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1RCMO4Rht0Tyq-ucg22Gag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "y-EW7YYByh-A-Biy_N3T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "LNgW7YYBBkbVtX3n_R7G"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Hq3X6R0xZFl8IPGx4UbCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1Hq3X6R0xZFl8IPGx4UbCQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "XOEW7YYByh-A-Biy-9rd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["J6yDhkd9T90hDGIK4K7YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "vOEW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xdSUu7a3b1m64nGHPCzjtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xdSUu7a3b1m64nGHPCzjtQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ldgW7YYBBkbVtX3n_RyM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "v-EW7YYByh-A-Biy_t8M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDgwhZo9YDrEqaVRvCqKvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014949"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SDgwhZo9YDrEqaVRvCqKvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "x-EX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GITcXcM5OZJEsFYPj2RnOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GITcXcM5OZJEsFYPj2RnOg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "iuEX7YYByh-A-BiyC-Nk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sodrqLHefNrUwN3yDuu-2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sodrqLHefNrUwN3yDuu-2Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "wNgX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XxgF8S8zcb8A1_Ecius1nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XxgF8S8zcb8A1_Ecius1nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AuEX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["El5SJjgC1dQRf1W23p8Oog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["El5SJjgC1dQRf1W23p8Oog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BOEX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PCWzQZLMSEDq6jgDAbtbDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PCWzQZLMSEDq6jgDAbtbDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "UtgX7YYBBkbVtX3nCiNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xe-mgxEb6Ktl0FGwLrD9QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Xe-mgxEb6Ktl0FGwLrD9QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "i-EX7YYByh-A-BiyC-Nk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MRbhvMfZ_M5nO9oLscAerQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MRbhvMfZ_M5nO9oLscAerQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BuEX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7tyayDV_vQllSdORuTrY9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7tyayDV_vQllSdORuTrY9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tmcX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YOEX7YYByh-A-BiyDev8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cA8SM2W7SPYEpBx-8uBa1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "o-EX7YYByh-A-BiyDuyF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKsocjlsvM68oICIvKxy7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKsocjlsvM68oICIvKxy7A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "v9gX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["19wmIvUZK4UmSOptZH3T1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["19wmIvUZK4UmSOptZH3T1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "NGcX7YYBO2e_P_QbCoY2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r5Qk0y0lu82qLRvIh-Mz7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["r5Qk0y0lu82qLRvIh-Mz7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "T9gX7YYBBkbVtX3nCiNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["78TuS6LlqqCJkHSW5a7paQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["78TuS6LlqqCJkHSW5a7paQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "J2cX7YYBO2e_P_QbC4mb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6FS78rLcwklRcuuvZdYp0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6FS78rLcwklRcuuvZdYp0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "t2cX7YYBO2e_P_QbDIuZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QuzNOG3t4OkPYTKYBPqKPQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "FGcX7YYBO2e_P_QbDI3K"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "kGcX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BlQOTBVYBirJP-nXExTkPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BlQOTBVYBirJP-nXExTkPA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6GcX7YYBO2e_P_QbDY8s"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AgA2e1jJZcOpDqcyIoAQmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AgA2e1jJZcOpDqcyIoAQmA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6-EX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XXVig9Ie3HmFHZwzuss1kg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "peEX7YYByh-A-BiyDuyF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dkCDIssAAuuJOY5L9uP-Lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dkCDIssAAuuJOY5L9uP-Lw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "MGcX7YYBO2e_P_QbDZHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vurXS1ra6ryPwSCr6lehBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vurXS1ra6ryPwSCr6lehBw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "u9gX7YYBBkbVtX3nCSH9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WRL9yLuHTV7ynk4o7WuvDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WRL9yLuHTV7ynk4o7WuvDw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "U9gX7YYBBkbVtX3nCiNo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["84RiA4pbVL7KMlDvnXnbFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["84RiA4pbVL7KMlDvnXnbFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "32cX7YYBO2e_P_QbCoef"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZcpIqjO8GbOGxvXYAifsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZcpIqjO8GbOGxvXYAifsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "RuEX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["77WALZLy-UdbTovdy-aeKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["77WALZLy-UdbTovdy-aeKQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "R-EX7YYByh-A-BiyCuLc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZpiULf3cc4PAnQBQeWnmvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZpiULf3cc4PAnQBQeWnmvQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "xuEX7YYByh-A-BiyC-Tq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0427nwt0KroQkvwzA7egA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G0427nwt0KroQkvwzA7egA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "f2cX7YYBO2e_P_QbDIov"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T9ahUsDV5TPFP3kZTiNCSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T9ahUsDV5TPFP3kZTiNCSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "BeEX7YYByh-A-BiyDOZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5zce_f4N45Itu5RhOF9CQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B5zce_f4N45Itu5RhOF9CQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "dOEX7YYByh-A-BiyDedc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["11Tl3YbjvipxnomXbH8dow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["11Tl3YbjvipxnomXbH8dow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5-EX7YYByh-A-BiyDemY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcHz46TjNV4prWC1qZE-6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["bcHz46TjNV4prWC1qZE-6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "MmcX7YYBO2e_P_QbDZHL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5CUA13BfSKgPE5R1fmHh5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5CUA13BfSKgPE5R1fmHh5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "jWcX7YYBO2e_P_QbDI78"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OFdK4mvMOorRf1NaABBLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014955"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["OFdK4mvMOorRf1NaABBLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "w2cX7YYBO2e_P_QbHpyj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJhw6TGulS2g4dgLha_6Fw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JJhw6TGulS2g4dgLha_6Fw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KtgX7YYBBkbVtX3nHTPi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WGcX7YYBO2e_P_QbHJmW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2m3Q7K61sMG8WQrx7RXxxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2m3Q7K61sMG8WQrx7RXxxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y-EX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m89QqjE-qUNSc3kuFiQzbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m89QqjE-qUNSc3kuFiQzbA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pGcX7YYBO2e_P_QbHp7f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YC25zlLJYeBTwotVPhLafg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YC25zlLJYeBTwotVPhLafg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yuEX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EwRlWIvafZgNvSviLLBxdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EwRlWIvafZgNvSviLLBxdw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "YuEX7YYByh-A-BiyKftS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BuZCvHOLrtMxpNr2RzKFfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BuZCvHOLrtMxpNr2RzKFfA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "w-EX7YYByh-A-BiyHfVe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY70RGM6lV3NgAwSeTX8Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014959"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY70RGM6lV3NgAwSeTX8Tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "f-IX7YYByh-A-BiyOAGi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5DxLQCjm2m1lyk1iyQve0g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "geIX7YYByh-A-BiyOAGi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jKJw7MgwzsyLy5Y5-ZGSMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jKJw7MgwzsyLy5Y5-ZGSMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hmcX7YYBO2e_P_QbOrtJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["puIsGFT9D9ie7OaAMWkigA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["puIsGFT9D9ie7OaAMWkigA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YtgX7YYBBkbVtX3nO0KT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TlUPq402XAoMqzEe9Lt4Rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TlUPq402XAoMqzEe9Lt4Rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9uIX7YYByh-A-BiyOALn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_59QHFa1Rqj5C5mCwepbVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_59QHFa1Rqj5C5mCwepbVQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "QuIX7YYByh-A-BiyOQRi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RyXi5_2LXUkPg6q5AxjLZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RyXi5_2LXUkPg6q5AxjLZw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "O9gX7YYBBkbVtX3nPEVT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7QawPKHJF79qrjka8nzLbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7QawPKHJF79qrjka8nzLbQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "y9gX7YYBBkbVtX3nPEMb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7E99aSr-SRhrdHQrfS71Qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7E99aSr-SRhrdHQrfS71Qg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "uGcX7YYBO2e_P_QbOr_Z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "P-IX7YYByh-A-BiyPQgM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GRNWgj9ADC9F-xKz9gBTGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GRNWgj9ADC9F-xKz9gBTGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "IuIX7YYByh-A-BiyPQuP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GPbfHhT749ZR1t85wslN0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GPbfHhT749ZR1t85wslN0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AWcX7YYBO2e_P_QbObof"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GWuZD3Bv-Fozl4N5Yzi5dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GWuZD3Bv-Fozl4N5Yzi5dw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "3eIX7YYByh-A-BiyPAaT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FfU3KGa4jQE4GKP8Psa9ng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FfU3KGa4jQE4GKP8Psa9ng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "qNgX7YYBBkbVtX3nOj0O"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MVipWFhXPDISxsBT7IZfCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MVipWFhXPDISxsBT7IZfCw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "t-IX7YYByh-A-BiyPQlQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AgBnLrnFQyEiB71eITD44g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AgBnLrnFQyEiB71eITD44g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ueIX7YYByh-A-BiyPQlQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oTHLMe0BewCEp798WVpJtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oTHLMe0BewCEp798WVpJtg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "3uIX7YYByh-A-BiyPAaT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZ8clAp9paL9k-C90WPkIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aZ8clAp9paL9k-C90WPkIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "UGcX7YYBO2e_P_QbOr2M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkp5EyZaH9EKC1Tx2EnCYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014964"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qkp5EyZaH9EKC1Tx2EnCYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "C-IX7YYByh-A-BiyTBo1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXipnObSe0dCYjfUl0jbxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DXipnObSe0dCYjfUl0jbxA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "SWcX7YYBO2e_P_QbScVi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hYfhfJkQW17kyXXXeL8OrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hYfhfJkQW17kyXXXeL8OrQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "F2cX7YYBO2e_P_QbS8o-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gBMo3RNl4xbFgjyE8KLEeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gBMo3RNl4xbFgjyE8KLEeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "l-IX7YYByh-A-BiySRIf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yg46hsyTTfWnv4qsLCjw3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Yg46hsyTTfWnv4qsLCjw3Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dGcX7YYBO2e_P_QbTdFC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lSo6n255V9zP1aYcLPpfmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lSo6n255V9zP1aYcLPpfmQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "COIX7YYByh-A-BiySRTi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pCi6Bn9hgoUvfDY9KWDh-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pCi6Bn9hgoUvfDY9KWDh-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "nGcX7YYBO2e_P_QbSsgn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DajmKOxs5mZzvp3q0vIZRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DajmKOxs5mZzvp3q0vIZRw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "D-IX7YYByh-A-BiyShe9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzlp5KSh-SCscA1-K9srWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fzlp5KSh-SCscA1-K9srWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FOIX7YYByh-A-BiyTBx8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y-PPbP4nOCiClHW7_KYwMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y-PPbP4nOCiClHW7_KYwMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JdgX7YYBBkbVtX3nTU6_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NeIDUkh-VuAV72dDJWGFbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NeIDUkh-VuAV72dDJWGFbg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6OIX7YYByh-A-BiySBCO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k4HJrAiqQ3V4Sy2tIInxZQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dWcX7YYBO2e_P_QbS8t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jV0cBQfixQcleXHjzqFw0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jV0cBQfixQcleXHjzqFw0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9WcX7YYBO2e_P_QbS8zy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DeIX7YYByh-A-BiyTBo1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZRkXnh8pLDVlUVidYeFDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qZRkXnh8pLDVlUVidYeFDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-mcX7YYBO2e_P_QbScad"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AOFrzbtSbZoZPfOFvByqkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AOFrzbtSbZoZPfOFvByqkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "SmcX7YYBO2e_P_QbScVi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gra3ZDS-h8Qb6oN3nyQ91w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Gra3ZDS-h8Qb6oN3nyQ91w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "YeIX7YYByh-A-BiySxi3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014970"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "gWcX7YYBO2e_P_QbWtiF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["1QLTwfIs5p4VcZehcoW7uw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "keIX7YYByh-A-BiyXSZb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9raaEJOc-xp60E1LDA7XaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9raaEJOc-xp60E1LDA7XaA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JOIX7YYByh-A-BiyWSCu"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lgc01vu6tLGgLO8IPeQGXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["lgc01vu6tLGgLO8IPeQGXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "5-IX7YYByh-A-BiyXCQS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IalcRP42h8xF7vm8KTlEiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IalcRP42h8xF7vm8KTlEiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DGcX7YYBO2e_P_QbZ-K9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4sjDMbuo1EQDli2AMeF1pA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "HWcX7YYBO2e_P_QbWdTt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uD9v9EeBRS5EzcNLZ8q2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uD9v9EeBRS5EzcNLZ8q2gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "94526795721060"} {"create": {"_index": "profiling-events-all", "_id": "KOIX7YYByh-A-BiyWyN4"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJQF0B534N8TwJ-_OUbvmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kJQF0B534N8TwJ-_OUbvmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "WNgX7YYBBkbVtX3nW1nF"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P-dCdUT1LEJyae6UYwKugg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["P-dCdUT1LEJyae6UYwKugg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "HOIX7YYByh-A-BiyaCtL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JHzvVskHWYcoFwQr_Ta92A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JHzvVskHWYcoFwQr_Ta92A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "U2cX7YYBO2e_P_QbWtrI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fa7wECARkxA2ek4DYeGk9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014975"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fa7wECARkxA2ek4DYeGk9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "tdgX7YYBBkbVtX3neGzr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VSiIga6kK669vm3_VFRBxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VSiIga6kK669vm3_VFRBxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4dgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YjeaaN9Gs9Jtblq8lj7A3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YjeaaN9Gs9Jtblq8lj7A3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "b2cX7YYBO2e_P_Qbd-2U"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MY4vqWXc_k9n6qImvJUI7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "89gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ArhssJT4V7-kmdsezZTRQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ArhssJT4V7-kmdsezZTRQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "99gX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_rVC5Sy8eAmzcpq8FYPgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g_rVC5Sy8eAmzcpq8FYPgQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qNgX7YYBBkbVtX3neW_-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EO1Dm97l4fnw6_SNto3oog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EO1Dm97l4fnw6_SNto3oog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kWcX7YYBO2e_P_QbevM8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9rychglqQSAQzOKooiFxFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9rychglqQSAQzOKooiFxFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6tgX7YYBBkbVtX3ne3WS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5gjPCLIYZbMZi0UuOcP2yQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014980"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5gjPCLIYZbMZi0UuOcP2yQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "lmcX7YYBO2e_P_Qbh_zn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VV8E-OYfEEKqtlp023Tqng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VV8E-OYfEEKqtlp023Tqng"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YWcX7YYBO2e_P_Qbh_kU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fDQou-XRb52d9gCJh1ayOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fDQou-XRb52d9gCJh1ayOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8isTUPr0FRuKib4cU3qR3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8isTUPr0FRuKib4cU3qR3w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6-IX7YYByh-A-Biyh0Kg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D4jMgEWnva8oEa4cv5QFeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D4jMgEWnva8oEa4cv5QFeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7OIX7YYByh-A-Biyh0Kg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2mGUnlgeNy9yq7TLiDTKvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2mGUnlgeNy9yq7TLiDTKvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "l2cX7YYBO2e_P_Qbh_zn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RX6MWdoFei8k1kwyhzfnHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RX6MWdoFei8k1kwyhzfnHA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kwSNJ4ks9P4lHM_hMfcyhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kwSNJ4ks9P4lHM_hMfcyhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "89gX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Of2hetgQ4G3EMs-obnxUFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Of2hetgQ4G3EMs-obnxUFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "ydgX7YYBBkbVtX3niHnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qbOly4UeXgEZ8EetANZqFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qbOly4UeXgEZ8EetANZqFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dnFHj_GHdhJ9FbnuH0i77A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dnFHj_GHdhJ9FbnuH0i77A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "49gX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PacxNzOkBSNx_21zrmhePw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PacxNzOkBSNx_21zrmhePw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "LuIX7YYByh-A-BiyhkDE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7AM0prgm67n5d6K3VpPj9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7AM0prgm67n5d6K3VpPj9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "X2cX7YYBO2e_P_Qbh_kU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ezB3Rrr_knGYPkl_kYdRNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ezB3Rrr_knGYPkl_kYdRNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "9GcX7YYBO2e_P_Qbh_pU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H8A3dYuSIPwxTp-xzJya1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H8A3dYuSIPwxTp-xzJya1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "4-IX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-7ex70r3IhidNSVrzLcqAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-7ex70r3IhidNSVrzLcqAA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "5eIX7YYByh-A-BiyiER2"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3H-L6UsF703PU8SR2ZlmEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3H-L6UsF703PU8SR2ZlmEA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "x9gX7YYBBkbVtX3niHnT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pD-pJcDVTjS_r_eW7GWMcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["pD-pJcDVTjS_r_eW7GWMcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "smgX7YYBO2e_P_QbigJT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FGf4xT_jVUAejwXntzL3PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FGf4xT_jVUAejwXntzL3PQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "suIX7YYByh-A-BiyikmM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LdUr9M80lv8cnRJG6q84UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LdUr9M80lv8cnRJG6q84UA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "3tgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AZiPGUJq8VLe0bcF5JOdFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AZiPGUJq8VLe0bcF5JOdFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "7dgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xTmXxLtxYtdjX3OFWgcBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xTmXxLtxYtdjX3OFWgcBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "beIX7YYByh-A-Biyi0uU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BcwMHWALpdPlatMjiBW_wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BcwMHWALpdPlatMjiBW_wg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "EuIX7YYByh-A-BiyjE0n"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nngybL9jLob9MFAj_5uE0w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "82cX7YYBO2e_P_Qbh_pU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JiCE6XR1gukpwvjQ1r_0aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JiCE6XR1gukpwvjQ1r_0aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "VmgX7YYBO2e_P_QbiQGT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["and5_iwPhBY0DhBmGzzTUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["and5_iwPhBY0DhBmGzzTUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "s2gX7YYBO2e_P_QbigJT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jFhA24HccRgfwIBBwmJXqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jFhA24HccRgfwIBBwmJXqw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "5NgX7YYBBkbVtX3ni4E_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "EeIX7YYByh-A-BiyjE0n"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZndsICGWbrD6J4BVHqQM7g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "kOIX7YYByh-A-BiyiUZS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gSO16M9ILzhu6pqLHYZcKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014985"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gSO16M9ILzhu6pqLHYZcKg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "sdgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-nrrZMuYFG3kBv7-N6Cr3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-nrrZMuYFG3kBv7-N6Cr3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "beIX7YYByh-A-BiyqGFv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pa7eV1ClIoEc0MOWrL7aYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Pa7eV1ClIoEc0MOWrL7aYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eGgX7YYBO2e_P_QbqRsl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xcO8ATMuOlPrGlylAgvJmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ttgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_X9dFQVvkPI4ha0P8p2JiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_X9dFQVvkPI4ha0P8p2JiQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XGgX7YYBO2e_P_QbnBJG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RUNYTUN-F_zv9oJ0DjEKwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["RUNYTUN-F_zv9oJ0DjEKwQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aNgX7YYBBkbVtX3npo-z"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PU4AlGgy6OVgX5g2hXwflQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PU4AlGgy6OVgX5g2hXwflQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8OIX7YYByh-A-BiyqWJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CjSOPmGxE3Pc5_4gR1HXEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CjSOPmGxE3Pc5_4gR1HXEg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "JNgX7YYBBkbVtX3nmo13"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B-XLpCbHVWJllSfmbTHDIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B-XLpCbHVWJllSfmbTHDIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "sOIX7YYByh-A-Biym1t5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jwz5Ko_H_B_a_KaZUAnDNQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "XmgX7YYBO2e_P_Qbpxge"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D17V2ZvopmhLBd7dZ3Y1BA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["D17V2ZvopmhLBd7dZ3Y1BA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "92gX7YYBO2e_P_QbmxAt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T6-ZIWwGzZExC78sWJIpyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T6-ZIWwGzZExC78sWJIpyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "rtgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jgHKhfN_-iW4c9zXWgJycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jgHKhfN_-iW4c9zXWgJycA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "eWgX7YYBO2e_P_QbqRsl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EA7jmP4TdABUc9EMMeGdDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["EA7jmP4TdABUc9EMMeGdDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "7-IX7YYByh-A-BiyqWJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9Qdn4hxZXN_RRgCI0d2Aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "845379217314054"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["G9Qdn4hxZXN_RRgCI0d2Aw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "845379217314054"} {"create": {"_index": "profiling-events-all", "_id": "JdgX7YYBBkbVtX3nmo13"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V0FscKR06PIQDFIotfIKkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["V0FscKR06PIQDFIotfIKkQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "IOIX7YYByh-A-Biymlrf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZODq3DX4tCIqNNAVK-EkRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZODq3DX4tCIqNNAVK-EkRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "rdgX7YYBBkbVtX3nm46-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zbdiy9zDQAF_ITnyDDjh2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zbdiy9zDQAF_ITnyDDjh2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "XuIX7YYByh-A-Biypl4D"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vYaocYILvM8dc_gTgLR1Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vYaocYILvM8dc_gTgLR1Pw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "I9gX7YYBBkbVtX3nmo13"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YulTfvm-3LCwbTWK82hE8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YulTfvm-3LCwbTWK82hE8g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "x2gX7YYBO2e_P_QbnBXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xHAlWfQiLLyIuk2h7k7RXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xHAlWfQiLLyIuk2h7k7RXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "s9gX7YYBBkbVtX3nqJPE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4gZSOWlJ3pw6n-5AVME8fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4gZSOWlJ3pw6n-5AVME8fQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "KGgX7YYBO2e_P_QbnBSM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KOMN7HDuAGD1N2A7P0t7vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014992"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KOMN7HDuAGD1N2A7P0t7vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "1uIX7YYByh-A-Biytmj0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vOZHtJ4ahW-g2TWd1-Whrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vOZHtJ4ahW-g2TWd1-Whrw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "fNgX7YYBBkbVtX3nuay9"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Epu3otqsKY33No3a7Ut0Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Epu3otqsKY33No3a7Ut0Ug"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xdgX7YYBBkbVtX3nuq9-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UV9_m6EFKMbhnALIvI6q6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "tmgX7YYBO2e_P_Qbtynt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qf8e2CedM9huXl7Xm6xyyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Qf8e2CedM9huXl7Xm6xyyQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "K9gX7YYBBkbVtX3nua7-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N25AEjenMUPgCcs2AAiXqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N25AEjenMUPgCcs2AAiXqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "OdgX7YYBBkbVtX3nu7Ec"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mws_4U3r89UDU4qZjswO6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Mws_4U3r89UDU4qZjswO6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "7dgX7YYBBkbVtX3nt6I3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3U1GssMMNyEfwWToQlllWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3U1GssMMNyEfwWToQlllWQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "G-IX7YYByh-A-BiyuGot"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zCPc0-bKAM6-gvP4yKCRnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679014998"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zCPc0-bKAM6-gvP4yKCRnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6-IX7YYByh-A-BiyxnI6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KBx8UMYQRpX3PQkFGueoQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KBx8UMYQRpX3PQkFGueoQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9NgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oYwYA56C57graUtOG96_nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oYwYA56C57graUtOG96_nA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "B9gX7YYBBkbVtX3nybyn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cPi-mObQCSuLuQtVOYID8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["cPi-mObQCSuLuQtVOYID8A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8tgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZiZ1khLLMUzgYWGwfUZAEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ZiZ1khLLMUzgYWGwfUZAEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "CtgX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QOIxcZGbFuLnj5qiY-JZYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QOIxcZGbFuLnj5qiY-JZYA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "D9gX7YYBBkbVtX3nyLm_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MifoGEhGkhx--3Mqfb9VJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["MifoGEhGkhx--3Mqfb9VJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ENgX7YYBBkbVtX3nxrZ1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "aGgX7YYBO2e_P_QbyDUv"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B8raI5jTg6GXkSRywQ53Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["B8raI5jTg6GXkSRywQ53Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-dgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZxBtZLIjgWi7iyuWzr-iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WZxBtZLIjgWi7iyuWzr-iQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-tgX7YYBBkbVtX3nyLi_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7UUpVBUGNxt4Ms0zx7knOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7UUpVBUGNxt4Ms0zx7knOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "_GgX7YYBO2e_P_QbyDby"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rEkYGzV5vYiC3DhSryqVzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rEkYGzV5vYiC3DhSryqVzg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "912537196463226"} {"create": {"_index": "profiling-events-all", "_id": "edgX7YYBBkbVtX3nyr0i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aAA5WQcp21IBohTIC-KUcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aAA5WQcp21IBohTIC-KUcA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "185231699804121"} {"create": {"_index": "profiling-events-all", "_id": "0mgX7YYBO2e_P_QbxjDq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ljoZqY7uQluiDQvieY_xHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015003"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ljoZqY7uQluiDQvieY_xHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "sNgX7YYBBkbVtX3n5ctL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["94o6mawNwOW6nwyr1PAIHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["94o6mawNwOW6nwyr1PAIHQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EdgX7YYBBkbVtX3n589_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CHnrKd15QpNtnzP8YzFv4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "BeIX7YYByh-A-Biy5oju"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["23D9WYWWXJZPmgi3LP_trA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["23D9WYWWXJZPmgi3LP_trA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9tgX7YYBBkbVtX3n6tVt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0ymZCjvGyGb7nDgHKngF-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "YNgX7YYBBkbVtX3n5MiB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N9nJGzNJcPuG6DQBG0MiGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N9nJGzNJcPuG6DQBG0MiGw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "teIX7YYByh-A-Biy5oRq"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JCIWb9sew42V-gStKG8h5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["JCIWb9sew42V-gStKG8h5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "X9gX7YYBBkbVtX3n5MiB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hf9wGp5TNFiImJfF3zrljg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hf9wGp5TNFiImJfF3zrljg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LNgX7YYBBkbVtX3n6NFW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rrV7Sjn5wsa8oFmwH9R5gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rrV7Sjn5wsa8oFmwH9R5gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "593778632422369"} {"create": {"_index": "profiling-events-all", "_id": "k-IX7YYByh-A-Biy6IkE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Isxj784FFyTk5uDVdOf3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5Isxj784FFyTk5uDVdOf3A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "tWgX7YYBO2e_P_Qb6Vmk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XdZpM6-FAy3LMUV5bnSRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015009"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9XdZpM6-FAy3LMUV5bnSRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "bGgX7YYBO2e_P_Qb9Fyf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w8p8KkfgO4GoE7XBvV8MBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w8p8KkfgO4GoE7XBvV8MBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "q9gX7YYBBkbVtX3n9Ngk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eh7tA-88r0aWueK0gduVFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eh7tA-88r0aWueK0gduVFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "L9gX7YYBBkbVtX3n9t1e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNA5mzQxt3Xt7EAz1m9YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UNA5mzQxt3Xt7EAz1m9YnA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "qtgX7YYBBkbVtX3n9Ngk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2pZTlkqZkVB23pwCplHuMw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "pNgX7YYBBkbVtX3n9t6f"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NmZEPOVWjWJBf47eb30-vw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "INgX7YYBBkbVtX3n9Npj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_RT0FmavuUc4KjdMUFBgQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_RT0FmavuUc4KjdMUFBgQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "amgX7YYBO2e_P_Qb9Fyf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kj2172QFM0X7LD-Gx8UmJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kj2172QFM0X7LD-Gx8UmJw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "weIX7YYByh-A-Biy9pUg"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gy1xDSDhU1AfdIq4EG9-FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gy1xDSDhU1AfdIq4EG9-FQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "wuIX7YYByh-A-Biy9pUg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mUkt5tksckG11QdtV8P-sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mUkt5tksckG11QdtV8P-sg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "w-IX7YYByh-A-Biy9pUg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9lQsXvjy3LhFC1-b9LfXYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9lQsXvjy3LhFC1-b9LfXYw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "59gX7YYBBkbVtX3n9-Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zqqnfectBMqAU_ittPTUXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zqqnfectBMqAU_ittPTUXw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "6NgX7YYBBkbVtX3n9-Eh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oolBkHFnNQYADD_-CEG1dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oolBkHFnNQYADD_-CEG1dg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "YGgX7YYBO2e_P_Qb91-r"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ayg1IWi6ap3XN7RjaMkRog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ayg1IWi6ap3XN7RjaMkRog"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "2WgX7YYBO2e_P_Qb-GAl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["caAMI7G9uz-EPxuo9p-Rlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["caAMI7G9uz-EPxuo9p-Rlw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "22gX7YYBO2e_P_Qb-GAl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SK9-X9x1FnjCdB8Qub4LOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SK9-X9x1FnjCdB8Qub4LOA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "QNgX7YYBBkbVtX3n-eUA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FFVbA2EfVlyNzePqODxsIg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "omgX7YYBO2e_P_Qb-WKP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hphMgjf8tLvtIOhJJeMEOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "bWgX7YYBO2e_P_Qb9Fyf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aFO2N7Q8fL-8kQlkLemsmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aFO2N7Q8fL-8kQlkLemsmw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "6eIX7YYByh-A-Biy9JDf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9Lb4LkSEfwYPtryFWDMnyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9Lb4LkSEfwYPtryFWDMnyw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "72gX7YYBO2e_P_Qb9V0w"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FqFEzaoqPCnPS62kfi9IsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FqFEzaoqPCnPS62kfi9IsQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "3GgX7YYBO2e_P_Qb-GAl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dnsAy4vOc46KZJiS5dGT6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dnsAy4vOc46KZJiS5dGT6w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "fOIX7YYByh-A-Biy-Jux"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H5yRjwOS6pZnYwq27kzT4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015015"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H5yRjwOS6pZnYwq27kzT4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "3NgY7YYBBkbVtX3nFPOh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yrneF8Y5HnZdPRsa0iSPNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yrneF8Y5HnZdPRsa0iSPNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "AdgY7YYBBkbVtX3nFfV_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z1gE1Mfy7QCTfP_33bgibg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["z1gE1Mfy7QCTfP_33bgibg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "cmgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qS1V-akFaaHNHyzPM1noGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qS1V-akFaaHNHyzPM1noGA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "g2gY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0E7LlamNni9h1zgUjdYD5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["0E7LlamNni9h1zgUjdYD5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuIY7YYByh-A-BiyFK7j"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IszPO-W_NZpvHwzVXBdHRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IszPO-W_NZpvHwzVXBdHRA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "huIY7YYByh-A-BiyFa86"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Jwj9IGMM0jWZjOAtjE9d7Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "gmgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PpzV6LTOPBnvw6J3GGPQ2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "FOIY7YYByh-A-BiyFrK6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["669-RSmA7VOx7u87DdbW9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["669-RSmA7VOx7u87DdbW9A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "emgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FTqEftgEgF-4HalIRfrGpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["FTqEftgEgF-4HalIRfrGpw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "UNgY7YYBBkbVtX3nFvYC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["un7IXZTDX45vlOErtbBxEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["un7IXZTDX45vlOErtbBxEw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "dmgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-49a_E8AcF9JV2D17KJ99g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-49a_E8AcF9JV2D17KJ99g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "fmgY7YYBO2e_P_QbF3eI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v9LkFqrZacA9LLL7WL6jjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015021"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v9LkFqrZacA9LLL7WL6jjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "8OIY7YYByh-A-BiyJcLd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N1nRjzqOIB8y-j3xmzMaSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["N1nRjzqOIB8y-j3xmzMaSQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uNkY7YYBBkbVtX3nJgIl"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fu2t6m-D5UJUa1S1LIOpkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Fu2t6m-D5UJUa1S1LIOpkg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xOIY7YYByh-A-BiyJL_0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iIMesBrzxgfShUvivBC9VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["iIMesBrzxgfShUvivBC9VA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "w-IY7YYByh-A-BiyJL_0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["37H1sSWP9fHHtDykTwvxJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["37H1sSWP9fHHtDykTwvxJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1GgY7YYBO2e_P_QbJ3-M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_7eGxy7JatY66SnXVDAow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["X_7eGxy7JatY66SnXVDAow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "m-IY7YYByh-A-BiyI7hA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_lMqaF4gbGiFm8tgIiB6eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8eIY7YYByh-A-BiyJcLd"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DhfNoyryOAVBksH9W9zZsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DhfNoyryOAVBksH9W9zZsA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "buIY7YYByh-A-BiyJsRk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6qn8dRThwMb4sKyHdsYIBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6qn8dRThwMb4sKyHdsYIBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "7OIY7YYByh-A-BiyJsXZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zcSEgSwv-OAVAhTXWGeqFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "2OIY7YYByh-A-BiyJ8jR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzyVw9-CnV8kDbp00nDLdQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "weIY7YYByh-A-BiyJL_0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dZVhEMwoIzMGD6Fthzhnhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dZVhEMwoIzMGD6Fthzhnhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "VeIY7YYByh-A-BiyJcFE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LecKeTt-RiFscqL1ypA3eg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "1-IY7YYByh-A-BiyJ8jR"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "qdkY7YYBBkbVtX3nKAVC"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8giK6mKV7HDPF-jB4e6ajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8giK6mKV7HDPF-jB4e6ajg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "uNgY7YYBBkbVtX3nI_yO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CukCYxi-_rBPDB0cFOdQXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["CukCYxi-_rBPDB0cFOdQXg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "ZuIY7YYByh-A-BiyJ8dT"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zY_FUxiP8lY6XZ2ati0KCg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "02gY7YYBO2e_P_QbJ3-M"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6EgKcwZlK3OnMM95lvD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n6EgKcwZlK3OnMM95lvD6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "KtkY7YYBBkbVtX3nJQGU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8E3vSloXP4dGqDQFAfS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015026"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8E3vSloXP4dGqDQFAfS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "L-IY7YYByh-A-BiyNNjt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["prglnbSV--xvMX6ZLnrz1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["prglnbSV--xvMX6ZLnrz1w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "9WgY7YYBO2e_P_QbNYme"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jPkF12I7d8n_WLfO9tfRDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["jPkF12I7d8n_WLfO9tfRDQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "GOIY7YYByh-A-BiyQttA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DOr2yspH9ybCB1ZnzV8BJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "28424007785283"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DOr2yspH9ybCB1ZnzV8BJA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "28424007785283"} {"create": {"_index": "profiling-events-all", "_id": "_2gY7YYBO2e_P_QbN49p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FcDW-9IPZrZmO_AgR-UVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4FcDW-9IPZrZmO_AgR-UVw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-mgY7YYBO2e_P_QbN5Lt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7o7oGlZRFPD9hlZHJQFaeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7o7oGlZRFPD9hlZHJQFaeQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "1OIY7YYByh-A-BiyNNZi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6pwviWWoW88bcg8mc6jotg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6pwviWWoW88bcg8mc6jotg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "o2gY7YYBO2e_P_QbNIap"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n8NV_5qPZ2yDzkxHAj-OJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["n8NV_5qPZ2yDzkxHAj-OJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "82gY7YYBO2e_P_QbNoxr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrEr2m1NreTQiIcNz23_Gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NrEr2m1NreTQiIcNz23_Gw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-WgY7YYBO2e_P_QbN5Lt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9hAbTICOesyJ3rX_TlmZDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9hAbTICOesyJ3rX_TlmZDg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "dNkY7YYBBkbVtX3nNgnQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v78L_ndncKY9XP2euXU8Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["v78L_ndncKY9XP2euXU8Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "XmgY7YYBO2e_P_QbN44i"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KUsQV-D79LhBqY3oMEarqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KUsQV-D79LhBqY3oMEarqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "AGgY7YYBO2e_P_QbN5Bp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dePPhixGpLKqsMMaUhbMkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dePPhixGpLKqsMMaUhbMkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "GuIY7YYByh-A-BiyQttA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRG5hVGVXLYVZ0h02g0udQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PRG5hVGVXLYVZ0h02g0udQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "v2gY7YYBO2e_P_QbQpW8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BoYpwBWsEY6dEqkJuaGn-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["BoYpwBWsEY6dEqkJuaGn-A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "geIY7YYByh-A-BiyNtki"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y5c1W4V6QarCea1y_uUiVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Y5c1W4V6QarCea1y_uUiVg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "xOIY7YYByh-A-BiyQtx5"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_EsHnjdKV8IAlrfbLT931Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_EsHnjdKV8IAlrfbLT931Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "f2gY7YYBO2e_P_QbNYvj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xRhapVs8DimQtljSm9PXHw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "XNkY7YYBBkbVtX3nQwsG"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015031"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gWZieSymYI-RQt59eFJ4Sw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "2-IY7YYByh-A-BiyUejm"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ji4fn9UFUEQ7XfPSlCiJjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Ji4fn9UFUEQ7XfPSlCiJjg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LdkY7YYBBkbVtX3nVR6C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["7V6aRLUSfKlOcOf1w7yKYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VuIY7YYByh-A-BiyVPT1"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["09uypqtTxXlIuIrZVnABBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["09uypqtTxXlIuIrZVnABBQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "u2gY7YYBO2e_P_QbV7Dp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6De01qCjG4YpvC5r_qYJMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6De01qCjG4YpvC5r_qYJMQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "0WgY7YYBO2e_P_QbVKit"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qT_CR4Hw6yXc7SN2JGsRRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qT_CR4Hw6yXc7SN2JGsRRQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "utkY7YYBBkbVtX3nVh_a"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8E3vSloXP4dGqDQFAfS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015038"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8E3vSloXP4dGqDQFAfS1g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "6mgY7YYBO2e_P_QbY7m_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XS_2yHDH56Gg_3eKY-7t_A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MWgY7YYBO2e_P_QbY7vw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a3dWczo-TxKbn0vDhRIXuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["a3dWczo-TxKbn0vDhRIXuw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "NeIY7YYByh-A-Biycv3l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qPIovnY_UHTfC1t6f4Hr0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qPIovnY_UHTfC1t6f4Hr0A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mGgY7YYBO2e_P_QbZLxc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xxGhbam4Rod7gS2xhM6-rQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xxGhbam4Rod7gS2xhM6-rQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "655028827703124"} {"create": {"_index": "profiling-events-all", "_id": "mdkY7YYBBkbVtX3nZCuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NZK8SEhO7ETtVZs2wRmpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NZK8SEhO7ETtVZs2wRmpXA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "z2gY7YYBO2e_P_QbZb1H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6z5XvcuxP2TQmMVJ9Pasiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6z5XvcuxP2TQmMVJ9Pasiw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "LmgY7YYBO2e_P_QbY7vw"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aqb62SWn_8yiLVVMpKijFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["aqb62SWn_8yiLVVMpKijFA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KmgY7YYBO2e_P_QbccJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mqpWTSIkoRMfDHbRs_VWxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mqpWTSIkoRMfDHbRs_VWxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "EdkY7YYBBkbVtX3nYymL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5e_C8GLvZaqyeMf6f2Lo5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["5e_C8GLvZaqyeMf6f2Lo5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ndkY7YYBBkbVtX3nZCuT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SuyrLdAGlB-Gqd6pTlCwTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["SuyrLdAGlB-Gqd6pTlCwTg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "0GgY7YYBO2e_P_QbZb1H"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oHVZwEtujopOZewM6A1sxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oHVZwEtujopOZewM6A1sxw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "KmgY7YYBO2e_P_QbZb97"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xFHH2tMDnbWLZHLGtCUC2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xFHH2tMDnbWLZHLGtCUC2w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "imgY7YYBO2e_P_QbZcCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DYzhVpKjZS7RL_ti--DyeA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "22gY7YYBO2e_P_QbZsEV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qP-re8P6QAZOuUpAbsv0YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qP-re8P6QAZOuUpAbsv0YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "K2gY7YYBO2e_P_QbccJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xTmXxLtxYtdjX3OFWgcBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xTmXxLtxYtdjX3OFWgcBtA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "LGgY7YYBO2e_P_QbccJo"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AnUv3QN_2ZayAg10mwHHFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["AnUv3QN_2ZayAg10mwHHFg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "JmgY7YYBO2e_P_QbccS0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_wx1WU4Q3GTegN_cAxP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["d_wx1WU4Q3GTegN_cAxP6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "KWgY7YYBO2e_P_QbccS0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "M-IY7YYByh-A-Biycv3l"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TkPEPsUQlEC8_tTSu1y8wA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["TkPEPsUQlEC8_tTSu1y8wA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "UtkY7YYBBkbVtX3nZCoi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "zdkY7YYBBkbVtX3nZSze"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-Q9uCXR-TIx0LsEoXVwIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m-Q9uCXR-TIx0LsEoXVwIw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "A2gY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tdbcrn8vzrvt_4QGjPmE2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["tdbcrn8vzrvt_4QGjPmE2g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "52gY7YYBO2e_P_QbY7m_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eYaT0VAMxHUuQFovR7m_6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["eYaT0VAMxHUuQFovR7m_6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "LtkY7YYBBkbVtX3ncS0m"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8nNNC34bhCi_Q3XemgSrmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8nNNC34bhCi_Q3XemgSrmg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "mmgY7YYBO2e_P_QbZLxc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HPhJ76yG2kEeQYFKH7p-MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HPhJ76yG2kEeQYFKH7p-MA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "KWgY7YYBO2e_P_QbZb97"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XSWbewDyObNkMZFMkquH-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XSWbewDyObNkMZFMkquH-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "AWgY7YYBO2e_P_QbcsYD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKIT-6sz8Rcrv5dQo1Svfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015043"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["NKIT-6sz8Rcrv5dQo1Svfw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "ctkY7YYBBkbVtX3ndz2e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZnHw5ixNOyEf6O56DZTMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2ZnHw5ixNOyEf6O56DZTMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "XeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["du1yLIRbc8pGUnVxG87AUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["du1yLIRbc8pGUnVxG87AUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "WNkY7YYBBkbVtX3ngT9T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XHyQmv623xT6Vtggew3Wqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["XHyQmv623xT6Vtggew3Wqg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "h9kY7YYBBkbVtX3ngkIQ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mcFH-Ijp7M4Pm2g7nfowcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["mcFH-Ijp7M4Pm2g7nfowcw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uo-tsEXpjnB3_59QNk30uA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Uo-tsEXpjnB3_59QNk30uA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "VeMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3PF9c3wvWuSHWSRQ7lpy-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3PF9c3wvWuSHWSRQ7lpy-Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "2tkY7YYBBkbVtX3ngD3A"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oZgJu0hCy8YZbrC7PCpVUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["oZgJu0hCy8YZbrC7PCpVUw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "WuMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["htfRCBFoc4VoJwgN8Ytl-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["htfRCBFoc4VoJwgN8Ytl-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "I2gY7YYBO2e_P_QbhNYj"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wxU1Nh02nDVQ06j0OKiikg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wxU1Nh02nDVQ06j0OKiikg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "odkY7YYBBkbVtX3ng0c0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wVWlhmLrIg4ezt4I6Uq9DA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wVWlhmLrIg4ezt4I6Uq9DA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "ZOMY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9Ccnybqafbd-z2JQ7pZb5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9Ccnybqafbd-z2JQ7pZb5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "V-MY7YYByh-A-Biygw3o"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["no86XANJmCvhh479J_f39Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["no86XANJmCvhh479J_f39Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "kmgY7YYBO2e_P_QbhNep"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015048"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-k7aCJZCelwDj5FONxW39w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "62gY7YYBO2e_P_QbkuQ0"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wBeDaSzmKMf_8mF4P9fF3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wBeDaSzmKMf_8mF4P9fF3g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "xmgY7YYBO2e_P_QbkN-c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IdBN0EzRB0f6Qp7f7scKtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["IdBN0EzRB0f6Qp7f7scKtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "KWgY7YYBO2e_P_Qbk-gA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6iO15jj0vZmOpf_lsLTSaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["6iO15jj0vZmOpf_lsLTSaw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "52gY7YYBO2e_P_QbkuZr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yHqWimgCZ0j1831FpP56GA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yHqWimgCZ0j1831FpP56GA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "OGgY7YYBO2e_P_Qbk-uk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3qMqnk45t6i15Udhr4oapw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["3qMqnk45t6i15Udhr4oapw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "6WgY7YYBO2e_P_QbkuZr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rnLojn4fqQT9heBUwPp6cQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rnLojn4fqQT9heBUwPp6cQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "CGgY7YYBO2e_P_QblO3v"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H0rCzOrKLVxIax6VmWoTig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["H0rCzOrKLVxIax6VmWoTig"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "zGgY7YYBO2e_P_QbkeNU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sSkvuXEJhjIUI110bPCy-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sSkvuXEJhjIUI110bPCy-w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "FeMY7YYByh-A-Biykxnn"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["agY1HwGqzbbYSgz0edbUzw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "aOMY7YYByh-A-Biykhac"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WiKwol9D35rFlRLvyV8-tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["WiKwol9D35rFlRLvyV8-tg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "KmgY7YYBO2e_P_Qbk-gA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rItueCZaxnQa_9rqoUOBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rItueCZaxnQa_9rqoUOBQA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "iGgY7YYBO2e_P_Qbk-kz"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tv9_UdgbHXgClRQg7kZh6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Tv9_UdgbHXgClRQg7kZh6g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "xWgY7YYBO2e_P_QbkN-c"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QnM8HV7T1nK2sOOhDjm8wQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015054"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["QnM8HV7T1nK2sOOhDjm8wQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "gGkY7YYBO2e_P_QbsgDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcKiZ-b2RFkrAj_Eljk6ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qcKiZ-b2RFkrAj_Eljk6ag"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "g2kY7YYBO2e_P_QbswJc"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w8p8KkfgO4GoE7XBvV8MBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["w8p8KkfgO4GoE7XBvV8MBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "EmgY7YYBO2e_P_Qbr__k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sF6SO51w2foii8iV20ReXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sF6SO51w2foii8iV20ReXQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "MOMY7YYByh-A-BiysSoN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0YIAzJAuzs0WiByN2XHsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["e0YIAzJAuzs0WiByN2XHsg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OOMY7YYByh-A-Biysy8e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Al1SOAD2scJtZ2cFUS9UOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Al1SOAD2scJtZ2cFUS9UOw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ttkY7YYBBkbVtX3npWUb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YQRMSvf2Ru8AcSIkw-mV5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YQRMSvf2Ru8AcSIkw-mV5w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "keMY7YYByh-A-BiypicL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PrVZV3ALGpaU9_iaCjGLFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OeMY7YYByh-A-Biysy8e"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["arX6P2NMEiJ9Egas3Yju4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["arX6P2NMEiJ9Egas3Yju4Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4mgY7YYBO2e_P_QbpfyH"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UeKlcYjnbAHyjaIKIYLphA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UeKlcYjnbAHyjaIKIYLphA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "753717090513272"} {"create": {"_index": "profiling-events-all", "_id": "EWgY7YYBO2e_P_Qbr__k"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LozaztVRNbKlSptg74c_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LozaztVRNbKlSptg74c_Jg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "idkY7YYBBkbVtX3nsnOa"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["wQIwclgSqKb144G75yYx4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "59kY7YYBBkbVtX3nsGgr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zx1Svrv0kOSpq-dJ-FTQBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Zx1Svrv0kOSpq-dJ-FTQBg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "gWkY7YYBO2e_P_QbsgDe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["GsN99ThxwcvQFCb-5zng-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "6NkY7YYBBkbVtX3nsGgr"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2eLfnVQNj-bq7jOAcpsXhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["2eLfnVQNj-bq7jOAcpsXhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "G9kY7YYBBkbVtX3nsW5T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vRLmJJNBX8J2JJ9imi8dPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vRLmJJNBX8J2JJ9imi8dPw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "8tkY7YYBBkbVtX3nsW-T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zXbfPFB1rTaAbyUdHQG_SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zXbfPFB1rTaAbyUdHQG_SA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "leMY7YYByh-A-BiysCjE"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015060"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Su83jhjLPwV0cqJbphC9gg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "sOMY7YYByh-A-BiywTh8"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T3r2NA911Mk7g-SMfweEhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["T3r2NA911Mk7g-SMfweEhA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "-GkY7YYBO2e_P_QbwhAA"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZMRPR03JgKHHwNCTAFzKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["xZMRPR03JgKHHwNCTAFzKA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "t2kY7YYBO2e_P_Qbvwu3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["4O45TJyRIp_Dj0IxvNdxwA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "hWkY7YYBO2e_P_QbxBZh"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["rDKxniIVk0RWV976qb-dNg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ENkY7YYBBkbVtX3nw4Mt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzzAkS68b-k5mSq1f5rBNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["vzzAkS68b-k5mSq1f5rBNA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "89kY7YYBBkbVtX3nxYcT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t0TDin4EdglS8jVWcSlCQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["t0TDin4EdglS8jVWcSlCQQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "Z-MY7YYByh-A-BiywjpO"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HkfH8phILjoSDOJDy-1TVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["HkfH8phILjoSDOJDy-1TVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "735060693165125"} {"create": {"_index": "profiling-events-all", "_id": "oeMY7YYByh-A-BiyvzV7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["owNoaLSdddyWnE6x23fIMg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "uGkY7YYBO2e_P_Qbvwu3"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["50PoVbLjF0hCNpsgtuMl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["50PoVbLjF0hCNpsgtuMl5g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "155673490075071"} {"create": {"_index": "profiling-events-all", "_id": "IGkY7YYBO2e_P_QbwQ-_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_2COh-c_qJv_z47LD8F6aQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["_2COh-c_qJv_z47LD8F6aQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "otkY7YYBBkbVtX3nwX8u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ52LnrbGCiUbg8bZ6HPVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["kZ52LnrbGCiUbg8bZ6HPVA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "ONkY7YYBBkbVtX3nwoGT"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zlmxsTTPMJDp5d_OFnqBkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015066"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["zlmxsTTPMJDp5d_OFnqBkA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "225915668823970"} {"create": {"_index": "profiling-events-all", "_id": "FdkY7YYBBkbVtX3n1Zdg"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x5qgOI8HL9M8dwwOC7QfqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["x5qgOI8HL9M8dwwOC7QfqQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "uOMY7YYByh-A-Biy00jx"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o0urJMnM1NXJ1Ig4b1nz4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["o0urJMnM1NXJ1Ig4b1nz4w"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DOMY7YYByh-A-Biy307C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Px9VhqdWCVDdltHe9kzD-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Px9VhqdWCVDdltHe9kzD-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "weMY7YYByh-A-Biy30wN"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c20y3BjHH79qpRmxtyoGUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["c20y3BjHH79qpRmxtyoGUg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8tkY7YYBBkbVtX3n4Z2b"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-xs-IN1fdS4tlSIAXAM4kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-xs-IN1fdS4tlSIAXAM4kA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mmkY7YYBO2e_P_Qb3yiB"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-BJw7BDfkkLGBaeu3mTtJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-BJw7BDfkkLGBaeu3mTtJQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "519693549829104"} {"create": {"_index": "profiling-events-all", "_id": "TWkY7YYBO2e_P_Qb4CzM"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L7ZSY8z08KFXsns3lbxH5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015072"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["L7ZSY8z08KFXsns3lbxH5Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "223817267962438"} {"create": {"_index": "profiling-events-all", "_id": "kNkY7YYBBkbVtX3n86-p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uYDpyfGeOoejLkBpWIKnAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uYDpyfGeOoejLkBpWIKnAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "8GkY7YYBO2e_P_Qb8z_u"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YytYhoY7fNuX6DzylApjYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["YytYhoY7fNuX6DzylApjYg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "z9kY7YYBBkbVtX3n8KgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gZIR2lVaJhOjBF7vmgmNTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gZIR2lVaJhOjBF7vmgmNTQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PeMY7YYByh-A-Biy71tL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q1rqIqW9LrYYmMwOZmvxpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741972803009497"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["Q1rqIqW9LrYYmMwOZmvxpQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "741972803009497"} {"create": {"_index": "profiling-events-all", "_id": "GdkY7YYBBkbVtX3n7qav"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XVoH_w6DR25OZwFIyOORA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["8XVoH_w6DR25OZwFIyOORA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "zdkY7YYBBkbVtX3n8KgW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g1TbNQs0ehcm3oPXvCWrtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["g1TbNQs0ehcm3oPXvCWrtw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "L2kY7YYBO2e_P_Qb8DhZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["l8dMyIgFlKWEMYc0z_PTTw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "aeMY7YYByh-A-Biy8WB6"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KS1N91hvcJHrA9nDriwgFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KS1N91hvcJHrA9nDriwgFQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "RWkY7YYBO2e_P_Qb8Tn-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaFi74Q4Xy5P6KWfZhKbcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["LaFi74Q4Xy5P6KWfZhKbcQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "o2kY7YYBO2e_P_Qb8jtD"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9IwRLO6YrlzStGJvTS80NQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9IwRLO6YrlzStGJvTS80NQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "ktkY7YYBBkbVtX3n86-p"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k0qCCYXqNQJOeLz2IVSZQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["k0qCCYXqNQJOeLz2IVSZQg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "umkY7YYBO2e_P_Qb8z4T"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["70l8tnr4W3Z0mVbnzrtQHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["70l8tnr4W3Z0mVbnzrtQHg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "101554038790413"} {"create": {"_index": "profiling-events-all", "_id": "GNkY7YYBBkbVtX3n7qav"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nUFT-4VjV49edA4VHVD06g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["nUFT-4VjV49edA4VHVD06g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "a9kY7YYBBkbVtX3n76eU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qOQyvHeuCAo4zHM-_nszjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["qOQyvHeuCAo4zHM-_nszjQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "R2kY7YYBO2e_P_Qb8Tn-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uc-1qhzAwxbFSC_X5eM6rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uc-1qhzAwxbFSC_X5eM6rg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "qeMY7YYByh-A-Biy8mGJ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015078"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["gqOeBsFKwbfOrCtYQX86QA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "0WkZ7YYBO2e_P_QbA0mU"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["531_Sc4IW-g1NnLnDZ_hAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["531_Sc4IW-g1NnLnDZ_hAw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "mOMY7YYByh-A-Biy_2g-"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yvtvFpnNQbBerz7eTt0frA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["yvtvFpnNQbBerz7eTt0frA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "yOMY7YYByh-A-Biy_WPI"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S_m5ubmad7O5PrXj5rj9Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["S_m5ubmad7O5PrXj5rj9Lg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "OdkY7YYBBkbVtX3n_rPy"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UIIxveWnS2le63DPhl04ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["UIIxveWnS2le63DPhl04ow"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "DNkZ7YYBBkbVtX3nALrp"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["hc391qiEl23bWsvU8MIb2A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "r9kZ7YYBBkbVtX3nAb2C"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["-89SlyV8Cy-1WAJzSWKJpA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "4eMZ7YYByh-A-BiyA25V"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fX0UJpw-KoGxCTaJrmVo4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fX0UJpw-KoGxCTaJrmVo4A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "8724791948669"} {"create": {"_index": "profiling-events-all", "_id": "s-MZ7YYByh-A-BiyAmtP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m7PtklSiInmoO66e9Bc5vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m7PtklSiInmoO66e9Bc5vA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "b2kZ7YYBO2e_P_QbAkjS"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uEFo0fAyVpU8ZWzISquVFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015082"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["uEFo0fAyVpU8ZWzISquVFw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "ZuMZ7YYByh-A-BiyIIrY"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m_wtbg2jNShExrSNavS6Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["m_wtbg2jNShExrSNavS6Zw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "jOMZ7YYByh-A-BiyIYwb"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9jnZQOhoYX8CygYi9lQy6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["9jnZQOhoYX8CygYi9lQy6A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "y9kZ7YYBBkbVtX3nHcyi"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["23l0LEXPY3gsZ-a1uM9WBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["23l0LEXPY3gsZ-a1uM9WBA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "smkZ7YYBO2e_P_QbH1uL"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ITsishoJBrPM8Hg7nurVvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["ITsishoJBrPM8Hg7nurVvw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "UeMZ7YYByh-A-BiyIZCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dhc8TGgYU9zTniCUbRsImw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "JdkZ7YYBBkbVtX3nItGe"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dePPhixGpLKqsMMaUhbMkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["dePPhixGpLKqsMMaUhbMkw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "142370256725062"} {"create": {"_index": "profiling-events-all", "_id": "SeMZ7YYByh-A-BiyIIcW"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VMoxalfDKAgIFkHWZsfI5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["VMoxalfDKAgIFkHWZsfI5A"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "63051752474107"} {"create": {"_index": "profiling-events-all", "_id": "NGkZ7YYBO2e_P_QbH13S"}} -{"Stacktrace.count": [2], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [2], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PSEpVUXXmwRmI0xaCh6Phw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "kmkZ7YYBO2e_P_QbHVZZ"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sZZOI74zGf6PIWKQ_dowgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["sZZOI74zGf6PIWKQ_dowgg"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} {"create": {"_index": "profiling-events-all", "_id": "0-MZ7YYByh-A-BiyIIiV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PlNxGYc1KQXo_krOBCj9YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["PlNxGYc1KQXo_krOBCj9YQ"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "UOMZ7YYByh-A-BiyIZCt"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["41RJH9BALozcwHa5Gm2tSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015089"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["41RJH9BALozcwHa5Gm2tSA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "771160218874332"} {"create": {"_index": "profiling-events-all", "_id": "dOMZ7YYByh-A-BiyLpYf"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["24W9wid4frXP9gbNlVQM1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["24W9wid4frXP9gbNlVQM1Q"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "QmkZ7YYBO2e_P_QbMG8B"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["73zzSG8Oeil4xVlA4Fb1Bw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "m9kZ7YYBBkbVtX3nLdV_"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVK6SdkNVQXTuBBVfv40gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["fVK6SdkNVQXTuBBVfv40gA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "PWkZ7YYBO2e_P_QbLmiP"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DuNugRyUNKQa9O6ipjRVvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["DuNugRyUNKQa9O6ipjRVvA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "396043593875219"} {"create": {"_index": "profiling-events-all", "_id": "ruMZ7YYByh-A-BiyMJc7"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["77Snmu-jdy67fU04W-9dhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["77Snmu-jdy67fU04W-9dhw"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "kuMZ7YYByh-A-BiyMZoV"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "container.name": ["instance-0000000010"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["spFZMKZslqx7eLmYXiBH-g"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "734549507527372"} {"create": {"_index": "profiling-events-all", "_id": "IOMZ7YYByh-A-BiyLZXk"}} -{"Stacktrace.count": [1], "service.name": ["3145700"], "host.ipstring": ["192.168.1.2"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KSia5T4oTtHLzN1hXKSrIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} +{"Stacktrace.count": [1], "service.name": ["3145700"], "os.kernel": "9.9.9-0", "tags": ["environment:qa", "region:eu-west-1"], "host.ip": ["192.168.1.2"], "@timestamp": ["1679015095"], "ecs.version": ["1.12.0"], "Stacktrace.id": ["KSia5T4oTtHLzN1hXKSrIA"], "agent.version": ["head-824028ac-1667300398"], "host.name": ["ip-192-168-1-2"], "host.id": ["9006254961723227446"], "process.thread.name": "206461370845245"} diff --git a/x-pack/plugins/profiling/server/lib/setup/steps/component_template_profiling_events.json b/x-pack/plugins/profiling/server/lib/setup/steps/component_template_profiling_events.json index d1df1d206b60f..8a45a18ff60e6 100644 --- a/x-pack/plugins/profiling/server/lib/setup/steps/component_template_profiling_events.json +++ b/x-pack/plugins/profiling/server/lib/setup/steps/component_template_profiling_events.json @@ -59,9 +59,6 @@ "host.ip": { "type": "ip" }, - "host.ipstring": { - "type": "keyword" - }, "host.name": { "type": "keyword" }, diff --git a/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_json.js b/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_json.js index adfb9aafc444e..daba32a3b9520 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_json.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_json.js @@ -6,22 +6,14 @@ */ import React from 'react'; - -import { EuiCodeEditor } from '../../../../../shared_imports'; +import { EuiCodeBlock } from '@elastic/eui'; export const TabJson = ({ json }) => { const jsonString = JSON.stringify(json, null, 2); return ( - + + {jsonString} + ); }; diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js index fa63639ef4d06..dc38bd3af9afd 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js @@ -322,9 +322,8 @@ describe('', () => { const tabContent = find('rollupJobDetailTabContent'); it('should render the "EuiCodeEditor" with the job "json" data', () => { - const euiCodeEditor = tabContent.find('EuiCodeEditor'); - expect(euiCodeEditor.length).toBeTruthy(); - expect(JSON.parse(euiCodeEditor.props().value)).toEqual(defaultJob.json); + const euiCodeEditor = tabContent.find('[data-test-subj="jsonCodeBlock"]').at(0); + expect(JSON.parse(euiCodeEditor.text())).toEqual(defaultJob.json); }); }); }); diff --git a/x-pack/plugins/rollup/public/shared_imports.ts b/x-pack/plugins/rollup/public/shared_imports.ts index cee1fe39bc16a..ed0d444ccb160 100644 --- a/x-pack/plugins/rollup/public/shared_imports.ts +++ b/x-pack/plugins/rollup/public/shared_imports.ts @@ -5,12 +5,7 @@ * 2.0. */ -export { - extractQueryParams, - indices, - SectionLoading, - EuiCodeEditor, -} from '@kbn/es-ui-shared-plugin/public'; +export { extractQueryParams, indices, SectionLoading } from '@kbn/es-ui-shared-plugin/public'; export { KibanaContextProvider, diff --git a/x-pack/plugins/rollup/server/collectors/register.test.ts b/x-pack/plugins/rollup/server/collectors/register.test.ts index 5180c54c9d41a..bcb026714dab5 100644 --- a/x-pack/plugins/rollup/server/collectors/register.test.ts +++ b/x-pack/plugins/rollup/server/collectors/register.test.ts @@ -10,17 +10,18 @@ import { registerRollupUsageCollector } from './register'; describe('registerRollupUsageCollector', () => { const mockIndex = 'mock_index'; + const getIndexForType = () => Promise.resolve(mockIndex); it('makes a usage collector and registers it`', () => { const mockCollectorSet = createUsageCollectionSetupMock(); - registerRollupUsageCollector(mockCollectorSet, mockIndex); + registerRollupUsageCollector(mockCollectorSet, getIndexForType); expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1); expect(mockCollectorSet.registerCollector).toBeCalledTimes(1); }); it('makeUsageCollector configs fit the shape', () => { const mockCollectorSet = createUsageCollectionSetupMock(); - registerRollupUsageCollector(mockCollectorSet, mockIndex); + registerRollupUsageCollector(mockCollectorSet, getIndexForType); expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({ type: 'rollups', isReady: expect.any(Function), @@ -81,7 +82,7 @@ describe('registerRollupUsageCollector', () => { it('makeUsageCollector config.isReady returns true', () => { const mockCollectorSet = createUsageCollectionSetupMock(); - registerRollupUsageCollector(mockCollectorSet, mockIndex); + registerRollupUsageCollector(mockCollectorSet, getIndexForType); const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; expect(usageCollectorConfig.isReady()).toBe(true); }); diff --git a/x-pack/plugins/rollup/server/collectors/register.ts b/x-pack/plugins/rollup/server/collectors/register.ts index 6fe44ee0309f9..6c893e058f0e7 100644 --- a/x-pack/plugins/rollup/server/collectors/register.ts +++ b/x-pack/plugins/rollup/server/collectors/register.ts @@ -38,7 +38,7 @@ interface Usage { export function registerRollupUsageCollector( usageCollection: UsageCollectionSetup, - kibanaIndex: string + getIndexForType: (type: string) => Promise ): void { const collector = usageCollection.makeUsageCollector({ type: 'rollups', @@ -92,23 +92,26 @@ export function registerRollupUsageCollector( }, }, fetch: async ({ esClient }: CollectorFetchContext) => { - const rollupIndexPatterns = await fetchRollupIndexPatterns(kibanaIndex, esClient); + const indexPatternIndex = await getIndexForType('index-pattern'); + const rollupIndexPatterns = await fetchRollupIndexPatterns(indexPatternIndex, esClient); const rollupIndexPatternToFlagMap = createIdToFlagMap(rollupIndexPatterns); + const searchIndex = await getIndexForType('search'); const rollupSavedSearches = await fetchRollupSavedSearches( - kibanaIndex, + searchIndex, esClient, rollupIndexPatternToFlagMap ); const rollupSavedSearchesToFlagMap = createIdToFlagMap(rollupSavedSearches); + const visualizationIndex = await getIndexForType('visualization'); const { rollupVisualizations, rollupVisualizationsFromSavedSearches, rollupLensVisualizations, rollupLensVisualizationsFromSavedSearches, } = await fetchRollupVisualizations( - kibanaIndex, + visualizationIndex, esClient, rollupIndexPatternToFlagMap, rollupSavedSearchesToFlagMap diff --git a/x-pack/plugins/rollup/server/plugin.ts b/x-pack/plugins/rollup/server/plugin.ts index 91538ae9a375d..06416685ab508 100644 --- a/x-pack/plugins/rollup/server/plugin.ts +++ b/x-pack/plugins/rollup/server/plugin.ts @@ -92,7 +92,9 @@ export class RollupPlugin implements Plugin { if (usageCollection) { try { - registerRollupUsageCollector(usageCollection, savedObjects.getKibanaIndex()); + const getIndexForType = (type: string) => + getStartServices().then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); + registerRollupUsageCollector(usageCollection, getIndexForType); } catch (e) { this.logger.warn(`Registering Rollup collector failed: ${e}`); } diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts index 581c77670a153..4efe4d876a3bc 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.test.ts @@ -72,6 +72,11 @@ it('matches snapshot', () => { "required": false, "type": "date", }, + "kibana.alert.maintenance_window_ids": Object { + "array": true, + "required": false, + "type": "keyword", + }, "kibana.alert.reason": Object { "array": false, "required": false, @@ -269,6 +274,13 @@ it('matches snapshot', () => { "required": false, "type": "date_range", }, + "kibana.alert.url": Object { + "array": false, + "ignore_above": 2048, + "index": false, + "required": false, + "type": "keyword", + }, "kibana.alert.uuid": Object { "array": false, "required": true, diff --git a/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts b/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts index 93e182e53af63..d914d6e7a580f 100644 --- a/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts +++ b/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts @@ -77,7 +77,7 @@ type CastSingle> = t.Type< >; const createCastArrayRt = >(type: T): CastArray => { - const union = t.union([type, t.array(type)]); + const union = t.union([type, t.array(t.union([type, t.nullType]))]); return new t.Type('castArray', union.is, union.validate, (a) => (Array.isArray(a) ? a : [a])); }; diff --git a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts index 1e55fbe047ddc..3bf83993df1e8 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts @@ -2098,6 +2098,7 @@ describe('createGetSummarizedAlertsFn', () => { query: { kql: 'kibana.alert.rule.name:test', dsl: '{"bool":{"minimum_should_match":1,"should":[{"match":{"kibana.alert.rule.name":"test"}}]}}', + filters: [], }, timeframe: { days: [1, 2, 3, 4, 5], @@ -2147,11 +2148,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2159,17 +2161,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -2231,11 +2234,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2243,17 +2247,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -2315,11 +2320,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2327,17 +2333,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { diff --git a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts index 4a7a727276ad4..d4fe127b240ee 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts @@ -573,10 +573,11 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta script: { script: { source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', params: { days: alertsFilter.timeframe.days, timezone: alertsFilter.timeframe.timezone, + datetimeField: TIMESTAMP, }, }, }, @@ -585,12 +586,12 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta script: { script: { source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -610,6 +611,7 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta start: alertsFilter.timeframe.hours.start, end: alertsFilter.timeframe.hours.end, timezone: alertsFilter.timeframe.timezone, + datetimeField: TIMESTAMP, }, }, }, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts index 375deb87aa7fd..daaac95f952f1 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts @@ -9,6 +9,7 @@ import { loggerMock } from '@kbn/logging-mocks'; import { pick } from 'lodash'; import { ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, ALERT_RULE_CATEGORY, ALERT_RULE_CONSUMER, ALERT_RULE_NAME, @@ -921,6 +922,332 @@ describe('createLifecycleExecutor', () => { }); }); + describe('set maintenance window ids on the document', () => { + const maintenanceWindowIds = ['test-id-1', 'test-id-2']; + + it('updates documents with maintenance window ids for newly firing alerts', async () => { + const logger = loggerMock.create(); + const ruleDataClientMock = createRuleDataClientMock(); + const executor = createLifecycleExecutor( + logger, + ruleDataClientMock + )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { + services.alertWithLifecycle({ + id: 'TEST_ALERT_0', + fields: { [TAGS]: ['source-tag1', 'source-tag2'] }, + }); + services.alertWithLifecycle({ + id: 'TEST_ALERT_1', + fields: { [TAGS]: ['source-tag3', 'source-tag4'] }, + }); + + return { state }; + }); + + await executor( + createDefaultAlertExecutorOptions({ + params: {}, + state: { wrapped: initialRuleState, trackedAlerts: {}, trackedAlertsRecovered: {} }, + logger, + maintenanceWindowIds, + }) + ); + + expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( + expect.objectContaining({ + body: [ + // alert documents + { index: { _id: expect.any(String) } }, + expect.objectContaining({ + [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [TAGS]: ['source-tag1', 'source-tag2', 'rule-tag1', 'rule-tag2'], + [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, + }), + { index: { _id: expect.any(String) } }, + expect.objectContaining({ + [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [EVENT_ACTION]: 'open', + [EVENT_KIND]: 'signal', + [TAGS]: ['source-tag3', 'source-tag4', 'rule-tag1', 'rule-tag2'], + [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, + }), + ], + }) + ); + expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.arrayContaining([ + // evaluation documents + { index: {} }, + expect.objectContaining({ + [EVENT_KIND]: 'event', + }), + ]), + }) + ); + }); + + it('updates documents with maintenance window ids for repeatedly firing alerts', async () => { + const logger = loggerMock.create(); + const ruleDataClientMock = createRuleDataClientMock(); + ruleDataClientMock.getReader().search.mockResolvedValue({ + hits: { + hits: [ + { + _source: { + '@timestamp': '', + [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', + [ALERT_UUID]: 'ALERT_0_UUID', + [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', + [ALERT_RULE_CONSUMER]: 'CONSUMER', + [ALERT_RULE_NAME]: 'NAME', + [ALERT_RULE_PRODUCER]: 'PRODUCER', + [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', + [ALERT_RULE_UUID]: 'RULE_UUID', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [ALERT_WORKFLOW_STATUS]: 'closed', + [SPACE_IDS]: ['fake-space-id'], + labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc + }, + }, + { + _source: { + '@timestamp': '', + [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', + [ALERT_UUID]: 'ALERT_1_UUID', + [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', + [ALERT_RULE_CONSUMER]: 'CONSUMER', + [ALERT_RULE_NAME]: 'NAME', + [ALERT_RULE_PRODUCER]: 'PRODUCER', + [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', + [ALERT_RULE_UUID]: 'RULE_UUID', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [ALERT_WORKFLOW_STATUS]: 'open', + [SPACE_IDS]: ['fake-space-id'], + labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc + }, + }, + ], + }, + } as any); + + const executor = createLifecycleExecutor( + logger, + ruleDataClientMock + )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { + services.alertWithLifecycle({ + id: 'TEST_ALERT_0', + fields: {}, + }); + services.alertWithLifecycle({ + id: 'TEST_ALERT_1', + fields: {}, + }); + + return { state }; + }); + + await executor( + createDefaultAlertExecutorOptions({ + alertId: 'TEST_ALERT_0', + params: {}, + state: { + wrapped: initialRuleState, + trackedAlerts: { + TEST_ALERT_0: { + alertId: 'TEST_ALERT_0', + alertUuid: 'TEST_ALERT_0_UUID', + started: '2020-01-01T12:00:00.000Z', + flappingHistory: [], + flapping: false, + pendingRecoveredCount: 0, + }, + TEST_ALERT_1: { + alertId: 'TEST_ALERT_1', + alertUuid: 'TEST_ALERT_1_UUID', + started: '2020-01-02T12:00:00.000Z', + flappingHistory: [], + flapping: false, + pendingRecoveredCount: 0, + }, + }, + trackedAlertsRecovered: {}, + }, + logger, + maintenanceWindowIds, + }) + ); + + expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( + expect.objectContaining({ + body: [ + // alert document + { index: { _id: 'TEST_ALERT_0_UUID' } }, + expect.objectContaining({ + [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', + [ALERT_WORKFLOW_STATUS]: 'closed', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, + [EVENT_ACTION]: 'active', + [EVENT_KIND]: 'signal', + [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, + }), + { index: { _id: 'TEST_ALERT_1_UUID' } }, + expect.objectContaining({ + [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', + [ALERT_WORKFLOW_STATUS]: 'open', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [EVENT_ACTION]: 'active', + [EVENT_KIND]: 'signal', + [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, + }), + ], + }) + ); + expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.arrayContaining([ + // evaluation documents + { index: {} }, + expect.objectContaining({ + [EVENT_KIND]: 'event', + }), + ]), + }) + ); + }); + + it('updates document with maintenance window ids for recovered alerts', async () => { + const logger = loggerMock.create(); + const ruleDataClientMock = createRuleDataClientMock(); + ruleDataClientMock.getReader().search.mockResolvedValue({ + hits: { + hits: [ + { + _source: { + '@timestamp': '', + [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', + [ALERT_UUID]: 'ALERT_0_UUID', + [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', + [ALERT_RULE_CONSUMER]: 'CONSUMER', + [ALERT_RULE_NAME]: 'NAME', + [ALERT_RULE_PRODUCER]: 'PRODUCER', + [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', + [ALERT_RULE_UUID]: 'RULE_UUID', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [SPACE_IDS]: ['fake-space-id'], + labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc + [TAGS]: ['source-tag1', 'source-tag2'], + }, + }, + { + _source: { + '@timestamp': '', + [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', + [ALERT_UUID]: 'ALERT_1_UUID', + [ALERT_RULE_CATEGORY]: 'RULE_TYPE_NAME', + [ALERT_RULE_CONSUMER]: 'CONSUMER', + [ALERT_RULE_NAME]: 'NAME', + [ALERT_RULE_PRODUCER]: 'PRODUCER', + [ALERT_RULE_TYPE_ID]: 'RULE_TYPE_ID', + [ALERT_RULE_UUID]: 'RULE_UUID', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [SPACE_IDS]: ['fake-space-id'], + labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc + [TAGS]: ['source-tag3', 'source-tag4'], + }, + }, + ], + }, + } as any); + const executor = createLifecycleExecutor( + logger, + ruleDataClientMock + )<{}, TestRuleState, never, never, never>(async ({ services, state }) => { + // TEST_ALERT_0 has recovered + services.alertWithLifecycle({ + id: 'TEST_ALERT_1', + fields: {}, + }); + + return { state }; + }); + + await executor( + createDefaultAlertExecutorOptions({ + alertId: 'TEST_ALERT_0', + params: {}, + state: { + wrapped: initialRuleState, + trackedAlerts: { + TEST_ALERT_0: { + alertId: 'TEST_ALERT_0', + alertUuid: 'TEST_ALERT_0_UUID', + started: '2020-01-01T12:00:00.000Z', + flappingHistory: [], + flapping: false, + pendingRecoveredCount: 0, + }, + TEST_ALERT_1: { + alertId: 'TEST_ALERT_1', + alertUuid: 'TEST_ALERT_1_UUID', + started: '2020-01-02T12:00:00.000Z', + flappingHistory: [], + flapping: false, + pendingRecoveredCount: 0, + }, + }, + trackedAlertsRecovered: {}, + }, + logger, + maintenanceWindowIds, + }) + ); + + expect((await ruleDataClientMock.getWriter()).bulk).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.arrayContaining([ + // alert document + { index: { _id: 'TEST_ALERT_0_UUID' } }, + expect.objectContaining({ + [ALERT_INSTANCE_ID]: 'TEST_ALERT_0', + [ALERT_STATUS]: ALERT_STATUS_RECOVERED, + labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, + [TAGS]: ['source-tag1', 'source-tag2', 'rule-tag1', 'rule-tag2'], + [EVENT_ACTION]: 'close', + [EVENT_KIND]: 'signal', + [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, + }), + { index: { _id: 'TEST_ALERT_1_UUID' } }, + expect.objectContaining({ + [ALERT_INSTANCE_ID]: 'TEST_ALERT_1', + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + [EVENT_ACTION]: 'active', + [EVENT_KIND]: 'signal', + [TAGS]: ['source-tag3', 'source-tag4', 'rule-tag1', 'rule-tag2'], + [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds, + }), + ]), + }) + ); + expect((await ruleDataClientMock.getWriter()).bulk).not.toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.arrayContaining([ + // evaluation documents + { index: {} }, + expect.objectContaining({ + [EVENT_KIND]: 'event', + }), + ]), + }) + ); + }); + }); + describe('set flapping on the document', () => { const flapping = new Array(16).fill(false).concat([true, true, true, true]); const notFlapping = new Array(20).fill(false); diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index 1b9c03745cda0..a213f25cc0fcd 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -43,6 +43,7 @@ import { TIMESTAMP, VERSION, ALERT_FLAPPING, + ALERT_MAINTENANCE_WINDOW_IDS, } from '../../common/technical_rule_data_field_names'; import { CommonAlertFieldNameLatest, CommonAlertIdFieldNameLatest } from '../../common/schemas'; import { IRuleDataClient } from '../rule_data_client'; @@ -131,6 +132,7 @@ export const createLifecycleExecutor = services: { alertFactory, shouldWriteAlerts }, state: previousState, flappingSettings, + maintenanceWindowIds, rule, } = options; @@ -299,6 +301,9 @@ export const createLifecycleExecutor = [VERSION]: ruleDataClient.kibanaVersion, [ALERT_FLAPPING]: flapping, ...(isRecovered ? { [ALERT_END]: commonRuleFields[TIMESTAMP] } : {}), + ...(maintenanceWindowIds?.length + ? { [ALERT_MAINTENANCE_WINDOW_IDS]: maintenanceWindowIds } + : {}), }; return { diff --git a/x-pack/plugins/rule_registry/server/utils/rule_executor.test_helpers.ts b/x-pack/plugins/rule_registry/server/utils/rule_executor.test_helpers.ts index 58b370847de5f..588e2efa3eeb8 100644 --- a/x-pack/plugins/rule_registry/server/utils/rule_executor.test_helpers.ts +++ b/x-pack/plugins/rule_registry/server/utils/rule_executor.test_helpers.ts @@ -39,6 +39,7 @@ export const createDefaultAlertExecutorOptions = < startedAt = new Date(), updatedAt = new Date(), shouldWriteAlerts = true, + maintenanceWindowIds, }: { alertId?: string; ruleName?: string; @@ -49,6 +50,7 @@ export const createDefaultAlertExecutorOptions = < startedAt?: Date; updatedAt?: Date; shouldWriteAlerts?: boolean; + maintenanceWindowIds?: string[]; }): RuleExecutorOptions => ({ startedAt, rule: { @@ -92,4 +94,5 @@ export const createDefaultAlertExecutorOptions = < executionId: 'b33f65d7-6e8b-4aae-8d20-c93613deb33f', logger, flappingSettings: DEFAULT_FLAPPING_SETTINGS, + ...(maintenanceWindowIds ? { maintenanceWindowIds } : {}), }); diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.ts index 59c40ab4f124b..39ab63e1b01d4 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.ts @@ -36,7 +36,7 @@ export class SavedObjectTaggingPlugin implements Plugin<{}, SavedObjectTaggingStart, SetupDeps, StartDeps> { public setup( - { savedObjects, http }: CoreSetup, + { savedObjects, http, getStartServices }: CoreSetup, { features, usageCollection, security }: SetupDeps ) { savedObjects.registerType(tagType); @@ -54,10 +54,11 @@ export class SavedObjectTaggingPlugin features.registerKibanaFeature(savedObjectsTaggingFeature); if (usageCollection) { + const kibanaIndices = savedObjects.getAllIndices(); usageCollection.registerCollector( createTagUsageCollector({ usageCollection, - kibanaIndex: savedObjects.getKibanaIndex(), + kibanaIndices, }) ); } diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts b/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts index 8e10bfd5ad1f5..a98b39888a6cc 100644 --- a/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts +++ b/x-pack/plugins/saved_objects_tagging/server/usage/fetch_tag_usage_data.ts @@ -31,13 +31,13 @@ interface AggregatedTagUsage { export const fetchTagUsageData = async ({ esClient, - kibanaIndex, + kibanaIndices, }: { esClient: ElasticsearchClient; - kibanaIndex: string; + kibanaIndices: string[]; }): Promise => { const body = await esClient.search({ - index: [kibanaIndex], + index: kibanaIndices, ignore_unavailable: true, filter_path: 'aggregations', body: { diff --git a/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts b/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts index 6e4494f225fe7..9af63f3cb6adb 100644 --- a/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts +++ b/x-pack/plugins/saved_objects_tagging/server/usage/tag_usage_collector.ts @@ -12,17 +12,17 @@ import { tagUsageCollectorSchema } from './schema'; export const createTagUsageCollector = ({ usageCollection, - kibanaIndex, + kibanaIndices, }: { usageCollection: UsageCollectionSetup; - kibanaIndex: string; + kibanaIndices: string[]; }) => { return usageCollection.makeUsageCollector({ type: 'saved_objects_tagging', isReady: () => true, schema: tagUsageCollectorSchema, - fetch: ({ esClient }) => { - return fetchTagUsageData({ esClient, kibanaIndex }); + fetch: async ({ esClient }) => { + return fetchTagUsageData({ esClient, kibanaIndices }); }, }); }; diff --git a/x-pack/plugins/security/common/licensing/license_features.ts b/x-pack/plugins/security/common/licensing/license_features.ts index 9069788a3c304..c5f05c83c8e48 100644 --- a/x-pack/plugins/security/common/licensing/license_features.ts +++ b/x-pack/plugins/security/common/licensing/license_features.ts @@ -54,6 +54,11 @@ export interface SecurityLicenseFeatures { */ readonly allowRoleFieldLevelSecurity: boolean; + /** + * Indicates whether we allow users to define remote index privileges in roles. + */ + readonly allowRoleRemoteIndexPrivileges: boolean; + /** * Indicates whether we allow Role-based access control (RBAC). */ diff --git a/x-pack/plugins/security/common/licensing/license_service.test.ts b/x-pack/plugins/security/common/licensing/license_service.test.ts index d7c5a9084ef68..8bf9f4a030051 100644 --- a/x-pack/plugins/security/common/licensing/license_service.test.ts +++ b/x-pack/plugins/security/common/licensing/license_service.test.ts @@ -25,6 +25,7 @@ describe('license features', function () { allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, layout: 'error-es-unavailable', allowRbac: false, allowSubFeaturePrivileges: false, @@ -48,6 +49,7 @@ describe('license features', function () { allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, layout: 'error-xpack-unavailable', allowRbac: false, allowSubFeaturePrivileges: false, @@ -78,6 +80,7 @@ describe('license features', function () { "allowRbac": false, "allowRoleDocumentLevelSecurity": false, "allowRoleFieldLevelSecurity": false, + "allowRoleRemoteIndexPrivileges": false, "allowSubFeaturePrivileges": false, "allowUserProfileCollaboration": false, "layout": "error-xpack-unavailable", @@ -100,6 +103,7 @@ describe('license features', function () { "allowRbac": true, "allowRoleDocumentLevelSecurity": true, "allowRoleFieldLevelSecurity": true, + "allowRoleRemoteIndexPrivileges": true, "allowSubFeaturePrivileges": true, "allowUserProfileCollaboration": true, "showLinks": true, @@ -132,6 +136,7 @@ describe('license features', function () { allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, allowRbac: true, allowSubFeaturePrivileges: false, allowAuditLogging: false, @@ -158,6 +163,7 @@ describe('license features', function () { allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, allowRbac: false, allowSubFeaturePrivileges: false, allowAuditLogging: false, @@ -183,6 +189,7 @@ describe('license features', function () { allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, allowRbac: true, allowSubFeaturePrivileges: false, allowAuditLogging: false, @@ -208,6 +215,7 @@ describe('license features', function () { allowAccessAgreement: true, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, allowRbac: true, allowSubFeaturePrivileges: true, allowAuditLogging: true, @@ -233,6 +241,7 @@ describe('license features', function () { allowAccessAgreement: true, allowRoleDocumentLevelSecurity: true, allowRoleFieldLevelSecurity: true, + allowRoleRemoteIndexPrivileges: true, allowRbac: true, allowSubFeaturePrivileges: true, allowAuditLogging: true, diff --git a/x-pack/plugins/security/common/licensing/license_service.ts b/x-pack/plugins/security/common/licensing/license_service.ts index b315c875bb3b8..cbb658dde9888 100644 --- a/x-pack/plugins/security/common/licensing/license_service.ts +++ b/x-pack/plugins/security/common/licensing/license_service.ts @@ -84,6 +84,7 @@ export class SecurityLicenseService { allowAuditLogging: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, allowRbac: false, allowSubFeaturePrivileges: false, allowUserProfileCollaboration: false, @@ -104,6 +105,7 @@ export class SecurityLicenseService { allowAuditLogging: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, allowRbac: false, allowSubFeaturePrivileges: false, allowUserProfileCollaboration: false, @@ -124,6 +126,7 @@ export class SecurityLicenseService { // Only platinum and trial licenses are compliant with field- and document-level security. allowRoleDocumentLevelSecurity: isLicensePlatinumOrBetter, allowRoleFieldLevelSecurity: isLicensePlatinumOrBetter, + allowRoleRemoteIndexPrivileges: isLicensePlatinumOrBetter, allowRbac: true, allowUserProfileCollaboration: isLicenseStandardOrBetter, }; diff --git a/x-pack/plugins/security/common/model/index.ts b/x-pack/plugins/security/common/model/index.ts index 78275d72434c8..a1f3e88fdf56c 100644 --- a/x-pack/plugins/security/common/model/index.ts +++ b/x-pack/plugins/security/common/model/index.ts @@ -35,7 +35,12 @@ export { shouldProviderUseLoginForm } from './authentication_provider'; export type { BuiltinESPrivileges } from './builtin_es_privileges'; export type { RawKibanaPrivileges, RawKibanaFeaturePrivileges } from './raw_kibana_privileges'; export type { FeaturesPrivileges } from './features_privileges'; -export type { Role, RoleIndexPrivilege, RoleKibanaPrivilege } from './role'; +export type { + Role, + RoleIndexPrivilege, + RoleRemoteIndexPrivilege, + RoleKibanaPrivilege, +} from './role'; export { copyRole, isRoleDeprecated, diff --git a/x-pack/plugins/security/common/model/role.ts b/x-pack/plugins/security/common/model/role.ts index 31ff3cc484b8f..17971905fc1ed 100644 --- a/x-pack/plugins/security/common/model/role.ts +++ b/x-pack/plugins/security/common/model/role.ts @@ -21,6 +21,10 @@ export interface RoleIndexPrivilege { query?: string; } +export interface RoleRemoteIndexPrivilege extends RoleIndexPrivilege { + clusters: string[]; +} + export interface RoleKibanaPrivilege { spaces: string[]; base: string[]; @@ -33,6 +37,7 @@ export interface Role { elasticsearch: { cluster: string[]; indices: RoleIndexPrivilege[]; + remote_indices?: RoleRemoteIndexPrivilege[]; run_as: string[]; }; kibana: RoleKibanaPrivilege[]; diff --git a/x-pack/plugins/security/common/model/user_profile.ts b/x-pack/plugins/security/common/model/user_profile.ts index 2cb133a2b5bb4..943934cdf8abd 100644 --- a/x-pack/plugins/security/common/model/user_profile.ts +++ b/x-pack/plugins/security/common/model/user_profile.ts @@ -90,6 +90,13 @@ export interface UserProfileAvatarData { imageUrl?: string; } +/** + * User settings stored in the data object of the User Profile + */ +export interface UserSettingsData { + darkMode?: string; +} + /** * Extended user information returned in user profile (both basic and security related properties). */ diff --git a/x-pack/plugins/security/public/account_management/account_management_page.tsx b/x-pack/plugins/security/public/account_management/account_management_page.tsx index 940d0997432e5..620e3249c05df 100644 --- a/x-pack/plugins/security/public/account_management/account_management_page.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_page.tsx @@ -13,7 +13,7 @@ import type { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { UserProfileAvatarData } from '../../common'; +import type { UserProfileData } from '../../common'; import { canUserHaveProfile } from '../../common/model'; import { useCurrentUser, useUserProfile } from '../components'; import { Breadcrumb } from '../components/breadcrumb'; @@ -23,7 +23,7 @@ export const AccountManagementPage: FunctionComponent = () => { const { services } = useKibana(); const currentUser = useCurrentUser(); - const userProfile = useUserProfile<{ avatar: UserProfileAvatarData }>('avatar'); + const userProfile = useUserProfile('avatar,userSettings'); // If we fail to load profile, we treat it as a failure _only_ if user is supposed // to have a profile. For example, anonymous and users authenticated via diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx index fafe66a00515c..57473324b6b4e 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.test.tsx @@ -75,6 +75,9 @@ describe('useUserProfileForm', () => { "imageUrl": "", "initials": "fn", }, + "userSettings": Object { + "darkMode": "", + }, }, "user": Object { "email": "email", @@ -230,4 +233,78 @@ describe('useUserProfileForm', () => { expect(testWrapper.exists('UserAvatar')).toBeFalsy(); }); }); + + describe('Dark Mode Form', () => { + it('should display if the User is not a cloud user', () => { + const data: UserProfileData = {}; + + const nonCloudUser = mockAuthenticatedUser({ elastic_cloud_user: false }); + + const testWrapper = mount( + + + + ); + + expect(testWrapper.exists('[data-test-subj="darkModeButton"]')).toBeTruthy(); + }); + + it('should not display if the User is a cloud user', () => { + const data: UserProfileData = {}; + + const cloudUser = mockAuthenticatedUser({ elastic_cloud_user: true }); + + const testWrapper = mount( + + + + ); + + expect(testWrapper.exists('[data-test-subj="darkModeButton"]')).toBeFalsy(); + }); + + it('should add special toast after submitting form successfully since darkMode requires a refresh', async () => { + const data: UserProfileData = {}; + const { result } = renderHook(() => useUserProfileForm({ user, data }), { wrapper }); + + await act(async () => { + await result.current.submitForm(); + }); + + expect(coreStart.notifications.toasts.addSuccess).toHaveBeenNthCalledWith( + 1, + { title: 'Profile updated' }, + {} + ); + + await act(async () => { + await result.current.setFieldValue('data.userSettings.darkMode', 'dark'); + await result.current.submitForm(); + }); + + expect(coreStart.notifications.toasts.addSuccess).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ title: 'Profile updated' }), + expect.objectContaining({ toastLifeTimeMs: 300000 }) + ); + }); + }); }); diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx index 55fe5f3b877ed..3e7d15990e11d 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx @@ -29,10 +29,10 @@ import type { FunctionComponent } from 'react'; import React, { useRef, useState } from 'react'; import useUpdateEffect from 'react-use/lib/useUpdateEffect'; -import type { CoreStart } from '@kbn/core/public'; +import type { CoreStart, ToastInput, ToastOptions } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; import { UserAvatar } from '@kbn/user-profile-components'; import type { AuthenticatedUser, UserProfileAvatarData } from '../../../common'; @@ -42,6 +42,7 @@ import { getUserAvatarColor, getUserAvatarInitials, } from '../../../common/model'; +import type { UserSettingsData } from '../../../common/model/user_profile'; import { useSecurityApiClients } from '../../components'; import { Breadcrumb } from '../../components/breadcrumb'; import { @@ -60,6 +61,7 @@ export interface UserProfileProps { user: AuthenticatedUser; data?: { avatar?: UserProfileAvatarData; + userSettings?: UserSettingsData; }; } @@ -74,6 +76,9 @@ export interface UserProfileFormValues { color: string; imageUrl: string; }; + userSettings: { + darkMode: string; + }; }; avatarType: 'initials' | 'image'; } @@ -137,6 +142,91 @@ function UserDetailsEditor({ user }: { user: AuthenticatedUser }) { ); } +function UserSettingsEditor({ formik }: { formik: ReturnType }) { + if (!formik.values.data) { + return null; + } + + return ( + + + + } + description={ + + } + > + + + + } + fullWidth + > + + ), + }, + { + id: 'light', + label: ( + + ), + iconType: 'sun', + }, + { + id: 'dark', + label: ( + + ), + iconType: 'moon', + }, + ]} + onChange={(id: string) => formik.setFieldValue('data.userSettings.darkMode', id)} + isFullWidth + /> + + + ); +} + function UserAvatarEditor({ user, formik, @@ -498,81 +588,83 @@ export const UserProfile: FunctionComponent = ({ user, data }) } return ( - - - - {showChangePasswordForm ? ( - setShowChangePasswordForm(false)} - onSuccess={() => setShowChangePasswordForm(false)} - /> - ) : null} - - - ), - pageTitleProps: { id: titleId }, - rightSideItems: rightSideItems.reverse().map((item) => ( - - - {item.title} - - - - - - ), - description: ( - - {item.description || ( - - - - )} - - ), - }, - ]} - compressed - /> - )), - }} - bottomBar={formChanges.count > 0 ? : null} - bottomBarProps={{ paddingSize: 'm', position: 'fixed' }} - restrictWidth={1000} + <> + + + -
    - - {isCloudUser ? null : } - setShowChangePasswordForm(true)} + {showChangePasswordForm ? ( + setShowChangePasswordForm(false)} + onSuccess={() => setShowChangePasswordForm(false)} /> - - -
    -
    -
    -
    + ) : null} + + + ), + pageTitleProps: { id: titleId }, + rightSideItems: rightSideItems.reverse().map((item) => ( + + + {item.title} + + + + + + ), + description: ( + + {item.description || ( + + + + )} + + ), + }, + ]} + compressed + /> + )), + }} + bottomBar={formChanges.count > 0 ? : null} + bottomBarProps={{ paddingSize: 'm', position: 'fixed' }} + restrictWidth={1000} + > +
    + + {isCloudUser ? null : } + setShowChangePasswordForm(true)} + /> + {isCloudUser ? null : } + +
    + + + + ); }; @@ -592,12 +684,16 @@ export function useUserProfileForm({ user, data }: UserProfileProps) { color: data.avatar?.color || getUserAvatarColor(user), imageUrl: data.avatar?.imageUrl || '', }, + userSettings: { + darkMode: data.userSettings?.darkMode || '', + }, } : undefined, avatarType: data?.avatar?.imageUrl ? 'image' : 'initials', }); const [validateOnBlurOrChange, setValidateOnBlurOrChange] = useState(false); + const formik = useFormik({ onSubmit: async (values) => { const submitActions = []; @@ -639,12 +735,59 @@ export function useUserProfileForm({ user, data }: UserProfileProps) { return; } + let isRefreshRequired = false; + if (initialValues.data?.userSettings.darkMode !== values.data?.userSettings.darkMode) { + isRefreshRequired = true; + } + resetInitialValues(values); - services.notifications.toasts.addSuccess( - i18n.translate('xpack.security.accountManagement.userProfile.submitSuccessTitle', { + + let successToastInput: ToastInput = { + title: i18n.translate('xpack.security.accountManagement.userProfile.submitSuccessTitle', { defaultMessage: 'Profile updated', - }) - ); + }), + }; + + let successToastOptions: ToastOptions = {}; + + if (isRefreshRequired) { + successToastOptions = { + toastLifeTimeMs: 1000 * 60 * 5, + }; + + successToastInput = { + ...successToastInput, + text: toMountPoint( + + +

    + {i18n.translate( + 'xpack.security.accountManagement.userProfile.requiresPageReloadToastDescription', + { + defaultMessage: + 'One or more settings require you to reload the page to take effect.', + } + )} +

    + window.location.reload()} + data-test-subj="windowReloadButton" + > + {i18n.translate( + 'xpack.security.accountManagement.userProfile.requiresPageReloadToastButtonLabel', + { + defaultMessage: 'Reload page', + } + )} + +
    +
    + ), + }; + } + + services.notifications.toasts.addSuccess(successToastInput, successToastOptions); }, initialValues, enableReinitialize: true, diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts index 79e2335919b1a..5465bc24b7e31 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts @@ -9,11 +9,12 @@ import type { HttpStart } from '@kbn/core/public'; import type { RoleMapping } from '../../../common/model'; -interface CheckRoleMappingFeaturesResponse { +export interface CheckRoleMappingFeaturesResponse { canManageRoleMappings: boolean; canUseInlineScripts: boolean; canUseStoredScripts: boolean; hasCompatibleRealms: boolean; + canUseRemoteIndices: boolean; } type DeleteRoleMappingsResponse = Array<{ diff --git a/x-pack/plugins/security/public/management/roles/edit_role/__snapshots__/validate_role.test.ts.snap b/x-pack/plugins/security/public/management/roles/edit_role/__snapshots__/validate_role.test.ts.snap index faf1f4704ce0d..8cb1c41ed06ae 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/__snapshots__/validate_role.test.ts.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/__snapshots__/validate_role.test.ts.snap @@ -1,3 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`validateIndexPrivileges it throws when indices is not an array 1`] = `"Expected \\"role.elasticsearch.indices\\" to be an array"`; + +exports[`validateRemoteIndexPrivileges it throws when indices is not an array 1`] = `"Expected \\"role.elasticsearch.remote_indices\\" to be an array"`; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index 090c7e6854aeb..52e3d768ef07e 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -140,11 +140,13 @@ function getProps({ role, canManageSpaces = true, spacesEnabled = true, + canUseRemoteIndices = true, }: { action: 'edit' | 'clone'; role?: Role; canManageSpaces?: boolean; spacesEnabled?: boolean; + canUseRemoteIndices?: boolean; }) { const rolesAPIClient = rolesAPIClientMock.create(); rolesAPIClient.getRole.mockResolvedValue(role); @@ -171,12 +173,15 @@ function getProps({ const { fatalErrors } = coreMock.createSetup(); const { http, docLinks, notifications } = coreMock.createStart(); http.get.mockImplementation(async (path: any) => { - if (!spacesEnabled) { - throw { response: { status: 404 } }; // eslint-disable-line no-throw-literal - } if (path === '/api/spaces/space') { + if (!spacesEnabled) { + throw { response: { status: 404 } }; // eslint-disable-line no-throw-literal + } return buildSpaces(); } + if (path === '/internal/security/_check_role_mapping_features') { + return { canUseRemoteIndices }; + } }); return { @@ -265,6 +270,8 @@ describe('', () => { expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectReadOnlyFormButtons(wrapper); }); @@ -291,6 +298,8 @@ describe('', () => { expect(wrapper.find(SpaceAwarePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -308,6 +317,8 @@ describe('', () => { expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( false ); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -480,6 +491,8 @@ describe('', () => { expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectReadOnlyFormButtons(wrapper); }); @@ -507,6 +520,8 @@ describe('', () => { expect(wrapper.find(SimplePrivilegeSection)).toHaveLength(1); expect(wrapper.find('[data-test-subj="userCannotManageSpacesCallout"]')).toHaveLength(0); expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe(true); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -524,6 +539,8 @@ describe('', () => { expect(wrapper.find('input[data-test-subj="roleFormNameInput"]').prop('disabled')).toBe( false ); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); expectSaveFormButtons(wrapper); }); @@ -612,6 +629,19 @@ describe('', () => { }); }); + it('hides remote index privileges section when not supported', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(0); + }); + it('registers fatal error if features endpoint fails unexpectedly', async () => { const error = { response: { status: 500 } }; const getFeatures = jest.fn().mockRejectedValue(error); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index eb99bcb24a8bc..9388ab92a0a76 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import type { ChangeEvent, FocusEvent, FunctionComponent, HTMLProps } from 'react'; import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { @@ -56,6 +57,7 @@ import { prepareRoleClone, } from '../../../../common/model'; import { useCapabilities } from '../../../components/use_capabilities'; +import type { CheckRoleMappingFeaturesResponse } from '../../role_mappings/role_mappings_api_client'; import type { UserAPIClient } from '../../users'; import type { IndicesAPIClient } from '../indices_api_client'; import { KibanaPrivileges } from '../model'; @@ -86,6 +88,12 @@ interface Props { spacesApiUi?: SpacesApiUi; } +function useFeatureCheck(http: HttpStart) { + return useAsync(() => + http.get('/internal/security/_check_role_mapping_features') + ); +} + function useRunAsUsers( userAPIClient: PublicMethodsOf, fatalErrors: FatalErrorsSetup @@ -180,7 +188,8 @@ function useRole( return; } - if (fetchedRole.elasticsearch.indices.length === 0) { + const isEditingExistingRole = !!roleName && action === 'edit'; + if (!isEditingExistingRole && fetchedRole.elasticsearch.indices.length === 0) { const emptyOption: RoleIndexPrivilege = { names: [], privileges: [], @@ -310,6 +319,7 @@ export const EditRolePage: FunctionComponent = ({ const privileges = usePrivileges(privilegesAPIClient, fatalErrors); const spaces = useSpaces(http, fatalErrors); const features = useFeatures(getFeatures, fatalErrors); + const featureCheckState = useFeatureCheck(http); const [role, setRole] = useRole( rolesAPIClient, fatalErrors, @@ -328,7 +338,15 @@ export const EditRolePage: FunctionComponent = ({ } }, [hasReadOnlyPrivileges, isEditingExistingRole]); // eslint-disable-line react-hooks/exhaustive-deps - if (!role || !runAsUsers || !indexPatternsTitles || !privileges || !spaces || !features) { + if ( + !role || + !runAsUsers || + !indexPatternsTitles || + !privileges || + !spaces || + !features || + !featureCheckState.value + ) { return null; } @@ -456,6 +474,7 @@ export const EditRolePage: FunctionComponent = ({ builtinESPrivileges={builtInESPrivileges} license={license} docLinks={docLinks} + canUseRemoteIndices={featureCheckState.value?.canUseRemoteIndices} />
    ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap index ae57c00324ccd..4769f7d79ae8f 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/cluster_privileges.test.tsx.snap @@ -28,6 +28,7 @@ exports[`it renders without crashing 1`] = ` }, ] } + placeholder="Add an action…" selectedOptions={Array []} singleSelection={false} sortMatchesBy="none" diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap index 25196b9929f5a..8f160c6e57abc 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap @@ -165,6 +165,7 @@ exports[`it renders without crashing 1`] = ` } editable={true} indexPatterns={Array []} + indexType="indices" indicesAPIClient={ Object { "getFields": [MockFunction], @@ -199,18 +200,5 @@ exports[`it renders without crashing 1`] = ` } } /> - - - - `; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap index 90ee476659584..98ef1cb598f34 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privilege_form.test.tsx.snap @@ -2,152 +2,155 @@ exports[`it renders without crashing 1`] = ` - + - - - + + + + } + labelType="label" + > + - } - labelType="label" - > - - - - - + + + + } + labelType="label" + > + - } - labelType="label" - > - + + + + + + + } onChange={[Function]} - onCreateOption={[Function]} - options={ - Array [ - Object { - "label": "all", - }, - Object { - "label": "read", - }, - Object { - "label": "write", - }, - Object { - "label": "index", - }, - ] + /> + + + + + + } - selectedOptions={Array []} - singleSelection={false} - sortMatchesBy="none" + onChange={[Function]} /> - - - - - - - - } - onChange={[Function]} - /> - - - - - - - } - onChange={[Function]} - /> - - + + + - - - + diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privileges.test.tsx.snap index 722fdf32a62b3..a193988ffd224 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/index_privileges.test.tsx.snap @@ -12,6 +12,7 @@ exports[`it renders without crashing 1`] = ` } editable={true} indexPatterns={Array []} + indexType="indices" indicesAPIClient={ Object { "getFields": [MockFunction], diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx index 4eb9827990849..c5463c4458b11 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx @@ -53,6 +53,10 @@ export class ClusterPrivileges extends Component { onChange={this.onClusterPrivilegesChange} onCreateOption={this.onCreateCustomPrivilege} isDisabled={isRoleReadOnly(role) || !editable} + placeholder={i18n.translate( + 'xpack.security.management.editRole.clusterPrivileges.placeholder', + { defaultMessage: 'Add an action…' } + )} /> ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx index 52feba5ae83ad..1a1486f6d82e3 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.test.tsx @@ -8,14 +8,12 @@ import React from 'react'; import { coreMock } from '@kbn/core/public/mocks'; -import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; import { licenseMock } from '../../../../../../common/licensing/index.mock'; import { indicesAPIClientMock } from '../../../index.mock'; import { RoleValidator } from '../../validate_role'; -import { ClusterPrivileges } from './cluster_privileges'; import { ElasticsearchPrivileges } from './elasticsearch_privileges'; -import { IndexPrivileges } from './index_privileges'; function getProps() { const license = licenseMock.create(); @@ -56,20 +54,30 @@ test('it renders without crashing', () => { }); test('it renders ClusterPrivileges', () => { - expect( - mountWithIntl().find(ClusterPrivileges) - ).toHaveLength(1); + const wrapper = shallowWithIntl(); + expect(wrapper.find('ClusterPrivileges')).toHaveLength(1); }); -test('it renders IndexPrivileges', () => { - expect( - mountWithIntl().find(IndexPrivileges) - ).toHaveLength(1); +test('it renders index privileges section', () => { + const wrapper = shallowWithIntl(); + expect(wrapper.find('IndexPrivileges[indexType="indices"]')).toHaveLength(1); +}); + +test('it does not render remote index privileges section by default', () => { + const wrapper = shallowWithIntl(); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(0); +}); + +test('it renders remote index privileges section when `canUseRemoteIndices` is enabled', () => { + const wrapper = shallowWithIntl(); + expect(wrapper.find('IndexPrivileges[indexType="remote_indices"]')).toHaveLength(1); }); test('it renders fields as disabled when not editable', () => { const wrapper = shallowWithIntl(); expect(wrapper.find('EuiComboBox').prop('isDisabled')).toBe(true); expect(wrapper.find('ClusterPrivileges').prop('editable')).toBe(false); - expect(wrapper.find('IndexPrivileges').prop('editable')).toBe(false); + expect( + wrapper.find('IndexPrivileges').everyWhere((component) => component.prop('editable')) + ).toBe(false); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx index 6ae205c62cfa7..e963c4eda6d92 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx @@ -6,11 +6,9 @@ */ import { - EuiButton, EuiComboBox, EuiDescribedFormGroup, EuiFormRow, - EuiHorizontalRule, EuiLink, EuiSpacer, EuiText, @@ -42,6 +40,7 @@ interface Props { validator: RoleValidator; builtinESPrivileges: BuiltinESPrivileges; indexPatterns: string[]; + canUseRemoteIndices?: boolean; } export class ElasticsearchPrivileges extends Component { @@ -64,6 +63,7 @@ export class ElasticsearchPrivileges extends Component { indexPatterns, license, builtinESPrivileges, + canUseRemoteIndices, } = this.props; return ( @@ -141,10 +141,9 @@ export class ElasticsearchPrivileges extends Component { isDisabled={!editable} /> - - +

    { />

    - - + +

    { {this.learnMore(docLinks.links.security.indicesPrivileges)}

    - - {editable && ( + {canUseRemoteIndices && ( <> - - - - + + + + +

    + +

    +
    + + +

    + + {this.learnMore(docLinks.links.security.indicesPrivileges)} +

    +
    + )} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx index 793bc2f45433c..8ed57e646326f 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.test.tsx @@ -17,68 +17,125 @@ import { RoleValidator } from '../../validate_role'; import { IndexPrivilegeForm } from './index_privilege_form'; test('it renders without crashing', () => { - const props = { - indexPrivilege: { - names: [], - privileges: [], - query: '', - field_security: { - grant: [], - }, - }, - formIndex: 0, - indexPatterns: [], - indicesAPIClient: indicesAPIClientMock.create(), - availableIndexPrivileges: ['all', 'read', 'write', 'index'], - isRoleReadOnly: false, - allowDocumentLevelSecurity: true, - allowFieldLevelSecurity: true, - validator: new RoleValidator(), - onChange: jest.fn(), - onDelete: jest.fn(), - intl: {} as any, - }; - - const wrapper = shallowWithIntl(); + const wrapper = shallowWithIntl( + + ); expect(wrapper).toMatchSnapshot(); }); test('it allows for custom index privileges', () => { - const props = { - indexPrivilege: { - names: ['foo'], - privileges: ['existing-custom', 'read'], - query: '', - field_security: { - grant: [], - }, - }, - formIndex: 0, - indexPatterns: [], - indicesAPIClient: indicesAPIClientMock.create(), - availableIndexPrivileges: ['all', 'read', 'write', 'index'], - isRoleReadOnly: false, - allowDocumentLevelSecurity: true, - allowFieldLevelSecurity: true, - validator: new RoleValidator(), - onChange: jest.fn(), - onDelete: jest.fn(), - intl: {} as any, - }; - - const wrapper = mountWithIntl(); + const onChange = jest.fn(); + const wrapper = mountWithIntl( + + ); const indexPrivsSelect = wrapper.find('EuiComboBox[data-test-subj="privilegesInput0"]'); (indexPrivsSelect.props() as any).onCreateOption('custom-index-privilege'); - expect(props.onChange).toHaveBeenCalledWith( + expect(onChange).toHaveBeenCalledWith( expect.objectContaining({ privileges: ['existing-custom', 'read', 'custom-index-privilege'] }) ); }); +test('should not render clusters field for local indices', () => { + const wrapper = shallowWithIntl( + + ); + expect(wrapper.find('[data-test-subj="clustersInput0"]')).toHaveLength(0); +}); + +test('should render clusters field for remote indices', () => { + const wrapper = shallowWithIntl( + + ); + expect(wrapper.find('[data-test-subj="clustersInput0"]')).toHaveLength(1); +}); + describe('delete button', () => { const props = { + indexType: 'indices' as const, indexPrivilege: { names: [], privileges: [], @@ -131,6 +188,7 @@ describe('delete button', () => { describe(`document level security`, () => { const props = { + indexType: 'indices' as const, indexPrivilege: { names: [], privileges: [], @@ -178,13 +236,9 @@ describe(`document level security`, () => { }); test('both inputs are shown when allowed, and query is not empty', () => { - const testProps = { - ...props, - }; - const wrapper = mountWithIntl( - + ); expect(wrapper.find('EuiSwitch[data-test-subj="restrictDocumentsQuery0"]')).toHaveLength(1); @@ -194,6 +248,7 @@ describe(`document level security`, () => { describe('field level security', () => { const props = { + indexType: 'indices' as const, indexPrivilege: { names: [], privileges: [], @@ -380,11 +435,7 @@ describe('field level security', () => { }); test('it does not display a warning when fields are granted', () => { - const testProps = { - ...props, - }; - - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1); expect(wrapper.find('div.indexPrivilegeForm__deniedFieldsRow')).toHaveLength(1); expect(wrapper.find('.euiFormHelpText')).toHaveLength(0); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx index 934de8921ec88..58d1bd8061bf8 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx @@ -12,7 +12,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormRow, - EuiHorizontalRule, + EuiPanel, EuiSpacer, EuiSwitch, } from '@elastic/eui'; @@ -25,7 +25,7 @@ import { CodeEditorField } from '@kbn/kibana-react-plugin/public'; import type { monaco } from '@kbn/monaco'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { RoleIndexPrivilege } from '../../../../../../common/model'; +import type { RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../../../../common/model'; import type { IndicesAPIClient } from '../../../indices_api_client'; import type { RoleValidator } from '../../validate_role'; @@ -34,11 +34,12 @@ const toOption = (value: string) => ({ label: value }); interface Props { formIndex: number; - indexPrivilege: RoleIndexPrivilege; + indexType: 'indices' | 'remote_indices'; + indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege; indexPatterns: string[]; availableIndexPrivileges: string[]; indicesAPIClient: PublicMethodsOf; - onChange: (indexPrivilege: RoleIndexPrivilege) => void; + onChange: (indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege) => void; onDelete: () => void; isRoleReadOnly: boolean; allowDocumentLevelSecurity: boolean; @@ -89,22 +90,29 @@ export class IndexPrivilegeForm extends Component { public render() { return ( - - - {this.getPrivilegeForm()} + + + + {this.getPrivilegeForm()} + {!this.props.isRoleReadOnly && ( - - - + )} @@ -114,18 +122,58 @@ export class IndexPrivilegeForm extends Component { private getPrivilegeForm = () => { return ( - + <> + {this.props.indexType === 'remote_indices' ? ( + + + } + fullWidth + {...this.props.validator.validateRemoteIndexPrivilegeClustersField( + this.props.indexPrivilege as RoleRemoteIndexPrivilege + )} + > + + + + ) : null} + this.props.indexType === 'remote_indices' ? ( + + ) : ( + + ) } - fullWidth={true} - {...this.props.validator.validateIndexPrivilege(this.props.indexPrivilege)} + fullWidth + {...this.props.validator.validateIndexPrivilegeNamesField(this.props.indexPrivilege)} > { onCreateOption={this.onCreateIndexPatternOption} onChange={this.onIndexPatternsChange} isDisabled={this.props.isRoleReadOnly} + placeholder={i18n.translate( + 'xpack.security.management.editRole.indexPrivilegeForm.indicesPlaceholder', + { defaultMessage: 'Add an index pattern…' } + )} + fullWidth /> @@ -145,7 +198,10 @@ export class IndexPrivilegeForm extends Component { defaultMessage="Privileges" /> } - fullWidth={true} + fullWidth + {...this.props.validator.validateIndexPrivilegePrivilegesField( + this.props.indexPrivilege + )} > { onChange={this.onPrivilegeChange} onCreateOption={this.onCreateCustomPrivilege} isDisabled={this.props.isRoleReadOnly} + placeholder={i18n.translate( + 'xpack.security.management.editRole.indexPrivilegeForm.privilegesPlaceholder', + { defaultMessage: 'Add an action…' } + )} + fullWidth /> - - {this.getFieldLevelControls()} - {this.getGrantedDocumentsControl()} - + ); }; @@ -189,17 +247,20 @@ export class IndexPrivilegeForm extends Component { }; private getFieldLevelControls = () => { - const { allowFieldLevelSecurity, allowDocumentLevelSecurity, indexPrivilege, isRoleReadOnly } = - this.props; + const { allowFieldLevelSecurity, indexPrivilege, isRoleReadOnly } = this.props; + const { grant, except } = this.getFieldSecurity(indexPrivilege); if (!allowFieldLevelSecurity) { return null; } - const { grant, except } = this.getFieldSecurity(indexPrivilege); + if (isRoleReadOnly && !this.state.fieldSecurityExpanded) { + return null; + } return ( <> + {!isRoleReadOnly && ( @@ -230,7 +291,7 @@ export class IndexPrivilegeForm extends Component { defaultMessage="Granted fields" /> } - fullWidth={true} + fullWidth className="indexPrivilegeForm__grantedFieldsRow" helpText={ !isRoleReadOnly && grant.length === 0 ? ( @@ -250,6 +311,11 @@ export class IndexPrivilegeForm extends Component { isDisabled={this.props.isRoleReadOnly} async={true} isLoading={this.state.isFieldListLoading} + placeholder={i18n.translate( + 'xpack.security.management.editRole.indexPrivilegeForm.fieldPlaceholder', + { defaultMessage: 'Add a field pattern…' } + )} + fullWidth /> @@ -261,7 +327,7 @@ export class IndexPrivilegeForm extends Component { defaultMessage="Denied fields" /> } - fullWidth={true} + fullWidth className="indexPrivilegeForm__deniedFieldsRow" > { isDisabled={isRoleReadOnly} async={true} isLoading={this.state.isFieldListLoading} + placeholder={i18n.translate( + 'xpack.security.management.editRole.indexPrivilegeForm.deniedFieldPlaceholder', + { defaultMessage: 'Add a field pattern…' } + )} + fullWidth /> @@ -280,7 +351,6 @@ export class IndexPrivilegeForm extends Component { )} - {allowDocumentLevelSecurity && } ); }; @@ -292,64 +362,71 @@ export class IndexPrivilegeForm extends Component { return null; } + if (this.props.isRoleReadOnly && !this.state.queryExpanded) { + return null; + } + return ( - - {!this.props.isRoleReadOnly && ( - - - } - compressed={true} - checked={this.state.queryExpanded} - onChange={this.toggleDocumentQuery} - disabled={isRoleReadOnly} - /> - - )} - {this.state.queryExpanded && ( - - - } - fullWidth={true} - data-test-subj={`queryInput${this.props.formIndex}`} - > - + + + {!this.props.isRoleReadOnly && ( + + + } + compressed={true} + checked={this.state.queryExpanded} + onChange={this.toggleDocumentQuery} + disabled={isRoleReadOnly} /> - - - )} - + + )} + {this.state.queryExpanded && ( + + + } + fullWidth + data-test-subj={`queryInput${this.props.formIndex}`} + > + + + + )} + + ); }; @@ -444,6 +521,27 @@ export class IndexPrivilegeForm extends Component { } }; + private onCreateClusterOption = (option: any) => { + const nextClusters = ( + 'clusters' in this.props.indexPrivilege && this.props.indexPrivilege.clusters + ? this.props.indexPrivilege.clusters + : [] + ).concat([option]); + + this.props.onChange({ + ...this.props.indexPrivilege, + clusters: nextClusters, + }); + }; + + private onClustersChange = (nextOptions: EuiComboBoxOptionOption[]) => { + const clusters = nextOptions.map(fromOption); + this.props.onChange({ + ...this.props.indexPrivilege, + clusters, + }); + }; + private onCreateIndexPatternOption = (option: any) => { const newIndexPatterns = this.props.indexPrivilege.names.concat([option]); @@ -548,12 +646,14 @@ export class IndexPrivilegeForm extends Component { }); }; - private getFieldSecurity = (indexPrivilege: RoleIndexPrivilege) => { + private getFieldSecurity = (indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege) => { const { grant = [], except = [] } = indexPrivilege.field_security || {}; return { grant, except }; }; - private isFieldSecurityConfigured = (indexPrivilege: RoleIndexPrivilege) => { + private isFieldSecurityConfigured = ( + indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege + ) => { const { grant, except } = this.getFieldSecurity(indexPrivilege); return except.length > 0 || (grant.length > 0 && !_.isEqual(grant, ['*'])); }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx index 91f761d1157fa..5247b56d52a91 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.test.tsx @@ -28,34 +28,34 @@ test('it renders without crashing', async () => { allowRoleDocumentLevelSecurity: true, } as any); - const props = { - role: { - name: '', - kibana: [], - elasticsearch: { - cluster: [], - indices: [], - run_as: [], - }, - }, - onChange: jest.fn(), - indexPatterns: [], - editable: true, - validator: new RoleValidator(), - availableIndexPrivileges: ['all', 'read', 'write', 'index'], - indicesAPIClient: indicesAPIClientMock.create(), - license, - }; const wrapper = shallowWithIntl( - + ); await flushPromises(); expect(wrapper.children()).toMatchSnapshot(); }); -test('it renders a IndexPrivilegeForm for each privilege on the role', async () => { +test('it renders an IndexPrivilegeForm for each index privilege on the role', async () => { const license = licenseMock.create(); license.getFeatures.mockReturnValue({ allowRoleFieldLevelSecurity: true, @@ -65,40 +65,105 @@ test('it renders a IndexPrivilegeForm for each privilege on the role', async () const indicesAPIClient = indicesAPIClientMock.create(); indicesAPIClient.getFields.mockResolvedValue(['foo']); - const props = { - role: { - name: '', - kibana: [], - elasticsearch: { - cluster: [], - indices: [ - { - names: ['foo*'], - privileges: ['all'], - query: '*', - field_security: { - grant: ['some_field'], - }, + const wrapper = mountWithIntl( + + + + ); + await flushPromises(); + expect(wrapper.find(IndexPrivilegeForm)).toHaveLength(2); +}); + +test('it renders an IndexPrivilegeForm for each remote index privilege on the role', async () => { + const license = licenseMock.create(); + license.getFeatures.mockReturnValue({ + allowRoleFieldLevelSecurity: true, + allowRoleDocumentLevelSecurity: true, + } as any); + + const indicesAPIClient = indicesAPIClientMock.create(); + indicesAPIClient.getFields.mockResolvedValue(['foo']); + const wrapper = mountWithIntl( - + ); await flushPromises(); - expect(wrapper.find(IndexPrivilegeForm)).toHaveLength(1); + expect(wrapper.find(IndexPrivilegeForm)).toHaveLength(2); }); test('it renders fields as disabled when not editable', async () => { @@ -144,5 +209,7 @@ test('it renders fields as disabled when not editable', async () => { ); await flushPromises(); - expect(wrapper.find('IndexPrivilegeForm').prop('isRoleReadOnly')).toBe(true); + expect( + wrapper.find('IndexPrivilegeForm').everyWhere((component) => component.prop('isRoleReadOnly')) + ).toBe(true); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx index 8c8eafa6d6a2b..b47cff5b21669 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx @@ -5,8 +5,10 @@ * 2.0. */ +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiSpacer } from '@elastic/eui'; import React, { Component, Fragment } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { SecurityLicense } from '../../../../../../common/licensing'; @@ -17,8 +19,9 @@ import type { RoleValidator } from '../../validate_role'; import { IndexPrivilegeForm } from './index_privilege_form'; interface Props { + indexType: 'indices' | 'remote_indices'; + indexPatterns?: string[]; role: Role; - indexPatterns: string[]; availableIndexPrivileges: string[]; indicesAPIClient: PublicMethodsOf; license: SecurityLicense; @@ -46,12 +49,20 @@ export class IndexPrivileges extends Component { } public render() { - const { indices = [] } = this.props.role.elasticsearch; + const indices = this.props.role.elasticsearch[this.props.indexType] ?? []; - const { indexPatterns, license, availableIndexPrivileges, indicesAPIClient } = this.props; - const { allowRoleDocumentLevelSecurity, allowRoleFieldLevelSecurity } = license.getFeatures(); + const { indexPatterns = [], license, availableIndexPrivileges, indicesAPIClient } = this.props; + const { + allowRoleDocumentLevelSecurity, + allowRoleFieldLevelSecurity, + allowRoleRemoteIndexPrivileges, + } = license.getFeatures(); + + const remoteIndexPrivilegesDisabled = + this.props.indexType === 'remote_indices' && !allowRoleRemoteIndexPrivileges; const props = { + indexType: this.props.indexType, indexPatterns, indicesAPIClient, // If editing an existing role while that has been disabled, always show the FLS/DLS fields because currently @@ -59,30 +70,73 @@ export class IndexPrivileges extends Component { // doesn't permit FLS/DLS). allowDocumentLevelSecurity: allowRoleDocumentLevelSecurity || !isRoleEnabled(this.props.role), allowFieldLevelSecurity: allowRoleFieldLevelSecurity || !isRoleEnabled(this.props.role), - isRoleReadOnly: !this.props.editable || isRoleReadOnly(this.props.role), + isRoleReadOnly: + !this.props.editable || isRoleReadOnly(this.props.role) || remoteIndexPrivilegesDisabled, }; - const forms = indices.map((indexPrivilege: RoleIndexPrivilege, idx) => ( - - )); - - return {forms}; + return ( + + {indices.map((indexPrivilege, i) => ( + + ))} + {this.props.editable && ( + <> + + + + + {this.props.indexType === 'remote_indices' ? ( + + ) : ( + + )} + + + {remoteIndexPrivilegesDisabled && ( + + + } + position="right" + /> + + )} + + + )} + + ); } public addIndexPrivilege = () => { - const { role } = this.props; + const { role, indexType } = this.props; + const indices = role.elasticsearch[indexType] ?? []; const newIndices = [ - ...role.elasticsearch.indices, + ...indices, { names: [], privileges: [], @@ -96,15 +150,15 @@ export class IndexPrivileges extends Component { ...this.props.role, elasticsearch: { ...this.props.role.elasticsearch, - indices: newIndices, + [indexType]: newIndices, }, }); }; public onIndexPrivilegeChange = (privilegeIndex: number) => { return (updatedPrivilege: RoleIndexPrivilege) => { - const { role } = this.props; - const { indices } = role.elasticsearch; + const { role, indexType } = this.props; + const indices = role.elasticsearch[indexType] ?? []; const newIndices = [...indices]; newIndices[privilegeIndex] = updatedPrivilege; @@ -113,7 +167,7 @@ export class IndexPrivileges extends Component { ...this.props.role, elasticsearch: { ...this.props.role.elasticsearch, - indices: newIndices, + [indexType]: newIndices, }, }); }; @@ -121,16 +175,17 @@ export class IndexPrivileges extends Component { public onIndexPrivilegeDelete = (privilegeIndex: number) => { return () => { - const { role } = this.props; + const { role, indexType } = this.props; - const newIndices = [...role.elasticsearch.indices]; + const indices = role.elasticsearch[indexType] ?? []; + const newIndices = [...indices]; newIndices.splice(privilegeIndex, 1); this.props.onChange({ ...this.props.role, elasticsearch: { ...this.props.role.elasticsearch, - indices: newIndices, + [indexType]: newIndices, }, }); }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts index f508e444dc714..61c0902b4ef90 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts @@ -103,7 +103,7 @@ describe('validateIndexPrivileges', () => { validator = new RoleValidator({ shouldValidate: true }); }); - test('it ignores privilegs with no indices defined', () => { + test('it ignores privileges with no indices defined', () => { const role = { name: '', elasticsearch: { @@ -124,7 +124,7 @@ describe('validateIndexPrivileges', () => { }); }); - test('it requires privilges when an index is defined', () => { + test('it requires privileges when an index is defined', () => { const role = { name: '', elasticsearch: { @@ -161,6 +161,97 @@ describe('validateIndexPrivileges', () => { }); }); +describe('validateRemoteIndexPrivileges', () => { + beforeEach(() => { + validator = new RoleValidator({ shouldValidate: true }); + }); + + test('it ignores empty remote privileges', () => { + const role = { + name: '', + elasticsearch: { + indices: [], + remote_indices: [ + { + clusters: [], + names: [], + privileges: [], + }, + ], + cluster: [], + run_as: [], + }, + kibana: [], + }; + + expect(validator.validateRemoteIndexPrivileges(role)).toEqual({ + isInvalid: false, + }); + }); + + test('it requires privileges when an index is defined', () => { + const role = { + name: '', + elasticsearch: { + cluster: [], + indices: [], + remote_indices: [ + { + clusters: ['cluster'], + names: ['index-*'], + privileges: [], + }, + ], + run_as: [], + }, + kibana: [], + }; + + expect(validator.validateRemoteIndexPrivileges(role)).toEqual({ + isInvalid: true, + }); + }); + + test('it requires indices and privileges when clusters is defined', () => { + const role = { + name: '', + elasticsearch: { + cluster: [], + indices: [], + remote_indices: [ + { + clusters: ['cluster'], + names: [], + privileges: [], + }, + ], + run_as: [], + }, + kibana: [], + }; + + expect(validator.validateRemoteIndexPrivileges(role)).toEqual({ + isInvalid: true, + }); + }); + + test('it throws when indices is not an array', () => { + const role = { + name: '', + elasticsearch: { + cluster: [], + indices: [], + remote_indices: 'asdf', + run_as: [], + }, + kibana: [], + }; + + // @ts-ignore + expect(() => validator.validateRemoteIndexPrivileges(role)).toThrowErrorMatchingSnapshot(); + }); +}); + describe('validateSpacePrivileges', () => { beforeEach(() => { validator = new RoleValidator({ shouldValidate: true }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts index ac509dba353d2..ee3e85959e312 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { MAX_NAME_LENGTH, NAME_REGEX } from '../../../../common/constants'; -import type { Role, RoleIndexPrivilege } from '../../../../common/model'; +import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../../common/model'; interface RoleValidatorOptions { shouldValidate?: boolean; @@ -97,32 +97,145 @@ export class RoleValidator { ); } - const areIndicesValid = - role.elasticsearch.indices - .map((indexPriv) => this.validateIndexPrivilege(indexPriv)) - .find((result: RoleValidationResult) => result.isInvalid) == null; + const areIndicesInvalid = role.elasticsearch.indices.reduce((isInvalid, indexPriv) => { + if ( + this.validateIndexPrivilegeNamesField(indexPriv).isInvalid || + this.validateIndexPrivilegePrivilegesField(indexPriv).isInvalid + ) { + return true; + } + return isInvalid; + }, false); - if (areIndicesValid) { + if (areIndicesInvalid) { + return invalid(); + } + + return valid(); + } + + public validateRemoteIndexPrivileges(role: Role): RoleValidationResult { + if (!this.shouldValidate) { return valid(); } - return invalid(); + + if (!role.elasticsearch.remote_indices) { + return valid(); + } + + if (!Array.isArray(role.elasticsearch.remote_indices)) { + throw new TypeError( + i18n.translate('xpack.security.management.editRole.validateRole.indicesTypeErrorMessage', { + defaultMessage: 'Expected {elasticIndices} to be an array', + values: { + elasticIndices: '"role.elasticsearch.remote_indices"', + }, + }) + ); + } + + const areRemoteIndicesInvalid = role.elasticsearch.remote_indices.reduce( + (isInvalid, indexPriv) => { + if ( + this.validateRemoteIndexPrivilegeClustersField(indexPriv).isInvalid || + this.validateIndexPrivilegeNamesField(indexPriv).isInvalid || + this.validateIndexPrivilegePrivilegesField(indexPriv).isInvalid + ) { + return true; + } + return isInvalid; + }, + false + ); + + if (areRemoteIndicesInvalid) { + return invalid(); + } + + return valid(); } - public validateIndexPrivilege(indexPrivilege: RoleIndexPrivilege): RoleValidationResult { + public validateRemoteIndexPrivilegeClustersField( + indexPrivilege: RoleRemoteIndexPrivilege + ): RoleValidationResult { if (!this.shouldValidate) { return valid(); } - if (indexPrivilege.names.length && !indexPrivilege.privileges.length) { + // Ignore if all other fields are empty + if (!indexPrivilege.names.length && !indexPrivilege.privileges.length) { + return valid(); + } + + if (!indexPrivilege.clusters || !indexPrivilege.clusters.length) { + return invalid( + i18n.translate( + 'xpack.security.management.editRole.validateRole.oneRemoteClusterRequiredWarningMessage', + { + defaultMessage: 'Enter or select at least one remote cluster', + } + ) + ); + } + + return valid(); + } + + public validateIndexPrivilegeNamesField( + indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege + ): RoleValidationResult { + if (!this.shouldValidate) { + return valid(); + } + + // Ignore if all other fields are empty + if ( + (!('clusters' in indexPrivilege) || !indexPrivilege.clusters.length) && + !indexPrivilege.privileges.length + ) { + return valid(); + } + + if (!indexPrivilege.names.length) { + return invalid( + i18n.translate( + 'xpack.security.management.editRole.validateRole.oneIndexRequiredWarningMessage', + { + defaultMessage: 'Enter or select at least one index pattern', + } + ) + ); + } + + return valid(); + } + + public validateIndexPrivilegePrivilegesField( + indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege + ): RoleValidationResult { + if (!this.shouldValidate) { + return valid(); + } + + // Ignore if all other fields are empty + if ( + (!('clusters' in indexPrivilege) || !indexPrivilege.clusters.length) && + !indexPrivilege.names.length + ) { + return valid(); + } + + if (!indexPrivilege.privileges.length) { return invalid( i18n.translate( 'xpack.security.management.editRole.validateRole.onePrivilegeRequiredWarningMessage', { - defaultMessage: 'At least one privilege is required', + defaultMessage: 'Enter or select at least one action', } ) ); } + return valid(); } @@ -198,9 +311,15 @@ export class RoleValidator { public validateForSave(role: Role): RoleValidationResult { const { isInvalid: isNameInvalid } = this.validateRoleName(role); const { isInvalid: areIndicesInvalid } = this.validateIndexPrivileges(role); + const { isInvalid: areRemoteIndicesInvalid } = this.validateRemoteIndexPrivileges(role); const { isInvalid: areSpacePrivilegesInvalid } = this.validateSpacePrivileges(role); - if (isNameInvalid || areIndicesInvalid || areSpacePrivilegesInvalid) { + if ( + isNameInvalid || + areIndicesInvalid || + areRemoteIndicesInvalid || + areSpacePrivilegesInvalid + ) { return invalid(); } diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts index 917843666378e..ce7af35dccf22 100644 --- a/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts +++ b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts @@ -27,6 +27,7 @@ describe('RolesAPIClient', () => { elasticsearch: { cluster: [], indices: [{ names: [], privileges: [] }], + remote_indices: [{ clusters: [], names: [], privileges: [] }], run_as: [], }, kibana: [], @@ -38,6 +39,7 @@ describe('RolesAPIClient', () => { elasticsearch: { cluster: [], indices: [], + remote_indices: [], run_as: [], }, kibana: [], @@ -107,6 +109,9 @@ describe('RolesAPIClient', () => { elasticsearch: { cluster: [], indices: [{ names: ['.kibana*'], privileges: ['all'], query: 'something' }], + remote_indices: [ + { clusters: ['cluster'], names: ['.kibana*'], privileges: ['all'], query: 'something' }, + ], run_as: [], }, kibana: [], @@ -118,6 +123,9 @@ describe('RolesAPIClient', () => { elasticsearch: { cluster: [], indices: [{ names: ['.kibana*'], privileges: ['all'], query: 'something' }], + remote_indices: [ + { clusters: ['cluster'], names: ['.kibana*'], privileges: ['all'], query: 'something' }, + ], run_as: [], }, kibana: [], diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.ts index e57e003ab3f7d..6d59976a82ada 100644 --- a/x-pack/plugins/security/public/management/roles/roles_api_client.ts +++ b/x-pack/plugins/security/public/management/roles/roles_api_client.ts @@ -7,7 +7,7 @@ import type { HttpStart } from '@kbn/core/public'; -import type { Role, RoleIndexPrivilege } from '../../../common/model'; +import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../common/model'; import { copyRole } from '../../../common/model'; export class RolesAPIClient { @@ -34,14 +34,28 @@ export class RolesAPIClient { private transformRoleForSave(role: Role) { // Remove any placeholder index privileges - const isPlaceholderPrivilege = (indexPrivilege: RoleIndexPrivilege) => - indexPrivilege.names.length === 0; + const isPlaceholderPrivilege = ( + indexPrivilege: RoleIndexPrivilege | RoleRemoteIndexPrivilege + ) => { + if ( + 'clusters' in indexPrivilege && + indexPrivilege.clusters && + indexPrivilege.clusters.length > 0 + ) { + return false; + } + return indexPrivilege.names.length === 0 && indexPrivilege.privileges.length === 0; + }; role.elasticsearch.indices = role.elasticsearch.indices.filter( (indexPrivilege) => !isPlaceholderPrivilege(indexPrivilege) ); + role.elasticsearch.remote_indices = role.elasticsearch.remote_indices?.filter( + (indexPrivilege) => !isPlaceholderPrivilege(indexPrivilege) + ); // Remove any placeholder query entries role.elasticsearch.indices.forEach((index) => index.query || delete index.query); + role.elasticsearch.remote_indices?.forEach((index) => index.query || delete index.query); role.kibana.forEach((kibanaPrivilege) => { // If a base privilege is defined, then do not persist feature privileges diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts index dd07324835303..7d0039c9106d2 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts @@ -66,8 +66,145 @@ const createMockUser = (user: Partial = {}) => ...user, } as AuthenticatedUser); +const kibanaFeature1 = new KibanaFeature({ + id: 'kibanaFeature1', + name: 'KibanaFeature1', + app: ['app1'], + category: { id: 'foo', label: 'foo' }, + privileges: { + all: { + app: ['foo'], + catalogue: ['foo'], + savedObject: { + all: ['foo'], + read: [], + }, + ui: ['save', 'show'], + }, + read: { + app: ['foo'], + catalogue: ['foo'], + savedObject: { + all: [], + read: ['foo'], + }, + ui: ['show'], + }, + }, +}); + +const kibanaFeature2 = new KibanaFeature({ + id: 'kibanaFeature2', + name: 'KibanaFeature2', + app: ['app1', 'app2'], + category: { id: 'foo', label: 'foo' }, + privileges: { + all: { + app: ['foo'], + catalogue: ['foo'], + savedObject: { + all: ['foo'], + read: [], + }, + ui: ['save', 'show'], + }, + read: { + app: ['foo'], + catalogue: ['foo'], + savedObject: { + all: [], + read: ['foo'], + }, + ui: ['show'], + }, + }, +}); + +const optOutKibanaFeature = new KibanaFeature({ + id: 'optOutFeature', + name: 'Feature that opts out of Kibana sec model', + app: [], + category: { id: 'optOut', label: 'optOut' }, + privileges: null, +}); + +const esManagementFeature = new ElasticsearchFeature({ + id: 'esManagementFeature', + management: { + kibana: ['esManagement'], + }, + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: [], + }, + ], +}); + describe('usingPrivileges', () => { describe('checkPrivileges errors', () => { + const inputCapabilities = Object.freeze({ + navLinks: { + app1: true, + app2: true, + app3: true, + }, + management: { + kibana: { + indices: true, + }, + }, + catalogue: {}, + kibanaFeature2: { + foo: true, + bar: true, + }, + optOutFeature: { + foo: true, + bar: true, + }, + esManagementFeature: { + foo: true, + bar: true, + }, + unregisteredFeature: { + foo: true, + bar: true, + }, + }); + + const expectedDisabled = Object.freeze({ + navLinks: { + app1: false, + app2: false, + app3: true, // will not diable unregistered app link + }, + management: { + kibana: { + indices: false, + }, + }, + catalogue: {}, + kibanaFeature2: { + foo: false, + bar: false, + }, + optOutFeature: { + // will not disbale features that opt out of Kibana security + foo: true, + bar: true, + }, + esManagementFeature: { + foo: false, + bar: false, + }, + unregisteredFeature: { + // will not disble unregistered features + foo: true, + bar: true, + }, + }); + test(`disables uiCapabilities when a 401 is thrown`, async () => { const mockAuthz = createMockAuthz({ rejectCheckPrivileges: { statusCode: 401, message: 'super informative message' }, @@ -76,76 +213,16 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, - [ - new KibanaFeature({ - id: 'fooFeature', - name: 'Foo KibanaFeature', - app: ['fooApp', 'foo'], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - ], - [ - new ElasticsearchFeature({ - id: 'esFeature', - privileges: [ - { - requiredClusterPrivileges: [], - ui: [], - }, - ], - }), - ], + [kibanaFeature2, optOutKibanaFeature], + [esManagementFeature], mockLoggers.get(), mockAuthz, createMockUser() ); - const result = await usingPrivileges( - Object.freeze({ - navLinks: { - foo: true, - fooApp: true, - bar: true, - }, - management: { - kibana: { - indices: true, - }, - }, - catalogue: {}, - fooFeature: { - foo: true, - bar: true, - }, - barFeature: { - foo: true, - bar: true, - }, - }) - ); + const result = await usingPrivileges(inputCapabilities); - expect(result).toEqual({ - navLinks: { - foo: false, - fooApp: false, - bar: true, - }, - management: { - kibana: { - indices: false, - }, - }, - catalogue: {}, - fooFeature: { - foo: false, - bar: false, - }, - barFeature: { - foo: false, - bar: false, - }, - }); + expect(result).toEqual(expectedDisabled); expect(loggingSystemMock.collect(mockLoggers).debug).toMatchInlineSnapshot(` Array [ @@ -164,74 +241,17 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, - [ - new KibanaFeature({ - id: 'fooFeature', - name: 'Foo KibanaFeature', - app: ['foo'], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - ], - [ - new ElasticsearchFeature({ - id: 'esFeature', - privileges: [ - { - requiredClusterPrivileges: [], - ui: [], - }, - ], - }), - ], + [kibanaFeature2, optOutKibanaFeature], + [esManagementFeature], mockLoggers.get(), mockAuthz, createMockUser() ); - const result = await usingPrivileges( - Object.freeze({ - navLinks: { - foo: true, - bar: true, - }, - management: { - kibana: { - indices: true, - }, - }, - catalogue: {}, - fooFeature: { - foo: true, - bar: true, - }, - barFeature: { - foo: true, - bar: true, - }, - }) - ); + const result = await usingPrivileges(inputCapabilities); + + expect(result).toEqual(expectedDisabled); - expect(result).toEqual({ - navLinks: { - foo: false, - bar: true, - }, - management: { - kibana: { - indices: false, - }, - }, - catalogue: {}, - fooFeature: { - foo: false, - bar: false, - }, - barFeature: { - foo: false, - bar: false, - }, - }); expect(loggingSystemMock.collect(mockLoggers).debug).toMatchInlineSnapshot(` Array [ Array [ @@ -284,24 +304,64 @@ describe('usingPrivileges', () => { }); }); + const esFeatures = [ + new ElasticsearchFeature({ + id: 'esFeature', + privileges: [ + { + requiredClusterPrivileges: ['manage'], + ui: ['es_manage'], + }, + { + requiredClusterPrivileges: ['monitor'], + ui: ['es_monitor'], + }, + ], + }), + new ElasticsearchFeature({ + id: 'esSecurityFeature', + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: ['es_manage_sec'], + }, + ], + }), + new ElasticsearchFeature({ + id: 'esManagementFeature', + management: { + kibana: ['esManagement'], + }, + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: [], + }, + ], + }), + ]; + test(`disables ui capabilities when they don't have privileges`, async () => { + // grant some privileges const mockAuthz = createMockAuthz({ resolveCheckPrivileges: { privileges: { kibana: [ - { privilege: actions.ui.get('navLinks', 'foo'), authorized: true }, - { privilege: actions.ui.get('navLinks', 'bar'), authorized: false }, - { privilege: actions.ui.get('navLinks', 'quz'), authorized: false }, + { privilege: actions.ui.get('navLinks', 'app1'), authorized: true }, + { privilege: actions.ui.get('navLinks', 'app2'), authorized: false }, + { privilege: actions.ui.get('navLinks', 'app3'), authorized: false }, { privilege: actions.ui.get('management', 'kibana', 'indices'), authorized: true }, { privilege: actions.ui.get('management', 'kibana', 'settings'), authorized: false }, { privilege: actions.ui.get('management', 'kibana', 'esManagement'), authorized: false, }, - { privilege: actions.ui.get('fooFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('fooFeature', 'bar'), authorized: false }, - { privilege: actions.ui.get('barFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('barFeature', 'bar'), authorized: false }, + { privilege: actions.ui.get('kibanaFeature1', 'foo'), authorized: true }, + { privilege: actions.ui.get('kibanaFeature1', 'bar'), authorized: false }, + { privilege: actions.ui.get('kibanaFeature2', 'foo'), authorized: true }, + { privilege: actions.ui.get('kibanaFeature2', 'bar'), authorized: false }, + { privilege: actions.ui.get('optOutFeature', 'foo'), authorized: false }, + { privilege: actions.ui.get('optOutFeature', 'bar'), authorized: false }, ], elasticsearch: { cluster: [ @@ -317,58 +377,8 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, - [ - new KibanaFeature({ - id: 'fooFeature', - name: 'Foo KibanaFeature', - app: [], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - new KibanaFeature({ - id: 'barFeature', - name: 'Bar KibanaFeature', - app: ['bar'], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - ], - [ - new ElasticsearchFeature({ - id: 'esFeature', - privileges: [ - { - requiredClusterPrivileges: ['manage'], - ui: ['es_manage'], - }, - { - requiredClusterPrivileges: ['monitor'], - ui: ['es_monitor'], - }, - ], - }), - new ElasticsearchFeature({ - id: 'esSecurityFeature', - privileges: [ - { - requiredClusterPrivileges: ['manage_security'], - ui: ['es_manage_sec'], - }, - ], - }), - new ElasticsearchFeature({ - id: 'esManagementFeature', - management: { - kibana: ['esManagement'], - }, - privileges: [ - { - requiredClusterPrivileges: ['manage_security'], - ui: [], - }, - ], - }), - ], + [kibanaFeature1, kibanaFeature2, optOutKibanaFeature], + esFeatures, loggingSystemMock.create().get(), mockAuthz, createMockUser() @@ -377,9 +387,9 @@ describe('usingPrivileges', () => { const result = await usingPrivileges( Object.freeze({ navLinks: { - foo: true, - bar: true, - quz: true, + app1: true, + app2: true, + app3: true, }, management: { kibana: { @@ -389,11 +399,15 @@ describe('usingPrivileges', () => { }, }, catalogue: {}, - fooFeature: { + kibanaFeature1: { foo: true, bar: true, }, - barFeature: { + kibanaFeature2: { + foo: true, + bar: true, + }, + optOutFeature: { foo: true, bar: true, }, @@ -410,9 +424,9 @@ describe('usingPrivileges', () => { expect(result).toEqual({ navLinks: { - foo: true, - bar: false, - quz: true, + app1: true, + app2: false, + app3: true, }, management: { kibana: { @@ -422,14 +436,19 @@ describe('usingPrivileges', () => { }, }, catalogue: {}, - fooFeature: { + kibanaFeature1: { foo: true, bar: false, }, - barFeature: { + kibanaFeature2: { foo: true, bar: false, }, + optOutFeature: { + // these stay enabled because they opt out of Kibana security + foo: true, + bar: true, + }, esFeature: { es_manage: false, es_monitor: true, @@ -442,6 +461,7 @@ describe('usingPrivileges', () => { }); test(`doesn't re-enable disabled uiCapabilities`, async () => { + // grant all privileges const mockAuthz = createMockAuthz({ resolveCheckPrivileges: { privileges: { @@ -449,13 +469,19 @@ describe('usingPrivileges', () => { { privilege: actions.ui.get('navLinks', 'foo'), authorized: true }, { privilege: actions.ui.get('navLinks', 'bar'), authorized: true }, { privilege: actions.ui.get('management', 'kibana', 'indices'), authorized: true }, - { privilege: actions.ui.get('fooFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('fooFeature', 'bar'), authorized: true }, - { privilege: actions.ui.get('barFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('barFeature', 'bar'), authorized: true }, + { privilege: actions.ui.get('kibanaFeature1', 'foo'), authorized: true }, + { privilege: actions.ui.get('kibanaFeature1', 'bar'), authorized: true }, + { privilege: actions.ui.get('kibanaFeature2', 'foo'), authorized: true }, + { privilege: actions.ui.get('kibanaFeature2', 'bar'), authorized: true }, + { privilege: actions.ui.get('optOutFeature', 'foo'), authorized: true }, + { privilege: actions.ui.get('optOutFeature', 'bar'), authorized: true }, ], elasticsearch: { - cluster: [], + cluster: [ + { privilege: 'manage', authorized: true }, + { privilege: 'monitor', authorized: true }, + { privilege: 'manage_security', authorized: true }, + ], index: {}, }, }, @@ -464,62 +490,14 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, - [ - new KibanaFeature({ - id: 'fooFeature', - name: 'Foo KibanaFeature', - app: [], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - new KibanaFeature({ - id: 'barFeature', - name: 'Bar KibanaFeature', - app: [], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - ], - [ - new ElasticsearchFeature({ - id: 'esFeature', - privileges: [ - { - requiredClusterPrivileges: [], - ui: [], - }, - ], - }), - ], + [kibanaFeature1, kibanaFeature2, optOutKibanaFeature], + esFeatures, loggingSystemMock.create().get(), mockAuthz, createMockUser() ); - const result = await usingPrivileges( - Object.freeze({ - navLinks: { - foo: false, - bar: false, - }, - management: { - kibana: { - indices: false, - }, - }, - catalogue: {}, - fooFeature: { - foo: false, - bar: false, - }, - barFeature: { - foo: false, - bar: false, - }, - }) - ); - - expect(result).toEqual({ + const allFalseCapabilities = Object.freeze({ navLinks: { foo: false, bar: false, @@ -530,36 +508,43 @@ describe('usingPrivileges', () => { }, }, catalogue: {}, - fooFeature: { + kibanaFeature1: { + foo: false, + bar: false, + }, + kibanaFeature2: { foo: false, bar: false, }, - barFeature: { + optOutFeature: { foo: false, bar: false, }, + esFeature: { + es_manage: false, + es_monitor: false, + }, + esSecurityFeature: { + es_manage_sec: false, + }, + esManagementFeature: {}, }); + const result = await usingPrivileges(allFalseCapabilities); + + expect(result).toEqual(allFalseCapabilities); }); }); describe('all', () => { - test(`disables uiCapabilities`, () => { + test(`disables only registered uiCapabilities that do not opt out of kibana security`, () => { const mockAuthz = createMockAuthz({ rejectCheckPrivileges: new Error(`Don't use me`) }); const { all } = disableUICapabilitiesFactory( mockRequest, - [ - new KibanaFeature({ - id: 'fooFeature', - name: 'Foo KibanaFeature', - app: ['foo'], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }), - ], + [kibanaFeature1, optOutKibanaFeature], [ new ElasticsearchFeature({ - id: 'esFeature', + id: 'esFeature1', privileges: [ { requiredClusterPrivileges: [], @@ -576,8 +561,8 @@ describe('all', () => { const result = all( Object.freeze({ navLinks: { - foo: true, - bar: true, + app1: true, + app2: true, // there is no app2 registered }, management: { kibana: { @@ -585,41 +570,61 @@ describe('all', () => { }, }, catalogue: {}, - fooFeature: { + kibanaFeature1: { foo: true, bar: true, }, - barFeature: { + kibanaFeature2: { + // there is no kibanaFeature2 registered foo: true, bar: true, }, - esFeature: { + optOutFeature: { + foo: true, + bar: true, + }, + esFeature1: { + bar: true, + }, + esFeature2: { bar: true, }, }) ); expect(result).toEqual({ navLinks: { - foo: false, - bar: true, + app1: false, + app2: true, // does NOT disable because it is not a registered navlink }, management: { kibana: { - indices: false, + indices: false, // nested values are always disabled }, }, catalogue: {}, - fooFeature: { + kibanaFeature1: { + // registered kibana features with privileges get diabled foo: false, bar: false, }, - barFeature: { - foo: false, - bar: false, + kibanaFeature2: { + // does NOT disable because it is not a registered Kibana feature + foo: true, + bar: true, }, - esFeature: { + optOutFeature: { + // does NOT disable because it opts out (does not define privileges) + foo: true, + bar: true, + }, + esFeature1: { + // registered es features get diabled bar: false, }, + esFeature2: { + // does NOT disable because it is not a registered ES feature + bar: true, + }, }); }); }); diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts index 161366cb7309c..6023ea402ae56 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts @@ -67,20 +67,58 @@ export function disableUICapabilitiesFactory( }; }, {}); - const shouldDisableFeatureUICapability = ( - featureId: keyof UICapabilities, - uiCapability: string + const isCatalogueItemReferencedByFeatureSet = ( + catalogueEntry: string, + featureSet: Array | undefined }>> ) => { - // if the navLink isn't for a feature that we have registered, we don't wish to - // disable it based on privileges - return featureId !== 'navLinks' || featureNavLinkIds.includes(uiCapability); + return featureSet.some((feature) => (feature.catalogue ?? []).includes(catalogueEntry)); + }; + + const shouldAffectCapability = (featureId: keyof UICapabilities, uiCapability: string) => { + // This method answers: 'Should we affect a capability based on privileges?' + + // 'spaces' and 'fileUpload' feature ID's are handled independently + // The spaces and file_upload plugins have their own capabilites switchers + + // Always affect global settings + if (featureId === 'globalSettings') { + return true; + } + + // If the feature is 'catalogue', return true if it is the 'spaces' capability + // (we always want to affect that) or if we have a feature that references it + // (i.e. found in the 'catalogue' property of a registered Kibana or ES feature) + if (featureId === 'catalogue') { + return ( + uiCapability === 'spaces' || + isCatalogueItemReferencedByFeatureSet(uiCapability, features) || + isCatalogueItemReferencedByFeatureSet(uiCapability, elasticsearchFeatures) + ); + } + + // if the feature is 'navLinks', return true if the nav link was registered + // (i.e. found in the 'app' property of a registered Kibana feature) + if (featureId === 'navLinks') { + return featureNavLinkIds.includes(uiCapability); + } + + // if the feature is a Kibana feature, return true if it defines privileges + // (i.e. it adheres to the Kibana security model) + // Kibana features with no privileges opt out of the Kibana security model and + // are not subject to our control(e.g.Enterprise Search features) + const kibanaFeature = features.find((f) => f.id === featureId); + if (!!kibanaFeature) return !!kibanaFeature.privileges; + + // Lastly return true if the feature is a registered es feature (we always want to affect these), + // otherwise false(we don't know what this feature is so we don't touch it) + return !!elasticsearchFeatureMap[featureId]; }; const disableAll = (uiCapabilities: UICapabilities) => { return mapValues(uiCapabilities, (featureUICapabilities, featureId) => mapValues(featureUICapabilities, (value, uiCapability) => { if (typeof value === 'boolean') { - if (shouldDisableFeatureUICapability(featureId!, uiCapability!)) { + if (shouldAffectCapability(featureId!, uiCapability!)) { return false; } return value; @@ -175,7 +213,7 @@ export function disableUICapabilitiesFactory( ); // Catalogue and management capbility buckets can also be influenced by ES privileges, - // so the early return is not possible for these. + // so the early return is not possible for these *unless we have the required Kibana privileges. if ((!isCatalogueFeature && !isManagementFeature) || hasRequiredKibanaPrivileges) { return hasRequiredKibanaPrivileges; } @@ -230,7 +268,7 @@ export function disableUICapabilitiesFactory( featureUICapabilities, (value: boolean | Record, uiCapability) => { if (typeof value === 'boolean') { - if (!shouldDisableFeatureUICapability(featureId!, uiCapability!)) { + if (!shouldAffectCapability(featureId!, uiCapability!)) { return value; } return checkPrivilegesForCapability(value, featureId!, uiCapability!); diff --git a/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts b/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts index a491594c4256f..fe13d0a7960b0 100644 --- a/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts +++ b/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts @@ -25,6 +25,7 @@ export type ElasticsearchRole = Pick; cluster: Role['elasticsearch']['cluster']; indices: Role['elasticsearch']['indices']; + remote_indices?: Role['elasticsearch']['remote_indices']; run_as: Role['elasticsearch']['run_as']; }; @@ -48,6 +49,7 @@ export function transformElasticsearchRoleToRole( elasticsearch: { cluster: elasticsearchRole.cluster, indices: elasticsearchRole.indices, + remote_indices: elasticsearchRole.remote_indices, run_as: elasticsearchRole.run_as, }, kibana: kibanaTransformResult.success ? (kibanaTransformResult.value as Role['kibana']) : [], diff --git a/x-pack/plugins/security/server/deprecations/privilege_deprecations.test.ts b/x-pack/plugins/security/server/deprecations/privilege_deprecations.test.ts index a3836c4773595..2ab306ce82108 100644 --- a/x-pack/plugins/security/server/deprecations/privilege_deprecations.test.ts +++ b/x-pack/plugins/security/server/deprecations/privilege_deprecations.test.ts @@ -66,6 +66,7 @@ describe('#getPrivilegeDeprecationsService', () => { "elasticsearch": Object { "cluster": Array [], "indices": Array [], + "remote_indices": undefined, "run_as": Array [], }, "kibana": Array [ @@ -138,6 +139,7 @@ describe('#getPrivilegeDeprecationsService', () => { "elasticsearch": Object { "cluster": Array [], "indices": Array [], + "remote_indices": undefined, "run_as": Array [], }, "kibana": Array [ diff --git a/x-pack/plugins/security/server/lib/role_schema.ts b/x-pack/plugins/security/server/lib/role_schema.ts index 135a24cf04085..fe59a1f740b41 100644 --- a/x-pack/plugins/security/server/lib/role_schema.ts +++ b/x-pack/plugins/security/server/lib/role_schema.ts @@ -66,6 +66,55 @@ export const elasticsearchRoleSchema = schema.object({ ) ), + /** + * An optional list of remote indices permissions entries. + */ + remote_indices: schema.maybe( + schema.arrayOf( + schema.object({ + /** + * Required list of remote clusters to which the permissions in this entry apply. + */ + clusters: schema.arrayOf(schema.string(), { minSize: 1 }), + + /** + * Required list of remote indices (or index name patterns) to which the permissions in this + * entry apply. + */ + names: schema.arrayOf(schema.string(), { minSize: 1 }), + + /** + * An optional set of the document fields that the owners of the role have read access to. + */ + field_security: schema.maybe( + schema.recordOf( + schema.oneOf([schema.literal('grant'), schema.literal('except')]), + schema.arrayOf(schema.string()) + ) + ), + + /** + * Required list of the index level privileges that the owners of the role have on the + * specified indices. + */ + privileges: schema.arrayOf(schema.string(), { minSize: 1 }), + + /** + * An optional search query that defines the documents the owners of the role have read access + * to. A document within the specified indices must match this query in order for it to be + * accessible by the owners of the role. + */ + query: schema.maybe(schema.string()), + + /** + * An optional flag used to indicate if index pattern wildcards or regexps should cover + * restricted indices. + */ + allow_restricted_indices: schema.maybe(schema.boolean()), + }) + ) + ), + /** * An optional list of users that the owners of this role can impersonate. */ diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index de90d3e336db0..2d38ce20fb5ae 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -16,6 +16,7 @@ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { ConfigSchema } from './config'; import type { PluginSetupDependencies, PluginStartDependencies } from './plugin'; import { SecurityPlugin } from './plugin'; +import { userProfileServiceMock } from './user_profile/user_profile_service.mock'; describe('Security Plugin', () => { let plugin: SecurityPlugin; @@ -36,7 +37,9 @@ describe('Security Plugin', () => { ) ); - mockCoreSetup = coreMock.createSetup(); + mockCoreSetup = coreMock.createSetup({ + pluginStartContract: { userProfiles: userProfileServiceMock.createStart() }, + }); mockCoreSetup.http.getServerInfo.mockReturnValue({ hostname: 'localhost', name: 'kibana', diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 7e29008ae754b..e3f38699c0651 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -59,6 +59,9 @@ import { setupSpacesClient } from './spaces'; import { registerSecurityUsageCollector } from './usage_collector'; import { UserProfileService } from './user_profile'; import type { UserProfileServiceStart, UserProfileServiceStartInternal } from './user_profile'; +import { UserProfileSettingsClient } from './user_profile/user_profile_settings_client'; +import type { UserSettingServiceStart } from './user_profile/user_setting_service'; +import { UserSettingService } from './user_profile/user_setting_service'; export type SpacesService = Pick< SpacesPluginSetup['spacesService'], @@ -195,6 +198,9 @@ export class SecurityPlugin private readonly userProfileService: UserProfileService; private userProfileStart?: UserProfileServiceStartInternal; + + private readonly userSettingService: UserSettingService; + private userSettingServiceStart?: UserSettingServiceStart; private readonly getUserProfileService = () => { if (!this.userProfileStart) { throw new Error(`userProfileStart is not registered!`); @@ -222,14 +228,18 @@ export class SecurityPlugin this.userProfileService = new UserProfileService( this.initializerContext.logger.get('user-profile') ); + this.userSettingService = new UserSettingService( + this.initializerContext.logger.get('user-settings') + ); + this.analyticsService = new AnalyticsService(this.initializerContext.logger.get('analytics')); } public setup( - core: CoreSetup, + core: CoreSetup, { features, licensing, taskManager, usageCollection, spaces }: PluginSetupDependencies ) { - this.kibanaIndexName = core.savedObjects.getKibanaIndex(); + this.kibanaIndexName = core.savedObjects.getDefaultIndex(); const config$ = this.initializerContext.config.create>().pipe( map((rawConfig) => createConfig(rawConfig, this.initializerContext.logger.get('config'), { @@ -245,10 +255,25 @@ export class SecurityPlugin const kibanaIndexName = this.getKibanaIndexName(); // A subset of `start` services we need during `setup`. - const startServicesPromise = core.getStartServices().then(([coreServices, depsServices]) => ({ - elasticsearch: coreServices.elasticsearch, - features: depsServices.features, - })); + const startServicesPromise = core + .getStartServices() + .then(([coreServices, depsServices, startServices]) => ({ + elasticsearch: coreServices.elasticsearch, + features: depsServices.features, + userProfiles: startServices.userProfiles, + })); + + /** + * Once the UserProfileServiceStart is available, use it to start the SecurityPlugin > UserSettingService. + * + * Then the UserProfileSettingsClient is created with the SecurityPlugin > UserSettingServiceStart and set on + * the Core > UserSettingsServiceSetup + */ + startServicesPromise.then(({ userProfiles }) => { + this.userSettingServiceStart = this.userSettingService.start(userProfiles); + const client = new UserProfileSettingsClient(this.userSettingServiceStart); + core.userSettings.setUserProfileSettings(client); + }); const { license } = this.securityLicenseService.setup({ license$: licensing.license$, @@ -381,6 +406,7 @@ export class SecurityPlugin this.session = session; this.userProfileStart = this.userProfileService.start({ clusterClient, session }); + this.userSettingServiceStart = this.userSettingService.start(this.userProfileStart); const config = this.getConfig(); this.authenticationStart = this.authenticationService.start({ diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts index ed1648c069d4a..597396a5837ff 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts @@ -257,6 +257,20 @@ describe('Put payload schema', () => { query: `{ "match": { "title": "foo" } }`, }, ], + + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], }, kibana: [ @@ -305,6 +319,33 @@ describe('Put payload schema', () => { "query": "{ \\"match\\": { \\"title\\": \\"foo\\" } }", }, ], + "remote_indices": Array [ + Object { + "clusters": Array [ + "test-cluster-name-1", + "test-cluster-name-2", + ], + "field_security": Object { + "except": Array [ + "test-field-security-except-1", + "test-field-security-except-2", + ], + "grant": Array [ + "test-field-security-grant-1", + "test-field-security-grant-2", + ], + }, + "names": Array [ + "test-index-name-1", + "test-index-name-2", + ], + "privileges": Array [ + "test-index-privilege-1", + "test-index-privilege-2", + ], + "query": "{ \\"match\\": { \\"title\\": \\"foo\\" } }", + }, + ], "run_as": Array [ "test-run-as-1", "test-run-as-2", diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts index 19ce403b77d86..a52399d4a04ec 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts @@ -21,7 +21,12 @@ export const transformPutPayloadToElasticsearchRole = ( allExistingApplications: ElasticsearchRole['applications'] = [] ) => { const { - elasticsearch = { cluster: undefined, indices: undefined, run_as: undefined }, + elasticsearch = { + cluster: undefined, + indices: undefined, + remote_indices: undefined, + run_as: undefined, + }, kibana = [], } = rolePayload; const otherApplications = allExistingApplications.filter( @@ -32,6 +37,7 @@ export const transformPutPayloadToElasticsearchRole = ( metadata: rolePayload.metadata, cluster: elasticsearch.cluster || [], indices: elasticsearch.indices || [], + remote_indices: elasticsearch.remote_indices || [], run_as: elasticsearch.run_as || [], applications: [ ...transformPrivilegesToElasticsearchPrivileges(application, kibana), diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts index f719b9c5e94ad..1e59f7a5d6a0f 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts @@ -325,6 +325,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [], }, @@ -358,6 +359,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [ { @@ -400,6 +402,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [ { @@ -440,6 +443,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [ { @@ -535,6 +539,7 @@ describe('PUT role', () => { query: `{ "match": { "title": "foo" } }`, }, ], + remote_indices: [], metadata: { foo: 'test-metadata' }, run_as: ['test-run-as-1', 'test-run-as-2'], }, @@ -656,6 +661,7 @@ describe('PUT role', () => { query: `{ "match": { "title": "foo" } }`, }, ], + remote_indices: [], metadata: { foo: 'test-metadata' }, run_as: ['test-run-as-1', 'test-run-as-2'], }, @@ -759,6 +765,7 @@ describe('PUT role', () => { privileges: ['test-index-privilege-1', 'test-index-privilege-2'], }, ], + remote_indices: [], metadata: { foo: 'test-metadata' }, run_as: ['test-run-as-1', 'test-run-as-2'], }, @@ -796,6 +803,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [ { @@ -840,6 +848,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [ { @@ -884,6 +893,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], + remote_indices: [], run_as: [], applications: [ { diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts index 0efe93d21c1b2..ce0a38ef73039 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts @@ -21,6 +21,9 @@ interface TestOptions { } const defaultXpackUsageResponse = { + remote_clusters: { + size: 0, + }, security: { realms: { native: { @@ -94,6 +97,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); @@ -117,10 +121,31 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); + getFeatureCheckTest( + 'indicates canUseRemoteIndices=false when cluster does not support remote indices', + { + xpackUsageResponse: () => ({ + ...defaultXpackUsageResponse, + remote_clusters: undefined, + }), + asserts: { + statusCode: 200, + result: { + canManageRoleMappings: true, + canUseInlineScripts: true, + canUseStoredScripts: true, + hasCompatibleRealms: true, + canUseRemoteIndices: false, + }, + }, + } + ); + getFeatureCheckTest('disallows stored scripts when disabled', { nodeSettingsResponse: () => ({ nodes: { @@ -140,6 +165,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: false, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); @@ -163,12 +189,14 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: false, canUseStoredScripts: true, hasCompatibleRealms: true, + canUseRemoteIndices: true, }, }, }); getFeatureCheckTest('indicates incompatible realms when only native and file are enabled', { xpackUsageResponse: () => ({ + ...defaultXpackUsageResponse, security: { realms: { native: { @@ -189,6 +217,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: false, + canUseRemoteIndices: true, }, }, }); @@ -219,6 +248,7 @@ describe('GET role mappings feature check', () => { canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: false, + canUseRemoteIndices: false, }, }, } diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts index 9715f92cb5a37..309cdfbeab456 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts @@ -24,6 +24,9 @@ interface NodeSettingsResponse { } interface XPackUsageResponse { + remote_clusters?: { + size: number; + }; security: { realms: { [realmName: string]: { @@ -128,6 +131,7 @@ async function getEnabledRoleMappingsFeatures(esClient: ElasticsearchClient, log hasCompatibleRealms, canUseStoredScripts, canUseInlineScripts, + canUseRemoteIndices: !!xpackUsage.remote_clusters, }; } diff --git a/x-pack/plugins/security/server/routes/views/login.test.ts b/x-pack/plugins/security/server/routes/views/login.test.ts index 0e07c2460f9ee..7575aafead6e0 100644 --- a/x-pack/plugins/security/server/routes/views/login.test.ts +++ b/x-pack/plugins/security/server/routes/views/login.test.ts @@ -166,6 +166,7 @@ describe('Login view routes', () => { allowRbac: false, allowRoleDocumentLevelSecurity: true, allowRoleFieldLevelSecurity: false, + allowRoleRemoteIndexPrivileges: false, layout: 'error-es-unavailable', showLinks: false, showRoleMappingsManagement: true, diff --git a/x-pack/plugins/security/server/user_profile/index.ts b/x-pack/plugins/security/server/user_profile/index.ts index a5b13c69c798c..9c6fd67458e6b 100644 --- a/x-pack/plugins/security/server/user_profile/index.ts +++ b/x-pack/plugins/security/server/user_profile/index.ts @@ -6,6 +6,7 @@ */ export { UserProfileService } from './user_profile_service'; + export type { UserProfileServiceStart, UserProfileServiceStartInternal, diff --git a/x-pack/plugins/security/server/user_profile/user_profile_settings_client.test.ts b/x-pack/plugins/security/server/user_profile/user_profile_settings_client.test.ts new file mode 100644 index 0000000000000..dfcc88c8ca5a1 --- /dev/null +++ b/x-pack/plugins/security/server/user_profile/user_profile_settings_client.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. + */ + +import type { httpServerMock } from '@kbn/core-http-server-mocks'; + +import { UserProfileSettingsClient } from './user_profile_settings_client'; +import type { UserSettingServiceStart } from './user_setting_service'; + +describe('UserProfileSettingsClient', () => { + let mockRequest: ReturnType; + let client: UserProfileSettingsClient; + + beforeEach(() => { + const userSettingsServiceStart = { + getCurrentUserProfileSettings: jest.fn(), + } as jest.Mocked; + + userSettingsServiceStart.getCurrentUserProfileSettings.mockResolvedValue({ darkMode: 'dark' }); + + client = new UserProfileSettingsClient(userSettingsServiceStart); + }); + + describe('#get', () => { + it('should return user settings', async () => { + const userSettings = await client.get(mockRequest); + expect(userSettings).toEqual({ darkMode: 'dark' }); + }); + }); +}); diff --git a/x-pack/plugins/security/server/user_profile/user_profile_settings_client.ts b/x-pack/plugins/security/server/user_profile/user_profile_settings_client.ts new file mode 100644 index 0000000000000..a888f22256acc --- /dev/null +++ b/x-pack/plugins/security/server/user_profile/user_profile_settings_client.ts @@ -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 type { KibanaRequest } from '@kbn/core-http-server'; +import type { UserProfileSettingsClientContract } from '@kbn/core-user-settings-server'; + +import type { UserSettingServiceStart } from './user_setting_service'; + +/** + * A wrapper client around {@link UserSettingServiceStart} that exposes a method to get the current user's profile + */ +export class UserProfileSettingsClient implements UserProfileSettingsClientContract { + private userSettingsServiceStart: UserSettingServiceStart; + + constructor(userSettingsServiceStart: UserSettingServiceStart) { + this.userSettingsServiceStart = userSettingsServiceStart; + } + + /** + * Returns the current user's user profile settings + * + * @param request the KibanaRequest that is required to get the current user and their settings + */ + async get(request: KibanaRequest): Promise> { + return await this.userSettingsServiceStart.getCurrentUserProfileSettings(request); + } +} diff --git a/x-pack/plugins/security/server/user_profile/user_setting_service.ts b/x-pack/plugins/security/server/user_profile/user_setting_service.ts new file mode 100644 index 0000000000000..7c5ca3c1c7ef8 --- /dev/null +++ b/x-pack/plugins/security/server/user_profile/user_setting_service.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 type { KibanaRequest } from '@kbn/core-http-server'; +import type { Logger } from '@kbn/logging'; + +import type { UserProfileGetCurrentParams, UserProfileServiceStart } from './user_profile_service'; + +export interface UserSettingServiceStart { + /** + * Returns the currently signed-in user's settings from their User Profile + * + * @param request the KibanaRequest that is required to get the current user and their settings + */ + getCurrentUserProfileSettings(request: KibanaRequest): Promise>; +} + +/** + * A service that wraps the {@link UserProfileServiceStart} so that only the 'getCurrent' method is made available + */ +export class UserSettingService { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + + start(userProfileServiceStart: UserProfileServiceStart): UserSettingServiceStart { + return { + getCurrentUserProfileSettings: async (request) => { + const params: UserProfileGetCurrentParams = { + request, + dataPath: 'userSettings', + }; + + const currentUserProfile = await userProfileServiceStart.getCurrent(params); + + let result = {} as Record; + + if (currentUserProfile?.data?.userSettings) { + result = currentUserProfile?.data?.userSettings as Record; + } else { + this.logger.debug('User Settings not found.'); + } + return result; + }, + }; + } +} diff --git a/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts b/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts new file mode 100644 index 0000000000000..a5dafd16f60ec --- /dev/null +++ b/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts @@ -0,0 +1,143 @@ +/* + * 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 { SecurityGetUserProfileResponse } from '@elastic/elasticsearch/lib/api/types'; + +import { + elasticsearchServiceMock, + httpServerMock, + loggingSystemMock, +} from '@kbn/core/server/mocks'; + +import type { UserProfileWithSecurity } from '../../common'; +import { licenseMock } from '../../common/licensing/index.mock'; +import { userProfileMock } from '../../common/model/user_profile.mock'; +import { authorizationMock } from '../authorization/index.mock'; +import { sessionMock } from '../session_management/session.mock'; +import type { UserProfileServiceStart } from './user_profile_service'; +import { UserProfileService } from './user_profile_service'; +import { UserSettingService } from './user_setting_service'; + +const logger = loggingSystemMock.createLogger(); +describe('UserSettingService', () => { + let mockStartParams: { + clusterClient: ReturnType; + session: ReturnType; + }; + + let mockAuthz: ReturnType; + let userProfileService: UserProfileService; + let userSettingsService: UserSettingService; + let userProfileServiceStart: UserProfileServiceStart; + + beforeEach(() => { + mockStartParams = { + clusterClient: elasticsearchServiceMock.createClusterClient(), + session: sessionMock.create(), + }; + + mockAuthz = authorizationMock.create(); + + userProfileService = new UserProfileService(logger); + userSettingsService = new UserSettingService(logger); + + userProfileService.setup({ + authz: mockAuthz, + license: licenseMock.create({ allowUserProfileCollaboration: true }), + }); + + userProfileServiceStart = userProfileService.start(mockStartParams); + }); + + afterEach(() => { + logger.error.mockClear(); + }); + + it('should expose correct start contract', () => { + const userSettingServiceStart = userSettingsService.start(userProfileServiceStart); + expect(userSettingServiceStart).toMatchInlineSnapshot(` + Object { + "getCurrentUserProfileSettings": [Function], + } + `); + }); + + describe('#getCurrentUserProfileSettings', () => { + let mockUserProfile: UserProfileWithSecurity; + let mockRequest: ReturnType; + beforeEach(() => { + mockRequest = httpServerMock.createKibanaRequest(); + }); + + it('returns user settings data', async () => { + mockUserProfile = userProfileMock.createWithSecurity({ + uid: 'UID', + user: { + username: 'user-1', + full_name: 'full-name-1', + realm_name: 'some-realm', + realm_domain: 'some-domain', + roles: ['role-1'], + }, + data: { + kibana: { + userSettings: { + darkMode: 'dark', + }, + }, + }, + }); + + mockStartParams.clusterClient.asInternalUser.security.getUserProfile.mockResolvedValue({ + profiles: [mockUserProfile], + } as unknown as SecurityGetUserProfileResponse); + + mockStartParams.session.get.mockResolvedValue({ + error: null, + value: sessionMock.createValue({ userProfileId: mockUserProfile.uid }), + }); + + userProfileServiceStart = userProfileService.start(mockStartParams); + const userSettingServiceStart = userSettingsService.start(userProfileServiceStart); + await expect( + userSettingServiceStart.getCurrentUserProfileSettings(mockRequest) + ).resolves.toEqual({ darkMode: 'dark' }); + }); + + it('logs a warning and returns ', async () => { + mockUserProfile = userProfileMock.createWithSecurity({ + uid: 'UID', + user: { + username: 'user-1', + full_name: 'full-name-1', + realm_name: 'some-realm', + realm_domain: 'some-domain', + roles: ['role-1'], + }, + data: {}, + }); + + mockStartParams.clusterClient.asInternalUser.security.getUserProfile.mockResolvedValue({ + profiles: [mockUserProfile], + } as unknown as SecurityGetUserProfileResponse); + + mockStartParams.session.get.mockResolvedValue({ + error: null, + value: sessionMock.createValue({ userProfileId: mockUserProfile.uid }), + }); + + userProfileServiceStart = userProfileService.start(mockStartParams); + const userSettingServiceStart = userSettingsService.start(userProfileServiceStart); + + await expect( + userSettingServiceStart.getCurrentUserProfileSettings(mockRequest) + ).resolves.toEqual({}); + + expect(logger.debug).toHaveBeenCalledWith('User Settings not found.'); + }); + }); +}); diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 7278e39b18249..8cf0c59772ae2 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -58,6 +58,9 @@ "@kbn/ecs", "@kbn/safer-lodash-set", "@kbn/shared-ux-router", + "@kbn/core-http-server", + "@kbn/core-http-server-mocks", + "@kbn/core-user-settings-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index aba27d9b617d5..7da50bd569a47 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; + /** * as const * @@ -154,6 +156,7 @@ export const DATA_QUALITY_PATH = '/data_quality' as const; export const DETECTION_RESPONSE_PATH = '/detection_response' as const; export const DETECTIONS_PATH = '/detections' as const; export const ALERTS_PATH = '/alerts' as const; +export const ALERT_DETAILS_REDIRECT_PATH = `${ALERTS_PATH}/redirect` as const; export const RULES_PATH = '/rules' as const; export const RULES_CREATE_PATH = `${RULES_PATH}/create` as const; export const EXCEPTIONS_PATH = '/exceptions' as const; @@ -377,9 +380,18 @@ export const ML_GROUP_ID = 'security' as const; export const LEGACY_ML_GROUP_ID = 'siem' as const; export const ML_GROUP_IDS = [ML_GROUP_ID, LEGACY_ML_GROUP_ID] as const; +/** + * Rule Actions + */ export const NOTIFICATION_THROTTLE_NO_ACTIONS = 'no_actions' as const; export const NOTIFICATION_THROTTLE_RULE = 'rule' as const; +export const NOTIFICATION_DEFAULT_FREQUENCY = { + notifyWhen: RuleNotifyWhen.ACTIVE, + throttle: null, + summary: true, +}; + export const showAllOthersBucket: string[] = [ 'destination.ip', 'event.action', diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts index 3cee4c3dbe384..712312c50140d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts @@ -133,7 +133,10 @@ describe('Perform bulk action request schema', () => { const payload: PerformBulkActionRequestBody = { query: 'name: test', action: BulkActionType.duplicate, - [BulkActionType.duplicate]: { include_exceptions: false }, + [BulkActionType.duplicate]: { + include_exceptions: false, + include_expired_exceptions: false, + }, }; const message = retrieveValidationMessage(payload); expect(getPaths(left(message.errors))).toEqual([]); @@ -512,28 +515,6 @@ describe('Perform bulk action request schema', () => { expect(message.schema).toEqual({}); }); - test('invalid request: missing throttle in payload', () => { - const payload = { - query: 'name: test', - action: BulkActionType.edit, - [BulkActionType.edit]: [ - { - type: BulkActionEditType.add_rule_actions, - value: { - actions: [], - }, - }, - ], - }; - - const message = retrieveValidationMessage(payload); - - expect(getPaths(left(message.errors))).toEqual( - expect.arrayContaining(['Invalid value "undefined" supplied to "edit,value,throttle"']) - ); - expect(message.schema).toEqual({}); - }); - test('invalid request: missing actions in payload', () => { const payload = { query: 'name: test', diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts index b0860c55ebd5a..e0a392885bad9 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { NonEmptyArray, TimeDuration } from '@kbn/securitysolution-io-ts-types'; import { + RuleActionFrequency, RuleActionGroup, RuleActionId, RuleActionParams, @@ -96,11 +97,14 @@ const BulkActionEditPayloadTimeline = t.type({ */ type NormalizedRuleAction = t.TypeOf; const NormalizedRuleAction = t.exact( - t.type({ - group: RuleActionGroup, - id: RuleActionId, - params: RuleActionParams, - }) + t.intersection([ + t.type({ + group: RuleActionGroup, + id: RuleActionId, + params: RuleActionParams, + }), + t.partial({ frequency: RuleActionFrequency }), + ]) ); export type BulkActionEditPayloadRuleActions = t.TypeOf; @@ -109,10 +113,12 @@ export const BulkActionEditPayloadRuleActions = t.type({ t.literal(BulkActionEditType.add_rule_actions), t.literal(BulkActionEditType.set_rule_actions), ]), - value: t.type({ - throttle: ThrottleForBulkActions, - actions: t.array(NormalizedRuleAction), - }), + value: t.intersection([ + t.partial({ throttle: ThrottleForBulkActions }), + t.type({ + actions: t.array(NormalizedRuleAction), + }), + ]), }); type BulkActionEditPayloadSchedule = t.TypeOf; @@ -136,6 +142,7 @@ export const BulkActionEditPayload = t.union([ const bulkActionDuplicatePayload = t.exact( t.type({ include_exceptions: t.boolean, + include_expired_exceptions: t.boolean, }) ); diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts index 710c0b55a86f9..ba290d30b57a3 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/constants.ts @@ -7,5 +7,6 @@ export enum DuplicateOptions { withExceptions = 'withExceptions', + withExceptionsExcludeExpiredExceptions = 'withExceptionsExcludeExpiredExceptions', withoutExceptions = 'withoutExceptions', } diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts index e0b427cdcefbc..ff4bb72a5eb65 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts @@ -119,6 +119,8 @@ export const baseSchema = buildRuleSchemas({ output_index: AlertsIndex, namespace: AlertsIndexNamespace, meta: RuleMetadata, + // Throttle + throttle: RuleActionThrottle, }, defaultable: { // Main attributes @@ -134,7 +136,6 @@ export const baseSchema = buildRuleSchemas({ to: RuleIntervalTo, // Rule actions actions: RuleActionArray, - throttle: RuleActionThrottle, // Rule exceptions exceptions_list: ExceptionListArray, // Misc attributes diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/specific_attributes/query_attributes.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/specific_attributes/query_attributes.ts index d83a187fe516a..a3d5d56698d9c 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/specific_attributes/query_attributes.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/specific_attributes/query_attributes.ts @@ -9,8 +9,27 @@ import * as t from 'io-ts'; import { LimitedSizeArray, PositiveIntegerGreaterThanZero, + enumeration, } from '@kbn/securitysolution-io-ts-types'; +/** + * describes how alerts will be generated for documents with missing suppress by fields + */ +export enum AlertSuppressionMissingFieldsStrategy { + // per each document a separate alert will be created + DoNotSuppress = 'doNotSuppress', + // only alert will be created per suppress by bucket + Suppress = 'suppress', +} + +export type AlertSuppressionMissingFields = t.TypeOf; +export const AlertSuppressionMissingFields = enumeration( + 'AlertSuppressionMissingFields', + AlertSuppressionMissingFieldsStrategy +); +export const DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY = + AlertSuppressionMissingFieldsStrategy.Suppress; + export const AlertSuppressionGroupBy = LimitedSizeArray({ codec: t.string, minSize: 1, @@ -41,6 +60,7 @@ export const AlertSuppression = t.intersection([ t.exact( t.partial({ duration: AlertSuppressionDuration, + missing_fields_strategy: AlertSuppressionMissingFields, }) ), ]); @@ -55,6 +75,7 @@ export const AlertSuppressionCamel = t.intersection([ t.exact( t.partial({ duration: AlertSuppressionDuration, + missingFieldsStrategy: AlertSuppressionMissingFields, }) ), ]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/8.8.0/index.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/8.8.0/index.ts new file mode 100644 index 0000000000000..66c06b8406d3c --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/8.8.0/index.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 type { ALERT_URL, ALERT_UUID } from '@kbn/rule-data-utils'; +import type { AlertWithCommonFields800 } from '@kbn/rule-registry-plugin/common/schemas/8.0.0'; +import type { + Ancestor840, + BaseFields840, + EqlBuildingBlockFields840, + EqlShellFields840, + NewTermsFields840, +} from '../8.4.0'; + +/* DO NOT MODIFY THIS SCHEMA TO ADD NEW FIELDS. These types represent the alerts that shipped in 8.8.0. +Any changes to these types should be bug fixes so the types more accurately represent the alerts from 8.8.0. +If you are adding new fields for a new release of Kibana, create a new sibling folder to this one +for the version to be released and add the field(s) to the schema in that folder. +Then, update `../index.ts` to import from the new folder that has the latest schemas, add the +new schemas to the union of all alert schemas, and re-export the new schemas as the `*Latest` schemas. +*/ + +export type { Ancestor840 as Ancestor880 }; +export interface BaseFields880 extends BaseFields840 { + [ALERT_URL]: string | undefined; + [ALERT_UUID]: string; +} + +export interface WrappedFields880 { + _id: string; + _index: string; + _source: T; +} + +export type GenericAlert880 = AlertWithCommonFields800; + +export type EqlShellFields880 = EqlShellFields840 & BaseFields880; + +export type EqlBuildingBlockFields880 = EqlBuildingBlockFields840 & BaseFields880; + +export type NewTermsFields880 = NewTermsFields840 & BaseFields880; + +export type NewTermsAlert880 = NewTermsFields840 & BaseFields880; + +export type EqlBuildingBlockAlert880 = AlertWithCommonFields800; + +export type EqlShellAlert880 = AlertWithCommonFields800; + +export type DetectionAlert880 = + | GenericAlert880 + | EqlShellAlert880 + | EqlBuildingBlockAlert880 + | NewTermsAlert880; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/index.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/index.ts index 2fdf426f0aea0..1d3e3f0d35f4f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/index.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts/index.ts @@ -7,18 +7,18 @@ import type { DetectionAlert800 } from './8.0.0'; -import type { - Ancestor840, - BaseFields840, - DetectionAlert840, - WrappedFields840, - EqlBuildingBlockFields840, - EqlShellFields840, - NewTermsFields840, -} from './8.4.0'; - +import type { DetectionAlert840 } from './8.4.0'; import type { DetectionAlert860 } from './8.6.0'; import type { DetectionAlert870 } from './8.7.0'; +import type { + Ancestor880, + BaseFields880, + DetectionAlert880, + EqlBuildingBlockFields880, + EqlShellFields880, + NewTermsFields880, + WrappedFields880, +} from './8.8.0'; // When new Alert schemas are created for new Kibana versions, add the DetectionAlert type from the new version // here, e.g. `export type DetectionAlert = DetectionAlert800 | DetectionAlert820` if a new schema is created in 8.2.0 @@ -26,14 +26,15 @@ export type DetectionAlert = | DetectionAlert800 | DetectionAlert840 | DetectionAlert860 - | DetectionAlert870; + | DetectionAlert870 + | DetectionAlert880; export type { - Ancestor840 as AncestorLatest, - BaseFields840 as BaseFieldsLatest, - DetectionAlert860 as DetectionAlertLatest, - WrappedFields840 as WrappedFieldsLatest, - EqlBuildingBlockFields840 as EqlBuildingBlockFieldsLatest, - EqlShellFields840 as EqlShellFieldsLatest, - NewTermsFields840 as NewTermsFieldsLatest, + Ancestor880 as AncestorLatest, + BaseFields880 as BaseFieldsLatest, + DetectionAlert880 as DetectionAlertLatest, + WrappedFields880 as WrappedFieldsLatest, + EqlBuildingBlockFields880 as EqlBuildingBlockFieldsLatest, + EqlShellFields880 as EqlShellFieldsLatest, + NewTermsFields880 as NewTermsFieldsLatest, }; diff --git a/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts b/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts index 1b74bcd320aad..3808837dc0df2 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts @@ -16,6 +16,7 @@ export const transformRuleToAlertAction = ({ action_type_id: actionTypeId, params, uuid, + frequency, alerts_filter: alertsFilter, }: RuleAlertAction): RuleAction => ({ group, @@ -24,6 +25,7 @@ export const transformRuleToAlertAction = ({ actionTypeId, ...(alertsFilter && { alertsFilter }), ...(uuid && { uuid }), + ...(frequency && { frequency }), }); export const transformAlertToRuleAction = ({ @@ -32,6 +34,7 @@ export const transformAlertToRuleAction = ({ actionTypeId, params, uuid, + frequency, alertsFilter, }: RuleAction): RuleAlertAction => ({ group, @@ -40,6 +43,7 @@ export const transformAlertToRuleAction = ({ action_type_id: actionTypeId, ...(alertsFilter && { alerts_filter: alertsFilter }), ...(uuid && { uuid }), + ...(frequency && { frequency }), }); export const transformRuleToAlertResponseAction = ({ diff --git a/x-pack/plugins/security_solution/common/endpoint/constants.ts b/x-pack/plugins/security_solution/common/endpoint/constants.ts index 56dd1ee3655e5..a4f7d7661e7c6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/constants.ts @@ -88,6 +88,7 @@ export const ACTION_STATUS_ROUTE = `${BASE_ENDPOINT_ROUTE}/action_status`; export const ACTION_DETAILS_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/{action_id}`; export const ACTION_AGENT_FILE_INFO_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/{action_id}/file/{file_id}`; export const ACTION_AGENT_FILE_DOWNLOAD_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/{action_id}/file/{file_id}/download`; +export const ACTION_STATE_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/state`; export const failedFleetActionErrorCode = '424'; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts index 855dd3a3fe439..fdf75da0a134e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts @@ -155,38 +155,37 @@ export class EndpointActionGenerator extends BaseDataGenerator { TOutputType extends object = object, TParameters extends EndpointActionDataParameterTypes = EndpointActionDataParameterTypes >( - overrides: Partial> = {} + overrides: DeepPartial> = {} ): ActionDetails { - const details: ActionDetails = merge( - { - agents: ['agent-a'], - command: 'isolate', - completedAt: '2022-04-30T16:08:47.449Z', - hosts: { 'agent-a': { name: 'Host-agent-a' } }, - id: '123', - isCompleted: true, - isExpired: false, - wasSuccessful: true, - errors: undefined, - startedAt: '2022-04-27T16:08:47.449Z', - status: 'successful', - comment: 'thisisacomment', - createdBy: 'auserid', - parameters: undefined, - outputs: {}, - agentState: { - 'agent-a': { - errors: undefined, - isCompleted: true, - completedAt: '2022-04-30T16:08:47.449Z', - wasSuccessful: true, - }, + const details: ActionDetails = { + agents: ['agent-a'], + command: 'isolate', + completedAt: '2022-04-30T16:08:47.449Z', + hosts: { 'agent-a': { name: 'Host-agent-a' } }, + id: '123', + isCompleted: true, + isExpired: false, + wasSuccessful: true, + errors: undefined, + startedAt: '2022-04-27T16:08:47.449Z', + status: 'successful', + comment: 'thisisacomment', + createdBy: 'auserid', + parameters: undefined, + outputs: {}, + agentState: { + 'agent-a': { + errors: undefined, + isCompleted: true, + completedAt: '2022-04-30T16:08:47.449Z', + wasSuccessful: true, }, }, - overrides - ); + }; - if (details.command === 'get-file') { + const command = overrides.command ?? details.command; + + if (command === 'get-file') { if (!details.parameters) { ( details as ActionDetails< @@ -213,7 +212,7 @@ export class EndpointActionGenerator extends BaseDataGenerator { } } - if (details.command === 'execute') { + if (command === 'execute') { if (!details.parameters) { ( details as ActionDetails< @@ -233,14 +232,17 @@ export class EndpointActionGenerator extends BaseDataGenerator { [details.agents[0]]: this.generateExecuteActionResponseOutput({ content: { output_file_id: getFileDownloadId(details, details.agents[0]), - ...overrides.outputs?.[details.agents[0]].content, + ...(overrides.outputs?.[details.agents[0]]?.content ?? {}), }, }), }; } } - return details as unknown as ActionDetails; + return merge(details, overrides as ActionDetails) as unknown as ActionDetails< + TOutputType, + TParameters + >; } randomGetFileFailureCode(): string { @@ -310,6 +312,7 @@ export class EndpointActionGenerator extends BaseDataGenerator { { type: 'json', content: { + code: 'ra_execute_success_done', stdout: this.randomChoice([ this.randomString(1280), this.randomString(3580), diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts index e040fa4811677..bc429feb208d7 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts @@ -12,14 +12,15 @@ import { merge, set } from 'lodash'; import { gte } from 'semver'; import type { EndpointCapabilities } from '../service/response_actions/constants'; import { BaseDataGenerator } from './base_data_generator'; -import type { HostMetadataInterface, OSFields } from '../types'; -import { EndpointStatus, HostPolicyResponseActionStatus } from '../types'; +import type { HostMetadataInterface, OSFields, HostInfoInterface } from '../types'; +import { EndpointStatus, HostPolicyResponseActionStatus, HostStatus } from '../types'; export interface GetCustomEndpointMetadataGeneratorOptions { /** Version for agent/endpoint. Defaults to the stack version */ version: string; /** OS type for the generated endpoint hosts */ os: 'macOS' | 'windows' | 'linux'; + isolation: boolean; } /** @@ -33,6 +34,7 @@ export class EndpointMetadataGenerator extends BaseDataGenerator { static custom({ version, os, + isolation, }: Partial = {}): typeof EndpointMetadataGenerator { return class extends EndpointMetadataGenerator { generate(overrides: DeepPartial = {}): HostMetadataInterface { @@ -54,6 +56,9 @@ export class EndpointMetadataGenerator extends BaseDataGenerator { set(overrides, 'host.os', EndpointMetadataGenerator.windowsOSFields); } } + if (isolation !== undefined) { + set(overrides, 'Endpoint.state.isolation', isolation); + } return super.generate(overrides); } @@ -104,10 +109,10 @@ export class EndpointMetadataGenerator extends BaseDataGenerator { /** Generate an Endpoint host metadata document */ generate(overrides: DeepPartial = {}): HostMetadataInterface { const ts = overrides['@timestamp'] ?? new Date().getTime(); - const hostName = this.randomHostname(); + const hostName = overrides?.host?.hostname ?? this.randomHostname(); const agentVersion = overrides?.agent?.version ?? this.randomVersion(); const agentId = this.seededUUIDv4(); - const isIsolated = this.randomBoolean(0.3); + const isIsolated = overrides?.Endpoint?.state?.isolation ?? this.randomBoolean(0.3); const capabilities: EndpointCapabilities[] = ['isolation']; // v8.4 introduced additional endpoint capabilities @@ -184,6 +189,31 @@ export class EndpointMetadataGenerator extends BaseDataGenerator { return merge(hostMetadataDoc, overrides); } + /** Generates the complete `HostInfo` as returned by a call to the Endpoint host details api */ + generateHostInfo(overrides: DeepPartial = {}): HostInfoInterface { + const hostInfo: HostInfoInterface = { + metadata: this.generate(), + host_status: HostStatus.HEALTHY, + policy_info: { + endpoint: { + id: 'policy-123', + revision: 4, + }, + agent: { + applied: { + id: 'policy-123', + revision: 4, + }, + configured: { + id: 'policy-123', + revision: 4, + }, + }, + }, + }; + return merge(hostInfo, overrides); + } + protected randomOsFields(): OSFields { return this.randomChoice([ EndpointMetadataGenerator.windowsOSFields, diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts index 5578c179ba1f5..8396f86a45e97 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_rule_alert_generator.ts @@ -37,6 +37,8 @@ export class EndpointRuleAlertGenerator extends BaseDataGenerator { const endpointMetadataGenerator = new EndpointMetadataGenerator(); const endpointMetadata = endpointMetadataGenerator.generate({ agent: { version: kibanaPackageJson.version }, + host: { hostname: overrides?.host?.hostname }, + Endpoint: { state: { isolation: overrides?.Endpoint?.state?.isolation } }, }); const now = overrides['@timestamp'] ?? new Date().toISOString(); const endpointAgentId = overrides?.agent?.id ?? this.seededUUIDv4(); 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 f9f96e650c056..684694bdb5c9a 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 @@ -75,6 +75,7 @@ export interface IndexedHostsResponse * @param policyResponseIndex * @param enrollFleet * @param generator + * @param disableEndpointActionsForHost */ export async function indexEndpointHostDocs({ numDocs, @@ -86,6 +87,7 @@ export async function indexEndpointHostDocs({ policyResponseIndex, enrollFleet, generator, + withResponseActions = true, }: { numDocs: number; client: Client; @@ -96,6 +98,7 @@ export async function indexEndpointHostDocs({ policyResponseIndex: string; enrollFleet: boolean; generator: EndpointDocGenerator; + withResponseActions?: boolean; }): Promise { const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents const timestamp = new Date().getTime(); @@ -190,13 +193,15 @@ export async function indexEndpointHostDocs({ }, }; - // Create some fleet endpoint actions and .logs-endpoint actions for this Host - const actionsResponse = await indexEndpointAndFleetActionsForHost( - client, - hostMetadata, - undefined - ); - mergeAndAppendArrays(response, actionsResponse); + if (withResponseActions) { + // Create some fleet endpoint actions and .logs-endpoint actions for this Host + const actionsResponse = await indexEndpointAndFleetActionsForHost( + client, + hostMetadata, + undefined + ); + mergeAndAppendArrays(response, actionsResponse); + } } await client diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_rule_alerts.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_rule_alerts.ts index 74e9d82a714e9..1c5883c052135 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_rule_alerts.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_rule_alerts.ts @@ -22,6 +22,8 @@ import { EndpointRuleAlertGenerator } from '../data_generators/endpoint_rule_ale export interface IndexEndpointRuleAlertsOptions { esClient: Client; endpointAgentId: string; + endpointHostname?: string; + endpointIsolated?: boolean; count?: number; log?: ToolingLog; } @@ -40,12 +42,16 @@ export interface DeletedIndexedEndpointRuleAlerts { * written them to for a given endpoint * @param esClient * @param endpointAgentId + * @param endpointHostname + * @param endpointIsolated * @param count * @param log */ export const indexEndpointRuleAlerts = async ({ esClient, endpointAgentId, + endpointHostname, + endpointIsolated, count = 1, log = new ToolingLog(), }: IndexEndpointRuleAlertsOptions): Promise => { @@ -57,7 +63,11 @@ export const indexEndpointRuleAlerts = async ({ const indexedAlerts: estypes.IndexResponse[] = []; for (let n = 0; n < count; n++) { - const alert = alertsGenerator.generate({ agent: { id: endpointAgentId } }); + const alert = alertsGenerator.generate({ + agent: { id: endpointAgentId }, + host: { hostname: endpointHostname }, + ...(endpointIsolated ? { Endpoint: { state: { isolation: endpointIsolated } } } : {}), + }); const indexedAlert = await esClient.index({ index: `${DEFAULT_ALERTS_INDEX}-default`, refresh: 'wait_for', 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 d867c25ececf7..76ee903eb6889 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -410,7 +410,9 @@ export class EndpointDocGenerator extends BaseDataGenerator { private createHostData(): CommonHostInfo { const { agent, elastic, host, Endpoint } = this.metadataGenerator.generate({ - Endpoint: { policy: { applied: this.randomChoice(APPLIED_POLICIES) } }, + Endpoint: { + policy: { applied: this.randomChoice(APPLIED_POLICIES) }, + }, }); return { agent, elastic, host, Endpoint }; 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 77b3135c12353..db5039e5a72f0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -48,6 +48,7 @@ export type IndexedHostsAndAlertsResponse = IndexedHostsResponse; * @param fleet * @param options * @param DocGenerator + * @param withResponseActions */ export async function indexHostsAndAlerts( client: Client, @@ -62,7 +63,8 @@ export async function indexHostsAndAlerts( alertsPerHost: number, fleet: boolean, options: TreeOptions = {}, - DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator + DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator, + withResponseActions = true ): Promise { const random = seedrandom(seed); const epmEndpointPackage = await getEndpointPackageInfo(kbnClient); @@ -114,6 +116,7 @@ export async function indexHostsAndAlerts( policyResponseIndex, enrollFleet: fleet, generator, + withResponseActions, }); mergeAndAppendArrays(response, indexedHosts); diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts index 5dc5059d21262..c22dd615a5d89 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts @@ -7,6 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; +import { RESPONSE_ACTION_API_COMMANDS_NAMES } from '../service/response_actions/constants'; import { EndpointActionListRequestSchema, NoParametersRequestSchema, @@ -185,7 +186,7 @@ describe('actions schemas', () => { }).not.toThrow(); }); - it.each(['isolate', 'unisolate', 'kill-process', 'suspend-process', 'running-processes'])( + it.each(RESPONSE_ACTION_API_COMMANDS_NAMES)( 'should work with commands query params with %s action', (command) => { expect(() => { diff --git a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts index b304dcd819d8b..ea7ee057dc273 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts @@ -76,6 +76,18 @@ export const commandToRBACMap: Record +>({ + isolate: 'isolate', + unisolate: 'release', + execute: 'execute', + 'get-file': 'get-file', + 'running-processes': 'processes', + 'kill-process': 'kill-process', + 'suspend-process': 'suspend-process', +}); + // 4 hrs in seconds // 4 * 60 * 60 export const DEFAULT_EXECUTE_ACTION_TIMEOUT = 14400; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index 58c07459de441..f8f5da28943b8 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -65,6 +65,7 @@ export interface ResponseActionGetFileOutputContent { } export interface ResponseActionExecuteOutputContent { + code: string; /* The truncated 'tail' output of the command */ stdout: string; /* The truncated 'tail' of any errors generated by the command */ @@ -307,8 +308,9 @@ export interface ResponseActionApiResponse { export interface EndpointPendingActions { agent_id: string; - pending_actions: { - /** Number of actions pending for each type. The `key` could be one of the `RESPONSE_ACTION_COMMANDS` values. */ + /** Number of actions pending for each type */ + pending_actions: Partial> & { + // Defined any other key just in case we get back some other actions [key: string]: number; }; } 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 75899b5422039..4ab2e1e3d2ed0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -473,8 +473,10 @@ export type PolicyInfo = Immutable<{ id: string; }>; -export type HostInfo = Immutable<{ - metadata: HostMetadata; +// Host Information as returned by the Host Details API. +// NOTE: `HostInfo` type is the original and defined as Immutable. +export interface HostInfoInterface { + metadata: HostMetadataInterface; host_status: HostStatus; policy_info?: { agent: { @@ -492,7 +494,9 @@ export type HostInfo = Immutable<{ */ endpoint: PolicyInfo; }; -}>; +} + +export type HostInfo = Immutable; // Host metadata document streamed up to ES by the Endpoint running on host machines. // NOTE: `HostMetadata` type is the original and defined as Immutable. If needing to diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/common/index.ts index 7f708ab3f6111..2e433b8cfd45f 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/common/index.ts @@ -7,7 +7,7 @@ import type { CloudEcs, HostEcs, OsEcs } from '@kbn/securitysolution-ecs'; import type { Hit, Hits, Maybe, SearchHit, StringOrNumber, TotalValue } from '../../../common'; -import type { EndpointPendingActions, HostStatus } from '../../../../endpoint/types'; +import type { EndpointPendingActions, HostInfo, HostStatus } from '../../../../endpoint/types'; import type { CommonFields } from '../..'; export enum HostPolicyResponseActionStatus { @@ -33,6 +33,8 @@ export interface EndpointFields { elasticAgentStatus?: Maybe; fleetAgentId?: Maybe; id?: Maybe; + /** The complete Endpoint Host Details information (which also includes some of the fields above */ + hostInfo?: HostInfo; } interface AgentFields { diff --git a/x-pack/plugins/security_solution/common/types/session_view/index.ts b/x-pack/plugins/security_solution/common/types/session_view/index.ts index 105e5cc6b1d84..be1a712c92312 100644 --- a/x-pack/plugins/security_solution/common/types/session_view/index.ts +++ b/x-pack/plugins/security_solution/common/types/session_view/index.ts @@ -6,7 +6,9 @@ */ export interface SessionViewConfig { + processIndex: string; sessionEntityId: string; + sessionStartTime: string; jumpToEntityId?: string; jumpToCursor?: string; investigatedAlertId?: string; diff --git a/x-pack/plugins/security_solution/common/utils/alert_detail_path.test.ts b/x-pack/plugins/security_solution/common/utils/alert_detail_path.test.ts new file mode 100644 index 0000000000000..be827e082db14 --- /dev/null +++ b/x-pack/plugins/security_solution/common/utils/alert_detail_path.test.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 { buildAlertDetailPath, getAlertDetailsUrl } from './alert_detail_path'; + +describe('alert_detail_path', () => { + const defaultArguments = { + alertId: 'testId', + index: 'testIndex', + timestamp: '2023-04-18T00:00:00.000Z', + }; + describe('buildAlertDetailPath', () => { + it('builds the alert detail path as expected', () => { + expect(buildAlertDetailPath(defaultArguments)).toMatchInlineSnapshot( + `"/alerts/redirect/testId?index=testIndex×tamp=2023-04-18T00:00:00.000Z"` + ); + }); + }); + describe('getAlertDetailsUrl', () => { + it('builds the alert detail path without a space id', () => { + expect( + getAlertDetailsUrl({ + ...defaultArguments, + basePath: 'http://somebasepath.com', + }) + ).toMatchInlineSnapshot( + `"http://somebasepath.com/app/security/alerts/redirect/testId?index=testIndex×tamp=2023-04-18T00:00:00.000Z"` + ); + }); + + it('builds the alert detail path with a space id', () => { + expect( + getAlertDetailsUrl({ + ...defaultArguments, + basePath: 'http://somebasepath.com', + spaceId: 'test-space', + }) + ).toMatchInlineSnapshot( + `"http://somebasepath.com/s/test-space/app/security/alerts/redirect/testId?index=testIndex×tamp=2023-04-18T00:00:00.000Z"` + ); + }); + + it('does not build the alert detail path without a basePath', () => { + expect( + getAlertDetailsUrl({ + ...defaultArguments, + spaceId: 'test-space', + }) + ).toBe(undefined); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/common/utils/alert_detail_path.ts b/x-pack/plugins/security_solution/common/utils/alert_detail_path.ts new file mode 100644 index 0000000000000..2fcc1b6687b7d --- /dev/null +++ b/x-pack/plugins/security_solution/common/utils/alert_detail_path.ts @@ -0,0 +1,39 @@ +/* + * 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 { addSpaceIdToPath } from '@kbn/spaces-plugin/common'; +import { ALERT_DETAILS_REDIRECT_PATH, APP_PATH } from '../constants'; + +export const buildAlertDetailPath = ({ + alertId, + index, + timestamp, +}: { + alertId: string; + index: string; + timestamp: string; +}) => `${ALERT_DETAILS_REDIRECT_PATH}/${alertId}?index=${index}×tamp=${timestamp}`; + +export const getAlertDetailsUrl = ({ + alertId, + index, + timestamp, + basePath, + spaceId, +}: { + alertId: string; + index: string; + timestamp: string; + basePath?: string; + spaceId?: string | null; +}) => { + const alertDetailPath = buildAlertDetailPath({ alertId, index, timestamp }); + const alertDetailPathWithAppPath = `${APP_PATH}${alertDetailPath}`; + return basePath + ? addSpaceIdToPath(basePath, spaceId ?? undefined, alertDetailPathWithAppPath) + : undefined; +}; diff --git a/x-pack/plugins/security_solution/cypress/e2e/cases/creation.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/cases/creation.cy.ts index 46bfd1f388ea6..260ef5a393b3e 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/cases/creation.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/cases/creation.cy.ts @@ -26,7 +26,6 @@ import { CASE_DETAILS_PAGE_TITLE, CASE_DETAILS_STATUS, CASE_DETAILS_TAGS, - CASE_DETAILS_USER_ACTION_DESCRIPTION_USERNAME, CASE_DETAILS_USER_ACTION_DESCRIPTION_EVENT, CASE_DETAILS_USERNAMES, PARTICIPANTS, @@ -99,8 +98,7 @@ describe('Cases', () => { const expectedTags = this.mycase.tags.join(''); cy.get(CASE_DETAILS_PAGE_TITLE).should('have.text', this.mycase.name); cy.get(CASE_DETAILS_STATUS).should('have.text', 'Open'); - cy.get(CASE_DETAILS_USER_ACTION_DESCRIPTION_USERNAME).should('have.text', this.mycase.reporter); - cy.get(CASE_DETAILS_USER_ACTION_DESCRIPTION_EVENT).should('have.text', 'added description'); + cy.get(CASE_DETAILS_USER_ACTION_DESCRIPTION_EVENT).should('have.text', 'Description'); cy.get(CASE_DETAILS_DESCRIPTION).should( 'have.text', `${this.mycase.description} ${this.mycase.timeline.title}` diff --git a/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts index 66820050516be..b8f8243ff9f87 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/dashboards/entity_analytics.cy.ts @@ -28,6 +28,10 @@ import { USERS_TABLE_ALERT_CELL, HOSTS_TABLE_ALERT_CELL, HOSTS_TABLE, + ANOMALIES_TABLE_NEXT_PAGE_BUTTON, + ANOMALIES_TABLE_ENABLE_JOB_BUTTON, + ANOMALIES_TABLE_ENABLE_JOB_LOADER, + ANOMALIES_TABLE_COUNT_COLUMN, } from '../../screens/entity_analytics'; import { openRiskTableFilterAndSelectTheLowOption } from '../../tasks/host_risk'; import { createRule } from '../../tasks/api_calls/rules'; @@ -35,6 +39,7 @@ import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { getNewRule } from '../../objects/rule'; import { clickOnFirstHostsAlerts, clickOnFirstUsersAlerts } from '../../tasks/risk_scores'; import { OPTION_LIST_LABELS, OPTION_LIST_VALUES } from '../../screens/common/filter_group'; +import { setRowsPerPageTo } from '../../tasks/table_pagination'; const TEST_USER_ALERTS = 2; const TEST_USER_NAME = 'test'; @@ -239,13 +244,39 @@ describe('Entity Analytics Dashboard', () => { }); describe('With anomalies data', () => { + before(() => { + esArchiverLoad('network'); + }); + + after(() => { + esArchiverUnload('network'); + }); + beforeEach(() => { visit(ENTITY_ANALYTICS_URL); }); - it('renders table', () => { + it('renders table with pagination', () => { cy.get(ANOMALIES_TABLE).should('be.visible'); - cy.get(ANOMALIES_TABLE_ROWS).should('have.length', 6); + cy.get(ANOMALIES_TABLE_ROWS).should('have.length', 10); + + // navigates to next page + cy.get(ANOMALIES_TABLE_NEXT_PAGE_BUTTON).click(); + cy.get(ANOMALIES_TABLE_ROWS).should('have.length', 10); + + // updates rows per page to 25 items + setRowsPerPageTo(25); + cy.get(ANOMALIES_TABLE_ROWS).should('have.length', 25); + }); + + it('enables a job', () => { + cy.get(ANOMALIES_TABLE_ROWS) + .eq(5) + .within(() => { + cy.get(ANOMALIES_TABLE_ENABLE_JOB_BUTTON).click(); + cy.get(ANOMALIES_TABLE_ENABLE_JOB_LOADER).should('be.visible'); + cy.get(ANOMALIES_TABLE_COUNT_COLUMN).should('include.text', '0'); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts index 947f92d3ec4aa..a42f81481d576 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/alerts_details.cy.ts @@ -134,7 +134,7 @@ describe('Alert details flyout', () => { cy.get('[data-test-subj="formatted-field-_id"]') .invoke('text') .then((alertId) => { - cy.visit(`http://localhost:5620/app/security/alerts/${alertId}`); + cy.visit(`http://localhost:5620/app/security/alerts/redirect/${alertId}`); cy.get('[data-test-subj="unifiedQueryInput"]').should('have.text', `_id: ${alertId}`); cy.get(ALERTS_COUNT).should('have.text', '1 alert'); cy.get(OVERVIEW_RULE).should('be.visible'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts index a5442b786040f..a15669a620d49 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts @@ -100,9 +100,7 @@ describe.skip('Alert details expandable flyout left panel', { testIsolation: fal it('should display content when switching buttons', () => { openVisualizeTab(); openSessionView(); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT) - .should('be.visible') - .and('have.text', 'Session view'); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT).should('be.visible'); openGraphAnalyzer(); cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT).should('be.visible'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts new file mode 100644 index 0000000000000..1d18b33350fd4 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA } from '../../../screens/document_expandable_flyout'; +import { + expandFirstAlertExpandableFlyout, + expandDocumentDetailsExpandableFlyoutLeftSection, +} from '../../../tasks/document_expandable_flyout'; +import { cleanKibana } from '../../../tasks/common'; +import { login, visit } from '../../../tasks/login'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { getNewRule } from '../../../objects/rule'; +import { ALERTS_URL } from '../../../urls/navigation'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; + +// Skipping these for now as the feature is protected behind a feature flag set to false by default +// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50 +describe.skip( + 'Alert details expandable flyout left panel session view', + { testIsolation: false }, + () => { + before(() => { + cleanKibana(); + login(); + createRule(getNewRule()); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertExpandableFlyout(); + expandDocumentDetailsExpandableFlyoutLeftSection(); + }); + + it('should display session view no data message', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA) + .should('be.visible') + .and('contain.text', 'No data to render') + .and('contain.text', 'No process events found for this query'); + }); + + it('should display session view component', () => {}); + } +); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index adb9d75dce78e..57ea1a2d4efdb 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -28,6 +28,11 @@ import { DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON, DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_TREE, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON, } from '../../../screens/document_expandable_flyout'; import { expandFirstAlertExpandableFlyout, @@ -35,6 +40,7 @@ import { toggleOverviewTabDescriptionSection, toggleOverviewTabInvestigationSection, toggleOverviewTabInsightsSection, + toggleOverviewTabVisualizationsSection, } from '../../../tasks/document_expandable_flyout'; import { cleanKibana } from '../../../tasks/common'; import { login, visit } from '../../../tasks/login'; @@ -178,6 +184,49 @@ describe.skip( .click(); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); }); + + // TODO work on getting proper IoC data to make the threat intelligence section work here + it.skip('should display threat intelligence section', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER) + .scrollIntoView() + .should('be.visible') + .and('have.text', 'Threat Intelligence'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT) + .should('be.visible') + .within(() => { + // threat match detected + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES) + .eq(0) + .should('be.visible') + .and('have.text', '1 threat match detected'); // TODO + + // field with threat enrichement + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES) + .eq(1) + .should('be.visible') + .and('have.text', '1 field enriched with threat intelligence'); // TODO + }); + }); + + // TODO work on getting proper IoC data to make the threat intelligence section work here + // and improve when we can navigate Threat Intelligence to sub tab directly + it.skip('should navigate to left panel, entities tab when view all fields of threat intelligence is clicked', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON) + .should('be.visible') + .click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); + }); + }); + + describe('visualizations section', () => { + before(() => { + toggleOverviewTabInsightsSection(); + toggleOverviewTabVisualizationsSection(); + }); + + it('should display analyzer preview', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_TREE).should('be.visible'); + }); }); } ); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts new file mode 100644 index 0000000000000..b7580642ba564 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts @@ -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 { getNewRule } from '../../../objects/rule'; +import { cleanKibana } from '../../../tasks/common'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { expandFirstAlertExpandableFlyout } from '../../../tasks/document_expandable_flyout'; +import { login, visit } from '../../../tasks/login'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { ALERTS_URL } from '../../../urls/navigation'; +import { + DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE, +} from '../../../screens/document_expandable_flyout'; + +// Skipping these for now as the feature is protected behind a feature flag set to false by default +// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50 +describe.skip('Expandable flyout state sync', { testIsolation: false }, () => { + const rule = getNewRule(); + + before(() => { + cleanKibana(); + login(); + createRule(rule); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertExpandableFlyout(); + }); + + it('should serialize its state to url', () => { + cy.url().should('include', 'eventFlyout'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + }); + + it('should reopen the flyout after browser refresh', () => { + cy.reload(); + + cy.url().should('include', 'eventFlyout'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + }); + + it('should clear the url state when flyout is closed', () => { + cy.reload(); + + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + + cy.get(DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON).click(); + + cy.url().should('not.include', 'eventFlyout'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts index e2d1204be84b2..213efb75e9860 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/investigate_in_timeline.cy.ts @@ -8,6 +8,7 @@ import { closeTimeline } from '../../tasks/timeline'; import { getNewRule } from '../../objects/rule'; import { PROVIDER_BADGE, QUERY_TAB_BUTTON, TIMELINE_TITLE } from '../../screens/timeline'; +import { FILTER_BADGE } from '../../screens/alerts'; import { expandFirstAlert, investigateFirstAlertInTimeline } from '../../tasks/alerts'; import { createRule } from '../../tasks/api_calls/rules'; @@ -23,7 +24,6 @@ import { INSIGHTS_RELATED_ALERTS_BY_ANCESTRY, INSIGHTS_RELATED_ALERTS_BY_SESSION, SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON, - SUMMARY_VIEW_PREVALENCE_CELL, } from '../../screens/alerts_details'; import { verifyInsightCount } from '../../tasks/alerts_details'; @@ -63,19 +63,29 @@ describe('Investigate in timeline', { testIsolation: false }, () => { }); it('should open a new timeline from a prevalence field', () => { - cy.get(SUMMARY_VIEW_PREVALENCE_CELL) - .first() - .invoke('text') - .then((alertCount) => { - // Click on the first button that lets us investigate in timeline - cy.get(ALERT_FLYOUT).find(SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON).first().click(); + // Only one alert matches the exact process args in this case + const alertCount = 1; - // Make sure a new timeline is created and opened - cy.get(TIMELINE_TITLE).should('contain.text', 'Untitled timeline'); + // Click on the last button that lets us investigate in timeline. + // We expect this to be the `process.args` row. + const investigateButton = cy + .get(ALERT_FLYOUT) + .find(SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON) + .last(); + investigateButton.should('have.text', alertCount); + investigateButton.click(); - // The alert count in this timeline should match the count shown on the alert flyout - cy.get(QUERY_TAB_BUTTON).should('contain.text', alertCount); - }); + // Make sure a new timeline is created and opened + cy.get(TIMELINE_TITLE).should('have.text', 'Untitled timeline'); + + // The alert count in this timeline should match the count shown on the alert flyout + cy.get(QUERY_TAB_BUTTON).should('contain.text', alertCount); + + // The correct filter is applied to the timeline query + cy.get(FILTER_BADGE).should( + 'have.text', + ' {"bool":{"must":[{"term":{"process.args":"-zsh"}},{"term":{"process.args":"unique"}}]}}' + ); }); it('should open a new timeline from an insights module', () => { diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts new file mode 100644 index 0000000000000..ca25b9955bf97 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts @@ -0,0 +1,132 @@ +/* + * 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 { + waitForRulesTableToBeLoaded, + goToTheRuleDetailsOf, + selectNumberOfRules, + duplicateSelectedRulesWithoutExceptions, + expectManagementTableRules, + duplicateSelectedRulesWithExceptions, + duplicateSelectedRulesWithNonExpiredExceptions, +} from '../../tasks/alerts_detection_rules'; + +import { goToExceptionsTab, viewExpiredExceptionItems } from '../../tasks/rule_details'; +import { login, visitWithoutDateRange } from '../../tasks/login'; + +import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; +import { createRule } from '../../tasks/api_calls/rules'; +import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common'; + +import { getNewRule } from '../../objects/rule'; + +import { esArchiverResetKibana } from '../../tasks/es_archiver'; + +import { createRuleExceptionItem } from '../../tasks/api_calls/exceptions'; +import { EXCEPTION_CARD_ITEM_NAME } from '../../screens/exceptions'; +import { + assertExceptionItemsExists, + assertNumberOfExceptionItemsExists, +} from '../../tasks/exceptions'; + +const RULE_NAME = 'Custom rule for bulk actions'; + +const prePopulatedIndexPatterns = ['index-1-*', 'index-2-*']; +const prePopulatedTags = ['test-default-tag-1', 'test-default-tag-2']; + +const defaultRuleData = { + index: prePopulatedIndexPatterns, + tags: prePopulatedTags, +}; + +const expiredDate = new Date(Date.now() - 1000000).toISOString(); +const futureDate = new Date(Date.now() + 1000000).toISOString(); + +const EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item'; + +const NON_EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item with future expiration'; + +describe('Detection rules, bulk duplicate', () => { + before(() => { + cleanKibana(); + login(); + }); + beforeEach(() => { + // Make sure persisted rules table state is cleared + resetRulesTableState(); + deleteAlertsAndRules(); + esArchiverResetKibana(); + createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1' })).then( + (response) => { + createRuleExceptionItem(response.body.id, [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: EXPIRED_EXCEPTION_ITEM_NAME, + type: 'simple', + expire_time: expiredDate, + }, + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'user.name', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: NON_EXPIRED_EXCEPTION_ITEM_NAME, + type: 'simple', + expire_time: futureDate, + }, + ]); + } + ); + + visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL); + + waitForRulesTableToBeLoaded(); + }); + + it('Duplicates rules', () => { + selectNumberOfRules(1); + duplicateSelectedRulesWithoutExceptions(); + expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); + }); + + describe('With exceptions', () => { + it('Duplicates rules with expired exceptions', () => { + selectNumberOfRules(1); + duplicateSelectedRulesWithExceptions(); + expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); + goToTheRuleDetailsOf(`${RULE_NAME} [Duplicate]`); + goToExceptionsTab(); + assertExceptionItemsExists(EXCEPTION_CARD_ITEM_NAME, [NON_EXPIRED_EXCEPTION_ITEM_NAME]); + viewExpiredExceptionItems(); + assertExceptionItemsExists(EXCEPTION_CARD_ITEM_NAME, [EXPIRED_EXCEPTION_ITEM_NAME]); + }); + + it('Duplicates rules with exceptions, excluding expired exceptions', () => { + selectNumberOfRules(1); + duplicateSelectedRulesWithNonExpiredExceptions(); + expectManagementTableRules([`${RULE_NAME} [Duplicate]`]); + goToTheRuleDetailsOf(`${RULE_NAME} [Duplicate]`); + goToExceptionsTab(); + assertExceptionItemsExists(EXCEPTION_CARD_ITEM_NAME, [NON_EXPIRED_EXCEPTION_ITEM_NAME]); + viewExpiredExceptionItems(); + assertNumberOfExceptionItemsExists(0); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts index fceecae2b1d5b..624dec7f1c955 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; import { ROLES } from '../../../common/test'; import { @@ -15,11 +16,18 @@ import { import { actionFormSelector } from '../../screens/common/rule_actions'; import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../tasks/common'; +import type { RuleActionCustomFrequency } from '../../tasks/common/rule_actions'; import { addSlackRuleAction, assertSlackRuleAction, addEmailConnectorAndRuleAction, assertEmailRuleAction, + assertSelectedCustomFrequencyOption, + assertSelectedPerRuleRunFrequencyOption, + assertSelectedSummaryOfAlertsOption, + pickCustomFrequencyOption, + pickPerRuleRunFrequencyOption, + pickSummaryOfAlertsOption, } from '../../tasks/common/rule_actions'; import { waitForRulesTableToBeLoaded, @@ -32,10 +40,8 @@ import { submitBulkEditForm, checkOverwriteRuleActionsCheckbox, openBulkEditRuleActionsForm, - pickActionFrequency, openBulkActionsMenu, } from '../../tasks/rules_bulk_edit'; -import { assertSelectedActionFrequency } from '../../tasks/edit_rule'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { esArchiverResetKibana } from '../../tasks/es_archiver'; @@ -75,7 +81,7 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { esArchiverResetKibana(); createSlackConnector().then(({ body }) => { - const actions = [ + const actions: RuleActionArray = [ { id: body.id, action_type_id: '.slack', @@ -83,6 +89,11 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { params: { message: expectedExistingSlackMessage, }, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, }, ]; @@ -120,7 +131,10 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { }); it('Add a rule action to rules (existing connector)', () => { - const expectedActionFrequency = 'Daily'; + const expectedActionFrequency: RuleActionCustomFrequency = { + throttle: 1, + throttleUnit: 'd', + }; loadPrebuiltDetectionRulesFromHeaderBtn(); @@ -131,8 +145,9 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // ensure rule actions info callout displayed on the form cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible'); - pickActionFrequency(expectedActionFrequency); addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited }); @@ -140,7 +155,8 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); assertSlackRuleAction(expectedExistingSlackMessage, 0); assertSlackRuleAction(expectedSlackMessage, 1); // ensure there is no third action @@ -148,16 +164,15 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { }); it('Overwrite rule actions in rules', () => { - const expectedActionFrequency = 'On each rule execution'; - loadPrebuiltDetectionRulesFromHeaderBtn(); // select both custom and prebuilt rules selectNumberOfRules(expectedNumberOfRulesToBeEdited); openBulkEditRuleActionsForm(); - pickActionFrequency(expectedActionFrequency); addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickPerRuleRunFrequencyOption(); // check overwrite box, ensure warning is displayed checkOverwriteRuleActionsCheckbox(); @@ -171,22 +186,27 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedPerRuleRunFrequencyOption(); assertSlackRuleAction(expectedSlackMessage); // ensure existing action was overwritten cy.get(actionFormSelector(1)).should('not.exist'); }); it('Add a rule action to rules (new connector)', () => { - const expectedActionFrequency = 'Hourly'; + const expectedActionFrequency: RuleActionCustomFrequency = { + throttle: 2, + throttleUnit: 'h', + }; const expectedEmail = 'test@example.com'; const expectedSubject = 'Subject'; selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); openBulkEditRuleActionsForm(); - pickActionFrequency(expectedActionFrequency); addEmailConnectorAndRuleAction(expectedEmail, expectedSubject); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); @@ -194,7 +214,8 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); assertEmailRuleAction(expectedEmail, expectedSubject); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts index 8e2ae1b85cce7..4965072e7038d 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts @@ -19,10 +19,13 @@ import { RULE_SWITCH, SEVERITY, } from '../../screens/alerts_detection_rules'; +import { + ACTIONS_NOTIFY_WHEN_BUTTON, + ACTIONS_SUMMARY_BUTTON, +} from '../../screens/common/rule_actions'; import { ABOUT_CONTINUE_BTN, ABOUT_EDIT_BUTTON, - ACTIONS_THROTTLE_INPUT, CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, DEFINE_EDIT_BUTTON, @@ -401,12 +404,11 @@ describe('Custom query rules', () => { goToActionsStepTab(); - cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); - - cy.get(ACTIONS_THROTTLE_INPUT).select('Weekly'); - addEmailConnectorAndRuleAction('test@example.com', 'Subject'); + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts'); + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run'); + goToAboutStepTab(); cy.get(TAGS_CLEAR_BUTTON).click({ force: true }); fillAboutRule(getEditedRule()); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts index 740bdcfb54dac..fbc28e8a2c671 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/indicator_match_rule.cy.ts @@ -58,12 +58,12 @@ import { INDICATOR_MATCH_ROW_RENDER, PROVIDER_BADGE } from '../../screens/timeli import { investigateFirstAlertInTimeline } from '../../tasks/alerts'; import { duplicateFirstRule, - duplicateSelectedRules, duplicateRuleFromMenu, goToRuleDetails, selectNumberOfRules, checkDuplicatedRule, expectNumberOfRules, + duplicateSelectedRulesWithExceptions, } from '../../tasks/alerts_detection_rules'; import { createRule } from '../../tasks/api_calls/rules'; import { loadPrepackagedTimelineTemplates } from '../../tasks/api_calls/timelines'; @@ -544,7 +544,7 @@ describe('indicator match', () => { it("Allows the rule to be duplicated from the table's bulk actions", () => { selectNumberOfRules(1); - duplicateSelectedRules(); + duplicateSelectedRulesWithExceptions(); checkDuplicatedRule(); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/maintenance_window.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/maintenance_window.cy.ts new file mode 100644 index 0000000000000..af534d325ebca --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/maintenance_window.cy.ts @@ -0,0 +1,58 @@ +/* + * 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 { INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH } from '@kbn/alerting-plugin/common'; +import type { MaintenanceWindowCreateBody } from '@kbn/alerting-plugin/common'; +import type { AsApiContract } from '@kbn/alerting-plugin/server/routes/lib'; +import { cleanKibana } from '../../tasks/common'; +import { login, visit } from '../../tasks/login'; +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; + +describe('Maintenance window callout on Rule Management page', () => { + let maintenanceWindowId = ''; + + before(() => { + cleanKibana(); + login(); + + const body: AsApiContract = { + title: 'My maintenance window', + duration: 60000, // 1 minute + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'Europe/Amsterdam', + freq: 0, + count: 1, + }, + }; + + // Create a test maintenance window + cy.request({ + method: 'POST', + url: INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH, + headers: { 'kbn-xsrf': 'cypress-creds' }, + body, + }).then((response) => { + maintenanceWindowId = response.body.id; + }); + }); + + after(() => { + // Delete a test maintenance window + cy.request({ + method: 'DELETE', + url: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/${maintenanceWindowId}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); + }); + + it('Displays the callout when there are running maintenance windows', () => { + visit(DETECTIONS_RULE_MANAGEMENT_URL); + + cy.contains('A maintenance window is currently running'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts index 5ed5ef8be059a..ab458e12dca2d 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts @@ -43,7 +43,7 @@ describe('Rule actions during detection rule creation', () => { }); const rule = getSimpleCustomQueryRule(); - const actions = { throttle: 'rule', connectors: [indexConnector] }; + const actions = { connectors: [indexConnector] }; const index = actions.connectors[0].index; const initialNumberOfDocuments = 0; const expectedJson = JSON.parse(actions.connectors[0].document); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts index 80ec42d3ab408..572e535d740fd 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts @@ -14,8 +14,8 @@ import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../.. import { EXCEPTIONS_URL } from '../../../urls/navigation'; import { - deleteExceptionListWithRuleReference, - deleteExceptionListWithoutRuleReference, + deleteExceptionListWithRuleReferenceByListId, + deleteExceptionListWithoutRuleReferenceByListId, exportExceptionList, searchForExceptionList, waitForExceptionsTableToBeLoaded, @@ -79,7 +79,7 @@ describe('Exceptions Table', () => { visitWithoutDateRange(EXCEPTIONS_URL); waitForExceptionsTableToBeLoaded(); - exportExceptionList(); + exportExceptionList(getExceptionList1().list_id); cy.wait('@export').then(({ response }) => { cy.wrap(response?.body).should( @@ -153,7 +153,7 @@ describe('Exceptions Table', () => { // just checking number of lists shown cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); - deleteExceptionListWithoutRuleReference(); + deleteExceptionListWithoutRuleReferenceByListId(getExceptionList1().list_id); // Using cy.contains because we do not care about the exact text, // just checking number of lists shown @@ -168,7 +168,7 @@ describe('Exceptions Table', () => { // just checking number of lists shown cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2'); - deleteExceptionListWithRuleReference(); + deleteExceptionListWithRuleReferenceByListId(getExceptionList2().list_id); // Using cy.contains because we do not care about the exact text, // just checking number of lists shown diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts index bc6279113c8e5..d5e68594b9967 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/all_exception_lists_read_only.cy.ts @@ -9,7 +9,10 @@ import { esArchiverResetKibana } from '../../../tasks/es_archiver'; import { cleanKibana } from '../../../tasks/common'; import { ROLES } from '../../../../common/test'; import { getExceptionList } from '../../../objects/exception'; -import { EXCEPTIONS_TABLE_SHOWING_LISTS } from '../../../screens/exceptions'; +import { + EXCEPTIONS_OVERFLOW_ACTIONS_BTN, + EXCEPTIONS_TABLE_SHOWING_LISTS, +} from '../../../screens/exceptions'; import { createExceptionList, deleteExceptionList } from '../../../tasks/api_calls/exceptions'; import { dismissCallOut, @@ -56,4 +59,8 @@ describe('All exception lists - read only', () => { getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); }); }); + + it('Exception list actions should be disabled', () => { + cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().should('be.disabled'); + }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts index 1888ee78b7223..bc3e732538ff0 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/exceptions/shared_exception_lists_management/manage_shared_exception_list.cy.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ROLES } from '../../../../common/test'; import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception'; import { getNewRule } from '../../../objects/rule'; @@ -14,135 +13,224 @@ import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../.. import { EXCEPTIONS_URL } from '../../../urls/navigation'; import { - deleteExceptionListWithRuleReference, - deleteExceptionListWithoutRuleReference, + assertExceptionListsExists, + duplicateSharedExceptionListFromListsManagementPageByListId, + findSharedExceptionListItemsByName, + deleteExceptionListWithoutRuleReferenceByListId, + deleteExceptionListWithRuleReferenceByListId, exportExceptionList, waitForExceptionsTableToBeLoaded, createSharedExceptionList, + linkRulesToExceptionList, + assertNumberLinkedRules, } from '../../../tasks/exceptions_table'; import { EXCEPTIONS_LIST_MANAGEMENT_NAME, - EXCEPTIONS_OVERFLOW_ACTIONS_BTN, EXCEPTIONS_TABLE_SHOWING_LISTS, } from '../../../screens/exceptions'; -import { createExceptionList } from '../../../tasks/api_calls/exceptions'; +import { createExceptionList, createExceptionListItem } from '../../../tasks/api_calls/exceptions'; import { esArchiverResetKibana } from '../../../tasks/es_archiver'; +import { assertNumberOfExceptionItemsExists } from '../../../tasks/exceptions'; + import { TOASTER } from '../../../screens/alerts_detection_rules'; const EXCEPTION_LIST_NAME = 'My shared list'; +const EXCEPTION_LIST_TO_DUPLICATE_NAME = 'A test list 2'; +const EXCEPTION_LIST_ITEM_NAME = 'Sample Exception List Item 1'; +const EXCEPTION_LIST_ITEM_NAME_2 = 'Sample Exception List Item 2'; + const getExceptionList1 = () => ({ ...getExceptionList(), name: EXCEPTION_LIST_NAME, list_id: 'exception_list_1', }); + const getExceptionList2 = () => ({ ...getExceptionList(), - name: 'Test list 2', + name: EXCEPTION_LIST_TO_DUPLICATE_NAME, list_id: 'exception_list_2', }); +const expiredDate = new Date(Date.now() - 1000000).toISOString(); +const futureDate = new Date(Date.now() + 1000000).toISOString(); + describe('Manage shared exception list', () => { - before(() => { - esArchiverResetKibana(); - login(); - - // Create exception list associated with a rule - createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) => - createRule( - getNewRule({ - exceptions_list: [ - { - id: response.body.id, - list_id: getExceptionList2().list_id, - type: getExceptionList2().type, - namespace_type: getExceptionList2().namespace_type, - }, - ], - }) - ) - ); - - // Create exception list not used by any rules - createExceptionList(getExceptionList1(), getExceptionList1().list_id).as( - 'exceptionListResponse' - ); - }); + describe('Create/Export/Delete', () => { + before(() => { + esArchiverResetKibana(); + login(); + + createRule(getNewRule({ name: 'Another rule' })); + + // Create exception list associated with a rule + createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) => + createRule( + getNewRule({ + exceptions_list: [ + { + id: response.body.id, + list_id: getExceptionList2().list_id, + type: getExceptionList2().type, + namespace_type: getExceptionList2().namespace_type, + }, + ], + }) + ) + ); - beforeEach(() => { - visitWithoutDateRange(EXCEPTIONS_URL); - waitForExceptionsTableToBeLoaded(); - }); + // Create exception list not used by any rules + createExceptionList(getExceptionList1(), getExceptionList1().list_id).as( + 'exceptionListResponse' + ); + }); + + beforeEach(() => { + visitWithoutDateRange(EXCEPTIONS_URL); + waitForExceptionsTableToBeLoaded(); + }); - it('Export exception list', function () { - cy.intercept(/(\/api\/exception_lists\/_export)/).as('export'); + it('Export exception list', function () { + cy.intercept(/(\/api\/exception_lists\/_export)/).as('export'); - exportExceptionList(); + exportExceptionList(getExceptionList1().list_id); - cy.wait('@export').then(({ response }) => { - cy.wrap(response?.body).should( - 'eql', - expectedExportedExceptionList(this.exceptionListResponse) - ); + cy.wait('@export').then(({ response }) => { + cy.wrap(response?.body).should( + 'eql', + expectedExportedExceptionList(this.exceptionListResponse) + ); + + cy.get(TOASTER).should( + 'have.text', + `Exception list "${EXCEPTION_LIST_NAME}" exported successfully` + ); + }); + }); + + it('Link rules to shared exception list', function () { + assertNumberLinkedRules(getExceptionList2().list_id, '1'); + linkRulesToExceptionList(getExceptionList2().list_id, 1); + assertNumberLinkedRules(getExceptionList2().list_id, '2'); + }); - cy.get(TOASTER).should( - 'have.text', - `Exception list "${EXCEPTION_LIST_NAME}" exported successfully` + it('Create exception list', function () { + createSharedExceptionList( + { name: 'Newly created list', description: 'This is my list.' }, + true ); + + // After creation - directed to list detail page + cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', 'Newly created list'); }); - }); - it('Create exception list', function () { - createSharedExceptionList({ name: EXCEPTION_LIST_NAME, description: 'This is my list.' }, true); + it('Delete exception list without rule reference', () => { + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '4'); - // After creation - directed to list detail page - cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', EXCEPTION_LIST_NAME); - }); + deleteExceptionListWithoutRuleReferenceByListId(getExceptionList1().list_id); + + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); + }); + + it('Deletes exception list with rule reference', () => { + waitForPageWithoutDateRange(EXCEPTIONS_URL); + waitForExceptionsTableToBeLoaded(); - it('Delete exception list without rule reference', () => { - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '4'); + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); - deleteExceptionListWithoutRuleReference(); + deleteExceptionListWithRuleReferenceByListId(getExceptionList2().list_id); - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2'); + }); }); - it('Deletes exception list with rule reference', () => { - waitForPageWithoutDateRange(EXCEPTIONS_URL); - waitForExceptionsTableToBeLoaded(); + describe('Duplicate', () => { + beforeEach(() => { + esArchiverResetKibana(); + login(); + + // Create exception list associated with a rule + createExceptionList(getExceptionList2(), getExceptionList2().list_id); + + createExceptionListItem(getExceptionList2().list_id, { + list_id: getExceptionList2().list_id, + item_id: 'simple_list_item_1', + tags: [], + type: 'simple', + description: 'Test exception item', + name: EXCEPTION_LIST_ITEM_NAME, + namespace_type: 'single', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + expire_time: expiredDate, + }); + createExceptionListItem(getExceptionList2().list_id, { + list_id: getExceptionList2().list_id, + item_id: 'simple_list_item_2', + tags: [], + type: 'simple', + description: 'Test exception item', + name: EXCEPTION_LIST_ITEM_NAME_2, + namespace_type: 'single', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + expire_time: futureDate, + }); + + visitWithoutDateRange(EXCEPTIONS_URL); + waitForExceptionsTableToBeLoaded(); + }); + + it('Duplicate exception list with expired items', function () { + duplicateSharedExceptionListFromListsManagementPageByListId( + getExceptionList2().list_id, + true + ); - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3'); + // After duplication - check for new list + assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]); - deleteExceptionListWithRuleReference(); + findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [ + EXCEPTION_LIST_ITEM_NAME, + EXCEPTION_LIST_ITEM_NAME_2, + ]); - // Using cy.contains because we do not care about the exact text, - // just checking number of lists shown - cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2'); - }); -}); + assertNumberOfExceptionItemsExists(2); + }); -describe('Manage shared exception list - read only', () => { - before(() => { - // First we login as a privileged user to create exception list - esArchiverResetKibana(); - login(ROLES.platform_engineer); - visitWithoutDateRange(EXCEPTIONS_URL, ROLES.platform_engineer); - createExceptionList(getExceptionList(), getExceptionList().list_id); + it('Duplicate exception list without expired items', function () { + duplicateSharedExceptionListFromListsManagementPageByListId( + getExceptionList2().list_id, + false + ); - // Then we login as read-only user to test. - login(ROLES.reader); - visitWithoutDateRange(EXCEPTIONS_URL, ROLES.reader); - waitForExceptionsTableToBeLoaded(); + // After duplication - check for new list + assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]); - cy.get(EXCEPTIONS_TABLE_SHOWING_LISTS).should('have.text', `Showing 1 list`); - }); + findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [ + EXCEPTION_LIST_ITEM_NAME_2, + ]); - it('Exception list actions should be disabled', () => { - cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().should('be.disabled'); + assertNumberOfExceptionItemsExists(1); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts index b8fa0c1f5a80f..f015e08139cea 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/timeline_templates/export.cy.ts @@ -15,7 +15,7 @@ import { import { TIMELINE_TEMPLATES_URL } from '../../urls/navigation'; import { createTimelineTemplate } from '../../tasks/api_calls/timelines'; import { cleanKibana } from '../../tasks/common'; -import { setRowsPerPageTo } from '../../tasks/table_pagination'; +import { searchByTitle } from '../../tasks/table_pagination'; describe('Export timelines', () => { before(() => { @@ -27,13 +27,14 @@ describe('Export timelines', () => { createTimelineTemplate(getTimelineTemplate()).then((response) => { cy.wrap(response).as('templateResponse'); cy.wrap(response.body.data.persistTimeline.timeline.savedObjectId).as('templateId'); + cy.wrap(response.body.data.persistTimeline.timeline.title).as('templateTitle'); }); login(); }); it('Exports a custom timeline template', function () { visitWithoutDateRange(TIMELINE_TEMPLATES_URL); - setRowsPerPageTo(20); + searchByTitle(this.templateTitle); exportTimeline(this.templateId); cy.wait('@export').then(({ response }) => { diff --git a/x-pack/plugins/security_solution/cypress/objects/exception.ts b/x-pack/plugins/security_solution/cypress/objects/exception.ts index 8427d828b6eea..d95455e34dc7e 100644 --- a/x-pack/plugins/security_solution/cypress/objects/exception.ts +++ b/x-pack/plugins/security_solution/cypress/objects/exception.ts @@ -31,6 +31,15 @@ export interface ExceptionListItem { tags: string[]; type: 'simple'; entries: Array<{ field: string; operator: string; type: string; value: string[] }>; + expire_time?: string; +} + +export interface RuleExceptionItem { + description: string; + name: string; + type: 'simple'; + entries: Array<{ field: string; operator: string; type: string; value: string[] | string }>; + expire_time: string | undefined; } export const getExceptionList = (): ExceptionList => ({ diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index ac630d1ee0fd9..af44aa14aa668 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -578,7 +578,6 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response = Partial>; export interface Actions { - throttle: RuleActionThrottle; connectors: Connectors[]; } diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts index 74df1f1999373..c3a092c63a949 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts @@ -68,8 +68,6 @@ export const UPDATE_ENRICHMENT_RANGE_BUTTON = '[data-test-subj="enrichment-butto export const OVERVIEW_TAB = '[data-test-subj="overviewTab"]'; -export const SUMMARY_VIEW_PREVALENCE_CELL = `${SUMMARY_VIEW} [data-test-subj='alert-prevalence']`; - export const SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON = `${SUMMARY_VIEW} [aria-label='Investigate in timeline']`; export const INSIGHTS_RELATED_ALERTS_BY_SESSION = `[data-test-subj='related-alerts-by-session']`; diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index 2af59e2ae67d7..66f61cc0898f1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -38,6 +38,13 @@ export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]'; export const DUPLICATE_RULE_BULK_BTN = '[data-test-subj="duplicateRuleBulk"]'; +export const DUPLICATE_WITH_EXCEPTIONS_OPTION = '[data-test-subj="withExceptions"] label'; + +export const DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION = + '[data-test-subj="withExceptionsExcludeExpiredExceptions"] label'; + +export const DUPLICATE_WITHOUT_EXCEPTIONS_OPTION = '[data-test-subj="withoutExceptions"] label'; + export const RULE_SEARCH_FIELD = '[data-test-subj="ruleSearchField"]'; export const EXPORT_ACTION_BTN = '[data-test-subj="exportRuleAction"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/case_details.ts b/x-pack/plugins/security_solution/cypress/screens/case_details.ts index fcd8b60557fc1..271ef54922d5d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/case_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/case_details.ts @@ -12,7 +12,7 @@ export const CASE_CONNECTOR = '[data-test-subj="connector-fields"] .euiCard__tit export const CASE_DELETE = '[data-test-subj="property-actions-trash"]'; export const CASE_DETAILS_DESCRIPTION = - '[data-test-subj="description-action"] [data-test-subj="user-action-markdown"]'; + '[data-test-subj="description"] [data-test-subj="scrollable-markdown"]'; export const CASE_DETAILS_PAGE_TITLE = '[data-test-subj="header-page-title"]'; @@ -21,13 +21,10 @@ export const CASE_DETAILS_STATUS = '[data-test-subj="case-view-status-dropdown"] export const CASE_DETAILS_TAGS = '[data-test-subj="case-tags"]'; export const CASE_DETAILS_TIMELINE_LINK_MARKDOWN = - '[data-test-subj="description-action"] [data-test-subj="user-action-markdown"] button'; + '[data-test-subj="description"] [data-test-subj="scrollable-markdown"] button'; export const CASE_DETAILS_USER_ACTION_DESCRIPTION_EVENT = - '[data-test-subj="description-action"] .euiCommentEvent__headerEvent'; - -export const CASE_DETAILS_USER_ACTION_DESCRIPTION_USERNAME = - '[data-test-subj="description-action"] .euiCommentEvent__headerUsername'; + '[data-test-subj="description"] [data-test-subj="description-title"]'; export const CASE_DETAILS_USERNAMES = '[data-test-subj="user-profile-username"]'; @@ -41,7 +38,7 @@ export const CASES_TAGS = (tagName: string) => { return `[data-test-subj="tag-${tagName}"]`; }; -export const CASE_USER_ACTION = '[data-test-subj="user-action-markdown"]'; +export const CASE_USER_ACTION = '[data-test-subj="scrollable-markdown"]'; export const CONNECTOR_CARD_DETAILS = '[data-test-subj="connector-card-details"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts b/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts index 2fe606fc6bf64..9a1702b96d63c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts @@ -41,3 +41,20 @@ export const INDEX_SELECTOR = "[data-test-subj='.index-siem-ActionTypeSelectOpti export const actionFormSelector = (position: number) => `[data-test-subj="alertActionAccordion-${position}"]`; + +export const ACTIONS_SUMMARY_BUTTON = '[data-test-subj="summaryOrPerRuleSelect"]'; + +export const ACTIONS_NOTIFY_WHEN_BUTTON = '[data-test-subj="notifyWhenSelect"]'; + +export const ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON = '[data-test-subj="onActiveAlert"]'; + +export const ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON = '[data-test-subj="onThrottleInterval"]'; + +export const ACTIONS_THROTTLE_INPUT = '[data-test-subj="throttleInput"]'; + +export const ACTIONS_THROTTLE_UNIT_INPUT = '[data-test-subj="throttleUnitInput"]'; + +export const ACTIONS_SUMMARY_ALERT_BUTTON = '[data-test-subj="actionNotifyWhen-option-summary"]'; + +export const ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON = + '[data-test-subj="actionNotifyWhen-option-for_each"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index a0cccb508ed66..b248cf06e1a0d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -13,9 +13,6 @@ export const ABOUT_EDIT_TAB = '[data-test-subj="edit-rule-about-tab"]'; export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]'; -export const ACTIONS_THROTTLE_INPUT = - '[data-test-subj="stepRuleActions"] [data-test-subj="select"]'; - export const ADD_FALSE_POSITIVE_BTN = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; diff --git a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts index 7dbc04942c71a..dafd16932d194 100644 --- a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts @@ -71,6 +71,12 @@ import { ENTITY_PANEL_HEADER_TEST_ID, ENTITY_PANEL_CONTENT_TEST_ID, ENTITIES_VIEW_ALL_BUTTON_TEST_ID, + VISUALIZATIONS_SECTION_HEADER_TEST_ID, + ANALYZER_TREE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID, } from '../../public/flyout/right/components/test_ids'; import { getClassSelector, @@ -133,6 +139,8 @@ export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON = getData ); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT = getDataTestSubjectSelector(SESSION_VIEW_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA = + getDataTestSubjectSelector('sessionView:sessionViewProcessEventsEmpty'); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON = getDataTestSubjectSelector(VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT = @@ -299,6 +307,19 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER = getDataTestSubjectSelector(ENTITY_PANEL_HEADER_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT = getDataTestSubjectSelector(ENTITY_PANEL_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID); + +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_VISUALIZATIONS_SECTION_HEADER = + getDataTestSubjectSelector(VISUALIZATIONS_SECTION_HEADER_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_TREE = + getDataTestSubjectSelector(ANALYZER_TREE_TEST_ID); /* Table tab */ @@ -326,3 +347,6 @@ export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_ROW_CELL_ADD_TO_TIMELINE = getDataTestSubjectSelector('actionItem-security-detailsFlyout-cellActions-addToTimeline'); export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_ROW_CELL_COPY_TO_CLIPBOARD = getDataTestSubjectSelector('actionItem-security-detailsFlyout-cellActions-copyToClipboard'); + +export const DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON = + getDataTestSubjectSelector('euiFlyoutCloseButton'); diff --git a/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts b/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts index 6095151fee57b..f41ae2a175f05 100644 --- a/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts +++ b/x-pack/plugins/security_solution/cypress/screens/entity_analytics.ts @@ -47,6 +47,15 @@ export const ANOMALIES_TABLE = export const ANOMALIES_TABLE_ROWS = '[data-test-subj="entity_analytics_anomalies"] .euiTableRow'; +export const ANOMALIES_TABLE_ENABLE_JOB_BUTTON = '[data-test-subj="enable-job"]'; + +export const ANOMALIES_TABLE_ENABLE_JOB_LOADER = '[data-test-subj="job-switch-loader"]'; + +export const ANOMALIES_TABLE_COUNT_COLUMN = '[data-test-subj="anomalies-table-column-count"]'; + +export const ANOMALIES_TABLE_NEXT_PAGE_BUTTON = + '[data-test-subj="entity_analytics_anomalies"] [data-test-subj="pagination-button-next"]'; + export const UPGRADE_CONFIRMATION_MODAL = (riskScoreEntity: RiskScoreEntity) => `[data-test-subj="${riskScoreEntity}-risk-score-upgrade-confirmation-modal"]`; diff --git a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts index b650c59364423..6a58105292eff 100644 --- a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts @@ -49,10 +49,20 @@ export const EXCEPTIONS_TABLE_SHOWING_LISTS = '[data-test-subj="showingException export const EXCEPTIONS_TABLE_DELETE_BTN = '[data-test-subj="sharedListOverflowCardActionItemDelete"]'; +export const EXCEPTIONS_TABLE_LINK_RULES_BTN = + '[data-test-subj="sharedListOverflowCardActionItemLinkRules"]'; + export const EXCEPTIONS_TABLE_EXPORT_MODAL_BTN = '[data-test-subj="sharedListOverflowCardActionItemExport"]'; -export const EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN = '[data-test-subj="confirmModalConfirmButton"]'; +export const EXCEPTIONS_TABLE_DUPLICATE_BTN = + '[data-test-subj="sharedListOverflowCardActionItemDuplicate"]'; + +export const EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN = + '[data-test-subj="confirmModalConfirmButton"]'; + +export const INCLUDE_EXPIRED_EXCEPTION_ITEMS_SWITCH = + '[data-test-subj="includeExpiredExceptionsConfirmationModalSwitch"]'; export const EXCEPTIONS_TABLE_SEARCH_CLEAR = '[data-test-subj="allExceptionListsPanel"] button.euiFormControlLayoutClearButton'; @@ -149,6 +159,13 @@ export const CREATE_SHARED_EXCEPTION_LIST_DESCRIPTION_INPUT = export const CREATE_SHARED_EXCEPTION_LIST_BTN = 'button[data-test-subj="exception-lists-form-create-shared"]'; +export const exceptionsTableListManagementListContainerByListId = (listId: string) => + `[data-test-subj="exceptionsManagementListCard-${listId}"]`; + +export const LINKED_RULES_BADGE = '[data-test-subj="exceptionListCardLinkedRulesBadge"]'; + +export const MANAGE_RULES_SAVE = '[data-test-subj="manageListRulesSaveButton"]'; + // Exception list management export const EXCEPTIONS_LIST_MANAGEMENT_NAME = '[data-test-subj="exceptionListManagementTitleText"]'; @@ -169,3 +186,5 @@ export const EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN = '[data-test-subj="editModal export const EXCEPTIONS_LIST_DETAILS_HEADER = '[data-test-subj="exceptionListManagementPageHeader"]'; + +export const EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME = '[data-test-subj="exceptionItemCardHeader"]'; 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 5487c5be0aded..4abb1cad40d05 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -39,6 +39,12 @@ export const DETAILS_TITLE = '.euiDescriptionList__title'; export const EXCEPTIONS_TAB = 'a[data-test-subj="navigation-rule_exceptions"]'; +export const EXCEPTIONS_TAB_EXPIRED_FILTER = '[data-test-subj="expired"]'; + +export const EXCEPTIONS_TAB_ACTIVE_FILTER = '[data-test-subj="active"]'; + +export const EXCEPTIONS_ITEM_CONTAINER = '[data-test-subj="exceptionsContainer"]'; + export const FALSE_POSITIVES_DETAILS = 'False positive examples'; export const INDEX_PATTERNS_DETAILS = 'Index patterns'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts index 9546b5da8ad8d..7a36c7fd7c74e 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts @@ -66,9 +66,6 @@ export const UPDATE_SCHEDULE_LOOKBACK_INPUT = export const UPDATE_SCHEDULE_TIME_UNIT_SELECT = '[data-test-subj="timeType"]'; -export const RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT = - '[data-test-subj="bulkEditRulesRuleActionThrottle"] [data-test-subj="select"]'; - export const RULES_BULK_EDIT_ACTIONS_INFO = '[data-test-subj="bulkEditRulesRuleActionInfo"]'; export const RULES_BULK_EDIT_ACTIONS_WARNING = '[data-test-subj="bulkEditRulesRuleActionsWarning"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts b/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts index e52194632954b..634cd6af9f0d1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts +++ b/x-pack/plugins/security_solution/cypress/screens/table_pagination.ts @@ -19,3 +19,5 @@ export const TABLE_FIRST_PAGE = tablePageSelector(1); export const TABLE_SECOND_PAGE = tablePageSelector(2); export const TABLE_SORT_COLUMN_BTN = '[data-test-subj="tableHeaderSortButton"]'; + +export const TABLE_SEARCH_BAR = '[data-test-subj="search-bar"]'; 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 f17fbac688f9d..3dd0f5d563597 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 @@ -62,6 +62,9 @@ import { DISABLED_RULES_BTN, REFRESH_RULES_TABLE_BUTTON, RULE_LAST_RUN, + DUPLICATE_WITHOUT_EXCEPTIONS_OPTION, + DUPLICATE_WITH_EXCEPTIONS_OPTION, + DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION, } from '../screens/alerts_detection_rules'; import type { RULES_MONITORING_TABLE } from '../screens/alerts_detection_rules'; import { EUI_CHECKBOX } from '../screens/common/controls'; @@ -145,10 +148,27 @@ export const deleteRuleFromDetailsPage = () => { .should(($el) => expect($el).to.be.not.visible); }; -export const duplicateSelectedRules = () => { +export const duplicateSelectedRulesWithoutExceptions = () => { cy.log('Duplicate selected rules'); cy.get(BULK_ACTIONS_BTN).click({ force: true }); cy.get(DUPLICATE_RULE_BULK_BTN).click(); + cy.get(DUPLICATE_WITHOUT_EXCEPTIONS_OPTION).click(); + cy.get(CONFIRM_DUPLICATE_RULE).click(); +}; + +export const duplicateSelectedRulesWithExceptions = () => { + cy.log('Duplicate selected rules'); + cy.get(BULK_ACTIONS_BTN).click({ force: true }); + cy.get(DUPLICATE_RULE_BULK_BTN).click(); + cy.get(DUPLICATE_WITH_EXCEPTIONS_OPTION).click(); + cy.get(CONFIRM_DUPLICATE_RULE).click(); +}; + +export const duplicateSelectedRulesWithNonExpiredExceptions = () => { + cy.log('Duplicate selected rules'); + cy.get(BULK_ACTIONS_BTN).click({ force: true }); + cy.get(DUPLICATE_RULE_BULK_BTN).click(); + cy.get(DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION).click(); cy.get(CONFIRM_DUPLICATE_RULE).click(); }; 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 fd070cfcda55e..61c5a0aa8efc3 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 type { ExceptionList, ExceptionListItem } from '../../objects/exception'; +import type { ExceptionList, ExceptionListItem, RuleExceptionItem } from '../../objects/exception'; export const createEndpointExceptionList = () => cy.request({ @@ -59,6 +59,18 @@ export const createExceptionListItem = ( value: ['some host', 'another host'], }, ], + expire_time: exceptionListItem?.expire_time, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); + +export const createRuleExceptionItem = (ruleId: string, exceptionListItems: RuleExceptionItem[]) => + cy.request({ + method: 'POST', + url: `/api/detection_engine/rules/${ruleId}/exceptions`, + body: { + items: exceptionListItems, }, headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/tour.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/tour.ts index 89f215ebcfd47..ee17795a46d52 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/tour.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/tour.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { API_BASE_PATH } from '@kbn/guided-onboarding-plugin/common'; import { siemGuideId } from '../../../common/guided_onboarding/siem_guide_config'; const alertsGuideActiveState = { @@ -21,7 +22,7 @@ const alertsGuideActiveState = { export const startAlertsCasesTour = () => cy.request({ method: 'PUT', - url: 'api/guided_onboarding/state', + url: `${API_BASE_PATH}/state`, headers: { 'kbn-xsrf': 'cypress-creds' }, body: { status: 'in_progress', @@ -32,7 +33,7 @@ export const startAlertsCasesTour = () => export const quitGlobalTour = () => cy.request({ method: 'PUT', - url: 'api/guided_onboarding/state', + url: `${API_BASE_PATH}/state`, headers: { 'kbn-xsrf': 'cypress-creds' }, body: { status: 'quit', diff --git a/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts b/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts index 2c289eea0f736..a9a45780567ae 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts @@ -22,6 +22,15 @@ import { EMAIL_CONNECTOR_PASSWORD_INPUT, FORM_VALIDATION_ERROR, JSON_EDITOR, + ACTIONS_SUMMARY_BUTTON, + ACTIONS_NOTIFY_WHEN_BUTTON, + ACTIONS_THROTTLE_INPUT, + ACTIONS_THROTTLE_UNIT_INPUT, + ACTIONS_SUMMARY_ALERT_BUTTON, + ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON, + ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON, + actionFormSelector, + ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON, } from '../../screens/common/rule_actions'; import { COMBO_BOX_INPUT, COMBO_BOX_SELECTION } from '../../screens/common/controls'; import type { EmailConnector, IndexConnector } from '../../objects/connector'; @@ -84,3 +93,79 @@ export const fillIndexConnectorForm = (connector: IndexConnector = getIndexConne parseSpecialCharSequences: false, }); }; + +export interface RuleActionCustomFrequency { + throttle?: number; + throttleUnit?: 's' | 'm' | 'h' | 'd'; +} + +export const pickSummaryOfAlertsOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).click(); + }); + cy.get(ACTIONS_SUMMARY_ALERT_BUTTON).click(); +}; +export const pickForEachAlertOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).click(); + }); + cy.get(ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON).click(); +}; + +export const pickCustomFrequencyOption = ( + { throttle = 1, throttleUnit = 'h' }: RuleActionCustomFrequency, + index = 0 +) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).click(); + }); + cy.get(ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON).click(); + form.within(() => { + cy.get(ACTIONS_THROTTLE_INPUT).type(`{selectAll}${throttle}`); + cy.get(ACTIONS_THROTTLE_UNIT_INPUT).select(throttleUnit); + }); +}; + +export const pickPerRuleRunFrequencyOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).click(); + }); + cy.get(ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON).click(); +}; + +export const assertSelectedSummaryOfAlertsOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts'); + }); +}; + +export const assertSelectedForEachAlertOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'For each alert'); + }); +}; + +export const assertSelectedCustomFrequencyOption = ( + { throttle = 1, throttleUnit = 'h' }: RuleActionCustomFrequency, + index = 0 +) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Custom frequency'); + cy.get(ACTIONS_THROTTLE_INPUT).should('have.value', throttle); + cy.get(ACTIONS_THROTTLE_UNIT_INPUT).should('have.value', throttleUnit); + }); +}; + +export const assertSelectedPerRuleRunFrequencyOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run'); + }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index b8274ed33c120..1f3f051c6f474 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -99,7 +99,6 @@ import { NEW_TERMS_HISTORY_SIZE, NEW_TERMS_HISTORY_TIME_TYPE, NEW_TERMS_INPUT_AREA, - ACTIONS_THROTTLE_INPUT, CONTINUE_BUTTON, CREATE_WITHOUT_ENABLING_BTN, RULE_INDICES, @@ -407,7 +406,6 @@ export const fillFrom = (from: RuleIntervalFrom = ruleFields.ruleIntervalFrom) = }; export const fillRuleAction = (actions: Actions) => { - cy.get(ACTIONS_THROTTLE_INPUT).select(actions.throttle); actions.connectors.forEach((connector) => { switch (connector.type) { case 'index': diff --git a/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts index 6665cd4890ecd..8fe8eac97e06b 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts @@ -33,6 +33,7 @@ import { DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_BUTTON, DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON, DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_VISUALIZATIONS_SECTION_HEADER, DOCUMENT_DETAILS_FLYOUT_FOOTER_TAKE_ACTION_BUTTON_DROPDOWN, KIBANA_NAVBAR_ALERTS_PAGE, KIBANA_NAVBAR_CASES_PAGE, @@ -144,6 +145,16 @@ export const toggleOverviewTabInsightsSection = () => .should('be.visible') .click(); +/** + * Toggle the Overview tab visualizations section in the document details expandable flyout right section + */ +export const toggleOverviewTabVisualizationsSection = () => + cy + .get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_VISUALIZATIONS_SECTION_HEADER) + .scrollIntoView() + .should('be.visible') + .click(); + /** * Open the Table tab in the document details expandable flyout right section */ diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts index a016691328ffd..42d5619c28a67 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -6,7 +6,6 @@ */ import { BACK_TO_RULE_DETAILS, EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; -import { ACTIONS_THROTTLE_INPUT } from '../screens/create_new_rule'; export const saveEditedRule = () => { cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); @@ -17,7 +16,3 @@ export const goBackToRuleDetails = () => { cy.get(BACK_TO_RULE_DETAILS).should('exist').click(); cy.get(BACK_TO_RULE_DETAILS).should('not.exist'); }; - -export const assertSelectedActionFrequency = (frequency: string) => { - cy.get(ACTIONS_THROTTLE_INPUT).find('option:selected').should('have.text', frequency); -}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts b/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts index e3e0d76ddd26a..c40176cd2eda4 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/exceptions.ts @@ -28,8 +28,24 @@ import { EXCEPTION_FIELD_MAPPING_CONFLICTS_TOOLTIP, EXCEPTION_FIELD_MAPPING_CONFLICTS_ACCORDION_ICON, EXCEPTION_FIELD_MAPPING_CONFLICTS_DESCRIPTION, + EXCEPTION_ITEM_VIEWER_CONTAINER, } from '../screens/exceptions'; +export const assertNumberOfExceptionItemsExists = (numberOfItems: number) => { + cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', numberOfItems); +}; + +export const expectToContainItem = (container: string, itemName: string) => { + cy.log(`Expecting exception items table to contain '${itemName}'`); + cy.get(container).should('include.text', itemName); +}; + +export const assertExceptionItemsExists = (container: string, itemNames: string[]) => { + for (const itemName of itemNames) { + expectToContainItem(container, itemName); + } +}; + export const addExceptionEntryFieldValueOfItemX = ( field: string, itemIndex = 0, diff --git a/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts b/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts index 9be5d281903c8..c9117751a210e 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/exceptions_table.ts @@ -14,7 +14,7 @@ import { EXCEPTIONS_TABLE_MODAL_CONFIRM_BTN, EXCEPTIONS_TABLE_EXPORT_MODAL_BTN, EXCEPTIONS_OVERFLOW_ACTIONS_BTN, - EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN, + EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN, MANAGE_EXCEPTION_CREATE_BUTTON_MENU, MANAGE_EXCEPTION_CREATE_LIST_BUTTON, CREATE_SHARED_EXCEPTION_LIST_NAME_INPUT, @@ -26,7 +26,17 @@ import { EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_DESCRIPTION_INPUT, EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN, EXCEPTIONS_LIST_DETAILS_HEADER, + EXCEPTIONS_TABLE_DUPLICATE_BTN, + EXCEPTIONS_TABLE_LIST_NAME, + INCLUDE_EXPIRED_EXCEPTION_ITEMS_SWITCH, + EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME, + exceptionsTableListManagementListContainerByListId, + EXCEPTIONS_TABLE_LINK_RULES_BTN, + RULE_ACTION_LINK_RULE_SWITCH, + LINKED_RULES_BADGE, + MANAGE_RULES_SAVE, } from '../screens/exceptions'; +import { assertExceptionItemsExists } from './exceptions'; export const clearSearchSelection = () => { cy.get(EXCEPTIONS_TABLE_SEARCH_CLEAR).first().click(); @@ -36,22 +46,44 @@ export const expandExceptionActions = () => { cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().click(); }; -export const exportExceptionList = () => { - cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().click(); +export const exportExceptionList = (listId: string) => { + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); cy.get(EXCEPTIONS_TABLE_EXPORT_MODAL_BTN).first().click(); - cy.get(EXCEPTIONS_TABLE_EXPORT_CONFIRM_BTN).first().click(); + cy.get(EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN).first().click(); }; -export const deleteExceptionListWithoutRuleReference = () => { - cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().click(); +export const assertNumberLinkedRules = (listId: string, numberOfRulesAsString: string) => { + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(LINKED_RULES_BADGE) + .contains(numberOfRulesAsString); +}; + +export const linkRulesToExceptionList = (listId: string, ruleSwitch: number = 0) => { + cy.log(`Open link rules flyout for list_id: '${listId}'`); + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); + cy.get(EXCEPTIONS_TABLE_LINK_RULES_BTN).first().click(); + cy.get(RULE_ACTION_LINK_RULE_SWITCH).eq(ruleSwitch).find('button').click(); + cy.get(MANAGE_RULES_SAVE).first().click(); +}; + +export const deleteExceptionListWithoutRuleReferenceByListId = (listId: string) => { + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); cy.get(EXCEPTIONS_TABLE_DELETE_BTN).first().click(); cy.get(EXCEPTIONS_TABLE_MODAL).should('exist'); cy.get(EXCEPTIONS_TABLE_MODAL_CONFIRM_BTN).first().click(); cy.get(EXCEPTIONS_TABLE_MODAL).should('not.exist'); }; -export const deleteExceptionListWithRuleReference = () => { - cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).last().click(); +export const deleteExceptionListWithRuleReferenceByListId = (listId: string) => { + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); cy.get(EXCEPTIONS_TABLE_DELETE_BTN).last().click(); cy.get(EXCEPTIONS_TABLE_MODAL).should('exist'); cy.get(EXCEPTIONS_TABLE_MODAL_CONFIRM_BTN).first().click(); @@ -92,10 +124,42 @@ export const createSharedExceptionList = ( } }; +export const expectToContainList = (listName: string) => { + cy.log(`Expecting exception lists table to contain '${listName}'`); + cy.get(EXCEPTIONS_TABLE_LIST_NAME).should('include.text', listName); +}; + +export const assertExceptionListsExists = (listNames: string[]) => { + for (const listName of listNames) { + expectToContainList(listName); + } +}; + +export const duplicateSharedExceptionListFromListsManagementPageByListId = ( + listId: string, + includeExpired: boolean +) => { + cy.log(`Duplicating list with list_id: '${listId}'`); + cy.get(exceptionsTableListManagementListContainerByListId(listId)) + .find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN) + .click(); + cy.get(EXCEPTIONS_TABLE_DUPLICATE_BTN).first().click(); + if (!includeExpired) { + cy.get(INCLUDE_EXPIRED_EXCEPTION_ITEMS_SWITCH).first().click(); + } + cy.get(EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN).first().click(); +}; + export const waitForExceptionListDetailToBeLoaded = () => { cy.get(EXCEPTIONS_LIST_DETAILS_HEADER).should('exist'); }; +export const findSharedExceptionListItemsByName = (listName: string, itemNames: string[]) => { + cy.contains(listName).click(); + waitForExceptionListDetailToBeLoaded(); + assertExceptionItemsExists(EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME, itemNames); +}; + export const editExceptionLisDetails = ({ name, description, 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 d30cbaf06e17b..d82e29e493ea3 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -29,6 +29,8 @@ import { ENDPOINT_EXCEPTIONS_TAB, EDIT_RULE_SETTINGS_LINK, BACK_TO_RULES_TABLE, + EXCEPTIONS_TAB_EXPIRED_FILTER, + EXCEPTIONS_TAB_ACTIVE_FILTER, } from '../screens/rule_details'; import { addExceptionConditions, @@ -105,6 +107,11 @@ export const goToExceptionsTab = () => { cy.get(EXCEPTIONS_TAB).click(); }; +export const viewExpiredExceptionItems = () => { + cy.get(EXCEPTIONS_TAB_EXPIRED_FILTER).click(); + cy.get(EXCEPTIONS_TAB_ACTIVE_FILTER).click(); +}; + export const goToEndpointExceptionsTab = () => { cy.get(ENDPOINT_EXCEPTIONS_TAB).should('exist'); cy.get(ENDPOINT_EXCEPTIONS_TAB).click(); diff --git a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts index b2203d1b1202a..b4bf088bafd6a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts @@ -39,7 +39,6 @@ import { UPDATE_SCHEDULE_LOOKBACK_INPUT, RULES_BULK_EDIT_SCHEDULES_WARNING, RULES_BULK_EDIT_OVERWRITE_ACTIONS_CHECKBOX, - RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT, } from '../screens/rules_bulk_edit'; import { SCHEDULE_DETAILS } from '../screens/rule_details'; @@ -292,7 +291,3 @@ export const assertRuleScheduleValues = ({ interval, lookback }: RuleSchedule) = cy.get('dd').eq(1).should('contain.text', lookback); }); }; - -export const pickActionFrequency = (frequency: string) => { - cy.get(RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT).select(frequency); -}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts b/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts index ae62ca290e418..2e59383b39645 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/table_pagination.ts @@ -10,6 +10,7 @@ import { rowsPerPageSelector, tablePageSelector, TABLE_PER_PAGE_POPOVER_BTN, + TABLE_SEARCH_BAR, TABLE_SORT_COLUMN_BTN, } from '../screens/table_pagination'; @@ -33,6 +34,14 @@ export const setRowsPerPageTo = (rowsCount: number) => { .should('not.exist'); }; +export const searchByTitle = (title: string) => { + cy.get(LOADING_SPINNER).should('not.exist'); + cy.get(TABLE_PER_PAGE_POPOVER_BTN).should('exist'); + cy.get(TABLE_SEARCH_BAR).click({ force: true }); + // EuiSearchBox needs the "search" event to be triggered, {enter} doesn't work + cy.get(TABLE_SEARCH_BAR).type(`"${title}"`).trigger('search'); +}; + export const expectRowsPerPage = (rowsCount: number) => { cy.get(TABLE_PER_PAGE_POPOVER_BTN).contains(`Rows per page: ${rowsCount}`); }; diff --git a/x-pack/plugins/security_solution/cypress/tsconfig.json b/x-pack/plugins/security_solution/cypress/tsconfig.json index 4af63c6d1b406..8f818e8663a94 100644 --- a/x-pack/plugins/security_solution/cypress/tsconfig.json +++ b/x-pack/plugins/security_solution/cypress/tsconfig.json @@ -28,6 +28,8 @@ "force": true }, "@kbn/rison", - "@kbn/datemath" + "@kbn/datemath", + "@kbn/guided-onboarding-plugin", + "@kbn/alerting-plugin" ] } diff --git a/x-pack/plugins/security_solution/kibana.jsonc b/x-pack/plugins/security_solution/kibana.jsonc index 9401b69d11657..a929c15b48641 100644 --- a/x-pack/plugins/security_solution/kibana.jsonc +++ b/x-pack/plugins/security_solution/kibana.jsonc @@ -32,6 +32,7 @@ "maps", "ruleRegistry", "sessionView", + "spaces", "taskManager", "threatIntelligence", "timelines", @@ -50,7 +51,6 @@ "ml", "newsfeed", "security", - "spaces", "usageCollection", "lists", "home", diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts new file mode 100644 index 0000000000000..7f3ed646da749 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts @@ -0,0 +1,247 @@ +/* + * 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 { SecurityAppStore } from '../../../common/store/types'; +import type { DataProvider } from '../../../../common/types'; +import { TimelineId } from '../../../../common/types'; +import { addProvider } from '../../../timelines/store/timeline/actions'; +import { createAddToNewTimelineCellActionFactory, getToastMessage } from './add_to_new_timeline'; +import type { CellActionExecutionContext } from '@kbn/cell-actions'; +import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; +import { timelineActions } from '../../../timelines/store/timeline'; + +const services = createStartServicesMock(); +const mockWarningToast = services.notifications.toasts.addWarning; + +const mockDispatch = jest.fn(); +const store = { + dispatch: mockDispatch, +} as unknown as SecurityAppStore; + +const value = 'the-value'; + +const context = { + field: { name: 'user.name', value, type: 'text' }, +} as CellActionExecutionContext; + +const defaultAddProviderAction = { + type: addProvider.type, + payload: { + id: TimelineId.active, + providers: [ + { + and: [], + enabled: true, + excluded: false, + id: 'event-field-default-timeline-1-user_name-0-the-value', + kqlQuery: '', + name: 'user.name', + queryMatch: { + field: 'user.name', + operator: ':', + value: 'the-value', + }, + }, + ], + }, +}; + +describe('createAddToNewTimelineCellAction', () => { + const addToTimelineCellActionFactory = createAddToNewTimelineCellActionFactory({ + store, + services, + }); + const addToTimelineAction = addToTimelineCellActionFactory({ id: 'testAddToTimeline', order: 1 }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return display name', () => { + expect(addToTimelineAction.getDisplayName(context)).toEqual('Investigate in timeline'); + }); + + it('should return icon type', () => { + expect(addToTimelineAction.getIconType(context)).toEqual('timeline'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await addToTimelineAction.isCompatible(context)).toEqual(true); + }); + it('should return false if field not allowed', async () => { + expect( + await addToTimelineAction.isCompatible({ + ...context, + field: { ...context.field, name: 'signal.reason' }, + }) + ).toEqual(false); + }); + }); + + describe('execute', () => { + it('should execute normally', async () => { + await addToTimelineAction.execute(context); + expect(mockDispatch).toHaveBeenCalledWith(defaultAddProviderAction); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + + it('should show warning if no provider added', async () => { + await addToTimelineAction.execute({ + ...context, + field: { + ...context.field, + type: GEO_FIELD_TYPE, + }, + }); + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); + + describe('should execute correctly when negateFilters is provided', () => { + it('should not exclude if negateFilters is false', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + negateFilters: false, + }, + }); + expect(mockDispatch).toHaveBeenCalledWith(defaultAddProviderAction); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + + it('should exclude if negateFilters is true', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + negateFilters: true, + }, + }); + expect(mockDispatch).toHaveBeenCalledWith({ + ...defaultAddProviderAction, + payload: { + ...defaultAddProviderAction.payload, + providers: [{ ...defaultAddProviderAction.payload.providers[0], excluded: true }], + }, + }); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + }); + + it('should clear the timeline', async () => { + await addToTimelineAction.execute(context); + expect(mockDispatch.mock.calls[0][0].type).toEqual(timelineActions.createTimeline.type); + }); + + it('should add the providers to the timeline', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + andFilters: [{ field: 'kibana.alert.severity', value: 'low' }], + }, + }); + + expect(mockDispatch).toBeCalledWith({ + ...defaultAddProviderAction, + payload: { + ...defaultAddProviderAction.payload, + providers: [ + { + ...defaultAddProviderAction.payload.providers[0], + id: 'event-field-default-timeline-1-user_name-0-the-value', + queryMatch: defaultAddProviderAction.payload.providers[0].queryMatch, + and: [ + { + enabled: true, + excluded: false, + id: 'event-field-default-timeline-1-kibana_alert_severity-0-low', + kqlQuery: '', + name: 'kibana.alert.severity', + queryMatch: { + field: 'kibana.alert.severity', + operator: ':', + value: 'low', + }, + and: [], + }, + ], + }, + ], + }, + }); + }); + }); + + describe('getToastMessage', () => { + it('handles empty input', () => { + const result = getToastMessage({ queryMatch: { value: null } } as unknown as DataProvider); + expect(result).toEqual(''); + }); + it('handles array input', () => { + const result = getToastMessage({ + queryMatch: { value: ['hello', 'world'] }, + } as unknown as DataProvider); + expect(result).toEqual('hello, world alerts'); + }); + + it('handles single filter', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [{ queryMatch: { field: 'kibana.alert.severity', value: 'critical' } }], + } as unknown as DataProvider); + expect(result).toEqual(`critical severity alerts from ${value}`); + }); + + it('handles multiple filters', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [ + { + queryMatch: { field: 'kibana.alert.workflow_status', value: 'open' }, + }, + { + queryMatch: { field: 'kibana.alert.severity', value: 'critical' }, + }, + ], + } as unknown as DataProvider); + expect(result).toEqual(`open, critical severity alerts from ${value}`); + }); + + it('ignores unrelated filters', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [ + { + queryMatch: { field: 'kibana.alert.workflow_status', value: 'open' }, + }, + { + queryMatch: { field: 'kibana.alert.severity', value: 'critical' }, + }, + // currently only supporting the above fields + { + queryMatch: { field: 'user.name', value: 'something' }, + }, + ], + } as unknown as DataProvider); + expect(result).toEqual(`open, critical severity alerts from ${value}`); + }); + + it('returns entity only when unrelated filters are passed', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [{ queryMatch: { field: 'user.name', value: 'something' } }], + } as unknown as DataProvider); + expect(result).toEqual(`${value} alerts`); + }); + + it('returns entity only when no filters are passed', () => { + const result = getToastMessage({ queryMatch: { value }, and: [] } as unknown as DataProvider); + expect(result).toEqual(`${value} alerts`); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts new file mode 100644 index 0000000000000..886162803bbf1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts @@ -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 { createCellActionFactory, type CellActionTemplate } from '@kbn/cell-actions'; +import { timelineActions } from '../../../timelines/store/timeline'; +import { addProvider } from '../../../timelines/store/timeline/actions'; +import type { DataProvider } from '../../../../common/types'; +import { TimelineId } from '../../../../common/types'; +import type { SecurityAppStore } from '../../../common/store'; +import { fieldHasCellActions } from '../../utils'; +import { + ADD_TO_NEW_TIMELINE, + ADD_TO_TIMELINE_FAILED_TEXT, + ADD_TO_TIMELINE_FAILED_TITLE, + ADD_TO_TIMELINE_ICON, + ADD_TO_TIMELINE_SUCCESS_TITLE, + ALERTS_COUNT, + SEVERITY, +} from '../constants'; +import { createDataProviders, isValidDataProviderField } from '../data_provider'; +import { SecurityCellActionType } from '../../constants'; +import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; +import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; + +const severityField = 'kibana.alert.severity'; +const statusField = 'kibana.alert.workflow_status'; + +export const getToastMessage = ({ queryMatch: { value }, and = [] }: DataProvider) => { + if (value == null) { + return ''; + } + const fieldValue = Array.isArray(value) ? value.join(', ') : value.toString(); + + const descriptors = and.reduce((msg, { queryMatch }) => { + if (Array.isArray(queryMatch.value)) { + return msg; + } + if (queryMatch.field === severityField) { + msg.push(SEVERITY(queryMatch.value.toString())); + } + if (queryMatch.field === statusField) { + msg.push(queryMatch.value.toString()); + } + return msg; + }, []); + + return ALERTS_COUNT(fieldValue, descriptors.join(', ')); +}; + +export const createAddToNewTimelineCellActionFactory = createCellActionFactory( + ({ + store, + services, + }: { + store: SecurityAppStore; + services: StartServices; + }): CellActionTemplate => { + const { notifications: notificationsService } = services; + + return { + type: SecurityCellActionType.ADD_TO_TIMELINE, + getIconType: () => ADD_TO_TIMELINE_ICON, + getDisplayName: () => ADD_TO_NEW_TIMELINE, + getDisplayNameTooltip: () => ADD_TO_NEW_TIMELINE, + isCompatible: async ({ field }) => + fieldHasCellActions(field.name) && isValidDataProviderField(field.name, field.type), + execute: async ({ field, metadata }) => { + const dataProviders = + createDataProviders({ + contextId: TimelineId.active, + fieldType: field.type, + values: field.value, + field: field.name, + negate: metadata?.negateFilters === true, + }) ?? []; + + for (const andFilter of metadata?.andFilters ?? []) { + const andDataProviders = + createDataProviders({ + contextId: TimelineId.active, + field: andFilter.field, + values: andFilter.value, + }) ?? []; + if (andDataProviders) { + for (const dataProvider of dataProviders) { + dataProvider.and.push(...andDataProviders); + } + } + } + + if (dataProviders.length > 0) { + // clear timeline + store.dispatch( + timelineActions.createTimeline({ + ...timelineDefaults, + id: TimelineId.active, + }) + ); + store.dispatch(addProvider({ id: TimelineId.active, providers: dataProviders })); + notificationsService.toasts.addSuccess({ + title: ADD_TO_TIMELINE_SUCCESS_TITLE(getToastMessage(dataProviders[0])), + }); + } else { + notificationsService.toasts.addWarning({ + title: ADD_TO_TIMELINE_FAILED_TITLE, + text: ADD_TO_TIMELINE_FAILED_TEXT, + }); + } + }, + }; + } +); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts index c122d7e312e4d..0396cad110367 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts @@ -15,6 +15,29 @@ export const ADD_TO_TIMELINE = i18n.translate( defaultMessage: 'Add to timeline', } ); +export const ADD_TO_NEW_TIMELINE = i18n.translate( + 'xpack.securitySolution.actions.cellValue.addToNewTimeline.displayName', + { + defaultMessage: 'Investigate in timeline', + } +); + +export const SEVERITY = (level: string) => + i18n.translate('xpack.securitySolution.actions.addToTimeline.severityLevel', { + values: { level }, + defaultMessage: `{level} severity`, + }); + +export const ALERTS_COUNT = (entity: string, description: string) => + description !== '' + ? i18n.translate('xpack.securitySolution.actions.addToTimeline.descriptiveAlertsCountMessage', { + values: { description, entity }, + defaultMessage: '{description} alerts from {entity}', + }) + : i18n.translate('xpack.securitySolution.actions.addToTimeline.alertsCountMessage', { + values: { entity }, + defaultMessage: '{entity} alerts', + }); export const ADD_TO_TIMELINE_SUCCESS_TITLE = (value: string) => i18n.translate('xpack.securitySolution.actions.addToTimeline.addedFieldMessage', { diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts index c639dde1e2337..72e6eee17e4d4 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts @@ -6,4 +6,5 @@ */ export { createAddToTimelineCellActionFactory } from './cell_action/add_to_timeline'; +export { createAddToNewTimelineCellActionFactory } from './cell_action/add_to_new_timeline'; export { createAddToTimelineLensAction } from './lens/add_to_timeline'; diff --git a/x-pack/plugins/security_solution/public/actions/constants.ts b/x-pack/plugins/security_solution/public/actions/constants.ts index 94c2601222847..eff71171fbcea 100644 --- a/x-pack/plugins/security_solution/public/actions/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/constants.ts @@ -7,6 +7,7 @@ export enum SecurityCellActionsTrigger { DEFAULT = 'security-default-cellActions', DETAILS_FLYOUT = 'security-detailsFlyout-cellActions', + ALERTS_COUNT = 'security-alertsCount-cellActions', } export enum SecurityCellActionType { diff --git a/x-pack/plugins/security_solution/public/actions/register.ts b/x-pack/plugins/security_solution/public/actions/register.ts index 0fafb561d45a1..bfea8d27163c4 100644 --- a/x-pack/plugins/security_solution/public/actions/register.ts +++ b/x-pack/plugins/security_solution/public/actions/register.ts @@ -6,14 +6,14 @@ */ import { CELL_VALUE_TRIGGER } from '@kbn/embeddable-plugin/public'; -import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type * as H from 'history'; import type { SecurityAppStore } from '../common/store/types'; -import type { StartPlugins, StartServices } from '../types'; +import type { StartServices } from '../types'; import { createFilterInCellActionFactory, createFilterOutCellActionFactory } from './filter'; import { createAddToTimelineLensAction, createAddToTimelineCellActionFactory, + createAddToNewTimelineCellActionFactory, } from './add_to_timeline'; import { createShowTopNCellActionFactory } from './show_top_n'; import { @@ -23,18 +23,20 @@ import { import { createToggleColumnCellActionFactory } from './toggle_column'; import { SecurityCellActionsTrigger } from './constants'; import type { SecurityCellActionName, SecurityCellActions } from './types'; +import { enhanceActionWithTelemetry } from './telemetry'; export const registerUIActions = ( - { uiActions }: StartPlugins, store: SecurityAppStore, history: H.History, services: StartServices ) => { - registerLensActions(uiActions, store); - registerCellActions(uiActions, store, history, services); + registerLensActions(store, services); + registerCellActions(store, history, services); }; -const registerLensActions = (uiActions: UiActionsStart, store: SecurityAppStore) => { +const registerLensActions = (store: SecurityAppStore, services: StartServices) => { + const { uiActions } = services; + const addToTimelineAction = createAddToTimelineLensAction({ store, order: 1 }); uiActions.addTriggerAction(CELL_VALUE_TRIGGER, addToTimelineAction); @@ -43,7 +45,6 @@ const registerLensActions = (uiActions: UiActionsStart, store: SecurityAppStore) }; const registerCellActions = ( - uiActions: UiActionsStart, store: SecurityAppStore, history: H.History, services: StartServices @@ -52,42 +53,61 @@ const registerCellActions = ( filterIn: createFilterInCellActionFactory({ store, services }), filterOut: createFilterOutCellActionFactory({ store, services }), addToTimeline: createAddToTimelineCellActionFactory({ store, services }), + addToNewTimeline: createAddToNewTimelineCellActionFactory({ store, services }), showTopN: createShowTopNCellActionFactory({ store, history, services }), copyToClipboard: createCopyToClipboardCellActionFactory({ services }), toggleColumn: createToggleColumnCellActionFactory({ store }), }; - registerCellActionsTrigger(uiActions, SecurityCellActionsTrigger.DEFAULT, cellActions, [ - 'filterIn', - 'filterOut', - 'addToTimeline', - 'showTopN', - 'copyToClipboard', - ]); + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.DEFAULT, + cellActions, + actionsOrder: ['filterIn', 'filterOut', 'addToTimeline', 'showTopN', 'copyToClipboard'], + services, + }); + + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.DETAILS_FLYOUT, + cellActions, + actionsOrder: [ + 'filterIn', + 'filterOut', + 'addToTimeline', + 'toggleColumn', + 'showTopN', + 'copyToClipboard', + ], + services, + }); - registerCellActionsTrigger(uiActions, SecurityCellActionsTrigger.DETAILS_FLYOUT, cellActions, [ - 'filterIn', - 'filterOut', - 'addToTimeline', - 'toggleColumn', - 'showTopN', - 'copyToClipboard', - ]); + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.ALERTS_COUNT, + cellActions, + actionsOrder: ['addToNewTimeline'], + services, + }); }; -const registerCellActionsTrigger = ( - uiActions: UiActionsStart, - triggerId: SecurityCellActionsTrigger, - cellActions: SecurityCellActions, - actionsOrder: SecurityCellActionName[] -) => { +const registerCellActionsTrigger = ({ + triggerId, + cellActions, + actionsOrder, + services, +}: { + triggerId: SecurityCellActionsTrigger; + cellActions: SecurityCellActions; + actionsOrder: SecurityCellActionName[]; + services: StartServices; +}) => { + const { uiActions } = services; uiActions.registerTrigger({ id: triggerId }); actionsOrder.forEach((actionName, order) => { const actionFactory = cellActions[actionName]; - uiActions.addTriggerAction( - triggerId, - actionFactory({ id: `${triggerId}-${actionName}`, order }) - ); + if (actionFactory) { + const action = actionFactory({ id: `${triggerId}-${actionName}`, order }); + + uiActions.addTriggerAction(triggerId, enhanceActionWithTelemetry(action, services)); + } }); }; diff --git a/x-pack/plugins/security_solution/public/actions/telemetry.test.ts b/x-pack/plugins/security_solution/public/actions/telemetry.test.ts new file mode 100644 index 0000000000000..82a691348a206 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/telemetry.test.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { StartServices } from '../types'; +import { enhanceActionWithTelemetry } from './telemetry'; +import { createAction } from '@kbn/ui-actions-plugin/public'; +import type { CellActionExecutionContext } from '@kbn/cell-actions'; + +const actionId = 'test_action_id'; +const displayName = 'test-actions'; +const fieldName = 'test.user.name'; +const fieldValue = 'test value'; +const metadata = undefined; + +const action = createAction({ + id: actionId, + execute: async () => {}, + getIconType: () => 'test-icon', + getDisplayName: () => displayName, +}); +const context = { + field: { name: fieldName, value: fieldValue, type: 'text' }, + metadata, +} as CellActionExecutionContext; + +describe('enhanceActionWithTelemetry', () => { + it('calls telemetry report when the action is executed', () => { + const telemetry = { reportCellActionClicked: jest.fn() }; + const services = { telemetry } as unknown as StartServices; + + const enhancedAction = enhanceActionWithTelemetry(action, services); + enhancedAction.execute(context); + + expect(telemetry.reportCellActionClicked).toHaveBeenCalledWith({ + displayName, + actionId, + fieldName, + metadata, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/telemetry.ts b/x-pack/plugins/security_solution/public/actions/telemetry.ts new file mode 100644 index 0000000000000..c051f11a81271 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/telemetry.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. + */ + +import type { CellAction, CellActionExecutionContext } from '@kbn/cell-actions'; +import type { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; +import type { StartServices } from '../types'; +import type { SecurityCellActionExecutionContext } from './types'; + +export const enhanceActionWithTelemetry = ( + action: CellAction, + services: StartServices +): CellAction => { + const { telemetry } = services; + const { execute, ...rest } = action; + const enhancedExecute = ( + context: ActionExecutionContext + ): Promise => { + telemetry.reportCellActionClicked({ + actionId: rest.id, + displayName: rest.getDisplayName(context), + fieldName: context.field.name, + metadata: context.metadata, + }); + + return execute(context); + }; + + return { ...rest, execute: enhancedExecute }; +}; diff --git a/x-pack/plugins/security_solution/public/actions/types.ts b/x-pack/plugins/security_solution/public/actions/types.ts index da6b2cc3919a9..e0cd6e764f3b8 100644 --- a/x-pack/plugins/security_solution/public/actions/types.ts +++ b/x-pack/plugins/security_solution/public/actions/types.ts @@ -6,6 +6,12 @@ */ import type { CellAction, CellActionExecutionContext, CellActionFactory } from '@kbn/cell-actions'; +import type { QueryOperator } from '../../common/types'; +export interface AndFilter { + field: string; + value: string | string[]; + operator?: QueryOperator; +} export interface SecurityMetadata extends Record { /** @@ -26,6 +32,20 @@ export interface SecurityMetadata extends Record { * and we need all the filtering actions to perform the opposite (negate) operation. */ negateFilters?: boolean; + /** + * `metadata.telemetry` is used by the telemetry service to add context to the event. + */ + telemetry?: { + /** + * It defines which UI component renders the CellActions. + */ + component: string; + }; + /** + * `metadata.andFilters` is used by the addToTimelineAction to add + * an "and" query to the main data provider + */ + andFilters?: AndFilter[]; } export interface SecurityCellActionExecutionContext extends CellActionExecutionContext { @@ -33,13 +53,15 @@ export interface SecurityCellActionExecutionContext extends CellActionExecutionC } export type SecurityCellAction = CellAction; -// All security cell actions names -export type SecurityCellActionName = - | 'filterIn' - | 'filterOut' - | 'addToTimeline' - | 'showTopN' - | 'copyToClipboard' - | 'toggleColumn'; +export interface SecurityCellActions { + filterIn?: CellActionFactory; + filterOut?: CellActionFactory; + addToTimeline?: CellActionFactory; + addToNewTimeline?: CellActionFactory; + showTopN?: CellActionFactory; + copyToClipboard?: CellActionFactory; + toggleColumn?: CellActionFactory; +} -export type SecurityCellActions = Record; +// All security cell actions names +export type SecurityCellActionName = keyof SecurityCellActions; diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 264df10831fb7..a70a915e6aed4 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -27,6 +27,7 @@ import { useShowTimeline } from '../../../common/utils/timeline/use_show_timelin import { useShowPagesWithEmptyView } from '../../../common/utils/empty_view/use_show_pages_with_empty_view'; import { useIsPolicySettingsBarVisible } from '../../../management/pages/policy/view/policy_hooks'; import { useIsGroupedNavigationEnabled } from '../../../common/components/navigation/helpers'; +import { useSyncFlyoutStateWithUrl } from '../../../flyout/url/use_sync_flyout_state_with_url'; const NO_DATA_PAGE_MAX_WIDTH = 950; @@ -75,6 +76,8 @@ export const SecuritySolutionTemplateWrapper: React.FC + )} - {}} /> + {}} + handleOnFlyoutClosed={handleFlyoutChangedOrClosed} + /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx index 5a3f4b3e25e0e..07342c4f60691 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx @@ -10,7 +10,6 @@ import React from 'react'; import { useLocation } from 'react-router-dom'; import { SecurityPageName } from '../../../../common/constants'; -import { useGlobalTime } from '../../containers/use_global_time'; import { DEFAULT_STACK_BY_FIELD, DEFAULT_STACK_BY_FIELD1, @@ -151,16 +150,6 @@ describe('AlertsTreemapPanel', () => { await waitFor(() => expect(screen.getByTestId('treemapPanel')).toBeInTheDocument()); }); - it('invokes useGlobalTime() with false to prevent global queries from being deleted when the component unmounts', async () => { - render( - - - - ); - - await waitFor(() => expect(useGlobalTime).toBeCalledWith(false)); - }); - it('renders the panel with a hidden overflow-x', async () => { render( diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx index a3ba582b3c974..33e526932c3e6 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx @@ -80,7 +80,7 @@ const AlertsTreemapPanelComponent: React.FC = ({ stackByWidth, title, }: Props) => { - const { to, from, deleteQuery, setQuery } = useGlobalTime(false); + const { to, from, deleteQuery, setQuery } = useGlobalTime(); // create a unique, but stable (across re-renders) query id const uniqueQueryId = useMemo(() => `${ALERTS_TREEMAP_ID}-${uuidv4()}`, []); diff --git a/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.test.tsx b/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.test.tsx index 78c57d598c3dc..6430e38911b51 100644 --- a/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.test.tsx @@ -13,7 +13,7 @@ jest.mock('@kbn/dashboard-plugin/public', () => { const actual = jest.requireActual('@kbn/dashboard-plugin/public'); return { ...actual, - LazyDashboardContainerRenderer: jest + DashboardRenderer: jest .fn() .mockImplementation(() =>
    ), }; @@ -45,7 +45,7 @@ describe('DashboardRenderer', () => { expect(queryByTestId(`dashboardRenderer`)).toBeInTheDocument(); }); - it('does not render when No Read Permission', () => { + it.skip('does not render when No Read Permission', () => { const testProps = { ...props, canReadDashboard: false, diff --git a/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.tsx b/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.tsx index e8c521e9fd8c4..1f4cdff815dfa 100644 --- a/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/dashboards/dashboard_renderer.tsx @@ -5,8 +5,8 @@ * 2.0. */ import React, { useCallback, useEffect, useState } from 'react'; -import type { DashboardContainer } from '@kbn/dashboard-plugin/public'; -import { LazyDashboardContainerRenderer } from '@kbn/dashboard-plugin/public'; +import type { DashboardAPI } from '@kbn/dashboard-plugin/public'; +import { DashboardRenderer as DashboardContainerRenderer } from '@kbn/dashboard-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import type { Filter, Query } from '@kbn/es-query'; @@ -28,7 +28,7 @@ const DashboardRendererComponent = ({ filters?: Filter[]; id: string; inputId?: InputsModelId.global | InputsModelId.timeline; - onDashboardContainerLoaded?: (dashboardContainer: DashboardContainer) => void; + onDashboardContainerLoaded?: (dashboardContainer: DashboardAPI) => void; query?: Query; savedObjectId: string | undefined; timeRange: { @@ -39,12 +39,12 @@ const DashboardRendererComponent = ({ }; }) => { const dispatch = useDispatch(); - const [dashboardContainer, setDashboardContainer] = useState(); + const [dashboardContainer, setDashboardContainer] = useState(); const getCreationOptions = useCallback( () => Promise.resolve({ - overrideInput: { timeRange, viewMode: ViewMode.VIEW, query, filters }, + initialInput: { timeRange, viewMode: ViewMode.VIEW, query, filters }, }), [filters, query, timeRange] ); @@ -73,17 +73,17 @@ const DashboardRendererComponent = ({ }, [dashboardContainer, filters, query, timeRange]); const handleDashboardLoaded = useCallback( - (container: DashboardContainer) => { + (container: DashboardAPI) => { setDashboardContainer(container); onDashboardContainerLoaded?.(container); }, [onDashboardContainerLoaded] ); return savedObjectId && canReadDashboard ? ( - ) : null; }; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_pending_action_status_badge.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_pending_action_status_badge.tsx deleted file mode 100644 index ee318996b97fe..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_pending_action_status_badge.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, { memo } from 'react'; -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiTextColor, EuiToolTip } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { EndpointHostIsolationStatusProps } from './host_isolation'; - -export const AgentPendingActionStatusBadge = memo< - { 'data-test-subj'?: string } & Pick ->(({ 'data-test-subj': dataTestSubj, pendingActions }) => { - return ( - - -
    - -
    - {!!pendingActions.pendingIsolate && ( - - - - - {pendingActions.pendingIsolate} - - )} - {!!pendingActions.pendingUnIsolate && ( - - - - - {pendingActions.pendingUnIsolate} - - )} - {!!pendingActions.pendingKillProcess && ( - - - - - {pendingActions.pendingKillProcess} - - )} - {!!pendingActions.pendingSuspendProcess && ( - - - - - {pendingActions.pendingSuspendProcess} - - )} - {!!pendingActions.pendingRunningProcesses && ( - - - - - {pendingActions.pendingRunningProcesses} - - )} -
    - } - > - - prev + curr, 0), - }} - /> - - - - ); -}); - -AgentPendingActionStatusBadge.displayName = 'AgentPendingActionStatusBadge'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx deleted file mode 100644 index 150189e273cb3..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx +++ /dev/null @@ -1,26 +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 { EuiBadge } from '@elastic/eui'; -import type { HostStatus } from '../../../../common/endpoint/types'; -import { HOST_STATUS_TO_BADGE_COLOR } from '../../../management/pages/endpoint_hosts/view/host_constants'; -import { getAgentStatusText } from './agent_status_text'; - -export const AgentStatus = React.memo(({ hostStatus }: { hostStatus: HostStatus }) => { - return ( - - {getAgentStatusText(hostStatus)} - - ); -}); - -AgentStatus.displayName = 'AgentStatus'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.test.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.test.tsx new file mode 100644 index 0000000000000..7fa169b32d348 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.test.tsx @@ -0,0 +1,366 @@ +/* + * 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 { AppContextTestRender } from '../../../mock/endpoint'; +import { createAppRootMockRenderer } from '../../../mock/endpoint'; +import type { + EndpointAgentStatusByIdProps, + EndpointAgentStatusProps, +} from './endpoint_agent_status'; +import { EndpointAgentStatus, EndpointAgentStatusById } from './endpoint_agent_status'; +import type { + EndpointPendingActions, + HostInfoInterface, +} from '../../../../../common/endpoint/types'; +import { HostStatus } from '../../../../../common/endpoint/types'; +import React from 'react'; +import { EndpointActionGenerator } from '../../../../../common/endpoint/data_generators/endpoint_action_generator'; +import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; +import { composeHttpHandlerMocks } from '../../../mock/endpoint/http_handler_mock_factory'; +import type { EndpointMetadataHttpMocksInterface } from '../../../../management/pages/endpoint_hosts/mocks'; +import { endpointMetadataHttpMocks } from '../../../../management/pages/endpoint_hosts/mocks'; +import type { ResponseActionsHttpMocksInterface } from '../../../../management/mocks/response_actions_http_mocks'; +import { responseActionsHttpMocks } from '../../../../management/mocks/response_actions_http_mocks'; +import { waitFor, within, fireEvent } from '@testing-library/react'; +import { getEmptyValue } from '../../empty_value'; +import { clone, set } from 'lodash'; + +type AgentStatusApiMocksInterface = EndpointMetadataHttpMocksInterface & + ResponseActionsHttpMocksInterface; + +// API mocks composed from the endpoint metadata API mock and the response actions API mocks +const agentStatusApiMocks = composeHttpHandlerMocks([ + endpointMetadataHttpMocks, + responseActionsHttpMocks, +]); + +describe('When showing Endpoint Agent Status', () => { + const ENDPOINT_ISOLATION_OBJ_PATH = 'metadata.Endpoint.state.isolation'; + + let appTestContext: AppContextTestRender; + let render: () => ReturnType; + let renderResult: ReturnType; + let endpointDetails: HostInfoInterface; + let actionsSummary: EndpointPendingActions; + let apiMocks: ReturnType; + + const triggerTooltip = () => { + fireEvent.mouseOver(renderResult.getByTestId('test-actionStatuses-tooltipTrigger')); + }; + + beforeEach(() => { + appTestContext = createAppRootMockRenderer(); + apiMocks = agentStatusApiMocks(appTestContext.coreStart.http); + + const actionGenerator = new EndpointActionGenerator('seed'); + + actionsSummary = actionGenerator.generateAgentPendingActionsSummary(); + actionsSummary.pending_actions = {}; + apiMocks.responseProvider.agentPendingActionsSummary.mockImplementation(() => { + return { + data: [actionsSummary], + }; + }); + + const metadataGenerator = new EndpointDocGenerator('seed'); + + endpointDetails = { + metadata: metadataGenerator.generateHostMetadata(), + host_status: HostStatus.HEALTHY, + } as HostInfoInterface; + apiMocks.responseProvider.metadataDetails.mockImplementation(() => endpointDetails); + }); + + describe('and using `EndpointAgentStatus` component', () => { + let renderProps: EndpointAgentStatusProps; + + beforeEach(() => { + renderProps = { + 'data-test-subj': 'test', + endpointHostInfo: endpointDetails, + }; + + render = () => { + renderResult = appTestContext.render(); + return renderResult; + }; + }); + + it('should display status', () => { + const { getByTestId } = render(); + + expect(getByTestId('test').textContent).toEqual('Healthy'); + }); + + it('should display status and isolated', () => { + set(endpointDetails, ENDPOINT_ISOLATION_OBJ_PATH, true); + const { getByTestId } = render(); + + expect(getByTestId('test').textContent).toEqual('HealthyIsolated'); + }); + + it('should display status and isolated and display other pending actions in tooltip', async () => { + set(endpointDetails, ENDPOINT_ISOLATION_OBJ_PATH, true); + actionsSummary.pending_actions = { + 'get-file': 2, + execute: 6, + }; + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyIsolated'); + + triggerTooltip(); + + await waitFor(() => { + expect( + within(renderResult.baseElement).getByTestId('test-actionStatuses-tooltipContent') + .textContent + ).toEqual('Pending actions:execute6get-file2'); + }); + }); + + it('should display status and action count', async () => { + actionsSummary.pending_actions = { + 'get-file': 2, + execute: 6, + }; + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('Healthy8 actions pending'); + }); + + it('should display status and isolating', async () => { + actionsSummary.pending_actions = { + isolate: 1, + }; + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyIsolating'); + }); + + it('should display status and isolating and have tooltip with other pending actions', async () => { + actionsSummary.pending_actions = { + isolate: 1, + 'kill-process': 1, + }; + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyIsolating'); + + triggerTooltip(); + + await waitFor(() => { + expect( + within(renderResult.baseElement).getByTestId('test-actionStatuses-tooltipContent') + .textContent + ).toEqual('Pending actions:isolate1kill-process1'); + }); + }); + + it('should display status and releasing', async () => { + actionsSummary.pending_actions = { + unisolate: 1, + }; + set(endpointDetails, ENDPOINT_ISOLATION_OBJ_PATH, true); + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyReleasing'); + }); + + it('should display status and releasing and show other pending actions in tooltip', async () => { + actionsSummary.pending_actions = { + unisolate: 1, + 'kill-process': 1, + }; + set(endpointDetails, ENDPOINT_ISOLATION_OBJ_PATH, true); + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyReleasing'); + + triggerTooltip(); + + await waitFor(() => { + expect( + within(renderResult.baseElement).getByTestId('test-actionStatuses-tooltipContent') + .textContent + ).toEqual('Pending actions:kill-process1release1'); + }); + }); + + it('should show individual action count in tooltip (including unknown actions) sorted asc', async () => { + actionsSummary.pending_actions = { + isolate: 1, + 'get-file': 2, + execute: 6, + 'kill-process': 1, + foo: 2, + }; + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyIsolating'); + + triggerTooltip(); + + await waitFor(() => { + expect( + within(renderResult.baseElement).getByTestId('test-actionStatuses-tooltipContent') + .textContent + ).toEqual('Pending actions:execute6foo2get-file2isolate1kill-process1'); + }); + }); + + it('should still display status and isolation state if action summary api fails', async () => { + set(endpointDetails, ENDPOINT_ISOLATION_OBJ_PATH, true); + apiMocks.responseProvider.agentPendingActionsSummary.mockImplementation(() => { + throw new Error('test error'); + }); + + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('HealthyIsolated'); + }); + + describe('and `autoRefresh` prop is set to true', () => { + beforeEach(() => { + renderProps.autoRefresh = true; + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should keep actions up to date when autoRefresh is true', async () => { + apiMocks.responseProvider.agentPendingActionsSummary.mockReturnValueOnce({ + data: [actionsSummary], + }); + + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.agentPendingActionsSummary).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual('Healthy'); + + apiMocks.responseProvider.agentPendingActionsSummary.mockReturnValueOnce({ + data: [ + { + ...actionsSummary, + pending_actions: { + 'kill-process': 2, + 'running-processes': 2, + }, + }, + ], + }); + + jest.runOnlyPendingTimers(); + + await waitFor(() => { + expect(getByTestId('test').textContent).toEqual('Healthy4 actions pending'); + }); + }); + }); + }); + + describe('And when using EndpointAgentStatusById', () => { + let renderProps: EndpointAgentStatusByIdProps; + + beforeEach(() => { + jest.useFakeTimers(); + + renderProps = { + 'data-test-subj': 'test', + endpointAgentId: '123', + }; + + render = () => { + renderResult = appTestContext.render(); + return renderResult; + }; + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should display status and isolated', async () => { + set(endpointDetails, ENDPOINT_ISOLATION_OBJ_PATH, true); + const { getByTestId } = render(); + + await waitFor(() => { + expect(getByTestId('test').textContent).toEqual('HealthyIsolated'); + }); + }); + + it('should display empty value if API call to host metadata fails', async () => { + apiMocks.responseProvider.metadataDetails.mockImplementation(() => { + throw new Error('test error'); + }); + const { getByTestId } = render(); + + await waitFor(() => { + expect(apiMocks.responseProvider.metadataDetails).toHaveBeenCalled(); + }); + + expect(getByTestId('test').textContent).toEqual(getEmptyValue()); + }); + + it('should keep agent status up to date when autoRefresh is true', async () => { + renderProps.autoRefresh = true; + apiMocks.responseProvider.metadataDetails.mockReturnValueOnce(endpointDetails); + + const { getByTestId } = render(); + + await waitFor(() => { + expect(getByTestId('test').textContent).toEqual('Healthy'); + }); + + apiMocks.responseProvider.metadataDetails.mockReturnValueOnce( + set(clone(endpointDetails), 'metadata.Endpoint.state.isolation', true) + ); + jest.runOnlyPendingTimers(); + + await waitFor(() => { + expect(getByTestId('test').textContent).toEqual('HealthyIsolated'); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx new file mode 100644 index 0000000000000..1b2d021634a2f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx @@ -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 React, { memo, useMemo } from 'react'; +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiTextColor, + EuiToolTip, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { DEFAULT_POLL_INTERVAL } from '../../../../management/common/constants'; +import { HOST_STATUS_TO_BADGE_COLOR } from '../../../../management/pages/endpoint_hosts/view/host_constants'; +import { getEmptyValue } from '../../empty_value'; +import type { ResponseActionsApiCommandNames } from '../../../../../common/endpoint/service/response_actions/constants'; +import { RESPONSE_ACTION_API_COMMANDS_TO_CONSOLE_COMMAND_MAP } from '../../../../../common/endpoint/service/response_actions/constants'; +import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; +import { useGetEndpointPendingActionsSummary } from '../../../../management/hooks/response_actions/use_get_endpoint_pending_actions_summary'; +import { useTestIdGenerator } from '../../../../management/hooks/use_test_id_generator'; +import type { HostInfo, EndpointPendingActions } from '../../../../../common/endpoint/types'; +import { useGetEndpointDetails } from '../../../../management/hooks'; +import { getAgentStatusText } from '../agent_status_text'; + +const TOOLTIP_CONTENT_STYLES: React.CSSProperties = Object.freeze({ width: 150 }); +const ISOLATING_LABEL = i18n.translate( + 'xpack.securitySolution.endpoint.agentAndActionsStatus.isIsolating', + { defaultMessage: 'Isolating' } +); +const RELEASING_LABEL = i18n.translate( + 'xpack.securitySolution.endpoint.agentAndActionsStatus.isUnIsolating', + { defaultMessage: 'Releasing' } +); +const ISOLATED_LABEL = i18n.translate( + 'xpack.securitySolution.endpoint.agentAndActionsStatus.isolated', + { defaultMessage: 'Isolated' } +); + +const EuiFlexGroupStyled = styled(EuiFlexGroup)` + .isolation-status { + margin-left: ${({ theme }) => theme.eui.euiSizeS}; + } +`; + +export interface EndpointAgentStatusProps { + endpointHostInfo: HostInfo; + /** + * If set to `true` (Default), then the endpoint isolation state and response actions count + * will be kept up to date by querying the API periodically. + * Only used if `pendingActions` is not defined. + */ + autoRefresh?: boolean; + /** + * The pending actions for the host (as return by the pending actions summary api). + * If undefined, then this component will call the API to retrieve that list of pending actions. + * NOTE: if this prop is defined, it will invalidate `autoRefresh` prop. + */ + pendingActions?: EndpointPendingActions['pending_actions']; + 'data-test-subj'?: string; +} + +/** + * Displays the status of an Endpoint agent along with its Isolation state or the number of pending + * response actions against it. + * + * TIP: if you only have the Endpoint's `agent.id`, then consider using `EndpointAgentStatusById`, + * which will call the needed APIs to get the information necessary to display the status. + */ +export const EndpointAgentStatus = memo( + ({ endpointHostInfo, autoRefresh = true, pendingActions, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + const { data: endpointPendingActions } = useGetEndpointPendingActionsSummary( + [endpointHostInfo.metadata.agent.id], + { + refetchInterval: autoRefresh ? DEFAULT_POLL_INTERVAL : false, + enabled: !pendingActions, + } + ); + + const [hasPendingActions, hostPendingActions] = useMemo< + [boolean, EndpointPendingActions['pending_actions']] + >(() => { + if (!endpointPendingActions && !pendingActions) { + return [false, {}]; + } + + const pending = pendingActions + ? pendingActions + : endpointPendingActions?.data[0].pending_actions ?? {}; + + return [Object.keys(pending).length > 0, pending]; + }, [endpointPendingActions, pendingActions]); + + const status = endpointHostInfo.host_status; + const isIsolated = Boolean(endpointHostInfo.metadata.Endpoint.state?.isolation); + + return ( + + + + {getAgentStatusText(status)} + + + {(isIsolated || hasPendingActions) && ( + + + + )} + + ); + } +); +EndpointAgentStatus.displayName = 'EndpointAgentStatus'; + +export interface EndpointAgentStatusByIdProps { + endpointAgentId: string; + /** + * If set to `true` (Default), then the endpoint status and isolation/action counts will + * be kept up to date by querying the API periodically + */ + autoRefresh?: boolean; + 'data-test-subj'?: string; +} + +/** + * Given an Endpoint Agent Id, it will make the necessary API calls and then display the agent + * status using the `` component. + * + * NOTE: if the `HostInfo` is already available, consider using `` component + * instead in order to avoid duplicate API calls. + */ +export const EndpointAgentStatusById = memo( + ({ endpointAgentId, autoRefresh, 'data-test-subj': dataTestSubj }) => { + const { data } = useGetEndpointDetails(endpointAgentId, { + refetchInterval: autoRefresh ? DEFAULT_POLL_INTERVAL : false, + }); + + const emptyValue = ( + +

    {getEmptyValue()}

    +
    + ); + + if (!data) { + return emptyValue; + } + + return ( + + ); + } +); +EndpointAgentStatusById.displayName = 'EndpointAgentStatusById'; + +interface EndpointHostResponseActionsStatusProps { + /** The host's individual pending action list as return by the pending action summary api */ + pendingActions: EndpointPendingActions['pending_actions']; + /** Is host currently isolated */ + isIsolated: boolean; + 'data-test-subj'?: string; +} + +const EndpointHostResponseActionsStatus = memo( + ({ pendingActions, isIsolated, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + const isPendingStatusDisabled = useIsExperimentalFeatureEnabled( + 'disableIsolationUIPendingStatuses' + ); + + interface PendingActionsState { + actionList: Array<{ label: string; count: number }>; + totalPending: number; + wasReleasing: boolean; + wasIsolating: boolean; + hasMultipleActionTypesPending: boolean; + hasPendingIsolate: boolean; + hasPendingUnIsolate: boolean; + } + + const { + totalPending, + actionList, + wasReleasing, + wasIsolating, + hasMultipleActionTypesPending, + hasPendingIsolate, + hasPendingUnIsolate, + } = useMemo(() => { + const list: Array<{ label: string; count: number }> = []; + let actionTotal = 0; + let actionTypesCount = 0; + + Object.entries(pendingActions) + .sort() + .forEach(([actionName, actionCount]) => { + actionTotal += actionCount; + actionTypesCount += 1; + + list.push({ + count: actionCount, + label: + RESPONSE_ACTION_API_COMMANDS_TO_CONSOLE_COMMAND_MAP[ + actionName as ResponseActionsApiCommandNames + ] ?? actionName, + }); + }); + + const pendingIsolate = pendingActions.isolate ?? 0; + const pendingUnIsolate = pendingActions.unisolate ?? 0; + + return { + actionList: list, + totalPending: actionTotal, + wasReleasing: pendingIsolate === 0 && pendingUnIsolate > 0, + wasIsolating: pendingIsolate > 0 && pendingUnIsolate === 0, + hasMultipleActionTypesPending: actionTypesCount > 1, + hasPendingIsolate: pendingIsolate > 0, + hasPendingUnIsolate: pendingUnIsolate > 0, + }; + }, [pendingActions]); + + const badgeDisplayValue = useMemo(() => { + return hasPendingIsolate ? ( + ISOLATING_LABEL + ) : hasPendingUnIsolate ? ( + RELEASING_LABEL + ) : isIsolated ? ( + ISOLATED_LABEL + ) : ( + + ); + }, [hasPendingIsolate, hasPendingUnIsolate, isIsolated, totalPending]); + + const isolatedBadge = useMemo(() => { + return ( + + {ISOLATED_LABEL} + + ); + }, [dataTestSubj]); + + if (isPendingStatusDisabled) { + // If nothing is pending and host is not currently isolated, then render nothing + if (!isIsolated) { + return null; + } + + return isolatedBadge; + } + + // If nothing is pending + if (totalPending === 0) { + // and host is either releasing and or currently released, then render nothing + if ((!wasIsolating && wasReleasing) || !isIsolated) { + return null; + } + // else host was isolating or is isolated, then show isolation badge + else if ((!isIsolated && wasIsolating && !wasReleasing) || isIsolated) { + return isolatedBadge; + } + } + + // If there are different types of action pending + // --OR-- + // the only type of actions pending is NOT isolate/release, + // then show a summary with tooltip + if (hasMultipleActionTypesPending || (!hasPendingIsolate && !hasPendingUnIsolate)) { + return ( + + +
    + +
    + {actionList.map(({ count, label }) => { + return ( + + {label} + {count} + + ); + })} +
    + } + > + + {badgeDisplayValue} + + + + ); + } + + // show pending isolation badge if a single type of isolation action has pending numbers. + // We don't care about the count here because if there were more than 1 of the same type + // (ex. 3 isolate... 0 release), then the action status displayed is still the same - "isolating". + return ( + + + {badgeDisplayValue} + + + ); + } +); +EndpointHostResponseActionsStatus.displayName = 'EndpointHostResponseActionsStatus'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/index.ts b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/index.ts new file mode 100644 index 0000000000000..1d94de32e333c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/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 * from './endpoint_agent_status'; +export type { EndpointAgentStatusProps } from './endpoint_agent_status'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx deleted file mode 100644 index 7f4cee7fa8973..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx +++ /dev/null @@ -1,138 +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 type { EndpointHostIsolationStatusProps } from './endpoint_host_isolation_status'; -import { EndpointHostIsolationStatus } from './endpoint_host_isolation_status'; -import type { AppContextTestRender } from '../../../mock/endpoint'; -import { createAppRootMockRenderer } from '../../../mock/endpoint'; - -describe('when using the EndpointHostIsolationStatus component', () => { - let render: ( - renderProps?: Partial - ) => ReturnType; - let appContext: AppContextTestRender; - - beforeEach(() => { - appContext = createAppRootMockRenderer(); - - render = (renderProps = {}) => - appContext.render( - - ); - }); - - it('should render `null` if not isolated and nothing is pending', () => { - const renderResult = render(); - expect(renderResult.container.textContent).toBe(''); - }); - - it('should show `Isolated` when no pending actions and isolated', () => { - const { getByTestId } = render({ isIsolated: true }); - expect(getByTestId('test').textContent).toBe('Isolated'); - }); - - it.each([ - [ - 'Isolating', - { - pendingActions: { - pendingIsolate: 1, - }, - }, - ], - [ - 'Releasing', - { - pendingActions: { - pendingUnIsolate: 1, - }, - }, - ], - [ - // Because they are both of the same type and there are no other types, - // the status should be `isolating` - 'Isolating', - { - pendingActions: { - pendingIsolate: 2, - }, - }, - ], - [ - // Because they are both of the same type and there are no other types, - // the status should be `Releasing` - 'Releasing', - { - pendingActions: { - pendingUnIsolate: 2, - }, - }, - ], - [ - '10 actions pending', - { - isIsolated: true, - pendingActions: { - pendingIsolate: 2, - pendingUnIsolate: 2, - pendingKillProcess: 2, - pendingSuspendProcess: 2, - pendingRunningProcesses: 2, - }, - }, - ], - [ - '1 action pending', - { - isIsolated: true, - pendingActions: { - pendingKillProcess: 1, - }, - }, - ], - ])('should show %s}', (expectedLabel, componentProps) => { - const { getByTestId } = render(componentProps); - expect(getByTestId('test').textContent).toBe(expectedLabel); - // Validate that the text color is set to `subdued` - expect(getByTestId('test-pending').classList.toString().includes('subdued')).toBe(true); - }); - - describe('and the disableIsolationUIPendingStatuses experimental feature flag is true', () => { - beforeEach(() => { - appContext.setExperimentalFlag({ disableIsolationUIPendingStatuses: true }); - }); - - it('should render `null` if not isolated', () => { - const renderResult = render({ - pendingActions: { - pendingIsolate: 10, - pendingUnIsolate: 20, - }, - }); - expect(renderResult.container.textContent).toBe(''); - }); - - it('should show `Isolated` when no pending actions and isolated', () => { - const { getByTestId } = render({ - isIsolated: true, - pendingActions: { - pendingIsolate: 10, - pendingUnIsolate: 20, - }, - }); - expect(getByTestId('test').textContent).toBe('Isolated'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx deleted file mode 100644 index 650999029c545..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx +++ /dev/null @@ -1,167 +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, useRef, useEffect } from 'react'; -import { EuiBadge, EuiTextColor } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useTestIdGenerator } from '../../../../management/hooks/use_test_id_generator'; -import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; -import { AgentPendingActionStatusBadge } from '../agent_pending_action_status_badge'; - -export interface EndpointHostIsolationStatusProps { - isIsolated: boolean; - pendingActions: { - /** the count of pending isolate actions */ - pendingIsolate?: number; - /** the count of pending unisolate actions */ - pendingUnIsolate?: number; - pendingKillProcess?: number; - pendingSuspendProcess?: number; - pendingRunningProcesses?: number; - }; - 'data-test-subj'?: string; -} - -/** - * Component will display a host isolation status based on whether it is currently isolated or there are - * isolate/unisolate actions pending. If none of these are applicable, no UI component will be rendered - * (`null` is returned) - */ -export const EndpointHostIsolationStatus = memo( - ({ isIsolated, pendingActions, 'data-test-subj': dataTestSubj }) => { - const getTestId = useTestIdGenerator(dataTestSubj); - const isPendingStatusDisabled = useIsExperimentalFeatureEnabled( - 'disableIsolationUIPendingStatuses' - ); - - const { - pendingIsolate = 0, - pendingUnIsolate = 0, - pendingKillProcess = 0, - pendingSuspendProcess = 0, - pendingRunningProcesses = 0, - } = pendingActions; - - const wasReleasing = useRef(false); - const wasIsolating = useRef(false); - - const totalPending = useMemo( - () => - pendingIsolate + - pendingUnIsolate + - pendingKillProcess + - pendingSuspendProcess + - pendingRunningProcesses, - [ - pendingIsolate, - pendingKillProcess, - pendingRunningProcesses, - pendingSuspendProcess, - pendingUnIsolate, - ] - ); - - const hasMultipleActionTypesPending = useMemo(() => { - return ( - Object.values(pendingActions).reduce((countOfTypes, pendingActionCount) => { - if (pendingActionCount > 0) { - return countOfTypes + 1; - } - return countOfTypes; - }, 0) > 1 - ); - }, [pendingActions]); - - useEffect(() => { - wasReleasing.current = pendingIsolate === 0 && pendingUnIsolate > 0; - wasIsolating.current = pendingIsolate > 0 && pendingUnIsolate === 0; - }, [pendingIsolate, pendingUnIsolate]); - - return useMemo(() => { - if (isPendingStatusDisabled) { - // If nothing is pending and host is not currently isolated, then render nothing - if (!isIsolated) { - return null; - } - - return ( - - - - ); - } - - // If nothing is pending - if (totalPending === 0) { - // and host is either releasing and or currently released, then render nothing - if ((!wasIsolating.current && wasReleasing.current) || !isIsolated) { - return null; - } - // else host was isolating or is isolated, then show isolation badge - else if ((!isIsolated && wasIsolating.current && !wasReleasing.current) || isIsolated) { - return ( - - - - ); - } - } - - // If there are different types of action pending - // --OR-- - // the only type of actions pending is NOT isolate/release, - // then show a summary with tooltip - if (hasMultipleActionTypesPending || (!pendingIsolate && !pendingUnIsolate)) { - return ( - - ); - } - - // show pending isolation badge if a single type of isolation action has pending numbers. - // We don't care about the count here because if there were more than 1 of the same type - // (ex. 3 isolate... 0 release), then the action status displayed is still the same - "isolating". - return ( - - - {pendingIsolate ? ( - - ) : ( - - )} - - - ); - }, [ - isPendingStatusDisabled, - totalPending, - hasMultipleActionTypesPending, - pendingIsolate, - pendingUnIsolate, - dataTestSubj, - getTestId, - isIsolated, - pendingActions, - ]); - } -); - -EndpointHostIsolationStatus.displayName = 'EndpointHostIsolationStatus'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts index 24b94cd6212b7..41763a6e88d37 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts @@ -8,5 +8,4 @@ export * from './isolate_success'; export * from './isolate_form'; export * from './unisolate_form'; -export * from './endpoint_host_isolation_status'; export * from './action_completion_return_button'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx index 5bffe92e6d019..fe0889e80cda7 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx @@ -18,6 +18,7 @@ import { EuiTextArea, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { MissingEncryptionKeyCallout } from '../../../../management/components/missing_encryption_key_callout'; import { CANCEL, COMMENT, COMMENT_PLACEHOLDER, CONFIRM } from './translations'; export interface EndpointIsolatedFormProps { @@ -42,62 +43,65 @@ export const EndpointIsolateForm = memo( ); return ( - - - -

    - {hostName} }} - /> -
    -

    -

    - {' '} - {messageAppend} -

    -
    -
    + <> + + + + +

    + {hostName} }} + /> +
    +

    +

    + {' '} + {messageAppend} +

    +
    +
    - - - + + + - - - - - {CANCEL} - - - - - {CONFIRM} - - - - -
    + + + + + {CANCEL} + + + + + {CONFIRM} + + + + +
    + ); } ); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/unisolate_form.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/unisolate_form.tsx index 8226175786ae0..b1ffb60c6bbff 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/unisolate_form.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/unisolate_form.tsx @@ -18,6 +18,7 @@ import { EuiTextArea, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { MissingEncryptionKeyCallout } from '../../../../management/components/missing_encryption_key_callout'; import { CANCEL, COMMENT, COMMENT_PLACEHOLDER, CONFIRM, UNISOLATE, ISOLATED } from './translations'; import type { EndpointIsolatedFormProps } from './isolate_form'; @@ -31,53 +32,56 @@ export const EndpointUnisolateForm = memo( ); return ( - - - -

    - {hostName}, - isolated: {ISOLATED}, - unisolate: {UNISOLATE}, - }} - />{' '} - {messageAppend} -

    -
    -
    + <> + + + + +

    + {hostName}, + isolated: {ISOLATED}, + unisolate: {UNISOLATE}, + }} + />{' '} + {messageAppend} +

    +
    +
    - - - + + + - - - - - {CANCEL} - - - - - {CONFIRM} - - - - -
    + + + + + {CANCEL} + + + + + {CONFIRM} + + + + +
    + ); } ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx index 793cba6810645..f591fad6a6629 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx @@ -47,6 +47,19 @@ const alwaysDisplayedFields: EventSummaryField[] = [ { id: 'agent.id', overrideField: AGENT_STATUS_FIELD_NAME, label: i18n.AGENT_STATUS }, { id: 'user.name' }, { id: 'rule.name' }, + { id: 'cloud.provider' }, + { id: 'cloud.region' }, + { id: 'cloud.provider' }, + { id: 'cloud.region' }, + { id: 'orchestrator.cluster.id' }, + { id: 'orchestrator.cluster.name' }, + { id: 'container.image.name' }, + { id: 'container.image.tag' }, + { id: 'orchestrator.namespace' }, + { id: 'orchestrator.resource.parent.type' }, + { id: 'orchestrator.resource.type' }, + { id: 'process.executable' }, + { id: 'file.path' }, { id: ALERT_RULE_TYPE, label: i18n.RULE_TYPE }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx index eb490469d16df..88685262ecc17 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_by_process_ancestry.test.tsx @@ -20,6 +20,7 @@ import { PROCESS_ANCESTRY_ERROR, PROCESS_ANCESTRY_EMPTY, } from './translations'; +import type { StatsNode } from '../../../containers/alerts/use_alert_prevalence_from_process_tree'; jest.mock('../../../containers/alerts/use_alert_prevalence_from_process_tree', () => ({ useAlertPrevalenceFromProcessTree: jest.fn(), @@ -96,10 +97,15 @@ describe('RelatedAlertsByProcessAncestry', () => { it('renders the text with a count and a timeline button when the request works', async () => { const mockAlertIds = ['1', '2']; + const mockStatsNodes = [ + { id: 'testid', name: 'process', parent: 'testid2' }, + { id: 'testid2', name: 'iexplore' }, + ]; mockUseAlertPrevalenceFromProcessTree.mockReturnValue({ loading: false, error: false, alertIds: mockAlertIds, + statsNodes: mockStatsNodes, }); render( @@ -123,6 +129,7 @@ describe('RelatedAlertsByProcessAncestry', () => { loading: false, error: false, alertIds: [] as string[], + statsNodes: [] as StatsNode[], }); render( @@ -142,6 +149,7 @@ describe('RelatedAlertsByProcessAncestry', () => { loading: false, error: false, alertIds: undefined, + statsNodes: undefined, }); render( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/prevalence_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/prevalence_cell.tsx index dca16cb5e896b..91fb095a16443 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/table/prevalence_cell.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/prevalence_cell.tsx @@ -56,6 +56,7 @@ const PrevalenceCell: React.FC = ({ {count} diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts b/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts index 8e35d3a7881b1..7ee53ae5d4bee 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts @@ -7,6 +7,7 @@ /* eslint-disable complexity */ +import type { Filter } from '@kbn/es-query'; import { escapeDataProviderId } from '@kbn/securitysolution-t-grid'; import { isArray, isEmpty, isString } from 'lodash/fp'; import { useMemo } from 'react'; @@ -47,6 +48,7 @@ export interface UseActionCellDataProvider { export interface ActionCellValuesAndDataProvider { values: string[]; dataProviders: DataProvider[]; + filters: Filter[]; } export const getDataProvider = ( @@ -93,6 +95,23 @@ export const useActionCellDataProvider = ({ const cellData = useMemo(() => { if (values === null || values === undefined) return null; const arrayValues = Array.isArray(values) ? values : [values]; + + // For fields with multiple values we need add an extra filter that makes sure + // that only fields that match ALL the values are queried later on. + let filters: Filter[] = []; + if (arrayValues.length > 1) { + filters = [ + { + meta: {}, + query: { + bool: { + must: arrayValues.map((value) => ({ term: { [field]: value } })), + }, + }, + }, + ]; + } + return arrayValues.reduce( (memo, value, index) => { let id: string = ''; @@ -157,7 +176,7 @@ export const useActionCellDataProvider = ({ memo.dataProviders.push(getDataProvider(field, id, value)); return memo; }, - { values: [], dataProviders: [] } + { values: [], dataProviders: [], filters } ); }, [ contextId, diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx index 423173c63b9f8..71babb11c62e5 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/buttons.tsx @@ -13,9 +13,10 @@ import { useFilterGroupInternalContext } from './hooks/use_filters'; import { ADD_CONTROLS, ADD_CONTROLS_MAX_LIMIT, - DISCARD_CHANGES, PENDING_CHANGES_REMINDER, + SAVE_CHANGES, } from './translations'; +import { TEST_IDS } from './constants'; interface AddControlProps extends Partial { onClick: () => void; @@ -29,10 +30,11 @@ export const AddControl: FC = ({ onClick, ...rest }) => { size="s" iconSize="m" display="base" - data-test-subj={'filter-group__add-control'} + aria-label={isDisabled ? ADD_CONTROLS_MAX_LIMIT : ADD_CONTROLS} + data-test-subj={TEST_IDS.ADD_CONTROL} onClick={onClick} {...rest} - iconType={'plusInCircle'} + iconType="plusInCircle" /> ); @@ -54,12 +56,13 @@ export const SaveControls: FC = ({ onClick }) => { = ({ onClick }) => { /> } isOpen={pendingChangesPopoverOpen} - anchorPosition={'upCenter'} + anchorPosition="upCenter" panelPaddingSize="none" closePopover={closePendingChangesPopover} panelProps={{ - 'data-test-subj': 'filter-group__save-popover', + 'data-test-subj': TEST_IDS.SAVE_CHANGE_POPOVER, }} >
    @@ -82,23 +85,3 @@ export const SaveControls: FC = ({ onClick }) => { ); }; - -interface DiscardChangesProps { - onClick: () => void; -} - -export const DiscardChanges: FC = ({ onClick }) => { - return ( - - - - ); -}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/constants.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/constants.ts new file mode 100644 index 0000000000000..075cbec275c9e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/constants.ts @@ -0,0 +1,25 @@ +/* + * 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 TEST_IDS = { + FILTER_CONTROLS: 'filter-group__items', + FILTER_LOADING: 'filter-group__loading', + MOCKED_CONTROL: 'mocked_control_group', + ADD_CONTROL: 'filter-group__add-control', + SAVE_CONTROL: 'filter-group__save', + SAVE_CHANGE_POPOVER: 'filter-group__save-popover', + FILTERS_CHANGED_BANNER: 'filter-group--changed-banner', + FILTERS_CHANGED_BANNER_SAVE: 'filter-group__save', + FILTERS_CHANGED_BANNER_DISCARD: 'filter-group__discard', + CONTEXT_MENU: { + BTN: 'filter-group__context', + MENU: 'filter-group__context-menu', + RESET: 'filter-group__context--reset', + EDIT: 'filter-group__context--edit', + DISCARD: `filter-group__context--discard`, + }, +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx index 90f940a8ee35c..bf672a5fabfa4 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx @@ -7,6 +7,7 @@ import { EuiButtonIcon, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; +import { TEST_IDS } from './constants'; import { useFilterGroupInternalContext } from './hooks/use_filters'; import { CONTEXT_MENU_RESET, @@ -84,9 +85,11 @@ export const FilterGroupContextMenu = () => { const resetButton = useMemo( () => ( {CONTEXT_MENU_RESET} @@ -98,15 +101,15 @@ export const FilterGroupContextMenu = () => { const editControlsButton = useMemo( () => ( {isViewMode ? EDIT_CONTROLS : DISCARD_CHANGES} @@ -121,7 +124,7 @@ export const FilterGroupContextMenu = () => { return ( { size="s" iconType="boxesHorizontal" onClick={toggleContextMenu} - data-test-subj="filter-group__context" + data-test-subj={TEST_IDS.CONTEXT_MENU.BTN} /> } isOpen={isContextMenuVisible} diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx new file mode 100644 index 0000000000000..f066b022af04e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx @@ -0,0 +1,759 @@ +/* + * 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 { FilterGroup } from '.'; +import type { ComponentProps, FC } from 'react'; +import React from 'react'; +import { act, render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { DEFAULT_DETECTION_PAGE_FILTERS } from '../../../../common/constants'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../mock'; +import type { + ControlGroupOutput, + ControlGroupInput, + ControlGroupContainer, +} from '@kbn/controls-plugin/public'; +import { OPTIONS_LIST_CONTROL } from '@kbn/controls-plugin/common'; +import { initialInputData, sampleOutputData } from './mocks/data'; +import { createStore } from '../../store'; +import { useGetInitialUrlParamValue } from '../../utils/global_query_string/helpers'; +import { TEST_IDS } from './constants'; +import { + controlGroupFilterInputMock$, + controlGroupFilterOutputMock$, + getControlGroupMock, +} from './mocks/control_group'; +import { getMockedControlGroupRenderer } from './mocks/control_group_renderer'; +import { URL_PARAM_ARRAY_EXCEPTION_MSG } from './translations'; + +jest.mock('../../utils/global_query_string/helpers', () => { + return { + ...jest.requireActual('../../utils/global_query_string/helpers'), + useGetInitialUrlParamValue: jest.fn().mockImplementation(() => () => null), + }; +}); + +jest.mock('../../utils/global_query_string', () => { + return { + ...jest.requireActual('../../utils/global_query_string'), + }; +}); + +jest.mock('../../hooks/use_space_id', () => { + return { + useSpaceId: jest.fn(() => 'test_space_id'), + }; +}); + +const LOCAL_STORAGE_KEY = 'securitySolution.test_space_id.pageFilters'; + +const controlGroupMock = getControlGroupMock(); + +const updateControlGroupInputMock = (newInput: ControlGroupInput) => { + act(() => { + controlGroupFilterInputMock$.next(newInput); + controlGroupMock.getInput.mockReturnValue(newInput); + }); +}; + +const updateControlGroupOutputMock = (newOutput: ControlGroupOutput) => { + controlGroupFilterOutputMock$.next(newOutput); +}; + +const MockedControlGroupRenderer = getMockedControlGroupRenderer( + controlGroupMock as unknown as ControlGroupContainer +); + +jest.mock('@kbn/controls-plugin/public/control_group/external_api/control_group_renderer', () => { + const { forwardRef: fR } = jest.requireActual('react'); + // const { ControlGroupRenderer: MockedCGR } = jest.requireActual( + // '@kbn/controls-plugin/public/control_group/external_api/control_group_renderer' + // ); + return { + _esModule: true, + // @ts-expect-error + ControlGroupRenderer: fR((props, ref) => ), + }; +}); + +const onFilterChangeMock = jest.fn(); +const onInitMock = jest.fn(); + +const state = mockGlobalState; +const { storage } = createSecuritySolutionStorageMock(); + +const getStoreWithCustomState = (newState: typeof state = state) => { + return createStore(newState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); +}; + +const TestComponent: FC> = (props) => ( + + + +); + +const openContextMenu = async () => { + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.BTN)); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.CONTEXT_MENU.RESET)).toBeVisible(); + }); +}; + +describe(' Filter Group Component ', () => { + beforeEach(() => { + jest.clearAllMocks(); + global.localStorage.clear(); + }); + describe('Basic Functions ', () => { + beforeEach(() => { + jest.clearAllMocks(); + global.localStorage.clear(); + }); + it('should render', async () => { + render(); + expect(screen.getByTestId(TEST_IDS.MOCKED_CONTROL)).toBeVisible(); + expect(screen.getByTestId(TEST_IDS.FILTER_CONTROLS)).toBeVisible(); + + expect(onInitMock.mock.calls.length).toBe(1); + expect(onInitMock.mock.calls[0][0]).toMatchObject(controlGroupMock); + }); + + it('should have context menu open when clicked', async () => { + render(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.BTN)); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)).toBeVisible(); + expect(screen.getByTestId(TEST_IDS.CONTEXT_MENU.RESET)).toBeVisible(); + }); + }); + + it('should go into edit mode without any issues', async () => { + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + await openContextMenu(); + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).toBeVisible(); + expect(screen.getByTestId(TEST_IDS.SAVE_CONTROL)).toBeVisible(); + expect(screen.getByTestId(TEST_IDS.SAVE_CONTROL)).toBeDisabled(); + }); + }); + + it('should have add button disable/enable when controls are more/less than max', async () => { + render(); + + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).toBeDisabled(); + }); + + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + await waitFor(() => { + // add button should be enabled now + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).not.toBeDisabled(); + // save button should also be enable since changes have taken place + expect(screen.getByTestId(TEST_IDS.SAVE_CONTROL)).not.toBeDisabled(); + }); + }); + + it('should open flyout when clicked on ADD', async () => { + render(); + + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).toBeDisabled(); + }); + + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + await waitFor(() => { + // add button should be enabled now + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).not.toBeDisabled(); + // save button should also be enable since changes have taken place + expect(screen.getByTestId(TEST_IDS.SAVE_CONTROL)).not.toBeDisabled(); + }); + + fireEvent.click(screen.getByTestId(TEST_IDS.ADD_CONTROL)); + + await waitFor(() => { + expect(controlGroupMock.openAddDataControlFlyout.mock.calls.length).toBe(1); + }); + }); + + it('should call controlGroupTransform which returns object WITHOUT placeholder when type != OPTION_LIST_CONTROL on opening Flyout', async () => { + const returnValueWatcher = jest.fn(); + controlGroupMock.openAddDataControlFlyout.mockImplementationOnce( + ({ controlInputTransform }) => { + if (controlInputTransform) { + const returnValue = controlInputTransform({}, 'NOT_OPTIONS_LIST_CONTROL'); + returnValueWatcher(returnValue); + } + } + ); + render(); + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + await waitFor(() => { + // add button should be enabled now + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).not.toBeDisabled(); + }); + + fireEvent.click(screen.getByTestId(TEST_IDS.ADD_CONTROL)); + + expect(returnValueWatcher.mock.calls[0][0]).not.toMatchObject( + expect.objectContaining({ + placeholder: '', + }) + ); + }); + + it('should call controlGroupTransform which returns object WITH correct placeholder value when type = OPTION_LIST_CONTROL on opening Flyout', async () => { + const returnValueWatcher = jest.fn(); + controlGroupMock.openAddDataControlFlyout.mockImplementationOnce( + ({ controlInputTransform }) => { + if (controlInputTransform) { + const returnValue = controlInputTransform({}, OPTIONS_LIST_CONTROL); + returnValueWatcher(returnValue); + } + } + ); + + render(); + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + await waitFor(() => { + // add button should be enabled now + expect(screen.getByTestId(TEST_IDS.ADD_CONTROL)).not.toBeDisabled(); + }); + + fireEvent.click(screen.getByTestId(TEST_IDS.ADD_CONTROL)); + + expect(returnValueWatcher.mock.calls[0][0]).toMatchObject( + expect.objectContaining({ + placeholder: '', + }) + ); + }); + + it('should save controls successfully', async () => { + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + await openContextMenu(); + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + + // modify controls + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + // clear any previous calls to the API + controlGroupMock.addOptionsListControl.mockClear(); + + fireEvent.click(screen.getByTestId(TEST_IDS.SAVE_CONTROL)); + + await waitFor(() => { + // edit model gone + expect(screen.queryAllByTestId(TEST_IDS.SAVE_CONTROL)).toHaveLength(0); + + // check if upsert was called correctely + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(1); + expect(controlGroupMock.addOptionsListControl.mock.calls[0][0]).toMatchObject({ + ...initialInputData.panels['0'].explicitInput, + }); + }); + }); + + it('should add persitable controls back on save, if deleted', async () => { + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + + // modify controls + const newInputData = { + ...initialInputData, + panels: { + // removed persitable control i.e. status at "0" key + '3': initialInputData.panels['3'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + // clear any previous calls to the API + controlGroupMock.addOptionsListControl.mockClear(); + + fireEvent.click(screen.getByTestId(TEST_IDS.SAVE_CONTROL)); + + await waitFor(() => { + // edit model gone + expect(screen.queryAllByTestId(TEST_IDS.SAVE_CONTROL)).toHaveLength(0); + // check if upsert was called correctely + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(2); + expect(controlGroupMock.addOptionsListControl.mock.calls[0][0]).toMatchObject({ + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + ...DEFAULT_DETECTION_PAGE_FILTERS[0], + }); + expect(controlGroupMock.addOptionsListControl.mock.calls[1][0]).toMatchObject({ + ...initialInputData.panels['3'].explicitInput, + }); + }); + }); + + it('should have Context menu changed when pending changes', async () => { + render(); + + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.SAVE_CHANGE_POPOVER)).toBeVisible(); + }); + + await openContextMenu(); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.CONTEXT_MENU.DISCARD)).toBeVisible(); + }); + }); + + it('should be able to discard changes', async () => { + render(); + + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + // await waitFor(() => { + // expect(screen.getByTestId(TEST_IDS.SAVE_CHANGE_POPOVER)).toBeVisible(); + // }); + await openContextMenu(); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.CONTEXT_MENU.DISCARD)).toBeVisible(); + }); + + controlGroupMock.updateInput.mockClear(); + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.DISCARD)); + + await waitFor(() => { + expect(controlGroupMock.updateInput).toHaveBeenCalled(); + expect(controlGroupMock.updateInput.mock.calls.length).toBe(2); + // discard changes + expect(controlGroupMock.updateInput.mock.calls[0][0]).toMatchObject({ + panels: initialInputData.panels, + }); + + // shift to view mode + expect(controlGroupMock.updateInput.mock.calls[1][0]).toMatchObject({ + viewMode: 'view', + }); + }); + }); + + it('should reset controls on clicking reset', async () => { + render(); + + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.CONTEXT_MENU.RESET)).toBeVisible(); + }); + + controlGroupMock.addOptionsListControl.mockClear(); + controlGroupMock.updateInput.mockClear(); + controlGroupMock.reload.mockClear(); + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.RESET)); + + await waitFor(() => { + // blanks the input + expect(controlGroupMock.updateInput.mock.calls.length).toBe(2); + expect(controlGroupMock.reload.mock.calls.length).toBe(1); + + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(4); + }); + }); + + it('should restore controls saved in local storage', () => { + global.localStorage.setItem( + LOCAL_STORAGE_KEY, + JSON.stringify({ + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + }) + ); + + // should create one control + // + render(); + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(1); + }); + + it('should show/hide pending changes popover on mouseout/mouseover', async () => { + render(); + + updateControlGroupInputMock(initialInputData as ControlGroupInput); + + await openContextMenu(); + + fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.EDIT)); + + // delete some panels + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['0'], + }, + } as ControlGroupInput; + + updateControlGroupInputMock(newInputData); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.SAVE_CHANGE_POPOVER)).toBeVisible(); + }); + + fireEvent.mouseOver(screen.getByTestId(TEST_IDS.SAVE_CONTROL)); + fireEvent.mouseOut(screen.getByTestId(TEST_IDS.SAVE_CONTROL)); + await waitFor(() => { + expect(screen.queryByTestId(TEST_IDS.SAVE_CHANGE_POPOVER)).toBeNull(); + }); + + fireEvent.mouseOver(screen.getByTestId(TEST_IDS.SAVE_CONTROL)); + await waitFor(() => { + expect(screen.queryByTestId(TEST_IDS.SAVE_CHANGE_POPOVER)).toBeVisible(); + }); + }); + }); + + describe('Filter Changed Banner', () => { + beforeEach(() => { + jest.clearAllMocks(); + global.localStorage.clear(); + }); + + it('should show banner if url filter and stored filters are not same', async () => { + (useGetInitialUrlParamValue as jest.Mock).mockImplementationOnce(() => { + return () => [ + { + fieldName: 'abc', + }, + ]; + }); + global.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(initialInputData)); + + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.FILTERS_CHANGED_BANNER)).toBeVisible(); + }); + }); + + it('should use url filters if url and stored filters are not same', async () => { + (useGetInitialUrlParamValue as jest.Mock).mockImplementationOnce(() => { + return () => [ + { + fieldName: 'abc', + }, + ]; + }); + global.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(initialInputData)); + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(2); + expect(controlGroupMock.addOptionsListControl.mock.calls[0][1]).toMatchObject({ + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + ...DEFAULT_DETECTION_PAGE_FILTERS[0], + }); + + expect(controlGroupMock.addOptionsListControl.mock.calls[1][1]).toMatchObject({ + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + fieldName: 'abc', + }); + + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.FILTERS_CHANGED_BANNER)).toBeVisible(); + }); + }); + + it('should ignore url params if there is an error in using them', async () => { + (useGetInitialUrlParamValue as jest.Mock).mockImplementationOnce(() => { + return () => ({ + fieldName: 'abc', + }); + }); + + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementationOnce(jest.fn()); + + render(); + + expect(consoleErrorSpy.mock.calls.length).toBe(1); + expect(String(consoleErrorSpy.mock.calls[0][0])).toMatch(URL_PARAM_ARRAY_EXCEPTION_MSG); + }); + }); + + describe('onFilterChange', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + global.localStorage.clear(); + }); + it('should call onFilterChange when new filters have been published', async () => { + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + updateControlGroupOutputMock(sampleOutputData); + await waitFor(() => { + expect(onFilterChangeMock.mock.calls.length).toBe(1); + expect(onFilterChangeMock.mock.calls[0][0]).toMatchObject(sampleOutputData.filters); + }); + + // updating output should call filter change again with different output + const changedOutput = { ...sampleOutputData, filters: [] }; + updateControlGroupOutputMock(changedOutput); + await waitFor(() => { + expect(onFilterChangeMock.mock.calls[1][0]).toMatchObject(changedOutput.filters); + }); + }); + + it('should pass empty onFilterChange as the initial state. Eg. in case of error', async () => { + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + updateControlGroupOutputMock(sampleOutputData); + + jest.advanceTimersByTime(1000); + updateControlGroupOutputMock({ + ...sampleOutputData, + filters: undefined, + }); + await waitFor(() => { + expect(onFilterChangeMock.mock.calls.length).toBe(2); + expect(onFilterChangeMock.mock.calls[1][0]).toMatchObject([]); + }); + + // updating output should call filter change again with different output + const changedOutput = { ...sampleOutputData, filters: [] }; + updateControlGroupOutputMock(changedOutput); + await waitFor(() => { + expect(onFilterChangeMock.mock.calls[1][0]).toMatchObject(changedOutput.filters); + }); + }); + + it('should not call onFilterChange if same set of filters are published twice', async () => { + render(); + updateControlGroupInputMock(initialInputData as ControlGroupInput); + updateControlGroupOutputMock(sampleOutputData); + + jest.advanceTimersByTime(1000); + + // updating output should call filter change again with different output + const changedOutput = { ...sampleOutputData }; + onFilterChangeMock.mockClear(); + updateControlGroupOutputMock(changedOutput); + await waitFor(() => { + expect(onFilterChangeMock).not.toHaveBeenCalled(); + }); + }); + }); + + describe('Restore from local storage', () => { + beforeEach(() => { + jest.clearAllMocks(); + global.localStorage.clear(); + }); + it('should restore from localstorage when one of the value is exists and exclude is false', async () => { + const savedData = { + ...initialInputData, + panels: { + ...initialInputData.panels, + '2': { + ...initialInputData.panels['2'], + explicitInput: { + ...initialInputData.panels['2'].explicitInput, + existsSelected: true, + exclude: false, + }, + }, + }, + }; + + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(savedData)); + + render(); + + await waitFor(() => { + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(5); + expect(controlGroupMock.addOptionsListControl.mock.calls[2][1]).toMatchObject( + expect.objectContaining({ + existsSelected: true, + exclude: false, + }) + ); + }); + }); + it('should restore from localstorage when one of the value has both exists and exclude true', async () => { + const savedData = { + ...initialInputData, + panels: { + ...initialInputData.panels, + '2': { + ...initialInputData.panels['2'], + explicitInput: { + ...initialInputData.panels['2'].explicitInput, + existsSelected: true, + exclude: true, + }, + }, + }, + }; + + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(savedData)); + + render(); + + await waitFor(() => { + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(5); + expect(controlGroupMock.addOptionsListControl.mock.calls[2][1]).toMatchObject( + expect.objectContaining({ + existsSelected: true, + exclude: true, + }) + ); + }); + }); + it('should restore from localstorage when some value has selected options', async () => { + const savedData = { + ...initialInputData, + panels: { + ...initialInputData.panels, + '2': { + ...initialInputData.panels['2'], + explicitInput: { + ...initialInputData.panels['2'].explicitInput, + selectedOptions: ['abc'], + }, + }, + }, + }; + + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(savedData)); + + render(); + + await waitFor(() => { + expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(5); + expect(controlGroupMock.addOptionsListControl.mock.calls[2][1]).toMatchObject( + expect.objectContaining({ + selectedOptions: ['abc'], + }) + ); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx index 381b5bb8d47c7..98aeacf105347 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/filters_changed_banner.tsx @@ -8,6 +8,7 @@ import { EuiButton, EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { FC } from 'react'; import React from 'react'; +import { TEST_IDS } from './constants'; import { FILTER_GROUP_BANNER_MESSAGE, FILTER_GROUP_BANNER_TITLE, @@ -28,19 +29,22 @@ export const FiltersChangedBanner: FC = ({

    {FILTER_GROUP_BANNER_MESSAGE}

    {SAVE_CHANGES} - + {REVERT_CHANGES}
    diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts index fd59ecd315d34..2462bf75a68bc 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_control_group_sync_to_local_storage.ts @@ -16,7 +16,7 @@ interface UseControlGroupSyncToLocalStorageArgs { } type UseControlGroupSyncToLocalStorage = (args: UseControlGroupSyncToLocalStorageArgs) => { - controlGroupInput: ControlGroupInput; + controlGroupInput: ControlGroupInput | undefined; setControlGroupInput: Dispatch>; getStoredControlGroupInput: () => ControlGroupInput | undefined; }; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/__tests__/use_filters_sync_to_local_storage.test.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters_sync_to_local_storage.test.ts similarity index 97% rename from x-pack/plugins/security_solution/public/common/components/filter_group/hooks/__tests__/use_filters_sync_to_local_storage.test.ts rename to x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters_sync_to_local_storage.test.ts index 703c0442b3086..66cedc5b0ebf5 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/__tests__/use_filters_sync_to_local_storage.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/hooks/use_filters_sync_to_local_storage.test.ts @@ -7,7 +7,7 @@ import type { ControlGroupInput } from '@kbn/controls-plugin/common'; import { renderHook } from '@testing-library/react-hooks'; -import { useControlGroupSyncToLocalStorage } from '../use_control_group_sync_to_local_storage'; +import { useControlGroupSyncToLocalStorage } from './use_control_group_sync_to_local_storage'; const TEST_STORAGE_KEY = 'test_key'; const DEFAULT_STORED_VALUE = { diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx index 49df7a202f9bd..533bf19146dad 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx @@ -6,15 +6,20 @@ */ import type { Filter } from '@kbn/es-query'; -import type { ControlPanelState, OptionsListEmbeddableInput } from '@kbn/controls-plugin/common'; +import type { + ControlInputTransform, + ControlPanelState, + OptionsListEmbeddableInput, +} from '@kbn/controls-plugin/common'; +import { OPTIONS_LIST_CONTROL } from '@kbn/controls-plugin/common'; import type { ControlGroupInput, - controlGroupInputBuilder, + ControlGroupInputBuilder, ControlGroupOutput, ControlGroupContainer, ControlGroupRendererProps, } from '@kbn/controls-plugin/public'; -import { LazyControlGroupRenderer } from '@kbn/controls-plugin/public'; +import { ControlGroupRenderer } from '@kbn/controls-plugin/public'; import type { PropsWithChildren } from 'react'; import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react'; import { ViewMode } from '@kbn/embeddable-plugin/public'; @@ -22,7 +27,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import type { Subscription } from 'rxjs'; import styled from 'styled-components'; import { cloneDeep, debounce, isEqual } from 'lodash'; -import { withSuspense } from '@kbn/shared-ux-utility'; +import type { ControlGroupCreationOptions } from '@kbn/controls-plugin/public/control_group/types'; import { useInitializeUrlParam } from '../../utils/global_query_string'; import { URL_PARAM_KEY } from '../../hooks/use_url_state'; import type { FilterGroupProps, FilterItemObj } from './types'; @@ -39,10 +44,8 @@ import { getFilterItemObjListFromControlInput } from './utils'; import { FiltersChangedBanner } from './filters_changed_banner'; import { FilterGroupContext } from './filter_group_context'; import { NUM_OF_CONTROLS } from './config'; - -type ControlGroupBuilder = typeof controlGroupInputBuilder; - -const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); +import { TEST_IDS } from './constants'; +import { URL_PARAM_ARRAY_EXCEPTION_MSG } from './translations'; const FilterWrapper = styled.div.attrs((props) => ({ className: props.className, @@ -63,7 +66,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { timeRange, filters, query, - chainingSystem = 'HIERARCHICAL', + chainingSystem, initialControls, spaceId, onInit, @@ -115,6 +118,9 @@ const FilterGroupComponent = (props: PropsWithChildren) => { return; } try { + if (!Array.isArray(param)) { + throw new Error(URL_PARAM_ARRAY_EXCEPTION_MSG); + } const storedControlGroupInput = getStoredControlInput(); if (storedControlGroupInput) { const panelsFormatted = getFilterItemObjListFromControlInput(storedControlGroupInput); @@ -154,7 +160,9 @@ const FilterGroupComponent = (props: PropsWithChildren) => { const handleInputUpdates = useCallback( (newInput: ControlGroupInput) => { - if (isEqual(getStoredControlInput(), newInput)) return; + if (isEqual(getStoredControlInput(), newInput)) { + return; + } if (!isEqual(newInput.panels, getStoredControlInput()?.panels) && !isViewMode) { setHasPendingChanges(true); } @@ -197,6 +205,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { const onControlGroupLoadHandler = useCallback( (controlGroupContainer: ControlGroupContainer) => { + if (!controlGroupContainer) return; if (onInit) onInit(controlGroupContainer); setControlGroup(controlGroupContainer); }, @@ -253,7 +262,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { title: item.title, selectedOptions: item.selectedOptions ?? [], existsSelected: item.existsSelected ?? false, - exclude: item.existsSelected, + exclude: item.exclude, })), ]; } @@ -264,7 +273,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { const getCreationOptions: ControlGroupRendererProps['getCreationOptions'] = useCallback( async ( defaultInput: Partial, - { addOptionsListControl }: ControlGroupBuilder + { addOptionsListControl }: ControlGroupInputBuilder ) => { const initialInput: Partial = { ...defaultInput, @@ -297,6 +306,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { return { initialInput, settings: { + fieldFilterPredicate: (f) => f.type !== 'number', showAddButton: false, staticDataViewId: dataViewId ?? '', editorConfig: { @@ -305,7 +315,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { hideAdditionalSettings: true, }, }, - }; + } as ControlGroupCreationOptions; }, [dataViewId, timeRange, filters, chainingSystem, query, selectControlsWithPriority] ); @@ -358,7 +368,6 @@ const FilterGroupComponent = (props: PropsWithChildren) => { }); } }); - orderedPanels.forEach((panel) => { if (panel.explicitInput.fieldName) controlGroup?.addOptionsListControl({ @@ -377,8 +386,22 @@ const FilterGroupComponent = (props: PropsWithChildren) => { setShowFiltersChangedBanner(false); }, [switchToViewMode, upsertPersistableControls]); + const newControlInputTranform: ControlInputTransform = (newInput, controlType) => { + // for any new controls, we want to avoid + // default placeholder + if (controlType === OPTIONS_LIST_CONTROL) { + return { + ...newInput, + placeholder: '', + }; + } + return newInput; + }; + const addControlsHandler = useCallback(() => { - controlGroup?.openAddDataControlFlyout(); + controlGroup?.openAddDataControlFlyout({ + controlInputTransform: newControlInputTranform, + }); }, [controlGroup]); return ( @@ -404,9 +427,9 @@ const FilterGroupComponent = (props: PropsWithChildren) => { {Array.isArray(initialUrlParam) ? ( - + {!controlGroup ? : null} @@ -418,6 +441,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { = NUM_OF_CONTROLS.MAX } /> diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/loading.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/loading.tsx index 0aad27ac2f379..22aed76d991c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/loading.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/loading.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiButton, EuiLoadingChart } from '@elastic/eui'; import styled from 'styled-components'; +import { TEST_IDS } from './constants'; const FilterGroupLoadingButton = styled(EuiButton)` height: 34px; @@ -16,7 +17,7 @@ const FilterGroupLoadingButton = styled(EuiButton)` export const FilterGroupLoading = () => { return ( - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/control_group.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/control_group.ts new file mode 100644 index 0000000000000..77dab4a745cb9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/control_group.ts @@ -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 type { ControlGroupOutput, ControlGroupInput } from '@kbn/controls-plugin/public'; +import { Subject } from 'rxjs'; + +export const controlGroupFilterOutputMock$ = new Subject(); + +export const controlGroupFilterInputMock$ = new Subject(); + +export const getInput$Mock = jest.fn(() => controlGroupFilterInputMock$); + +export const getOutput$Mock = jest.fn(() => controlGroupFilterOutputMock$); + +export const getControlGroupMock = () => { + return { + reload: jest.fn(), + getInput: jest.fn().mockReturnValue({ + viewMode: 'VIEW', + }), + updateInput: jest.fn(), + getOutput$: getOutput$Mock, + getInput$: getInput$Mock, + openAddDataControlFlyout: jest.fn(), + addOptionsListControl: jest.fn(), + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/control_group_renderer.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/control_group_renderer.tsx new file mode 100644 index 0000000000000..d0f7ce5b6e18b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/control_group_renderer.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + AwaitingControlGroupAPI, + ControlGroupContainer, + ControlGroupInputBuilder, + ControlGroupRendererProps, +} from '@kbn/controls-plugin/public'; +import React, { useState, forwardRef, useEffect, useImperativeHandle } from 'react'; +import { TEST_IDS } from '../constants'; +import { getControlGroupMock } from './control_group'; + +export const getMockedControlGroupRenderer = ( + controlGroupContainerMock: ControlGroupContainer | undefined +) => { + const controlGroupMock = controlGroupContainerMock ?? getControlGroupMock(); + + const MockedControlGroupRenderer = forwardRef( + ({ getCreationOptions }, ref) => { + useImperativeHandle(ref, () => controlGroupMock as unknown as ControlGroupContainer, []); + const [creationOptionsCalled, setCreationOptionsCalled] = useState(false); + + useEffect(() => { + if (creationOptionsCalled) return; + setCreationOptionsCalled(true); + if (getCreationOptions) { + getCreationOptions({}, { + addOptionsListControl: controlGroupMock.addOptionsListControl, + } as unknown as ControlGroupInputBuilder); + } + }, [getCreationOptions, creationOptionsCalled]); + return
    ; + } + ); + + MockedControlGroupRenderer.displayName = 'MockedControlGroup'; + return MockedControlGroupRenderer; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/data.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/data.ts new file mode 100644 index 0000000000000..75c7aee0fe0e9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/data.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. + */ + +export const sampleOutputData = { + loading: false, + rendered: true, + dataViewIds: ['security-solution-default'], + embeddableLoaded: { + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + }, + filters: [ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.building_block_type', + negate: true, + }, + query: { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + }, + ], +}; + +export const initialInputData = { + viewMode: 'view', + id: 'f9e81d5a-f6ab-4179-866d-c029554131be', + panels: { + '0': { + type: 'optionsListControl', + order: 0, + grow: true, + width: 'small', + explicitInput: { + id: '0', + dataViewId: 'security-solution-default', + fieldName: 'kibana.alert.workflow_status', + title: 'Status', + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + selectedOptions: [], + existsSelected: false, + exclude: false, + }, + }, + '1': { + type: 'optionsListControl', + order: 1, + grow: true, + width: 'small', + explicitInput: { + id: '1', + dataViewId: 'security-solution-default', + fieldName: 'kibana.alert.severity', + title: 'Severity', + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + selectedOptions: [], + existsSelected: false, + exclude: false, + }, + }, + '2': { + type: 'optionsListControl', + order: 2, + grow: true, + width: 'small', + explicitInput: { + id: '2', + dataViewId: 'security-solution-default', + fieldName: 'kibana.alert.building_block_type', + title: 'Bulding Block', + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + selectedOptions: [], + existsSelected: true, + exclude: true, + }, + }, + '3': { + type: 'optionsListControl', + order: 3, + grow: true, + width: 'small', + explicitInput: { + id: '3', + dataViewId: 'security-solution-default', + fieldName: 'user.name', + title: 'User', + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + selectedOptions: [], + existsSelected: false, + exclude: false, + }, + }, + '4': { + type: 'optionsListControl', + order: 4, + grow: true, + width: 'small', + explicitInput: { + id: '4', + dataViewId: 'security-solution-default', + fieldName: 'host.name', + title: 'Host', + hideExclude: true, + hideSort: true, + hidePanelTitles: true, + placeholder: '', + selectedOptions: [], + existsSelected: false, + exclude: false, + }, + }, + }, + defaultControlWidth: 'small', + defaultControlGrow: true, + controlStyle: 'oneLine', + chainingSystem: 'HIERARCHICAL', + ignoreParentSettings: { + ignoreFilters: false, + ignoreQuery: false, + ignoreTimerange: false, + ignoreValidations: false, + }, + timeRange: { + from: '2007-04-20T14:00:52.236Z', + to: '2023-04-20T21:59:59.999Z', + mode: 'absolute', + }, + filters: [], + query: { + query: '', + language: 'kuery', + }, +}; diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx new file mode 100644 index 0000000000000..3fa2ff03318cb --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx @@ -0,0 +1,78 @@ +/* + * 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 { ControlGroupContainer } from '@kbn/controls-plugin/public'; +import type { Filter } from '@kbn/es-query'; +import type { FC } from 'react'; +import React from 'react'; +import { TEST_IDS } from '../constants'; +import type { FilterGroupProps } from '../types'; +import { getControlGroupMock } from './control_group'; + +/** + * + * Retrieves a simple of FilterGroup Item. + * - Can also call callback onFilterChange with custom args + * + * @example + * const onFilterchangeMock = jest.fn(); + * const onInitMock = jest.fn(); + * + *const TestComponent = () => ( + * + * + * + *); + * + *jest.mock('..'); + * + *describe('Some test', () => { + * it('basic test', () => { + * (FilterGroup as jest.Mock).mockImplementationOnce( + * getMockedFilterGroupWithCustomFilters([ + * { + * meta: { + * params: ['open'], + * }, + * }, + * ]) + * ); + * + * render(); + * + * expect(onFilterchangeMock.mock.calls[0][0]).toMatchObject([ + * { + * meta: { + * params: ['open'], + * }, + * }, + * ]); + * }); + *}); + * + */ +export function getMockedFilterGroupWithCustomFilters(outputFilters: Filter[] | undefined) { + const FilterGroup: FC = ({ onInit, onFilterChange }) => { + if (onInit) { + onInit(getControlGroupMock() as unknown as ControlGroupContainer); + } + + if (onFilterChange) { + onFilterChange(outputFilters ?? []); + } + + return
    ; + }; + + return FilterGroup; +} diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts index 488400ebd5ef4..dd90c7acf71b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/translations.ts @@ -98,3 +98,10 @@ export const ADD_CONTROLS_MAX_LIMIT = i18n.translate( defaultMessage: 'Maximum of 4 controls can be added.', } ); + +export const URL_PARAM_ARRAY_EXCEPTION_MSG = i18n.translate( + 'xpack.securitySolution.filtersGroup.urlParam.arrayError', + { + defaultMessage: 'Page filter URL Params must be an array', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/utils.test.ts b/x-pack/plugins/security_solution/public/common/components/filter_group/utils.test.ts new file mode 100644 index 0000000000000..edcffd7502df9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/utils.test.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 { ControlGroupInput } from '@kbn/controls-plugin/common'; +import { getFilterItemObjListFromControlInput } from './utils'; +import { initialInputData } from './mocks/data'; + +describe('utils', () => { + describe('getFilterItemObjListFromControlOutput', () => { + it('should return ordered filterItem where passed in order', () => { + const filterItemObjList = getFilterItemObjListFromControlInput( + initialInputData as ControlGroupInput + ); + + filterItemObjList.forEach((item, idx) => { + const panelObj = + initialInputData.panels[String(idx) as keyof typeof initialInputData.panels] + .explicitInput; + expect(item).toMatchObject({ + fieldName: panelObj.fieldName, + selectedOptions: panelObj.selectedOptions, + title: panelObj.title, + existsSelected: panelObj.existsSelected, + exclude: panelObj.exclude, + }); + }); + }); + + it('should return ordered filterItem where NOT passed in order', () => { + const newInputData = { + ...initialInputData, + panels: { + '0': initialInputData.panels['3'], + '1': initialInputData.panels['0'], + }, + }; + const filterItemObjList = getFilterItemObjListFromControlInput( + newInputData as ControlGroupInput + ); + + let panelObj = newInputData.panels['1'].explicitInput; + expect(filterItemObjList[0]).toMatchObject({ + fieldName: panelObj.fieldName, + selectedOptions: panelObj.selectedOptions, + title: panelObj.title, + existsSelected: panelObj.existsSelected, + exclude: panelObj.exclude, + }); + + panelObj = newInputData.panels['0'].explicitInput; + expect(filterItemObjList[1]).toMatchObject({ + fieldName: panelObj.fieldName, + selectedOptions: panelObj.selectedOptions, + title: panelObj.title, + existsSelected: panelObj.existsSelected, + exclude: panelObj.exclude, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx index 62da8f750c78f..d7e887d54ea6e 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx @@ -383,7 +383,8 @@ describe('Actions', () => { ...mockTimelineData[0].ecs, event: { kind: ['alert'] }, agent: { type: ['endpoint'] }, - process: { entry_leader: { entity_id: ['test_id'] } }, + process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } }, + _index: '.ds-logs-endpoint.events.process-default', }; const wrapper = mount( @@ -400,7 +401,8 @@ describe('Actions', () => { ...mockTimelineData[0].ecs, event: { kind: ['alert'] }, agent: { type: ['endpoint'] }, - process: { entry_leader: { entity_id: ['test_id'] } }, + process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } }, + _index: '.ds-logs-endpoint.events.process-default', }; const wrapper = mount( @@ -425,7 +427,8 @@ describe('Actions', () => { ...mockTimelineData[0].ecs, event: { kind: ['alert'] }, agent: { type: ['endpoint'] }, - process: { entry_leader: { entity_id: ['test_id'] } }, + process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } }, + _index: '.ds-logs-endpoint.events.process-default', }; const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx index 5f53d14ff8219..93edfc65effb5 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.tsx @@ -39,7 +39,7 @@ import { useTourContext } from '../guided_onboarding_tour'; import { AlertsCasesTourSteps, SecurityStepId } from '../guided_onboarding_tour/tour_config'; import { isDetectionsAlertsTable } from '../top_n/helpers'; import { GuidedOnboardingTourStep } from '../guided_onboarding_tour/tour_step'; -import { DEFAULT_ACTION_BUTTON_WIDTH, isAlert } from './helpers'; +import { DEFAULT_ACTION_BUTTON_WIDTH, isAlert, getSessionViewProcessIndex } from './helpers'; const ActionsContainer = styled.div` align-items: center; @@ -149,10 +149,16 @@ const ActionsComponent: React.FC = ({ ]); const sessionViewConfig = useMemo(() => { - const { process, _id, timestamp } = ecsData; + const { process, _id, _index, timestamp, kibana } = ecsData; const sessionEntityId = process?.entry_leader?.entity_id?.[0]; + const sessionStartTime = process?.entry_leader?.start?.[0]; + const processIndex = getSessionViewProcessIndex(kibana?.alert?.ancestors?.index?.[0] || _index); - if (sessionEntityId === undefined) { + if ( + processIndex === undefined || + sessionEntityId === undefined || + sessionStartTime === undefined + ) { return null; } @@ -162,7 +168,9 @@ const ActionsComponent: React.FC = ({ (investigatedAlertId && ecsData.kibana?.alert.original_time?.[0]) || timestamp; return { + processIndex, sessionEntityId, + sessionStartTime, jumpToEntityId, jumpToCursor, investigatedAlertId, diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.test.ts index 6edd44ba74eb1..f7b9b33ae63fb 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.test.ts @@ -6,7 +6,12 @@ */ import { euiThemeVars } from '@kbn/ui-theme'; -import { DEFAULT_ACTION_BUTTON_WIDTH, getActionsColumnWidth, isAlert } from './helpers'; +import { + DEFAULT_ACTION_BUTTON_WIDTH, + getActionsColumnWidth, + isAlert, + getSessionViewProcessIndex, +} from './helpers'; describe('isAlert', () => { test('it returns true when the eventType is an alert', () => { @@ -48,3 +53,67 @@ describe('getActionsColumnWidth', () => { ); }); }); + +describe('getSessionViewProcessIndex', () => { + test('it returns process index for cloud_defend alert event index', () => { + const result = getSessionViewProcessIndex( + '.ds-logs-cloud_defend.alerts-default-2023.04.25-000001' + ); + + expect(result).toEqual('logs-cloud_defend.process*'); + }); + + test('it returns process index for cloud_defend file event index', () => { + const result = getSessionViewProcessIndex( + '.ds-logs-cloud_defend.file-default-2023.04.25-000001' + ); + + expect(result).toEqual('logs-cloud_defend.process*'); + }); + + test('it returns process index for cloud_defend process event index', () => { + const result = getSessionViewProcessIndex( + '.ds-logs-cloud_defend.process-default-2023.04.25-000001' + ); + + expect(result).toEqual('logs-cloud_defend.process*'); + }); + + test('it returns process index for cloud_defend that includes cluster', () => { + const result = getSessionViewProcessIndex( + 'aws_ec2:.ds-logs-cloud_defend.process-default-2023.04.25-000001' + ); + + expect(result).toEqual('aws_ec2:logs-cloud_defend.process*'); + }); + + test('it returns process index for endpoint file index', () => { + const result = getSessionViewProcessIndex( + '.ds-logs-endpoint.events.file-default-2023.04.25-000001' + ); + + expect(result).toEqual('logs-endpoint.events.process*'); + }); + + test('it returns process index for endpoint alerts index', () => { + const result = getSessionViewProcessIndex('.ds-logs-endpoint.alerts-default-2023.04.25-000001'); + + expect(result).toEqual('logs-endpoint.events.process*'); + }); + + test('it returns process index for endpoint process index', () => { + const result = getSessionViewProcessIndex( + '.ds-logs-endpoint.events.process-default-2023.04.25-000001' + ); + + expect(result).toEqual('logs-endpoint.events.process*'); + }); + + test('it returns process index for endpoint that includes cluster', () => { + const result = getSessionViewProcessIndex( + 'azure-01:.ds-logs-endpoint.events.process-default-2023.04.25-000001' + ); + + expect(result).toEqual('azure-01:logs-endpoint.events.process*'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.ts b/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.ts index 7530b591dac73..2eeb865e88202 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/helpers.ts @@ -50,3 +50,23 @@ export const getActionsColumnWidth = (actionButtonCount: number): number => { return contentWidth + leftRightCellPadding; }; + +// Currently both logs-endpoint.events.process* and logs-cloud_defend.process* are valid sources for session data. +// To avoid cross cluster searches, the original index of the event is used to infer the index to find data for the +// rest of the session. +export const getSessionViewProcessIndex = (eventIndex?: string | null) => { + if (!eventIndex) { + return; + } + + const match = eventIndex.match(/([a-z0-9_-]+:)?\.ds-logs-(endpoint|cloud_defend)/i); + const cluster = match?.[1]; + const clusterStr = cluster ? `${cluster}` : ''; + const service = match?.[2]; + + if (service === 'endpoint') { + return `${clusterStr}logs-endpoint.events.process*`; + } else if (service === 'cloud_defend') { + return `${clusterStr}logs-cloud_defend.process*`; + } +}; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap index 941078e41f917..4cc7aad784cc8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap @@ -10,7 +10,7 @@ exports[`entity_draggable renders correctly against snapshot 1`] = ` "value": "entity-value", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > diff --git a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.test.ts b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.test.ts index fa193a50afc8a..f4959bb223155 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.test.ts @@ -162,14 +162,9 @@ describe('useNotableAnomaliesSearch', () => { await waitForNextUpdate(); const names = result.current.data.map(({ name }) => name); - expect(names).toEqual([ - firstJobSecurityName, - secondJobSecurityName, - 'packetbeat_dns_tunneling', - 'packetbeat_rare_dns_question', - 'packetbeat_rare_server_domain', - 'suspicious_login_activity', - ]); + + expect(names[0]).toEqual(firstJobSecurityName); + expect(names[1]).toEqual(secondJobSecurityName); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx index 00a8c0cdb3f39..a50ea1e1f47f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx @@ -23,7 +23,7 @@ export const EntityComponent: React.FC = ({ entityName, entityValue }) => aggregatable: true, }} triggerId={SecurityCellActionsTrigger.DEFAULT} - mode={CellActionsMode.HOVER} + mode={CellActionsMode.HOVER_DOWN} visibleCellActions={5} > {`${entityName}: "${entityValue}"`} diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap index 08e1bbe2bfc80..2d9d6a69af1f0 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap @@ -10,7 +10,7 @@ exports[`draggable_score renders correctly against snapshot 1`] = ` "value": "du", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > @@ -28,7 +28,7 @@ exports[`draggable_score renders correctly against snapshot when the index is no "value": "du", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx index 3ad058dd2ab33..4e5fc8b29190b 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx @@ -26,7 +26,7 @@ export const ScoreComponent = ({ return ( ( {children} ); +const moduleId = 'test_module_id'; +const jobId = 'test_job_id'; + const TIMESTAMP = 99999999; const JOB = { + id: jobId, isInstalled: false, + isElasticJob: true, + moduleId, datafeedState: 'failed', jobState: 'failed', isCompatible: true, } as SecurityJob; -const mockSetupMlJob = jest.fn().mockReturnValue(Promise.resolve()); -const mockStartDatafeeds = jest.fn().mockReturnValue(Promise.resolve()); -const mockStopDatafeeds = jest.fn().mockReturnValue(Promise.resolve()); +const mockSetupMlJob = jest.fn(); +const mockStartDatafeeds = jest.fn(); +const mockStopDatafeeds = jest.fn(); jest.mock('../api', () => ({ setupMlJob: () => mockSetupMlJob(), @@ -45,95 +53,283 @@ jest.mock('../api', () => ({ stopDatafeeds: () => mockStopDatafeeds(), })); +const mockedTelemetry = createTelemetryServiceMock(); +jest.mock('../../../lib/kibana', () => { + const original = jest.requireActual('../../../lib/kibana'); + + return { + ...original, + useKibana: () => ({ + services: { + telemetry: mockedTelemetry, + }, + }), + }; +}); + describe('useSecurityJobsHelpers', () => { afterEach(() => { - mockSetupMlJob.mockReset(); mockStartDatafeeds.mockReset(); mockStopDatafeeds.mockReset(); - }); + mockSetupMlJob.mockReset(); - it('renders isLoading=true when installing job', async () => { - let resolvePromiseCb: (value: unknown) => void; - mockSetupMlJob.mockReturnValue( - new Promise((resolve) => { - resolvePromiseCb = resolve; - }) + mockStartDatafeeds.mockReturnValue( + Promise.resolve({ [`datafeed-${jobId}`]: { started: true } }) + ); + mockStopDatafeeds.mockReturnValue( + Promise.resolve([{ [`datafeed-${jobId}`]: { stopped: true } }]) ); - const { result, waitForNextUpdate } = renderHook(() => useEnableDataFeed(), { - wrapper, + mockSetupMlJob.mockReturnValue(Promise.resolve()); + }); + + describe('enableDatafeed', () => { + it('renders isLoading=true when installing job', async () => { + let resolvePromiseCb: (value: unknown) => void; + mockSetupMlJob.mockReturnValue( + new Promise((resolve) => { + resolvePromiseCb = resolve; + }) + ); + const { result, waitForNextUpdate } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + expect(result.current.isLoading).toBe(false); + + await act(async () => { + const enableDataFeedPromise = result.current.enableDatafeed(JOB, TIMESTAMP); + + await waitForNextUpdate(); + expect(result.current.isLoading).toBe(true); + + resolvePromiseCb({}); + await enableDataFeedPromise; + expect(result.current.isLoading).toBe(false); + }); }); - expect(result.current.isLoading).toBe(false); - await act(async () => { - const enableDataFeedPromise = result.current.enableDatafeed(JOB, TIMESTAMP, false); + it('does not call setupMlJob if job is already installed', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); - await waitForNextUpdate(); - expect(result.current.isLoading).toBe(true); + await act(async () => { + await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP); + }); - resolvePromiseCb({}); - await enableDataFeedPromise; - expect(result.current.isLoading).toBe(false); + expect(mockSetupMlJob).not.toBeCalled(); }); - }); - it('does not call setupMlJob if job is already installed', async () => { - mockSetupMlJob.mockReturnValue(Promise.resolve()); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, + it('calls setupMlJob if job is uninstalled', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed({ ...JOB, isInstalled: false }, TIMESTAMP); + }); + expect(mockSetupMlJob).toBeCalled(); }); - await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP, false); + it('calls startDatafeeds when enableDatafeed is called', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + expect(mockStartDatafeeds).toBeCalled(); + expect(mockStopDatafeeds).not.toBeCalled(); }); - expect(mockSetupMlJob).not.toBeCalled(); - }); + it('calls startDatafeeds with 2 weeks old start date', async () => { + jest.useFakeTimers().setSystemTime(new Date('1989-03-07')); - it('calls setupMlJob if job is uninstalled', async () => { - mockSetupMlJob.mockReturnValue(Promise.resolve()); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + expect(mockStartDatafeeds).toBeCalledWith({ + datafeedIds: [`datafeed-test_job_id`], + start: new Date('1989-02-21').getTime(), + }); }); - await act(async () => { - await result.current.enableDatafeed({ ...JOB, isInstalled: false }, TIMESTAMP, false); + + it('return enabled:true when startDataFeed successfully installed the job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.enableDatafeed(JOB, TIMESTAMP); + expect(response.enabled).toBeTruthy(); + }); }); - expect(mockSetupMlJob).toBeCalled(); - }); - it('calls startDatafeeds if enable param is true', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, + it('return enabled:false when startDataFeed promise is rejected while installing a job', async () => { + mockStartDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.enableDatafeed(JOB, TIMESTAMP); + expect(response.enabled).toBeFalsy(); + }); + }); + + it('return enabled:false when startDataFeed failed to install the job', async () => { + mockStartDatafeeds.mockReturnValue( + Promise.resolve({ [`datafeed-${jobId}`]: { started: false, error: 'test_error' } }) + ); + + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.enableDatafeed(JOB, TIMESTAMP); + expect(response.enabled).toBeFalsy(); + }); }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, true); + + describe('telemetry', () => { + it('reports telemetry when installing and enabling a job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.moduleInstalled, + isElasticJob: true, + jobId, + moduleId, + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.started, + isElasticJob: true, + jobId, + }); + }); + + it('reports telemetry when starting a job fails', async () => { + mockStartDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.startError, + errorMessage: 'Start job failure - test_error', + isElasticJob: true, + jobId, + }); + }); + + it('reports telemetry when installing a module fails', async () => { + mockSetupMlJob.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.enableDatafeed(JOB, TIMESTAMP); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.installationError, + errorMessage: 'Create job failure - test_error', + isElasticJob: true, + jobId, + moduleId, + }); + }); }); - expect(mockStartDatafeeds).toBeCalled(); - expect(mockStopDatafeeds).not.toBeCalled(); }); - it('calls stopDatafeeds if enable param is false', async () => { - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, + describe('disableDatafeed', () => { + it('return enabled:false when disableDatafeed successfully uninstalled the job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.disableDatafeed(JOB); + expect(response.enabled).toBeFalsy(); + }); }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, false); + + it('return enabled:true when promise is rejected while uninstalling the job', async () => { + mockStopDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.disableDatafeed(JOB); + expect(response.enabled).toBeTruthy(); + }); }); - expect(mockStartDatafeeds).not.toBeCalled(); - expect(mockStopDatafeeds).toBeCalled(); - }); - it('calls startDatafeeds with 2 weeks old start date', async () => { - jest.useFakeTimers().setSystemTime(new Date('1989-03-07')); + it('return enabled:true when disableDatafeed fails to uninstall the job', async () => { + mockStopDatafeeds.mockReturnValue( + Promise.resolve([{ [`datafeed-${jobId}`]: { stopped: false, error: 'test_error' } }]) + ); - const { result } = renderHook(() => useEnableDataFeed(), { - wrapper, + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + const response = await result.current.disableDatafeed(JOB); + expect(response.enabled).toBeTruthy(); + }); }); - await act(async () => { - await result.current.enableDatafeed(JOB, TIMESTAMP, true); + + it('calls stopDatafeeds when disableDatafeed is called', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.disableDatafeed(JOB); + }); + expect(mockStartDatafeeds).not.toBeCalled(); + expect(mockStopDatafeeds).toBeCalled(); }); - expect(mockStartDatafeeds).toBeCalledWith({ - datafeedIds: [`datafeed-undefined`], - start: new Date('1989-02-21').getTime(), + + describe('telemetry', () => { + it('reports telemetry when stopping a job', async () => { + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.disableDatafeed({ ...JOB, isInstalled: true }); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.stopped, + isElasticJob: true, + jobId, + }); + }); + + it('reports telemetry when stopping a job fails', async () => { + mockStopDatafeeds.mockReturnValue(Promise.reject(new Error('test_error'))); + const { result } = renderHook(() => useEnableDataFeed(), { + wrapper, + }); + await act(async () => { + await result.current.disableDatafeed({ ...JOB, isInstalled: true }); + }); + + expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + status: ML_JOB_TELEMETRY_STATUS.stopError, + errorMessage: 'Stop job failure - test_error', + isElasticJob: true, + jobId, + }); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts index 2d73d07c1787c..393e132436c38 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts @@ -5,24 +5,37 @@ * 2.0. */ +import { isEmpty } from 'lodash/fp'; import { useCallback, useState } from 'react'; import { useAppToasts } from '../../../hooks/use_app_toasts'; -import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../../lib/telemetry'; +import { useKibana } from '../../../lib/kibana'; +import { + METRIC_TYPE, + ML_JOB_TELEMETRY_STATUS, + TELEMETRY_EVENT, + track, +} from '../../../lib/telemetry'; + import { setupMlJob, startDatafeeds, stopDatafeeds } from '../api'; -import type { SecurityJob } from '../types'; +import type { ErrorResponse, SecurityJob } from '../types'; import * as i18n from './translations'; -// Enable/Disable Job & Datafeed -- passed to JobsTable for use as callback on JobSwitch +// Enable/Disable Job & Datafeed export const useEnableDataFeed = () => { + const { telemetry } = useKibana().services; + const { addError } = useAppToasts(); const [isLoading, setIsLoading] = useState(false); const enableDatafeed = useCallback( - async (job: SecurityJob, latestTimestampMs: number, enable: boolean) => { - submitTelemetry(job, enable); + async (job: SecurityJob, latestTimestampMs: number) => { + setIsLoading(true); + track( + METRIC_TYPE.COUNT, + job.isElasticJob ? TELEMETRY_EVENT.SIEM_JOB_ENABLED : TELEMETRY_EVENT.CUSTOM_JOB_ENABLED + ); if (!job.isInstalled) { - setIsLoading(true); try { await setupMlJob({ configTemplate: job.moduleId, @@ -30,11 +43,24 @@ export const useEnableDataFeed = () => { jobIdErrorFilter: [job.id], groups: job.groups, }); - setIsLoading(false); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + moduleId: job.moduleId, + status: ML_JOB_TELEMETRY_STATUS.moduleInstalled, + }); } catch (error) { - addError(error, { title: i18n.CREATE_JOB_FAILURE }); setIsLoading(false); - return; + addError(error, { title: i18n.CREATE_JOB_FAILURE }); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + moduleId: job.moduleId, + status: ML_JOB_TELEMETRY_STATUS.installationError, + errorMessage: `${i18n.CREATE_JOB_FAILURE} - ${error.message}`, + }); + + return { enabled: false }; } } @@ -42,41 +68,89 @@ export const useEnableDataFeed = () => { const date = new Date(); const maxStartTime = date.setDate(date.getDate() - 14); - setIsLoading(true); - if (enable) { - const startTime = Math.max(latestTimestampMs, maxStartTime); - try { - await startDatafeeds({ datafeedIds: [`datafeed-${job.id}`], start: startTime }); - } catch (error) { - track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_ENABLE_FAILURE); - addError(error, { title: i18n.START_JOB_FAILURE }); - } - } else { - try { - await stopDatafeeds({ datafeedIds: [`datafeed-${job.id}`] }); - } catch (error) { - track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_DISABLE_FAILURE); - addError(error, { title: i18n.STOP_JOB_FAILURE }); + const datafeedId = `datafeed-${job.id}`; + + const startTime = Math.max(latestTimestampMs, maxStartTime); + + try { + const response = await startDatafeeds({ + datafeedIds: [datafeedId], + start: startTime, + }); + + if (response[datafeedId]?.error) { + throw new Error(response[datafeedId].error); } + + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.started, + }); + + return { enabled: response[datafeedId] ? response[datafeedId].started : false }; + } catch (error) { + track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_ENABLE_FAILURE); + addError(error, { title: i18n.START_JOB_FAILURE }); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.startError, + errorMessage: `${i18n.START_JOB_FAILURE} - ${error.message}`, + }); + } finally { + setIsLoading(false); } - setIsLoading(false); + + return { enabled: false }; }, - [addError] + [addError, telemetry] ); - return { enableDatafeed, isLoading }; -}; + const disableDatafeed = useCallback( + async (job: SecurityJob) => { + track( + METRIC_TYPE.COUNT, + job.isElasticJob ? TELEMETRY_EVENT.SIEM_JOB_DISABLED : TELEMETRY_EVENT.CUSTOM_JOB_DISABLED + ); + setIsLoading(true); + + const datafeedId = `datafeed-${job.id}`; + + try { + const [response] = await stopDatafeeds({ datafeedIds: [datafeedId] }); -const submitTelemetry = (job: SecurityJob, enabled: boolean) => { - // Report type of job enabled/disabled - track( - METRIC_TYPE.COUNT, - job.isElasticJob - ? enabled - ? TELEMETRY_EVENT.SIEM_JOB_ENABLED - : TELEMETRY_EVENT.SIEM_JOB_DISABLED - : enabled - ? TELEMETRY_EVENT.CUSTOM_JOB_ENABLED - : TELEMETRY_EVENT.CUSTOM_JOB_DISABLED + if (isErrorResponse(response)) { + throw new Error(response.error); + } + + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.stopped, + }); + + return { enabled: response[datafeedId] ? !response[datafeedId].stopped : true }; + } catch (error) { + track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_DISABLE_FAILURE); + addError(error, { title: i18n.STOP_JOB_FAILURE }); + telemetry.reportMLJobUpdate({ + jobId: job.id, + isElasticJob: job.isElasticJob, + status: ML_JOB_TELEMETRY_STATUS.stopError, + errorMessage: `${i18n.STOP_JOB_FAILURE} - ${error.message}`, + }); + } finally { + setIsLoading(false); + } + + return { enabled: true }; + }, + [addError, telemetry] ); + + return { enableDatafeed, disableDatafeed, isLoading }; }; + +const isErrorResponse = (response: ErrorResponse): response is ErrorResponse => + !isEmpty(response.error); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx index d518a42f2907d..5f3b834d777b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx @@ -59,14 +59,22 @@ export const MlPopover = React.memo(() => { } = useSecurityJobs(); const docLinks = useKibana().services.docLinks; - const { enableDatafeed, isLoading: isLoadingEnableDataFeed } = useEnableDataFeed(); + const { + enableDatafeed, + disableDatafeed, + isLoading: isLoadingEnableDataFeed, + } = useEnableDataFeed(); const handleJobStateChange = useCallback( async (job: SecurityJob, latestTimestampMs: number, enable: boolean) => { - const result = await enableDatafeed(job, latestTimestampMs, enable); + if (enable) { + await enableDatafeed(job, latestTimestampMs); + } else { + await disableDatafeed(job); + } + refreshJobs(); - return result; }, - [refreshJobs, enableDatafeed] + [refreshJobs, enableDatafeed, disableDatafeed] ); const filteredJobs = filterJobs({ diff --git a/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx index 6705cc9b871fe..94348668e89e3 100644 --- a/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx @@ -45,7 +45,7 @@ export const getRowItemsWithActions = ({ return ( = ({ value, }) => { const { uiSettings } = useKibana().services; - const { from, deleteQuery, setQuery, to } = useGlobalTime(false); + const { from, deleteQuery, setQuery, to } = useGlobalTime(); const options = getOptions(isActiveTimeline(scopeId ?? '') ? activeTimelineEventType : undefined); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap index b6177143f024c..8a37b8b9fe54b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap @@ -184,7 +184,7 @@ Object { "color": "#aa6556", "fill": "none", "forAccessor": "1dd5663b-f062-43f8-8688-fc8166c2ca8e", - "icon": "warning", + "icon": "alert", "iconPosition": "left", "lineWidth": 2, "textVisibility": true, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts index 08cd3d131d166..97dbe4ea17e48 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts @@ -6,6 +6,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; +import type { XYState } from '@kbn/lens-plugin/public'; import { wrapper } from '../../../mocks'; import { useLensAttributes } from '../../../use_lens_attributes'; @@ -56,4 +57,42 @@ describe('getRiskScoreOverTimeAreaAttributes', () => { expect(result?.current).toMatchSnapshot(); }); + + it('should render a Reference Line with an Alert icon', () => { + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getRiskScoreOverTimeAreaAttributes, + stackByField: 'host', + extraOptions: { + spaceId: 'mockSpaceId', + }, + }), + { wrapper } + ); + + expect( + (result?.current?.state.visualization as XYState).layers.find( + (layer) => layer.layerType === 'referenceLine' + ) + ).toEqual( + expect.objectContaining({ + layerId: '1dd5663b-f062-43f8-8688-fc8166c2ca8e', + layerType: 'referenceLine', + accessors: ['1dd5663b-f062-43f8-8688-fc8166c2ca8e'], + yConfig: [ + { + forAccessor: '1dd5663b-f062-43f8-8688-fc8166c2ca8e', + axisMode: 'left', + lineWidth: 2, + color: '#aa6556', + icon: 'alert', + textVisibility: true, + fill: 'none', + iconPosition: 'left', + }, + ], + }) + ); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts index 8c5981d6e4a06..b100e5042a33a 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts @@ -56,7 +56,7 @@ export const getRiskScoreOverTimeAreaAttributes: GetLensAttributes = ( axisMode: 'left', lineWidth: 2, color: '#aa6556', - icon: 'warning', + icon: 'alert', textVisibility: true, fill: 'none', iconPosition: 'left', diff --git a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.test.ts b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.test.ts deleted file mode 100644 index b1a21d9ae492d..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.test.ts +++ /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 { renderHook } from '@testing-library/react-hooks'; - -import { TestProviders } from '../../mock'; -import { useAlertPrevalence } from './use_alert_prevalence'; -import { useGlobalTime } from '../use_global_time'; - -const from = '2022-07-28T08:20:18.966Z'; -const to = '2022-07-28T08:20:18.966Z'; -jest.mock('../use_global_time', () => { - const actual = jest.requireActual('../use_global_time'); - return { - ...actual, - useGlobalTime: jest - .fn() - .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }), - }; -}); - -describe('useAlertPrevalence', () => { - beforeEach(() => jest.resetAllMocks()); - - it('invokes useGlobalTime() with false to prevent global queries from being deleted when the component unmounts', () => { - renderHook( - () => - useAlertPrevalence({ - field: 'host.name', - value: ['Host-byc3w6qlpo'], - isActiveTimelines: false, - signalIndexName: null, - includeAlertIds: false, - }), - { - wrapper: TestProviders, - } - ); - - expect(useGlobalTime).toBeCalledWith(false); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.ts b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.ts index cbb6d144b055d..3e98e067bfe2d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.ts +++ b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence.ts @@ -44,7 +44,7 @@ export const useAlertPrevalence = ({ const timelineTime = useDeepEqualSelector((state) => inputsSelectors.timelineTimeRangeSelector(state) ); - const globalTime = useGlobalTime(false); + const globalTime = useGlobalTime(); let to: string | undefined; let from: string | undefined; if (ignoreTimerange === false) { @@ -68,13 +68,7 @@ export const useAlertPrevalence = ({ if (data) { const buckets = data.aggregations?.[ALERT_PREVALENCE_AGG]?.buckets; if (buckets && buckets.length > 0) { - /** - * Currently for array fields like `process.args` or potentially any `ip` fields - * We show the combined count of all occurences of the value, even though those values - * could be shared across multiple documents. To make this clearer, we should separate - * these values into separate table rows - */ - count = buckets?.reduce((sum, bucket) => sum + (bucket?.doc_count ?? 0), 0); + count = buckets[0].doc_count; } } @@ -132,31 +126,23 @@ const generateAlertPrevalenceQuery = ( }; } + // If we search for the prevalence of a field that has multiple values (e.g. process.args), + // we want to find alerts with the exact same values. if (Array.isArray(value) && value.length > 1) { - const shouldValues = value.map((val) => ({ match: { [field]: val } })); query = { bool: { - minimum_should_match: 1, - should: shouldValues, + must: value.map((term) => ({ term: { [field]: term } })) as object[], }, }; if (from !== undefined && to !== undefined) { - query = { - ...query, - bool: { - ...query.bool, - must: [ - { - range: { - '@timestamp': { - gte: from, - lte: to, - }, - }, - }, - ], + query.bool.must.push({ + range: { + '@timestamp': { + gte: from, + lte: to, + }, }, - }; + }); } } diff --git a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts index c48cc90a32f64..753d3a0bddd6d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts +++ b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts @@ -10,15 +10,29 @@ import { useTimelineDataFilters } from '../../../timelines/containers/use_timeli export const DETECTIONS_ALERTS_COUNT_ID = 'detections-alerts-count'; +export interface StatsNode { + data: object; + id: string; + name: string; + parent?: string; + stats: { + total: number; + byCategory: { + alerts?: number; + }; + }; +} interface UserAlertPrevalenceFromProcessTreeResult { loading: boolean; alertIds: undefined | string[]; + statsNodes: undefined | StatsNode[]; count?: number; error: boolean; } interface ProcessTreeAlertPrevalenceResponse { alertIds: string[] | undefined; + statsNodes: StatsNode[] | undefined; } interface EntityResponse { @@ -40,17 +54,7 @@ interface UseAlertDocumentAnalyzerSchema { } interface TreeResponse { - statsNodes: Array<{ - data: object; - id: string; - parent: string; - stats: { - total: number; - byCategory: { - alerts?: number; - }; - }; - }>; + statsNodes: StatsNode[]; alertIds: string[]; } @@ -126,18 +130,21 @@ export function useAlertPrevalenceFromProcessTree({ loading: true, error: false, alertIds: undefined, + statsNodes: undefined, }; } else if (query.data) { return { loading: false, error: false, alertIds: query.data.alertIds, + statsNodes: query.data.statsNodes, }; } else { return { loading: false, error: true, alertIds: undefined, + statsNodes: undefined, }; } } diff --git a/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.test.tsx index 480ecdb3674ff..46a2738d6247a 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.test.tsx @@ -37,23 +37,77 @@ describe('useGlobalTime', () => { expect(result1.to).toBe(0); }); - test('clear all queries at unmount when clearAllQuery is set to true', () => { - const { unmount } = renderHook(() => useGlobalTime()); + test('clear query at unmount when setQuery has been called', () => { + const { result, unmount } = renderHook(() => useGlobalTime()); + act(() => { + result.current.setQuery({ + id: 'query-2', + inspect: { dsl: [], response: [] }, + loading: false, + refetch: () => {}, + searchSessionId: 'session-1', + }); + }); + unmount(); - expect(mockDispatch.mock.calls[0][0].type).toEqual( - 'x-pack/security_solution/local/inputs/DELETE_ALL_QUERY' + expect(mockDispatch.mock.calls.length).toBe(2); + expect(mockDispatch.mock.calls[1][0].type).toEqual( + 'x-pack/security_solution/local/inputs/DELETE_QUERY' ); }); - test('do NOT clear all queries at unmount when clearAllQuery is set to false.', () => { - const { unmount } = renderHook(() => useGlobalTime(false)); + test('do NOT clear query at unmount when setQuery has not been called', () => { + const { unmount } = renderHook(() => useGlobalTime()); unmount(); expect(mockDispatch.mock.calls.length).toBe(0); }); - test('do NOT clear all queries when setting state and clearAllQuery is set to true', () => { - const { rerender } = renderHook(() => useGlobalTime()); - act(() => rerender()); - expect(mockDispatch.mock.calls.length).toBe(0); + test('do clears only the dismounted queries at unmount when setQuery is called', () => { + const { result, unmount } = renderHook(() => useGlobalTime()); + + act(() => { + result.current.setQuery({ + id: 'query-1', + inspect: { dsl: [], response: [] }, + loading: false, + refetch: () => {}, + searchSessionId: 'session-1', + }); + }); + + act(() => { + result.current.setQuery({ + id: 'query-2', + inspect: { dsl: [], response: [] }, + loading: false, + refetch: () => {}, + searchSessionId: 'session-1', + }); + }); + + const { result: theOneWillNotBeDismounted } = renderHook(() => useGlobalTime()); + + act(() => { + theOneWillNotBeDismounted.current.setQuery({ + id: 'query-3h', + inspect: { dsl: [], response: [] }, + loading: false, + refetch: () => {}, + searchSessionId: 'session-1', + }); + }); + unmount(); + expect(mockDispatch).toHaveBeenCalledTimes(5); + expect(mockDispatch.mock.calls[3][0].payload.id).toEqual('query-1'); + + expect(mockDispatch.mock.calls[3][0].type).toEqual( + 'x-pack/security_solution/local/inputs/DELETE_QUERY' + ); + + expect(mockDispatch.mock.calls[4][0].payload.id).toEqual('query-2'); + + expect(mockDispatch.mock.calls[4][0].type).toEqual( + 'x-pack/security_solution/local/inputs/DELETE_QUERY' + ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx index dbb57d57c3e6e..76cd23c8efba0 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx @@ -6,7 +6,7 @@ */ import { pick } from 'lodash/fp'; -import { useCallback, useState, useEffect, useMemo } from 'react'; +import { useCallback, useState, useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; import { InputsModelId } from '../../store/inputs/constants'; @@ -15,15 +15,18 @@ import { inputsSelectors } from '../../store'; import { inputsActions } from '../../store/actions'; import type { SetQuery, DeleteQuery } from './types'; -export const useGlobalTime = (clearAllQuery: boolean = true) => { +export const useGlobalTime = () => { const dispatch = useDispatch(); const { from, to } = useDeepEqualSelector((state) => pick(['from', 'to'], inputsSelectors.globalTimeRangeSelector(state)) ); const [isInitializing, setIsInitializing] = useState(true); + const queryId = useRef([]); + const setQuery = useCallback( - ({ id, inspect, loading, refetch, searchSessionId }: SetQuery) => + ({ id, inspect, loading, refetch, searchSessionId }: SetQuery) => { + queryId.current = [...queryId.current, id]; dispatch( inputsActions.setQuery({ inputId: InputsModelId.global, @@ -33,7 +36,8 @@ export const useGlobalTime = (clearAllQuery: boolean = true) => { refetch, searchSessionId, }) - ), + ); + }, [dispatch] ); @@ -50,13 +54,13 @@ export const useGlobalTime = (clearAllQuery: boolean = true) => { // This effect must not have any mutable dependencies. Otherwise, the cleanup function gets called before the component unmounts. useEffect(() => { return () => { - if (clearAllQuery) { - dispatch(inputsActions.deleteAllQuery({ id: InputsModelId.global })); + if (queryId.current.length > 0) { + queryId.current.forEach((id) => deleteQuery({ id })); } }; - }, [dispatch, clearAllQuery]); + }, [deleteQuery]); - const memoizedReturn = useMemo( + return useMemo( () => ({ isInitializing, from, @@ -66,8 +70,6 @@ export const useGlobalTime = (clearAllQuery: boolean = true) => { }), [deleteQuery, from, isInitializing, setQuery, to] ); - - return memoizedReturn; }; export type GlobalTimeArgs = Omit, 'deleteQuery'> & diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts index a5230c9ac599f..30d914f5ccc83 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts @@ -15,6 +15,9 @@ import { useQueryTimelineByIdOnUrlChange } from './timeline/use_query_timeline_b import { useInitFlyoutFromUrlParam } from './flyout/use_init_flyout_url_param'; import { useSyncFlyoutUrlParam } from './flyout/use_sync_flyout_url_param'; +// NOTE: the expandable flyout package url state is handled here: +// x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx + export const useUrlState = () => { useSyncGlobalQueryString(); useInitSearchBarFromUrlParams(); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts index a79f9c662ae23..9cd22c469160d 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts @@ -14,4 +14,7 @@ export const createTelemetryClientMock = (): jest.Mocked = reportEntityDetailsClicked: jest.fn(), reportEntityAlertsClicked: jest.fn(), reportEntityRiskFiltered: jest.fn(), + reportMLJobUpdate: jest.fn(), + reportCellActionClicked: jest.fn(), + reportAnomaliesCountClicked: jest.fn(), }); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts index e356b47f29c38..edd50340e873e 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts @@ -14,6 +14,9 @@ import type { ReportEntityDetailsClickedParams, ReportEntityAlertsClickedParams, ReportEntityRiskFilteredParams, + ReportMLJobUpdateParams, + ReportCellActionClickedParams, + ReportAnomaliesCountClickedParams, } from './types'; import { TelemetryEventTypes } from './types'; @@ -83,4 +86,16 @@ export class TelemetryClient implements TelemetryClientStart { selectedSeverity, }); }; + + public reportMLJobUpdate = (params: ReportMLJobUpdateParams) => { + this.analytics.reportEvent(TelemetryEventTypes.MLJobUpdate, params); + }; + + public reportCellActionClicked = (params: ReportCellActionClickedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.CellActionClicked, params); + }; + + public reportAnomaliesCountClicked = (params: ReportAnomaliesCountClickedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.AnomaliesCountClicked, params); + }; } diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts index 4087d8df7b5d4..e69f90b57a1f0 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_events.ts @@ -141,6 +141,101 @@ const entityRiskFilteredEvent: TelemetryEvent = { }, }; +const mlJobUpdateEvent: TelemetryEvent = { + eventType: TelemetryEventTypes.MLJobUpdate, + schema: { + jobId: { + type: 'keyword', + _meta: { + description: 'Job id', + optional: false, + }, + }, + isElasticJob: { + type: 'boolean', + _meta: { + description: 'If true the job is one of the pre-configure security solution modules', + optional: false, + }, + }, + moduleId: { + type: 'keyword', + _meta: { + description: 'Module id', + optional: true, + }, + }, + status: { + type: 'keyword', + _meta: { + description: 'It describes what has changed in the job.', + optional: false, + }, + }, + errorMessage: { + type: 'text', + _meta: { + description: 'Error message', + optional: true, + }, + }, + }, +}; + +const cellActionClickedEvent: TelemetryEvent = { + eventType: TelemetryEventTypes.CellActionClicked, + schema: { + fieldName: { + type: 'keyword', + _meta: { + description: 'Field Name', + optional: false, + }, + }, + actionId: { + type: 'keyword', + _meta: { + description: 'Action id', + optional: false, + }, + }, + displayName: { + type: 'keyword', + _meta: { + description: 'User friendly action name', + optional: false, + }, + }, + metadata: { + type: 'pass_through', + _meta: { + description: 'Action metadata', + optional: true, + }, + }, + }, +}; + +const anomaliesCountClickedEvent: TelemetryEvent = { + eventType: TelemetryEventTypes.AnomaliesCountClicked, + schema: { + jobId: { + type: 'keyword', + _meta: { + description: 'Job id', + optional: false, + }, + }, + count: { + type: 'integer', + _meta: { + description: 'Number of anomalies', + optional: false, + }, + }, + }, +}; + export const telemetryEvents = [ alertsGroupingToggledEvent, alertsGroupingChangedEvent, @@ -148,4 +243,7 @@ export const telemetryEvents = [ entityClickedEvent, entityAlertsClickedEvent, entityRiskFilteredEvent, + mlJobUpdateEvent, + cellActionClickedEvent, + anomaliesCountClickedEvent, ]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts index 78ed0ee9d3fd3..f9f7c84c488ed 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts @@ -8,6 +8,7 @@ import type { RootSchema } from '@kbn/analytics-client'; import type { AnalyticsServiceSetup } from '@kbn/core/public'; import type { RiskSeverity } from '../../../../common/search_strategy'; +import type { SecurityMetadata } from '../../../actions/types'; export interface TelemetryServiceSetupParams { analytics: AnalyticsServiceSetup; @@ -20,6 +21,9 @@ export enum TelemetryEventTypes { EntityDetailsClicked = 'Entity Details Clicked', EntityAlertsClicked = 'Entity Alerts Clicked', EntityRiskFiltered = 'Entity Risk Filtered', + MLJobUpdate = 'ML Job Update', + CellActionClicked = 'Cell Action Clicked', + AnomaliesCountClicked = 'Anomalies Count Clicked', } export interface ReportAlertsGroupingChangedParams { @@ -51,13 +55,46 @@ export interface ReportEntityRiskFilteredParams extends EntityParam { selectedSeverity: RiskSeverity; } +export enum ML_JOB_TELEMETRY_STATUS { + started = 'started', + startError = 'start_error', + stopped = 'stopped', + stopError = 'stop_error', + moduleInstalled = 'module_installed', + installationError = 'installationError', +} + +export interface ReportMLJobUpdateParams { + jobId: string; + isElasticJob: boolean; + status: ML_JOB_TELEMETRY_STATUS; + moduleId?: string; + errorMessage?: string; +} + +export interface ReportCellActionClickedParams { + metadata: SecurityMetadata | undefined; + displayName: string; + actionId: string; + fieldName: string; +} + +export interface ReportAnomaliesCountClickedParams { + jobId: string; + count: number; +} + export type TelemetryEventParams = | ReportAlertsGroupingChangedParams | ReportAlertsGroupingToggledParams | ReportAlertsTakeActionParams | ReportEntityDetailsClickedParams | ReportEntityAlertsClickedParams - | ReportEntityRiskFilteredParams; + | ReportEntityRiskFilteredParams + | ReportMLJobUpdateParams + | ReportCellActionClickedParams + | ReportCellActionClickedParams + | ReportAnomaliesCountClickedParams; export interface TelemetryClientStart { reportAlertsGroupingChanged(params: ReportAlertsGroupingChangedParams): void; @@ -67,6 +104,11 @@ export interface TelemetryClientStart { reportEntityDetailsClicked(params: ReportEntityDetailsClickedParams): void; reportEntityAlertsClicked(params: ReportEntityAlertsClickedParams): void; reportEntityRiskFiltered(params: ReportEntityRiskFilteredParams): void; + reportMLJobUpdate(params: ReportMLJobUpdateParams): void; + + reportCellActionClicked(params: ReportCellActionClickedParams): void; + + reportAnomaliesCountClicked(params: ReportAnomaliesCountClickedParams): void; } export type TelemetryEvent = @@ -93,4 +135,16 @@ export type TelemetryEvent = | { eventType: TelemetryEventTypes.EntityRiskFiltered; schema: RootSchema; + } + | { + eventType: TelemetryEventTypes.MLJobUpdate; + schema: RootSchema; + } + | { + eventType: TelemetryEventTypes.CellActionClicked; + schema: RootSchema; + } + | { + eventType: TelemetryEventTypes.AnomaliesCountClicked; + schema: RootSchema; }; diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts b/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts index 2afd961c7a9b7..4b0e5cdd2a0e3 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts +++ b/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts @@ -21,6 +21,7 @@ const { applyDeltaToColumnWidth, changeViewMode, removeColumn, + toggleDetailPanel, updateColumnOrder, updateColumns, updateColumnWidth, @@ -46,6 +47,7 @@ const tableActionTypes = [ updateShowBuildingBlockAlertsFilter.type, updateTotalCount.type, updateIsLoading.type, + toggleDetailPanel.type, ]; export const createDataTableLocalStorageEpic = diff --git a/x-pack/plugins/security_solution/public/common/store/grouping/actions.ts b/x-pack/plugins/security_solution/public/common/store/grouping/actions.ts index a61186aeb0f8f..d78be8b03cb4d 100644 --- a/x-pack/plugins/security_solution/public/common/store/grouping/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/grouping/actions.ts @@ -11,9 +11,5 @@ import type React from 'react'; const actionCreator = actionCreatorFactory('x-pack/security_solution/groups'); export const updateGroupSelector = actionCreator<{ - groupSelector: React.ReactElement; + groupSelector: React.ReactElement | null; }>('UPDATE_GROUP_SELECTOR'); - -export const updateSelectedGroup = actionCreator<{ - selectedGroup: string; -}>('UPDATE_SELECTED_GROUP'); diff --git a/x-pack/plugins/security_solution/public/common/store/grouping/reducer.ts b/x-pack/plugins/security_solution/public/common/store/grouping/reducer.ts index aaea793e4ca86..6914e4ad465fe 100644 --- a/x-pack/plugins/security_solution/public/common/store/grouping/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/grouping/reducer.ts @@ -6,20 +6,17 @@ */ import { reducerWithInitialState } from 'typescript-fsa-reducers'; -import { updateGroupSelector, updateSelectedGroup } from './actions'; +import { updateGroupSelector } from './actions'; import type { GroupModel } from './types'; export const initialGroupingState: GroupModel = { groupSelector: null, - selectedGroup: null, }; -export const groupsReducer = reducerWithInitialState(initialGroupingState) - .case(updateSelectedGroup, (state, { selectedGroup }) => ({ - ...state, - selectedGroup, - })) - .case(updateGroupSelector, (state, { groupSelector }) => ({ +export const groupsReducer = reducerWithInitialState(initialGroupingState).case( + updateGroupSelector, + (state, { groupSelector }) => ({ ...state, groupSelector, - })); + }) +); diff --git a/x-pack/plugins/security_solution/public/common/store/grouping/selectors.ts b/x-pack/plugins/security_solution/public/common/store/grouping/selectors.ts index eb63e256a4d9f..126fdac8c1b36 100644 --- a/x-pack/plugins/security_solution/public/common/store/grouping/selectors.ts +++ b/x-pack/plugins/security_solution/public/common/store/grouping/selectors.ts @@ -11,7 +11,3 @@ import type { GroupState } from './types'; const groupSelector = (state: GroupState) => state.groups.groupSelector; export const getGroupSelector = () => createSelector(groupSelector, (selector) => selector); - -export const selectedGroup = (state: GroupState) => state.groups.selectedGroup; - -export const getSelectedGroup = () => createSelector(selectedGroup, (group) => group); diff --git a/x-pack/plugins/security_solution/public/common/store/grouping/types.ts b/x-pack/plugins/security_solution/public/common/store/grouping/types.ts index 7d8fd4bc3eeca..d2250b15722ed 100644 --- a/x-pack/plugins/security_solution/public/common/store/grouping/types.ts +++ b/x-pack/plugins/security_solution/public/common/store/grouping/types.ts @@ -7,7 +7,6 @@ export interface GroupModel { groupSelector: React.ReactElement | null; - selectedGroup: string | null; } export interface GroupState { diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx index 124ebb67339c9..f87d4b1d71f07 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx @@ -7,12 +7,13 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react'; import { LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; -import type { DashboardContainer } from '@kbn/dashboard-plugin/public'; +import type { DashboardAPI } from '@kbn/dashboard-plugin/public'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { useParams } from 'react-router-dom'; -import { isEmpty, pick } from 'lodash/fp'; +import { pick } from 'lodash/fp'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { SecurityPageName } from '../../../../common/constants'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { useCapabilities } from '../../../common/lib/kibana'; @@ -25,12 +26,12 @@ import { FiltersGlobal } from '../../../common/components/filters_global'; import { InputsModelId } from '../../../common/store/inputs/constants'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { HeaderPage } from '../../../common/components/header_page'; -import { DASHBOARD_PAGE_TITLE } from '../translations'; +import { DASHBOARD_NOT_FOUND_TITLE } from './translations'; import { inputsSelectors } from '../../../common/store'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { EditDashboardButton } from '../../components/edit_dashboard_button'; -type DashboardDetails = Record; +type DashboardDetails = Record; const DashboardViewComponent: React.FC = () => { const { fromStr, toStr, from, to } = useDeepEqualSelector((state) => @@ -51,13 +52,20 @@ const DashboardViewComponent: React.FC = () => { const [currentState, setCurrentState] = useState( canReadDashboard ? null : DashboardViewPromptState.NoReadPermission ); - const [dashboardDetails, setDashboardDetails] = useState(); - const onDashboardContainerLoaded = useCallback((dashboardContainer: DashboardContainer) => { - const dashboardTitle = dashboardContainer.getTitle().trim(); - setDashboardDetails({ dashboardTitle }); + const [dashboardDetails, setDashboardDetails] = useState(); + const onDashboardContainerLoaded = useCallback((dashboard: DashboardAPI) => { + if (dashboard) { + const title = dashboard.getTitle().trim(); + if (title) { + setDashboardDetails({ title }); + } else { + setDashboardDetails({ title: DASHBOARD_NOT_FOUND_TITLE }); + } + } }, []); + + const dashboardExists = useMemo(() => dashboardDetails != null, [dashboardDetails]); const { detailName: savedObjectId } = useParams<{ detailName?: string }>(); - const dashboardExists = !isEmpty(dashboardDetails?.dashboardTitle); useEffect(() => { if (!indicesExist) { @@ -73,7 +81,7 @@ const DashboardViewComponent: React.FC = () => { )} - + }> {showWriteControls && dashboardExists && ( { )} - + ); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts b/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts index dfd25dcbe8512..ddfa94bd75584 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/translations.ts @@ -27,6 +27,13 @@ export const DASHBOARD_INDICES_NOT_FOUND_TITLE = i18n.translate( } ); +export const DASHBOARD_NOT_FOUND_TITLE = i18n.translate( + 'xpack.securitySolution.dashboards.dashboard.notFound.title', + { + defaultMessage: 'Not found', + } +); + export const EDIT_DASHBOARD_BUTTON_TITLE = i18n.translate( 'xpack.securitySolution.dashboards.dashboard.editDashboardButtonTitle', { diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts b/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts index 716b5a6d3a81d..2cdcc156e2946 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/dashboards/pages/translations.ts @@ -9,7 +9,3 @@ import { i18n } from '@kbn/i18n'; export const DASHBOARDS_PAGE_TITLE = i18n.translate('xpack.securitySolution.dashboards.pageTitle', { defaultMessage: 'Dashboards', }); - -export const DASHBOARD_PAGE_TITLE = i18n.translate('xpack.securitySolution.dashboard.pageTitle', { - defaultMessage: 'Dashboard', -}); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts b/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts index 8db389553c523..c71eedbec0264 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts +++ b/x-pack/plugins/security_solution/public/dashboards/pages/utils.ts @@ -6,21 +6,13 @@ */ import type { ChromeBreadcrumb } from '@kbn/core/public'; -import { isEmpty } from 'lodash/fp'; import type { RouteSpyState } from '../../common/utils/route/types'; export const getTrailingBreadcrumbs = (params: RouteSpyState): ChromeBreadcrumb[] => { - let breadcrumb: ChromeBreadcrumb[] = []; - - const dashboardTitle = params?.state?.dashboardTitle?.trim(); - if (params?.state?.dashboardTitle || params.detailName) { - breadcrumb = [ - ...breadcrumb, - { - text: !isEmpty(dashboardTitle) ? dashboardTitle : params.detailName, - }, - ]; + const breadcrumbName = params?.state?.dashboardName; + if (breadcrumbName) { + return [{ text: breadcrumbName }]; } - return breadcrumb; + return []; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index af6a82af1a9d5..8b7a9c62b1541 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -797,91 +797,6 @@ describe('helpers', () => { meta: { kibana_siem_app_url: 'http://localhost:5601/app/siem', }, - throttle: 'no_actions', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for no_actions', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: 'no_actions', - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: 'no_actions', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for rule', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: 'rule', - actions: [ - { - group: 'default', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [ - { - group: mockStepData.actions[0].group, - id: mockStepData.actions[0].id, - action_type_id: mockStepData.actions[0].actionTypeId, - params: mockStepData.actions[0].params, - }, - ], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: 'rule', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for interval', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: '1d', - actions: [ - { - group: 'default', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [ - { - group: mockStepData.actions[0].group, - id: mockStepData.actions[0].id, - action_type_id: mockStepData.actions[0].actionTypeId, - params: mockStepData.actions[0].params, - }, - ], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: mockStepData.throttle, }; expect(result).toEqual(expected); @@ -913,7 +828,6 @@ describe('helpers', () => { meta: { kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, - throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 5442727561ce1..2fe380a41b123 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -25,7 +25,6 @@ import type { Type, } from '@kbn/securitysolution-io-ts-alerting-types'; import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../common/constants'; import { assertUnreachable } from '../../../../../common/utility_types'; import { transformAlertToRuleAction, @@ -49,6 +48,7 @@ import { GroupByOptions, } from '../../../../detections/pages/detection_engine/rules/types'; import type { RuleCreateProps } from '../../../../../common/detection_engine/rule_schema'; +import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../common/detection_engine/rule_schema'; import { stepActionsDefaultValue } from '../../../../detections/components/rules/step_rule_actions'; export const getTimeTypeValue = (time: string): { unit: Unit; value: number } => { @@ -448,6 +448,9 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep ruleFields.groupByRadioSelection === GroupByOptions.PerTimePeriod ? ruleFields.groupByDuration : undefined, + missing_fields_strategy: + ruleFields.suppressionMissingFields || + DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY, }, } : {}), @@ -563,19 +566,12 @@ export const formatAboutStepData = ( }; export const formatActionsStepData = (actionsStepData: ActionsStepRule): ActionsStepRuleJson => { - const { - actions = [], - responseActions, - enabled, - kibanaSiemAppUrl, - throttle = NOTIFICATION_THROTTLE_NO_ACTIONS, - } = actionsStepData; + const { actions = [], responseActions, enabled, kibanaSiemAppUrl } = actionsStepData; return { actions: actions.map(transformAlertToRuleAction), response_actions: responseActions?.map(transformAlertToRuleResponseAction), enabled, - throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS, meta: { kibana_siem_app_url: kibanaSiemAppUrl, }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx index 829a6688f4b60..e07848c145d08 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx @@ -303,6 +303,7 @@ const EditRulePageComponent: FC = () => { {actionsStep.data != null && ( { }, ], [ + rule?.id, rule?.immutable, rule?.type, loading, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx index 28ed5a658558d..67a156e31edf2 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.test.tsx @@ -86,6 +86,11 @@ jest.mock('react-router-dom', () => { }; }); +// RuleDetailsSnoozeSettings is an isolated component and not essential for existing tests +jest.mock('../../../rule_management/components/rule_snooze_badge', () => ({ + RuleSnoozeBadge: () => <>, +})); + const mockRedirectLegacyUrl = jest.fn(); const mockGetLegacyUrlConflict = jest.fn(); jest.mock('../../../../common/lib/kibana', () => { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx index e53b23d16a46d..211321618068f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx @@ -140,6 +140,7 @@ import { EditRuleSettingButtonLink } from '../../../../detections/pages/detectio import { useStartMlJobs } from '../../../rule_management/logic/use_start_ml_jobs'; import { useBulkDuplicateExceptionsConfirmation } from '../../../rule_management_ui/components/rules_table/bulk_actions/use_bulk_duplicate_confirmation'; import { BulkActionDuplicateExceptionsConfirmation } from '../../../rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation'; +import { RuleSnoozeBadge } from '../../../rule_management/components/rule_snooze_badge'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -539,23 +540,30 @@ const RuleDetailsPageComponent: React.FC = ({ const lastExecutionMessage = lastExecution?.message ?? ''; const ruleStatusInfo = useMemo(() => { - return ruleLoading ? ( - - - - ) : ( - - - + return ( + <> + {ruleLoading ? ( + + + + ) : ( + + + + )} + + + + ); - }, [lastExecutionStatus, lastExecutionDate, ruleLoading, isExistingRule, refreshRule]); + }, [ruleId, lastExecutionStatus, lastExecutionDate, ruleLoading, isExistingRule, refreshRule]); const ruleError = useMemo(() => { return ruleLoading ? ( @@ -844,7 +852,7 @@ const RuleDetailsPageComponent: React.FC = ({ {ruleId != null && ( tableLayout="auto" - search={searchOptions} + search={searchOptions as Search} data-test-subj="addExceptionToRulesTable" tableCaption="Rules table" items={sortedRulesByLinkedRulesOnTop} @@ -48,10 +49,7 @@ const ExceptionsAddToRulesTableComponent: React.FC + ) : undefined } pagination={pagination} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.test.tsx index 42fbc6dce160e..5ec0af8ae9fd0 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.test.tsx @@ -146,6 +146,7 @@ describe('useAddToRulesTable', () => { const { options } = filters[0]; expect(options).toEqual([ { + field: 'tags', name: 'some fake tag 1', value: 'some fake tag 1', }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.tsx index eead15990f521..74b7905a37b17 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/add_to_rules_table/use_add_to_rules_table.tsx @@ -70,18 +70,19 @@ export const useAddToRulesTable = ({ tags.forEach((tag) => acc.add(tag)); return acc; }, new Set()); - return [...uniqueTags].map((tag) => ({ value: tag, name: tag })); + return Array.from(uniqueTags).map((tag) => ({ value: tag, name: tag, field: 'tags' })); }, [sortedRulesByLinkedRulesOnTop]); const searchOptions = useMemo( () => ({ box: { incremental: true, + schema: true, }, filters: [ { type: 'field_value_selection' as const, - field: 'tags', + operator: 'exact', name: i18n.translate( 'xpack.securitySolution.exceptions.addToRulesTable.tagsFilterLabel', { @@ -103,7 +104,7 @@ export const useAddToRulesTable = ({ name: commonI18n.LINK_COLUMN, align: 'left' as HorizontalAlignment, 'data-test-subj': 'ruleActionLinkRuleSwitch', - render: (_, rule: Rule) => ( + render: (_: unknown, rule: Rule) => ( ), }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts index 752e15bd86a43..7c352e5f6ce19 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/translations.ts @@ -20,9 +20,31 @@ export const VIEW_RULE_DETAIL_ACTION = i18n.translate( defaultMessage: 'View rule detail', } ); + export const LINK_COLUMN = i18n.translate( 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToRulesTableSelection.link_column', { defaultMessage: 'Link', } ); + +export const NAME_COLUMN = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToRulesTableSelection.name_column', + { + defaultMessage: 'Name', + } +); + +export const ACTION_COLUMN = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToRulesTableSelection.action_column', + { + defaultMessage: 'Action', + } +); + +export const TAGS_COLUMN = i18n.translate( + 'xpack.securitySolution.rule_exceptions.flyoutComponents.addToRulesTableSelection.tags_column', + { + defaultMessage: 'Tags', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx index 4d1570ba3b491..98e59d12681cc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/utils.tsx @@ -13,11 +13,13 @@ import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils'; import type { HorizontalAlignment } from '@elastic/eui'; +import { EuiBadge } from '@elastic/eui'; import type { Moment } from 'moment'; import { HeaderMenu, generateLinkedRulesMenuItems, } from '@kbn/securitysolution-exception-list-components'; +import { PopoverItems } from '../../../../common/components/popover_items'; import { SecurityPageName } from '../../../../../common/constants'; import { ListDetailsLinkAnchor } from '../../../../exceptions/components'; import { @@ -204,7 +206,7 @@ export const enrichExceptionItemsForUpdate = ({ export const getSharedListsTableColumns = () => [ { field: 'name', - name: 'Name', + name: i18n.NAME_COLUMN, sortable: true, 'data-test-subj': 'exceptionListNameCell', }, @@ -230,7 +232,7 @@ export const getSharedListsTableColumns = () => [ ), }, { - name: 'Action', + name: i18n.ACTION_COLUMN, 'data-test-subj': 'exceptionListRulesActionCell', render: (list: ExceptionListRuleReferencesSchema) => { @@ -255,13 +257,40 @@ export const getRulesTableColumn = () => [ { field: 'name', align: 'left' as HorizontalAlignment, - name: 'Name', + name: i18n.NAME_COLUMN, sortable: true, 'data-test-subj': 'ruleNameCell', truncateText: false, }, { - name: 'Action', + field: 'tags', + align: 'left' as HorizontalAlignment, + name: i18n.TAGS_COLUMN, + 'data-test-subj': 'ruleNameCell', + render: (tags: Rule['tags']) => { + if (tags.length === 0) { + return null; + } + + const renderItem = (tag: string, i: number) => ( + + {tag} + + ); + return ( + + ); + }, + }, + { + name: i18n.ACTION_COLUMN, 'data-test-subj': 'ruleAction-view', render: (rule: Rule) => { return ( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts index fe111c13debec..5e3248818bf4e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.test.ts @@ -35,6 +35,7 @@ import { previewRule, findRuleExceptionReferences, performBulkAction, + fetchRulesSnoozeSettings, } from './api'; const abortCtrl = new AbortController(); @@ -786,4 +787,90 @@ describe('Detections Rules API', () => { expect(result).toBe(fetchMockResult); }); }); + + describe('fetchRulesSnoozeSettings', () => { + beforeEach(() => { + fetchMock.mockClear(); + fetchMock.mockResolvedValue({ + data: [], + }); + }); + + test('requests snooze settings of multiple rules by their IDs', () => { + fetchRulesSnoozeSettings({ ids: ['id1', 'id2'] }); + + expect(fetchMock).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + query: expect.objectContaining({ + filter: 'alert.id:"alert:id1" or alert.id:"alert:id2"', + }), + }) + ); + }); + + test('requests the same number of rules as the number of ids provided', () => { + fetchRulesSnoozeSettings({ ids: ['id1', 'id2'] }); + + expect(fetchMock).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + query: expect.objectContaining({ + per_page: 2, + }), + }) + ); + }); + + test('requests only snooze settings fields', () => { + fetchRulesSnoozeSettings({ ids: ['id1', 'id2'] }); + + expect(fetchMock).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + query: expect.objectContaining({ + fields: JSON.stringify([ + 'muteAll', + 'activeSnoozes', + 'isSnoozedUntil', + 'snoozeSchedule', + ]), + }), + }) + ); + }); + + test('returns mapped data', async () => { + fetchMock.mockResolvedValue({ + data: [ + { + id: '1', + mute_all: false, + }, + { + id: '1', + mute_all: false, + active_snoozes: [], + is_snoozed_until: '2023-04-24T19:31:46.765Z', + }, + ], + }); + + const result = await fetchRulesSnoozeSettings({ ids: ['id1'] }); + + expect(result).toEqual([ + { + id: '1', + muteAll: false, + activeSnoozes: [], + }, + { + id: '1', + muteAll: false, + activeSnoozes: [], + isSnoozedUntil: new Date('2023-04-24T19:31:46.765Z'), + }, + ]); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts index 386dbf3c7b525..24b66cada346c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts @@ -9,7 +9,7 @@ import type { CreateRuleExceptionListItemSchema, ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; - +import { INTERNAL_ALERTING_API_FIND_RULES_PATH } from '@kbn/alerting-plugin/common'; import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common'; import { epmRouteService } from '@kbn/fleet-plugin/common'; import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types'; @@ -47,6 +47,7 @@ import type { CreateRulesProps, ExportDocumentsProps, FetchRuleProps, + FetchRuleSnoozingProps, FetchRulesProps, FetchRulesResponse, FindRulesReferencedByExceptionsProps, @@ -56,6 +57,8 @@ import type { PrePackagedRulesStatusResponse, PreviewRulesProps, Rule, + RuleSnoozeSettings, + RulesSnoozeSettingsBatchResponse, UpdateRulesProps, } from '../logic/types'; import { convertRulesFilterToKQL } from '../logic/utils'; @@ -184,6 +187,42 @@ export const fetchRuleById = async ({ id, signal }: FetchRuleProps): Promise => { + const response = await KibanaServices.get().http.fetch( + INTERNAL_ALERTING_API_FIND_RULES_PATH, + { + method: 'GET', + query: { + filter: ids.map((x) => `alert.id:"alert:${x}"`).join(' or '), + fields: JSON.stringify(['muteAll', 'activeSnoozes', 'isSnoozedUntil', 'snoozeSchedule']), + per_page: ids.length, + }, + signal, + } + ); + + return response.data?.map((snoozeSettings) => ({ + id: snoozeSettings?.id ?? '', + muteAll: snoozeSettings?.mute_all ?? false, + activeSnoozes: snoozeSettings?.active_snoozes ?? [], + isSnoozedUntil: snoozeSettings?.is_snoozed_until + ? new Date(snoozeSettings.is_snoozed_until) + : undefined, + snoozeSchedule: snoozeSettings?.snooze_schedule, + })); +}; + export interface BulkActionSummary { failed: number; skipped: number; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_create_prebuilt_rules_mutation.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_create_prebuilt_rules_mutation.ts index 41bce8f0cc154..37001caf43b8c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_create_prebuilt_rules_mutation.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_create_prebuilt_rules_mutation.ts @@ -12,6 +12,7 @@ import { createPrepackagedRules } from '../api'; import { useInvalidateFetchPrebuiltRulesStatusQuery } from './use_fetch_prebuilt_rules_status_query'; import { useInvalidateFindRulesQuery } from './use_find_rules_query'; import { useInvalidateFetchRuleManagementFiltersQuery } from './use_fetch_rule_management_filters_query'; +import { useInvalidateFetchRulesSnoozeSettingsQuery } from './use_fetch_rules_snooze_settings'; export const CREATE_PREBUILT_RULES_MUTATION_KEY = ['PUT', PREBUILT_RULES_URL]; @@ -19,6 +20,7 @@ export const useCreatePrebuiltRulesMutation = ( options?: UseMutationOptions ) => { const invalidateFindRulesQuery = useInvalidateFindRulesQuery(); + const invalidateFetchRulesSnoozeSettings = useInvalidateFetchRulesSnoozeSettingsQuery(); const invalidatePrePackagedRulesStatus = useInvalidateFetchPrebuiltRulesStatusQuery(); const invalidateFetchRuleManagementFilters = useInvalidateFetchRuleManagementFiltersQuery(); @@ -30,6 +32,7 @@ export const useCreatePrebuiltRulesMutation = ( // the number of rules might change after the installation invalidatePrePackagedRulesStatus(); invalidateFindRulesQuery(); + invalidateFetchRulesSnoozeSettings(); invalidateFetchRuleManagementFilters(); if (options?.onSettled) { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings.ts new file mode 100644 index 0000000000000..8e0ef31871826 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_fetch_rules_snooze_settings.ts @@ -0,0 +1,58 @@ +/* + * 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 { INTERNAL_ALERTING_API_FIND_RULES_PATH } from '@kbn/alerting-plugin/common'; +import type { UseQueryOptions } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useCallback } from 'react'; +import type { RuleSnoozeSettings } from '../../logic'; +import { fetchRulesSnoozeSettings } from '../api'; +import { DEFAULT_QUERY_OPTIONS } from './constants'; + +const FETCH_RULE_SNOOZE_SETTINGS_QUERY_KEY = ['GET', INTERNAL_ALERTING_API_FIND_RULES_PATH]; + +/** + * A wrapper around useQuery provides default values to the underlying query, + * like query key, abortion signal. + * + * @param queryArgs - fetch rule snoozing settings ids + * @param queryOptions - react-query options + * @returns useQuery result + */ +export const useFetchRulesSnoozeSettings = ( + ids: string[], + queryOptions?: UseQueryOptions +) => { + return useQuery( + [...FETCH_RULE_SNOOZE_SETTINGS_QUERY_KEY, ...ids], + ({ signal }) => fetchRulesSnoozeSettings({ ids, signal }), + { + ...DEFAULT_QUERY_OPTIONS, + ...queryOptions, + } + ); +}; + +/** + * We should use this hook to invalidate the cache. For example, rule + * snooze modification should lead to cache invalidation. + * + * @returns A rules cache invalidation callback + */ +export const useInvalidateFetchRulesSnoozeSettingsQuery = () => { + const queryClient = useQueryClient(); + + return useCallback(() => { + /** + * Invalidate all queries that start with FIND_RULES_QUERY_KEY. This + * includes the in-memory query cache and paged query cache. + */ + queryClient.invalidateQueries(FETCH_RULE_SNOOZE_SETTINGS_QUERY_KEY, { + refetchType: 'active', + }); + }, [queryClient]); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/index.ts new file mode 100644 index 0000000000000..8e231398688f0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/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 * from './rule_snooze_badge'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/rule_snooze_badge.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/rule_snooze_badge.tsx new file mode 100644 index 0000000000000..e488127c25691 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/rule_snooze_badge.tsx @@ -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 { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import type { RuleObjectId } from '../../../../../common/detection_engine/rule_schema'; +import { useUserData } from '../../../../detections/components/user_info'; +import { hasUserCRUDPermission } from '../../../../common/utils/privileges'; +import { useKibana } from '../../../../common/lib/kibana'; +import { useInvalidateFetchRulesSnoozeSettingsQuery } from '../../api/hooks/use_fetch_rules_snooze_settings'; +import { useRuleSnoozeSettings } from './use_rule_snooze_settings'; + +interface RuleSnoozeBadgeProps { + /** + * Rule's SO id (not ruleId) + */ + ruleId: RuleObjectId; + showTooltipInline?: boolean; +} + +export function RuleSnoozeBadge({ + ruleId, + showTooltipInline = false, +}: RuleSnoozeBadgeProps): JSX.Element { + const RulesListNotifyBadge = useKibana().services.triggersActionsUi.getRulesListNotifyBadge; + const { snoozeSettings, error } = useRuleSnoozeSettings(ruleId); + const [{ canUserCRUD }] = useUserData(); + const hasCRUDPermissions = hasUserCRUDPermission(canUserCRUD); + const invalidateFetchRuleSnoozeSettings = useInvalidateFetchRulesSnoozeSettingsQuery(); + const isLoading = !snoozeSettings; + const rule = useMemo( + () => ({ + id: snoozeSettings?.id ?? '', + muteAll: snoozeSettings?.muteAll ?? false, + activeSnoozes: snoozeSettings?.activeSnoozes ?? [], + isSnoozedUntil: snoozeSettings?.isSnoozedUntil + ? new Date(snoozeSettings.isSnoozedUntil) + : undefined, + snoozeSchedule: snoozeSettings?.snoozeSchedule, + isEditable: hasCRUDPermissions, + }), + [snoozeSettings, hasCRUDPermissions] + ); + + if (error) { + return ( + + + + ); + } + + return ( + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/translations.ts new file mode 100644 index 0000000000000..2c67bdab2744f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/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 UNABLE_TO_FETCH_RULES_SNOOZE_SETTINGS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rulesSnoozeBadge.error.unableToFetch', + { + defaultMessage: 'Unable to fetch snooze settings', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/use_rule_snooze_settings.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/use_rule_snooze_settings.ts new file mode 100644 index 0000000000000..94a857b1e9842 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_snooze_badge/use_rule_snooze_settings.ts @@ -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 type { RuleSnoozeSettings } from '../../logic'; +import { useFetchRulesSnoozeSettings } from '../../api/hooks/use_fetch_rules_snooze_settings'; +import { useRulesTableContextOptional } from '../../../rule_management_ui/components/rules_table/rules_table/rules_table_context'; +import * as i18n from './translations'; + +interface UseRuleSnoozeSettingsResult { + snoozeSettings?: RuleSnoozeSettings; + error?: string; +} + +export function useRuleSnoozeSettings(id: string): UseRuleSnoozeSettingsResult { + const { + state: { rulesSnoozeSettings: rulesTableSnoozeSettings }, + } = useRulesTableContextOptional() ?? { state: {} }; + const { + data: rulesSnoozeSettings, + isFetching: isSingleSnoozeSettingsFetching, + isError: isSingleSnoozeSettingsError, + } = useFetchRulesSnoozeSettings([id], { + enabled: !rulesTableSnoozeSettings?.data[id] && !rulesTableSnoozeSettings?.isFetching, + }); + const snoozeSettings = rulesTableSnoozeSettings?.data[id] ?? rulesSnoozeSettings?.[0]; + const isFetching = rulesTableSnoozeSettings?.isFetching || isSingleSnoozeSettingsFetching; + const isError = rulesTableSnoozeSettings?.isError || isSingleSnoozeSettingsError; + + return { + snoozeSettings, + error: + isError || (!snoozeSettings && !isFetching) + ? i18n.UNABLE_TO_FETCH_RULES_SNOOZE_SETTINGS + : undefined, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts index 0f93d66efbf6f..e22be9467c6a1 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts @@ -7,6 +7,7 @@ import * as t from 'io-ts'; +import type { RuleSnooze } from '@kbn/alerting-plugin/common'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { RiskScore, @@ -217,6 +218,26 @@ export interface FetchRulesProps { signal?: AbortSignal; } +export interface RuleSnoozeSettings { + id: string; + muteAll: boolean; + snoozeSchedule?: RuleSnooze; + activeSnoozes?: string[]; + isSnoozedUntil?: Date; +} + +interface RuleSnoozeSettingsResponse { + id: string; + mute_all: boolean; + snooze_schedule?: RuleSnooze; + active_snoozes?: string[]; + is_snoozed_until?: string; +} + +export interface RulesSnoozeSettingsBatchResponse { + data: RuleSnoozeSettingsResponse[]; +} + export type SortingOptions = t.TypeOf; export const SortingOptions = t.type({ field: FindRulesSortField, @@ -244,6 +265,11 @@ export interface FetchRuleProps { signal?: AbortSignal; } +export interface FetchRuleSnoozingProps { + ids: string[]; + signal?: AbortSignal; +} + export interface BasicFetchProps { signal: AbortSignal; } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx index a0afcad5901e4..cf12b8b0f1bf6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_start_ml_jobs.tsx @@ -43,7 +43,7 @@ export const useStartMlJobs = (): ReturnUseStartMlJobs => { } const latestTimestampMs = job.latestTimestampMs ?? 0; - await enableDatafeed(job, latestTimestampMs, true); + await enableDatafeed(job, latestTimestampMs); }) ); refetchJobs(); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/api.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/api.ts new file mode 100644 index 0000000000000..9d3c28429df5b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/api.ts @@ -0,0 +1,18 @@ +/* + * 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 { MaintenanceWindow } from '@kbn/alerting-plugin/common/maintenance_window'; +import { INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH } from '@kbn/alerting-plugin/common'; +import { KibanaServices } from '../../../../common/lib/kibana'; + +export const fetchActiveMaintenanceWindows = async ( + signal?: AbortSignal +): Promise => + KibanaServices.get().http.fetch(INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH, { + method: 'GET', + signal, + }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx new file mode 100644 index 0000000000000..20a8e0d1f2f94 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx @@ -0,0 +1,136 @@ +/* + * 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, waitFor, cleanup } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { MaintenanceWindowStatus } from '@kbn/alerting-plugin/common'; +import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock'; +import { MaintenanceWindowCallout } from './maintenance_window_callout'; +import { TestProviders } from '../../../../common/mock'; +import { fetchActiveMaintenanceWindows } from './api'; + +jest.mock('../../../../common/hooks/use_app_toasts'); + +jest.mock('./api', () => ({ + fetchActiveMaintenanceWindows: jest.fn(() => Promise.resolve([])), +})); + +const RUNNING_MAINTENANCE_WINDOW_1: Partial = { + title: 'Maintenance window 1', + id: '63057284-ac31-42ba-fe22-adfe9732e5ae', + status: MaintenanceWindowStatus.Running, + events: [{ gte: '2023-04-20T16:27:30.753Z', lte: '2023-04-20T16:57:30.753Z' }], +}; + +const RUNNING_MAINTENANCE_WINDOW_2: Partial = { + title: 'Maintenance window 2', + id: '45894340-df98-11ed-ac81-bfcb4982b4fd', + status: MaintenanceWindowStatus.Running, + events: [{ gte: '2023-04-20T16:47:42.871Z', lte: '2023-04-20T17:11:32.192Z' }], +}; + +const UPCOMING_MAINTENANCE_WINDOW: Partial = { + title: 'Upcoming maintenance window', + id: '5eafe070-e030-11ed-ac81-bfcb4982b4fd', + status: MaintenanceWindowStatus.Upcoming, + events: [ + { gte: '2023-04-21T10:36:14.028Z', lte: '2023-04-21T10:37:00.000Z' }, + { gte: '2023-04-28T10:36:14.028Z', lte: '2023-04-28T10:37:00.000Z' }, + ], +}; + +describe('MaintenanceWindowCallout', () => { + let appToastsMock: jest.Mocked>; + + beforeEach(() => { + jest.resetAllMocks(); + + appToastsMock = useAppToastsMock.create(); + (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); + }); + + afterEach(() => { + cleanup(); + jest.restoreAllMocks(); + }); + + it('should be visible if currently there is at least one "running" maintenance window', async () => { + (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([RUNNING_MAINTENANCE_WINDOW_1]); + + const { findByText } = render(, { wrapper: TestProviders }); + + expect(await findByText('A maintenance window is currently running')).toBeInTheDocument(); + }); + + it('should be visible if currently there are multiple "running" maintenance windows', async () => { + (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([ + RUNNING_MAINTENANCE_WINDOW_1, + RUNNING_MAINTENANCE_WINDOW_2, + ]); + + const { findAllByText } = render(, { wrapper: TestProviders }); + + expect(await findAllByText('A maintenance window is currently running')).toHaveLength(1); + }); + + it('should NOT be visible if currently there are no active (running or upcoming) maintenance windows', async () => { + (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([]); + + const { container } = render(, { wrapper: TestProviders }); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should NOT be visible if currently there are no "running" maintenance windows', async () => { + (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([UPCOMING_MAINTENANCE_WINDOW]); + + const { container } = render(, { wrapper: TestProviders }); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should see an error toast if there was an error while fetching maintenance windows', async () => { + const createReactQueryWrapper = () => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // Turn retries off, otherwise we won't be able to test errors + retry: false, + }, + }, + logger: { + // Turn network error logging off, so we don't log the failed request to the console + error: () => {}, + // eslint-disable-next-line no-console + log: console.log, + // eslint-disable-next-line no-console + warn: console.warn, + }, + }); + const wrapper: React.FC = ({ children }) => ( + {children} + ); + return wrapper; + }; + + const mockError = new Error('Network error'); + (fetchActiveMaintenanceWindows as jest.Mock).mockRejectedValue(mockError); + + render(, { wrapper: createReactQueryWrapper() }); + + await waitFor(() => { + expect(appToastsMock.addError).toHaveBeenCalledTimes(1); + expect(appToastsMock.addError).toHaveBeenCalledWith(mockError, { + title: 'Failed to check if any maintenance window is currently running', + toastMessage: "Notification actions won't run while a maintenance window is running.", + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx new file mode 100644 index 0000000000000..878347dc37c98 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx @@ -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 React from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { MaintenanceWindowStatus } from '@kbn/alerting-plugin/common'; +import { useFetchActiveMaintenanceWindows } from './use_fetch_active_maintenance_windows'; +import * as i18n from './translations'; + +export function MaintenanceWindowCallout(): JSX.Element | null { + const { data } = useFetchActiveMaintenanceWindows(); + const activeMaintenanceWindows = data || []; + + if (activeMaintenanceWindows.some(({ status }) => status === MaintenanceWindowStatus.Running)) { + return ( + + {i18n.MAINTENANCE_WINDOW_RUNNING_DESCRIPTION} + + ); + } + + return null; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/translations.ts new file mode 100644 index 0000000000000..21071aee618a1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/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 MAINTENANCE_WINDOW_RUNNING = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleManagementUi.maintenanceWindowCallout.maintenanceWindowActive', + { + defaultMessage: 'A maintenance window is currently running', + } +); + +export const MAINTENANCE_WINDOW_RUNNING_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleManagementUi.maintenanceWindowCallout.maintenanceWindowActiveDescription', + { + defaultMessage: "Notification actions won't run while a maintenance window is running.", + } +); + +export const FETCH_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleManagementUi.maintenanceWindowCallout.fetchError', + { + defaultMessage: 'Failed to check if any maintenance window is currently running', + } +); + +export const FETCH_ERROR_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleManagementUi.maintenanceWindowCallout.fetchErrorDescription', + { + defaultMessage: "Notification actions won't run while a maintenance window is running.", + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.ts new file mode 100644 index 0000000000000..3603cafbda935 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.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 { useQuery } from '@tanstack/react-query'; +import { INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH } from '@kbn/alerting-plugin/common'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import * as i18n from './translations'; +import { fetchActiveMaintenanceWindows } from './api'; + +export const useFetchActiveMaintenanceWindows = () => { + const { addError } = useAppToasts(); + + return useQuery( + ['GET', INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH], + ({ signal }) => fetchActiveMaintenanceWindows(signal), + { + refetchInterval: 60000, + onError: (error) => { + addError(error, { title: i18n.FETCH_ERROR, toastMessage: i18n.FETCH_ERROR_DESCRIPTION }); + }, + } + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 40707a4307f27..487052fcbf2ef 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -200,7 +200,6 @@ export const mockActionsStepRule = (enabled = false): ActionsStepRule => ({ actions: [], kibanaSiemAppUrl: 'http://localhost:5601/app/siem', enabled, - throttle: 'no_actions', }); export const mockDefineStepRule = (): DefineStepRule => ({ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx index cd8be2d925c3f..74575c327fbc8 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation.tsx @@ -46,21 +46,35 @@ const BulkActionDuplicateExceptionsConfirmationComponent = ({ defaultFocusedButton="confirm" onCancel={onCancel} > - - {i18n.MODAL_TEXT(rulesCount)}{' '} - - + {i18n.MODAL_TEXT(rulesCount)} + {i18n.DUPLICATE_EXCEPTIONS_INCLUDE_EXPIRED_EXCEPTIONS_LABEL(rulesCount)} + + + ), + 'data-test-subj': DuplicateOptions.withExceptions, + }, + { + id: DuplicateOptions.withExceptionsExcludeExpiredExceptions, + label: ( + + {i18n.DUPLICATE_EXCEPTIONS_TEXT(rulesCount)} + + + ), + 'data-test-subj': DuplicateOptions.withExceptionsExcludeExpiredExceptions, }, { id: DuplicateOptions.withoutExceptions, label: i18n.DUPLICATE_WITHOUT_EXCEPTIONS_TEXT(rulesCount), + 'data-test-subj': DuplicateOptions.withoutExceptions, }, ]} idSelected={selectedDuplicateOption} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx index 8b791ee2aece1..ff41017ac1771 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx @@ -23,21 +23,13 @@ import { Field, } from '../../../../../../shared_imports'; import { BulkActionEditType } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import type { - BulkActionEditPayload, - ThrottleForBulkActions, -} from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { NOTIFICATION_THROTTLE_RULE } from '../../../../../../../common/constants'; +import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; import { bulkAddRuleActions as i18n } from '../translations'; import { useKibana } from '../../../../../../common/lib/kibana'; -import { - ThrottleSelectField, - THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, -} from '../../../../../../detections/components/rules/throttle_select_field'; import { getAllActionMessageParams } from '../../../../../../detections/pages/detection_engine/rules/helpers'; import { RuleActionsField } from '../../../../../../detections/components/rules/rule_actions_field'; @@ -45,19 +37,16 @@ import { debouncedValidateRuleActionsField } from '../../../../../../detections/ const CommonUseField = getUseField({ component: Field }); +type BulkActionsRuleAction = RuleAction & Required>; + export interface RuleActionsFormData { - throttle: ThrottleForBulkActions; - actions: RuleAction[]; + actions: BulkActionsRuleAction[]; overwrite: boolean; } const getFormSchema = ( actionTypeRegistry: ActionTypeRegistryContract ): FormSchema => ({ - throttle: { - label: i18n.THROTTLE_LABEL, - helpText: i18n.THROTTLE_HELP_TEXT, - }, actions: { validations: [ { @@ -75,7 +64,6 @@ const getFormSchema = ( }); const defaultFormData: RuleActionsFormData = { - throttle: NOTIFICATION_THROTTLE_RULE, actions: [], overwrite: false, }; @@ -108,7 +96,7 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction return; } - const { actions = [], throttle: throttleToSubmit, overwrite: overwriteValue } = data; + const { actions = [], overwrite: overwriteValue } = data; const editAction = overwriteValue ? BulkActionEditType.set_rule_actions : BulkActionEditType.add_rule_actions; @@ -117,23 +105,10 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction type: editAction, value: { actions: actions.map(({ actionTypeId, ...action }) => action), - throttle: throttleToSubmit, }, }); }, [form, onConfirm]); - const throttleFieldComponentProps = useMemo( - () => ({ - idAria: 'bulkEditRulesRuleActionThrottle', - 'data-test-subj': 'bulkEditRulesRuleActionThrottle', - hasNoInitialSelection: false, - euiFieldProps: { - options: THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, - }, - }), - [] - ); - const messageVariables = useMemo(() => getAllActionMessageParams(), []); return ( @@ -156,24 +131,11 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction } >
      -
    • - -
    • {i18n.RULE_VARIABLES_DETAIL}
    - - - ( ), @@ -147,7 +132,7 @@ export const bulkDuplicateRuleActions = { MODAL_TEXT: (rulesCount: number): JSX.Element => ( ), @@ -155,7 +140,15 @@ export const bulkDuplicateRuleActions = { DUPLICATE_EXCEPTIONS_TEXT: (rulesCount: number) => ( + ), + + DUPLICATE_EXCEPTIONS_INCLUDE_EXPIRED_EXCEPTIONS_LABEL: (rulesCount: number) => ( + ), @@ -163,7 +156,7 @@ export const bulkDuplicateRuleActions = { DUPLICATE_WITHOUT_EXCEPTIONS_TEXT: (rulesCount: number) => ( ), @@ -186,7 +179,7 @@ export const bulkDuplicateRuleActions = { 'xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip', { defaultMessage: - ' If you duplicate exceptions, then the shared exceptions list will be duplicated by reference and the default rule exception will be copied and created as a new one', + ' If you choose to duplicate exceptions, the shared exceptions list will be duplicated by reference and the rule exceptions will be copied and created anew', } ), }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx index f9915777483a8..b1804e5c29f0a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx @@ -6,7 +6,6 @@ */ /* eslint-disable complexity */ -import { omit } from 'lodash'; import type { EuiContextMenuPanelDescriptor } from '@elastic/eui'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTextColor } from '@elastic/eui'; import type { Toast } from '@kbn/core/public'; @@ -137,7 +136,13 @@ export const useBulkActions = ({ type: BulkActionType.duplicate, duplicatePayload: { include_exceptions: - modalDuplicationConfirmationResult === DuplicateOptions.withExceptions, + modalDuplicationConfirmationResult === DuplicateOptions.withExceptions || + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions, + include_expired_exceptions: !( + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions + ), }, ...(isAllSelected ? { query: filterQuery } : { ids: selectedRuleIds }), }); @@ -222,16 +227,6 @@ export const useBulkActions = ({ return; } - // TODO: https://github.com/elastic/kibana/issues/148414 - // Strip frequency from actions to comply with Security Solution alert API - if ('actions' in editPayload.value) { - // `actions.frequency` is included in the payload from TriggersActionsUI ActionForm - // but is not included in the type definition for the editPayload, because this type - // definition comes from the Security Solution alert API - // TODO https://github.com/elastic/kibana/issues/148414 fix this discrepancy - editPayload.value.actions = editPayload.value.actions.map((a) => omit(a, 'frequency')); - } - startTransaction({ name: BULK_RULE_ACTIONS.EDIT }); const hideWarningToast = () => { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts index c8f49ebe4a6c6..f25b1331d766a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts @@ -50,7 +50,7 @@ export function computeDryRunEditPayload(editAction: BulkActionEditType): BulkAc return [ { type: editAction, - value: { throttle: '1h', actions: [] }, + value: { actions: [] }, }, ]; case BulkActionEditType.set_schedule: diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/__mocks__/rules_table_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/__mocks__/rules_table_context.tsx index 688b90c860ceb..9ee763899eab7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/__mocks__/rules_table_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/__mocks__/rules_table_context.tsx @@ -12,6 +12,12 @@ export const useRulesTableContextMock = { create: (): jest.Mocked => ({ state: { rules: [], + rulesSnoozeSettings: { + data: {}, + isLoading: false, + isFetching: false, + isError: false, + }, pagination: { page: 1, perPage: 20, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.test.tsx index cb2eb08c5381b..abc384cea3bfb 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.test.tsx @@ -9,7 +9,9 @@ import { renderHook } from '@testing-library/react-hooks'; import type { PropsWithChildren } from 'react'; import React from 'react'; import { useUiSetting$ } from '../../../../../common/lib/kibana'; +import type { Rule, RuleSnoozeSettings } from '../../../../rule_management/logic/types'; import { useFindRules } from '../../../../rule_management/logic/use_find_rules'; +import { useFetchRulesSnoozeSettings } from '../../../../rule_management/api/hooks/use_fetch_rules_snooze_settings'; import type { RulesTableState } from './rules_table_context'; import { RulesTableContextProvider, useRulesTableContext } from './rules_table_context'; import { @@ -23,22 +25,51 @@ import { useRulesTableSavedState } from './use_rules_table_saved_state'; jest.mock('../../../../../common/lib/kibana'); jest.mock('../../../../rule_management/logic/use_find_rules'); +jest.mock('../../../../rule_management/api/hooks/use_fetch_rules_snooze_settings'); jest.mock('./use_rules_table_saved_state'); -function renderUseRulesTableContext( - savedState: ReturnType -): RulesTableState { +function renderUseRulesTableContext({ + rules, + rulesSnoozeSettings, + savedState, +}: { + rules?: Rule[] | Error; + rulesSnoozeSettings?: RuleSnoozeSettings[] | Error; + savedState?: ReturnType; +}): RulesTableState { (useFindRules as jest.Mock).mockReturnValue({ - data: { rules: [], total: 0 }, + data: rules instanceof Error || !rules ? undefined : { rules, total: rules?.length }, refetch: jest.fn(), dataUpdatedAt: 0, - isFetched: false, - isFetching: false, - isLoading: false, + isFetched: !!rules, + isFetching: !rules, + isLoading: !rules, isRefetching: false, + isError: rules instanceof Error, + }); + (useFetchRulesSnoozeSettings as jest.Mock).mockReturnValue({ + data: rulesSnoozeSettings instanceof Error ? undefined : rulesSnoozeSettings, + isError: rulesSnoozeSettings instanceof Error, }); (useUiSetting$ as jest.Mock).mockReturnValue([{ on: false, value: 0, idleTimeout: 0 }]); - (useRulesTableSavedState as jest.Mock).mockReturnValue(savedState); + (useRulesTableSavedState as jest.Mock).mockReturnValue( + savedState ?? { + filter: { + searchTerm: undefined, + source: undefined, + tags: undefined, + enabled: undefined, + }, + sorting: { + field: undefined, + order: undefined, + }, + pagination: { + page: undefined, + perPage: undefined, + }, + } + ); const wrapper = ({ children }: PropsWithChildren<{}>) => ( {children} @@ -56,19 +87,22 @@ describe('RulesTableContextProvider', () => { describe('persisted state', () => { it('restores persisted rules table state', () => { const state = renderUseRulesTableContext({ - filter: { - searchTerm: 'test', - source: RuleSource.Custom, - tags: ['test'], - enabled: true, - }, - sorting: { - field: 'name', - order: 'asc', - }, - pagination: { - page: 2, - perPage: 10, + rules: [], + savedState: { + filter: { + searchTerm: 'test', + source: RuleSource.Custom, + tags: ['test'], + enabled: true, + }, + sorting: { + field: 'name', + order: 'asc', + }, + pagination: { + page: 2, + perPage: 10, + }, }, }); @@ -112,4 +146,94 @@ describe('RulesTableContextProvider', () => { expect(state.isDefault).toBeTruthy(); }); }); + + describe('state', () => { + describe('rules', () => { + it('returns an empty array while loading', () => { + const state = renderUseRulesTableContext({ + rules: undefined, + }); + + expect(state.rules).toEqual([]); + }); + + it('returns an empty array upon error', () => { + const state = renderUseRulesTableContext({ + rules: new Error('some error'), + }); + + expect(state.rules).toEqual([]); + }); + + it('returns rules while snooze settings are not loaded yet', () => { + const state = renderUseRulesTableContext({ + rules: [{ name: 'rule 1' }, { name: 'rule 2' }] as Rule[], + rulesSnoozeSettings: undefined, + }); + + expect(state.rules).toEqual([{ name: 'rule 1' }, { name: 'rule 2' }]); + }); + + it('returns rules even if snooze settings failed to be loaded', () => { + const state = renderUseRulesTableContext({ + rules: [{ name: 'rule 1' }, { name: 'rule 2' }] as Rule[], + rulesSnoozeSettings: new Error('some error'), + }); + + expect(state.rules).toEqual([{ name: 'rule 1' }, { name: 'rule 2' }]); + }); + + it('returns rules after snooze settings loaded', () => { + const state = renderUseRulesTableContext({ + rules: [ + { id: '1', name: 'rule 1' }, + { id: '2', name: 'rule 2' }, + ] as Rule[], + rulesSnoozeSettings: [ + { id: '1', muteAll: true, snoozeSchedule: [] }, + { id: '2', muteAll: false, snoozeSchedule: [] }, + ], + }); + + expect(state.rules).toEqual([ + { + id: '1', + name: 'rule 1', + }, + { + id: '2', + name: 'rule 2', + }, + ]); + }); + }); + + describe('rules snooze settings', () => { + it('returns snooze settings', () => { + const state = renderUseRulesTableContext({ + rules: [ + { id: '1', name: 'rule 1' }, + { id: '2', name: 'rule 2' }, + ] as Rule[], + rulesSnoozeSettings: [ + { id: '1', muteAll: true, snoozeSchedule: [] }, + { id: '2', muteAll: false, snoozeSchedule: [] }, + ], + }); + + expect(state.rulesSnoozeSettings.data).toEqual({ + '1': { + id: '1', + muteAll: true, + snoozeSchedule: [], + }, + '2': { + id: '2', + muteAll: false, + snoozeSchedule: [], + }, + }); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.tsx index f6ec9a3714f66..938174d0c567d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/rules_table_context.tsx @@ -15,6 +15,7 @@ import React, { useRef, useState, } from 'react'; +import { useFetchRulesSnoozeSettings } from '../../../../rule_management/api/hooks/use_fetch_rules_snooze_settings'; import { DEFAULT_RULES_TABLE_REFRESH_SETTING } from '../../../../../../common/constants'; import { invariant } from '../../../../../../common/utils/invariant'; import { URL_PARAM_KEY } from '../../../../../common/hooks/use_url_state'; @@ -24,6 +25,7 @@ import type { FilterOptions, PaginationOptions, Rule, + RuleSnoozeSettings, SortingOptions, } from '../../../../rule_management/logic/types'; import { useFindRules } from '../../../../rule_management/logic/use_find_rules'; @@ -37,6 +39,22 @@ import { import { RuleSource } from './rules_table_saved_state'; import { useRulesTableSavedState } from './use_rules_table_saved_state'; +interface RulesSnoozeSettings { + /** + * A map object using rule SO's id (not ruleId) as keys and snooze settings as values + */ + data: Record; + /** + * Sets to true during the first data loading + */ + isLoading: boolean; + /** + * Sets to true during data loading + */ + isFetching: boolean; + isError: boolean; +} + export interface RulesTableState { /** * Rules to display (sorted and paginated in case of in-memory) @@ -106,6 +124,10 @@ export interface RulesTableState { * Whether the state has its default value */ isDefault: boolean; + /** + * Rules snooze settings for the current rules + */ + rulesSnoozeSettings: RulesSnoozeSettings; } export type LoadingRuleAction = @@ -274,9 +296,28 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide } ); + // Fetch rules snooze settings + const { + data: rulesSnoozeSettings, + isLoading: isSnoozeSettingsLoading, + isFetching: isSnoozeSettingsFetching, + isError: isSnoozeSettingsFetchError, + refetch: refetchSnoozeSettings, + } = useFetchRulesSnoozeSettings( + rules.map((x) => x.id), + { enabled: rules.length > 0 } + ); + + const refetchRulesAndSnoozeSettings = useCallback(async () => { + const response = await refetch(); + await refetchSnoozeSettings(); + + return response; + }, [refetch, refetchSnoozeSettings]); + const actions = useMemo( () => ({ - reFetchRules: refetch, + reFetchRules: refetchRulesAndSnoozeSettings, setFilterOptions: handleFilterOptionsChange, setIsAllSelected, setIsRefreshOn, @@ -290,7 +331,7 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide clearFilters, }), [ - refetch, + refetchRulesAndSnoozeSettings, handleFilterOptionsChange, setIsAllSelected, setIsRefreshOn, @@ -305,10 +346,23 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide ] ); - const providerValue = useMemo( - () => ({ + const providerValue = useMemo(() => { + const rulesSnoozeSettingsMap = + rulesSnoozeSettings?.reduce((map, snoozeSettings) => { + map[snoozeSettings.id] = snoozeSettings; + + return map; + }, {} as Record) ?? {}; + + return { state: { rules, + rulesSnoozeSettings: { + data: rulesSnoozeSettingsMap, + isLoading: isSnoozeSettingsLoading, + isFetching: isSnoozeSettingsFetching, + isError: isSnoozeSettingsFetchError, + }, pagination: { page, perPage, @@ -335,29 +389,32 @@ export const RulesTableContextProvider = ({ children }: RulesTableContextProvide }), }, actions, - }), - [ - rules, - page, - perPage, - total, - filterOptions, - isPreflightInProgress, - isActionInProgress, - isAllSelected, - isFetched, - isFetching, - isLoading, - isRefetching, - isRefreshOn, - dataUpdatedAt, - loadingRules.ids, - loadingRules.action, - selectedRuleIds, - sortingOptions, - actions, - ] - ); + }; + }, [ + rules, + rulesSnoozeSettings, + isSnoozeSettingsLoading, + isSnoozeSettingsFetching, + isSnoozeSettingsFetchError, + page, + perPage, + total, + filterOptions, + isPreflightInProgress, + isActionInProgress, + isAllSelected, + isFetched, + isFetching, + isLoading, + isRefetching, + isRefreshOn, + dataUpdatedAt, + loadingRules.ids, + loadingRules.action, + selectedRuleIds, + sortingOptions, + actions, + ]); return {children}; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx index 1296e9728d2e6..cccd9f394d65e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_columns.tsx @@ -22,6 +22,7 @@ import type { } from '../../../../../common/detection_engine/rule_monitoring'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; +import { RuleSnoozeBadge } from '../../../rule_management/components/rule_snooze_badge'; import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; import { getRuleDetailsTabUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; @@ -106,6 +107,19 @@ const useEnabledColumn = ({ hasCRUDPermissions, startMlJobs }: ColumnsProps): Ta ); }; +const useRuleSnoozeColumn = (): TableColumn => { + return useMemo( + () => ({ + field: 'snooze', + name: i18n.COLUMN_SNOOZE, + render: (_, rule: Rule) => , + width: '100px', + sortable: false, + }), + [] + ); +}; + export const RuleLink = ({ name, id }: Pick) => { return ( @@ -248,6 +262,7 @@ export const useRulesColumns = ({ isLoadingJobs, mlJobs, }); + const snoozeColumn = useRuleSnoozeColumn(); return useMemo( () => [ @@ -317,6 +332,7 @@ export const useRulesColumns = ({ width: '18%', truncateText: true, }, + snoozeColumn, enabledColumn, ...(hasCRUDPermissions ? [actionsColumn] : []), ], @@ -324,6 +340,7 @@ export const useRulesColumns = ({ actionsColumn, enabledColumn, executionStatusColumn, + snoozeColumn, hasCRUDPermissions, ruleNameColumn, showRelatedIntegrations, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx index 62af07af3ea24..b5999b926b97f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx @@ -77,7 +77,13 @@ export const useRulesTableActions = ({ ids: [rule.id], duplicatePayload: { include_exceptions: - modalDuplicationConfirmationResult === DuplicateOptions.withExceptions, + modalDuplicationConfirmationResult === DuplicateOptions.withExceptions || + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions, + include_expired_exceptions: !( + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions + ), }, }); const createdRules = result?.attributes.results.created; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx index 95e0857874423..03231f2030eb2 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx @@ -42,6 +42,8 @@ import { RulesTableContextProvider } from '../../components/rules_table/rules_ta import * as i18n from '../../../../detections/pages/detection_engine/rules/translations'; import { useInvalidateFetchRuleManagementFiltersQuery } from '../../../rule_management/api/hooks/use_fetch_rule_management_filters_query'; +import { MaintenanceWindowCallout } from '../../components/maintenance_window_callout/maintenance_window_callout'; + const RulesPageComponent: React.FC = () => { const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState(); const [isValueListFlyoutVisible, showValueListFlyout, hideValueListFlyout] = useBoolState(); @@ -158,6 +160,7 @@ const RulesPageComponent: React.FC = () => { prePackagedTimelineStatus === 'timelineNeedUpdate') && ( )} + diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx index 9d7812b833b46..f68f8d54a5b19 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx @@ -56,7 +56,7 @@ export const getAlertsTypeTableColumns = ( { }); }); - it('invokes useGlobalTime() with false to prevent global queries from being deleted when the component unmounts', async () => { - await act(async () => { - mount( - - - - ); - - expect(useGlobalTime).toBeCalledWith(false); - }); - }); - it('renders with the specified `alignHeader` alignment', async () => { await act(async () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx index 26eb4522ed617..f9967a44ffb6e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx @@ -84,7 +84,7 @@ export const AlertsCountPanel = memo( isExpanded, setIsExpanded, }) => { - const { to, from, deleteQuery, setQuery } = useGlobalTime(false); + const { to, from, deleteQuery, setQuery } = useGlobalTime(); const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled'); const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled'); // create a unique, but stable (across re-renders) query id diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx index d1cb85cdf4564..e1945ca151cd8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx @@ -152,7 +152,7 @@ export const AlertsHistogramPanel = memo( isExpanded, setIsExpanded, }) => { - const { to, from, deleteQuery, setQuery } = useGlobalTime(false); + const { to, from, deleteQuery, setQuery } = useGlobalTime(); // create a unique, but stable (across re-renders) query id const uniqueQueryId = useMemo(() => `${DETECTIONS_HISTOGRAM_ID}-${uuidv4()}`, []); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.tsx index fb5024d3c2e50..e8d0ddd061e81 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.tsx @@ -82,7 +82,7 @@ export const useSummaryChartData: UseAlerts = ({ signalIndexName, skip = false, }) => { - const { to, from, deleteQuery, setQuery } = useGlobalTime(false); + const { to, from, deleteQuery, setQuery } = useGlobalTime(); const [updatedAt, setUpdatedAt] = useState(Date.now()); const [items, setItems] = useState([]); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx new file mode 100644 index 0000000000000..4a57d7cac8e73 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -0,0 +1,376 @@ +/* + * 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 { fireEvent, render, within } from '@testing-library/react'; +import type { Filter } from '@kbn/es-query'; +import useResizeObserver from 'use-resize-observer/polyfilled'; + +import '../../../common/mock/match_media'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../../common/mock'; +import type { AlertsTableComponentProps } from './alerts_grouping'; +import { GroupedAlertsTable } from './alerts_grouping'; +import { TableId } from '@kbn/securitysolution-data-table'; +import { useSourcererDataView } from '../../../common/containers/sourcerer'; +import type { UseFieldBrowserOptionsProps } from '../../../timelines/components/fields_browser'; +import { createStore } from '../../../common/store'; +import { useKibana as mockUseKibana } from '../../../common/lib/kibana/__mocks__'; +import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock'; +import { useQueryAlerts } from '../../containers/detection_engine/alerts/use_query'; +import { groupingSearchResponse } from './grouping_settings/mock'; + +jest.mock('../../containers/detection_engine/alerts/use_query'); +jest.mock('../../../common/containers/sourcerer'); +jest.mock('../../../common/utils/normalize_time_range'); +jest.mock('../../../common/containers/use_global_time', () => ({ + useGlobalTime: jest.fn().mockReturnValue({ + from: '2020-07-07T08:20:18.966Z', + isInitializing: false, + to: '2020-07-08T08:20:18.966Z', + setQuery: jest.fn(), + }), +})); + +const mockOptions = [ + { label: 'ruleName', key: 'kibana.alert.rule.name' }, + { label: 'userName', key: 'user.name' }, + { label: 'hostName', key: 'host.name' }, + { label: 'sourceIP', key: 'source.ip' }, +]; +// +jest.mock('./grouping_settings', () => { + const actual = jest.requireActual('./grouping_settings'); + + return { + ...actual, + getDefaultGroupingOptions: () => mockOptions, + }; +}); + +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +const mockUseFieldBrowserOptions = jest.fn(); +jest.mock('../../../timelines/components/fields_browser', () => ({ + useFieldBrowserOptions: (props: UseFieldBrowserOptionsProps) => mockUseFieldBrowserOptions(props), +})); + +const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; +jest.mock('use-resize-observer/polyfilled'); +mockUseResizeObserver.mockImplementation(() => ({})); +const mockedUseKibana = mockUseKibana(); +const mockedTelemetry = createTelemetryServiceMock(); +jest.mock('../../../common/lib/kibana', () => { + const original = jest.requireActual('../../../common/lib/kibana'); + + return { + ...original, + useKibana: () => ({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + telemetry: mockedTelemetry, + }, + }), + }; +}); + +jest.mock('./timeline_actions/use_add_bulk_to_timeline', () => ({ + useAddBulkToTimelineAction: jest.fn(() => {}), +})); +const sourcererDataView = { + indicesExist: true, + loading: false, + indexPattern: { + fields: [], + }, + browserFields: {}, +}; +const renderChildComponent = (groupingFilters: Filter[]) =>

    ; + +const testProps: AlertsTableComponentProps = { + defaultFilters: [], + from: '2020-07-07T08:20:18.966Z', + globalFilters: [], + globalQuery: { + query: 'query', + language: 'language', + }, + hasIndexMaintenance: true, + hasIndexWrite: true, + loading: false, + renderChildComponent, + runtimeMappings: {}, + signalIndexName: 'test', + tableId: TableId.test, + to: '2020-07-08T08:20:18.966Z', +}; + +const mockUseQueryAlerts = useQueryAlerts as jest.Mock; +const mockQueryResponse = { + loading: false, + data: {}, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; + +const getMockStorageState = (groups: string[] = ['none']) => + JSON.stringify({ + [testProps.tableId]: { + activeGroups: groups, + options: mockOptions, + }, + }); + +describe('GroupedAlertsTable', () => { + const { storage } = createSecuritySolutionStorageMock(); + let store: ReturnType; + + beforeEach(() => { + jest.clearAllMocks(); + store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + selectedPatterns: ['myFakebeat-*'], + }); + mockUseQueryAlerts.mockImplementation((i) => { + if (i.skip) { + return mockQueryResponse; + } + if (i.query.aggs.groupByFields.multi_terms != null) { + return { + ...mockQueryResponse, + data: groupingSearchResponse.ruleName, + }; + } + return { + ...mockQueryResponse, + data: i.query.aggs.groupByFields.terms.field != null ? groupingSearchResponse.hostName : {}, + }; + }); + }); + + it('calls the proper initial dispatch actions for groups', () => { + const { getByTestId, queryByTestId } = render( + + + + ); + + expect(queryByTestId('empty-results-panel')).not.toBeInTheDocument(); + expect(queryByTestId('group-selector-dropdown')).not.toBeInTheDocument(); + expect(getByTestId('alerts-table')).toBeInTheDocument(); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch.mock.calls[0][0].type).toEqual( + 'x-pack/security_solution/groups/UPDATE_GROUP_SELECTOR' + ); + }); + + it('renders empty grouping table when group is selected without data', async () => { + mockUseQueryAlerts.mockReturnValue(mockQueryResponse); + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name'])); + const { getByTestId, queryByTestId } = render( + + + + ); + expect(queryByTestId('alerts-table')).not.toBeInTheDocument(); + expect(getByTestId('empty-results-panel')).toBeInTheDocument(); + }); + + it('renders grouping table in first accordion level when single group is selected', async () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name'])); + + const { getAllByTestId } = render( + + + + ); + fireEvent.click(getAllByTestId('group-panel-toggle')[0]); + + const level0 = getAllByTestId('grouping-accordion-content')[0]; + expect(within(level0).getByTestId('alerts-table')).toBeInTheDocument(); + }); + + it('renders grouping table in second accordion level when 2 groups are selected', async () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name', 'host.name'])); + + const { getAllByTestId } = render( + + + + ); + fireEvent.click(getAllByTestId('group-panel-toggle')[0]); + + const level0 = getAllByTestId('grouping-accordion-content')[0]; + expect(within(level0).queryByTestId('alerts-table')).not.toBeInTheDocument(); + + fireEvent.click(within(level0).getAllByTestId('group-panel-toggle')[0]); + const level1 = within(getAllByTestId('grouping-accordion-content')[1]); + expect(level1.getByTestId('alerts-table')).toBeInTheDocument(); + }); + + it('resets all levels pagination when selected group changes', async () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name', 'host.name', 'user.name'])); + + const { getByTestId, getAllByTestId } = render( + + + + ); + + fireEvent.click(getByTestId('pagination-button-1')); + fireEvent.click(getAllByTestId('group-panel-toggle')[0]); + + const level0 = getAllByTestId('grouping-accordion-content')[0]; + fireEvent.click(within(level0).getByTestId('pagination-button-1')); + fireEvent.click(within(level0).getAllByTestId('group-panel-toggle')[0]); + + const level1 = getAllByTestId('grouping-accordion-content')[1]; + fireEvent.click(within(level1).getByTestId('pagination-button-1')); + + [ + getByTestId('grouping-level-0-pagination'), + getByTestId('grouping-level-1-pagination'), + getByTestId('grouping-level-2-pagination'), + ].forEach((pagination) => { + expect( + within(pagination).getByTestId('pagination-button-0').getAttribute('aria-current') + ).toEqual(null); + expect( + within(pagination).getByTestId('pagination-button-1').getAttribute('aria-current') + ).toEqual('true'); + }); + + fireEvent.click(getAllByTestId('group-selector-dropdown')[0]); + fireEvent.click(getAllByTestId('panel-user.name')[0]); + + [ + getByTestId('grouping-level-0-pagination'), + getByTestId('grouping-level-1-pagination'), + // level 2 has been removed with the group selection change + ].forEach((pagination) => { + expect( + within(pagination).getByTestId('pagination-button-0').getAttribute('aria-current') + ).toEqual('true'); + expect( + within(pagination).getByTestId('pagination-button-1').getAttribute('aria-current') + ).toEqual(null); + }); + }); + + it('resets all levels pagination when global query updates', async () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name', 'host.name', 'user.name'])); + + const { getByTestId, getAllByTestId, rerender } = render( + + + + ); + + fireEvent.click(getByTestId('pagination-button-1')); + fireEvent.click(getAllByTestId('group-panel-toggle')[0]); + + const level0 = getAllByTestId('grouping-accordion-content')[0]; + fireEvent.click(within(level0).getByTestId('pagination-button-1')); + fireEvent.click(within(level0).getAllByTestId('group-panel-toggle')[0]); + + const level1 = getAllByTestId('grouping-accordion-content')[1]; + fireEvent.click(within(level1).getByTestId('pagination-button-1')); + + rerender( + + + + ); + + [ + getByTestId('grouping-level-0-pagination'), + getByTestId('grouping-level-1-pagination'), + getByTestId('grouping-level-2-pagination'), + ].forEach((pagination) => { + expect( + within(pagination).getByTestId('pagination-button-0').getAttribute('aria-current') + ).toEqual('true'); + expect( + within(pagination).getByTestId('pagination-button-1').getAttribute('aria-current') + ).toEqual(null); + }); + }); + + it('resets only most inner group pagination when its parent groups open/close', async () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name', 'host.name', 'user.name'])); + + const { getByTestId, getAllByTestId } = render( + + + + ); + + fireEvent.click(getByTestId('pagination-button-1')); + fireEvent.click(getAllByTestId('group-panel-toggle')[0]); + + const level0 = getAllByTestId('grouping-accordion-content')[0]; + fireEvent.click(within(level0).getByTestId('pagination-button-1')); + fireEvent.click(within(level0).getAllByTestId('group-panel-toggle')[0]); + + const level1 = getAllByTestId('grouping-accordion-content')[1]; + fireEvent.click(within(level1).getByTestId('pagination-button-1')); + + fireEvent.click(within(level0).getAllByTestId('group-panel-toggle')[28]); + [ + getByTestId('grouping-level-0-pagination'), + getByTestId('grouping-level-1-pagination'), + ].forEach((pagination) => { + expect( + within(pagination).getByTestId('pagination-button-0').getAttribute('aria-current') + ).toEqual(null); + expect( + within(pagination).getByTestId('pagination-button-1').getAttribute('aria-current') + ).toEqual('true'); + }); + + expect( + within(getByTestId('grouping-level-2-pagination')) + .getByTestId('pagination-button-0') + .getAttribute('aria-current') + ).toEqual('true'); + expect( + within(getByTestId('grouping-level-2-pagination')) + .getByTestId('pagination-button-1') + .getAttribute('aria-current') + ).toEqual(null); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index e5868970f6768..99ae3f4ff3433 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -5,50 +5,29 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; -import { useDispatch } from 'react-redux'; -import { v4 as uuidv4 } from 'uuid'; +import { useDispatch, useSelector } from 'react-redux'; import type { Filter, Query } from '@kbn/es-query'; -import { buildEsQuery } from '@kbn/es-query'; -import { getEsQueryConfig } from '@kbn/data-plugin/common'; -import type { - GroupingFieldTotalAggregation, - GroupingAggregation, -} from '@kbn/securitysolution-grouping'; -import { useGrouping, isNoneGroup } from '@kbn/securitysolution-grouping'; +import type { GroupOption } from '@kbn/securitysolution-grouping'; +import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; +import { isEmpty, isEqual } from 'lodash/fp'; +import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; -import type { AlertsGroupingAggregation } from './grouping_settings/types'; +import { groupSelectors } from '../../../common/store/grouping'; +import type { State } from '../../../common/store'; +import { updateGroupSelector } from '../../../common/store/grouping/actions'; import type { Status } from '../../../../common/detection_engine/schemas/common'; -import { InspectButton } from '../../../common/components/inspect'; import { defaultUnit } from '../../../common/components/toolbar/unit'; -import { useGlobalTime } from '../../../common/containers/use_global_time'; -import { combineQueries } from '../../../common/lib/kuery'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; -import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; -import { useKibana } from '../../../common/lib/kibana'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; -import { useInspectButton } from '../alerts_kpis/common/hooks'; - -import { buildTimeRangeFilter } from './helpers'; -import * as i18n from './translations'; -import { useQueryAlerts } from '../../containers/detection_engine/alerts/use_query'; -import { ALERTS_QUERY_NAMES } from '../../containers/detection_engine/alerts/constants'; -import { - getAlertsGroupingQuery, - getDefaultGroupingOptions, - renderGroupPanel, - getStats, - useGroupTakeActionsItems, -} from './grouping_settings'; -import { updateGroupSelector, updateSelectedGroup } from '../../../common/store/grouping/actions'; +import { getDefaultGroupingOptions, renderGroupPanel, getStats } from './grouping_settings'; +import { useKibana } from '../../../common/lib/kibana'; +import { GroupedSubLevel } from './alerts_sub_grouping'; import { track } from '../../../common/lib/telemetry'; -const ALERTS_GROUPING_ID = 'alerts-grouping'; - export interface AlertsTableComponentProps { - currentAlertStatusFilterValue?: Status; + currentAlertStatusFilterValue?: Status[]; defaultFilters?: Filter[]; from: string; globalFilters: Filter[]; @@ -63,52 +42,37 @@ export interface AlertsTableComponentProps { to: string; } -export const GroupedAlertsTableComponent: React.FC = ({ - defaultFilters = [], - from, - globalFilters, - globalQuery, - hasIndexMaintenance, - hasIndexWrite, - loading, - tableId, - to, - runtimeMappings, - signalIndexName, - currentAlertStatusFilterValue, - renderChildComponent, -}) => { - const dispatch = useDispatch(); +const DEFAULT_PAGE_SIZE = 25; +const DEFAULT_PAGE_INDEX = 0; +const MAX_GROUPING_LEVELS = 3; - const { browserFields, indexPattern, selectedPatterns } = useSourcererDataView( - SourcererScopeName.detections +const useStorage = (storage: Storage, tableId: string) => + useMemo( + () => ({ + getStoragePageSize: (): number[] => { + const pageSizes = storage.get(`grouping-table-${tableId}`); + if (!pageSizes) { + return Array(MAX_GROUPING_LEVELS).fill(DEFAULT_PAGE_SIZE); + } + return pageSizes; + }, + setStoragePageSize: (pageSizes: number[]) => { + storage.set(`grouping-table-${tableId}`, pageSizes); + }, + }), + [storage, tableId] ); + +const GroupedAlertsTableComponent: React.FC = (props) => { + const dispatch = useDispatch(); + + const { indexPattern, selectedPatterns } = useSourcererDataView(SourcererScopeName.detections); + const { - services: { uiSettings, telemetry }, + services: { storage, telemetry }, } = useKibana(); - const getGlobalQuery = useCallback( - (customFilters: Filter[]) => { - if (browserFields != null && indexPattern != null) { - return combineQueries({ - config: getEsQueryConfig(uiSettings), - dataProviders: [], - indexPattern, - browserFields, - filters: [ - ...(defaultFilters ?? []), - ...globalFilters, - ...customFilters, - ...buildTimeRangeFilter(from, to), - ], - kqlQuery: globalQuery, - kqlMode: globalQuery.language, - }); - } - return null; - }, - [browserFields, indexPattern, uiSettings, defaultFilters, globalFilters, from, to, globalQuery] - ); + const { getStoragePageSize, setStoragePageSize } = useStorage(storage, props.tableId); const { onGroupChange, onGroupToggle } = useMemo( () => ({ @@ -125,153 +89,146 @@ export const GroupedAlertsTableComponent: React.FC = [telemetry] ); - // create a unique, but stable (across re-renders) query id - const uniqueQueryId = useMemo(() => `${ALERTS_GROUPING_ID}-${uuidv4()}`, []); - - const inspect = useMemo( - () => ( - - ), - [uniqueQueryId] - ); - - const { groupSelector, getGrouping, selectedGroup, pagination } = useGrouping({ + const { groupSelector, getGrouping, selectedGroups } = useGrouping({ componentProps: { groupPanelRenderer: renderGroupPanel, groupStatsRenderer: getStats, - inspectButton: inspect, onGroupToggle, - renderChildComponent, unit: defaultUnit, }, - defaultGroupingOptions: getDefaultGroupingOptions(tableId), + defaultGroupingOptions: getDefaultGroupingOptions(props.tableId), fields: indexPattern.fields, - groupingId: tableId, + groupingId: props.tableId, + maxGroupingLevels: MAX_GROUPING_LEVELS, onGroupChange, tracker: track, }); - const resetPagination = pagination.reset; - useEffect(() => { - dispatch(updateGroupSelector({ groupSelector })); - }, [dispatch, groupSelector]); + const getGroupSelector = groupSelectors.getGroupSelector(); - useEffect(() => { - dispatch(updateSelectedGroup({ selectedGroup })); - }, [dispatch, selectedGroup]); - - useInvalidFilterQuery({ - id: tableId, - filterQuery: getGlobalQuery([])?.filterQuery, - kqlError: getGlobalQuery([])?.kqlError, - query: globalQuery, - startDate: from, - endDate: to, - }); + const groupSelectorInRedux = useSelector((state: State) => getGroupSelector(state)); + const selectorOptions = useRef([]); - const { deleteQuery, setQuery } = useGlobalTime(false); - const additionalFilters = useMemo(() => { - resetPagination(); - try { - return [ - buildEsQuery(undefined, globalQuery != null ? [globalQuery] : [], [ - ...(globalFilters?.filter((f) => f.meta.disabled === false) ?? []), - ...(defaultFilters ?? []), - ]), - ]; - } catch (e) { - return []; + useEffect(() => { + if ( + isNoneGroup(selectedGroups) && + groupSelector.props.options.length > 0 && + (groupSelectorInRedux == null || + !isEqual(selectorOptions.current, groupSelector.props.options)) + ) { + selectorOptions.current = groupSelector.props.options; + dispatch(updateGroupSelector({ groupSelector })); + } else if (!isNoneGroup(selectedGroups) && groupSelectorInRedux !== null) { + dispatch(updateGroupSelector({ groupSelector: null })); } - }, [defaultFilters, globalFilters, globalQuery, resetPagination]); + }, [dispatch, groupSelector, groupSelectorInRedux, selectedGroups]); - const queryGroups = useMemo( - () => - getAlertsGroupingQuery({ - additionalFilters, - selectedGroup, - from, - runtimeMappings, - to, - pageSize: pagination.pageSize, - pageIndex: pagination.pageIndex, - }), - [ - additionalFilters, - selectedGroup, - from, - runtimeMappings, - to, - pagination.pageSize, - pagination.pageIndex, - ] + const [pageIndex, setPageIndex] = useState( + Array(MAX_GROUPING_LEVELS).fill(DEFAULT_PAGE_INDEX) ); + const [pageSize, setPageSize] = useState(getStoragePageSize); - const { - data: alertsGroupsData, - loading: isLoadingGroups, - refetch, - request, - response, - setQuery: setAlertsQuery, - } = useQueryAlerts< - {}, - GroupingAggregation & - GroupingFieldTotalAggregation - >({ - query: queryGroups, - indexName: signalIndexName, - queryName: ALERTS_QUERY_NAMES.ALERTS_GROUPING, - skip: isNoneGroup(selectedGroup), - }); + const resetAllPagination = useCallback(() => { + setPageIndex((curr) => curr.map(() => DEFAULT_PAGE_INDEX)); + }, []); useEffect(() => { - if (!isNoneGroup(selectedGroup)) { - setAlertsQuery(queryGroups); - } - }, [queryGroups, selectedGroup, setAlertsQuery]); + resetAllPagination(); + }, [resetAllPagination, selectedGroups]); + + const setPageVar = useCallback( + (newNumber: number, groupingLevel: number, pageType: 'index' | 'size') => { + if (pageType === 'index') { + setPageIndex((currentIndex) => { + const newArr = [...currentIndex]; + newArr[groupingLevel] = newNumber; + return newArr; + }); + } - useInspectButton({ - deleteQuery, - loading: isLoadingGroups, - response, - setQuery, - refetch, - request, - uniqueQueryId, - }); + if (pageType === 'size') { + setPageSize((currentIndex) => { + const newArr = [...currentIndex]; + newArr[groupingLevel] = newNumber; + setStoragePageSize(newArr); + return newArr; + }); + } + }, + [setStoragePageSize] + ); - const takeActionItems = useGroupTakeActionsItems({ - indexName: indexPattern.title, - currentStatus: currentAlertStatusFilterValue, - showAlertStatusActions: hasIndexWrite && hasIndexMaintenance, + const nonGroupingFilters = useRef({ + defaultFilters: props.defaultFilters, + globalFilters: props.globalFilters, + globalQuery: props.globalQuery, }); - const getTakeActionItems = useCallback( - (groupFilters: Filter[], groupNumber: number) => - takeActionItems({ - query: getGlobalQuery([...(defaultFilters ?? []), ...groupFilters])?.filterQuery, - tableId, - groupNumber, - selectedGroup, - }), - [defaultFilters, getGlobalQuery, selectedGroup, tableId, takeActionItems] - ); + useEffect(() => { + const nonGrouping = { + defaultFilters: props.defaultFilters, + globalFilters: props.globalFilters, + globalQuery: props.globalQuery, + }; + if (!isEqual(nonGroupingFilters.current, nonGrouping)) { + resetAllPagination(); + nonGroupingFilters.current = nonGrouping; + } + }, [props.defaultFilters, props.globalFilters, props.globalQuery, resetAllPagination]); + + const getLevel = useCallback( + (level: number, selectedGroup: string, parentGroupingFilter?: string) => { + let rcc; + if (level < selectedGroups.length - 1) { + rcc = (groupingFilters: Filter[]) => { + return getLevel( + level + 1, + selectedGroups[level + 1], + JSON.stringify([ + ...groupingFilters, + ...(parentGroupingFilter ? JSON.parse(parentGroupingFilter) : []), + ]) + ); + }; + } else { + rcc = (groupingFilters: Filter[]) => { + return props.renderChildComponent([ + ...groupingFilters, + ...(parentGroupingFilter ? JSON.parse(parentGroupingFilter) : []), + ]); + }; + } - const groupedAlerts = useMemo( - () => - getGrouping({ - data: alertsGroupsData?.aggregations, - isLoading: loading || isLoadingGroups, - takeActionItems: getTakeActionItems, - }), - [alertsGroupsData?.aggregations, getGrouping, getTakeActionItems, isLoadingGroups, loading] + const resetGroupChildrenPagination = (parentLevel: number) => { + setPageIndex((allPages) => { + const resetPages = allPages.splice(parentLevel + 1, allPages.length); + return [...allPages, ...resetPages.map(() => DEFAULT_PAGE_INDEX)]; + }); + }; + return ( + resetGroupChildrenPagination(level)} + pageIndex={pageIndex[level] ?? DEFAULT_PAGE_INDEX} + pageSize={pageSize[level] ?? DEFAULT_PAGE_SIZE} + parentGroupingFilter={parentGroupingFilter} + renderChildComponent={rcc} + selectedGroup={selectedGroup} + setPageIndex={(newIndex: number) => setPageVar(newIndex, level, 'index')} + setPageSize={(newSize: number) => setPageVar(newSize, level, 'size')} + /> + ); + }, + [getGrouping, pageIndex, pageSize, props, selectedGroups, setPageVar] ); if (isEmpty(selectedPatterns)) { return null; } - return groupedAlerts; + return getLevel(0, selectedGroups[0]); }; export const GroupedAlertsTable = React.memo(GroupedAlertsTableComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx new file mode 100644 index 0000000000000..cf8a8128cb166 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx @@ -0,0 +1,259 @@ +/* + * 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, useEffect, useMemo } from 'react'; +import { v4 as uuidv4 } from 'uuid'; +import type { Filter, Query } from '@kbn/es-query'; +import { buildEsQuery } from '@kbn/es-query'; +import type { GroupingAggregation } from '@kbn/securitysolution-grouping'; +import { isNoneGroup } from '@kbn/securitysolution-grouping'; +import { getEsQueryConfig } from '@kbn/data-plugin/common'; +import type { DynamicGroupingProps } from '@kbn/securitysolution-grouping/src'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; +import { combineQueries } from '../../../common/lib/kuery'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; +import type { AlertsGroupingAggregation } from './grouping_settings/types'; +import type { Status } from '../../../../common/detection_engine/schemas/common'; +import { InspectButton } from '../../../common/components/inspect'; +import { useSourcererDataView } from '../../../common/containers/sourcerer'; +import { useKibana } from '../../../common/lib/kibana'; +import { useGlobalTime } from '../../../common/containers/use_global_time'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; +import { useInspectButton } from '../alerts_kpis/common/hooks'; +import { buildTimeRangeFilter } from './helpers'; + +import * as i18n from './translations'; +import { useQueryAlerts } from '../../containers/detection_engine/alerts/use_query'; +import { ALERTS_QUERY_NAMES } from '../../containers/detection_engine/alerts/constants'; +import { getAlertsGroupingQuery, useGroupTakeActionsItems } from './grouping_settings'; + +const ALERTS_GROUPING_ID = 'alerts-grouping'; + +interface OwnProps { + currentAlertStatusFilterValue?: Status[]; + defaultFilters?: Filter[]; + from: string; + getGrouping: ( + props: Omit, 'groupSelector' | 'pagination'> + ) => React.ReactElement; + globalFilters: Filter[]; + globalQuery: Query; + groupingLevel?: number; + hasIndexMaintenance: boolean; + hasIndexWrite: boolean; + loading: boolean; + onGroupClose: () => void; + pageIndex: number; + pageSize: number; + parentGroupingFilter?: string; + renderChildComponent: (groupingFilters: Filter[]) => React.ReactElement; + runtimeMappings: MappingRuntimeFields; + selectedGroup: string; + setPageIndex: (newIndex: number) => void; + setPageSize: (newSize: number) => void; + signalIndexName: string | null; + tableId: TableIdLiteral; + to: string; +} + +export type AlertsTableComponentProps = OwnProps; + +export const GroupedSubLevelComponent: React.FC = ({ + currentAlertStatusFilterValue, + defaultFilters = [], + from, + getGrouping, + globalFilters, + globalQuery, + groupingLevel, + hasIndexMaintenance, + hasIndexWrite, + loading, + onGroupClose, + pageIndex, + pageSize, + parentGroupingFilter, + renderChildComponent, + runtimeMappings, + selectedGroup, + setPageIndex, + setPageSize, + signalIndexName, + tableId, + to, +}) => { + const { + services: { uiSettings }, + } = useKibana(); + const { browserFields, indexPattern } = useSourcererDataView(SourcererScopeName.detections); + + const getGlobalQuery = useCallback( + (customFilters: Filter[]) => { + if (browserFields != null && indexPattern != null) { + return combineQueries({ + config: getEsQueryConfig(uiSettings), + dataProviders: [], + indexPattern, + browserFields, + filters: [ + ...(defaultFilters ?? []), + ...globalFilters, + ...customFilters, + ...(parentGroupingFilter ? JSON.parse(parentGroupingFilter) : []), + ...buildTimeRangeFilter(from, to), + ], + kqlQuery: globalQuery, + kqlMode: globalQuery.language, + }); + } + return null; + }, + [ + browserFields, + defaultFilters, + from, + globalFilters, + globalQuery, + indexPattern, + parentGroupingFilter, + to, + uiSettings, + ] + ); + + const additionalFilters = useMemo(() => { + try { + return [ + buildEsQuery(undefined, globalQuery != null ? [globalQuery] : [], [ + ...(globalFilters?.filter((f) => f.meta.disabled === false) ?? []), + ...(defaultFilters ?? []), + ...(parentGroupingFilter ? JSON.parse(parentGroupingFilter) : []), + ]), + ]; + } catch (e) { + return []; + } + }, [defaultFilters, globalFilters, globalQuery, parentGroupingFilter]); + + const queryGroups = useMemo(() => { + return getAlertsGroupingQuery({ + additionalFilters, + selectedGroup, + from, + runtimeMappings, + to, + pageSize, + pageIndex, + }); + }, [additionalFilters, from, pageIndex, pageSize, runtimeMappings, selectedGroup, to]); + + const emptyGlobalQuery = useMemo(() => getGlobalQuery([]), [getGlobalQuery]); + + useInvalidFilterQuery({ + id: tableId, + filterQuery: emptyGlobalQuery?.filterQuery, + kqlError: emptyGlobalQuery?.kqlError, + query: globalQuery, + startDate: from, + endDate: to, + }); + + const { + data: alertsGroupsData, + loading: isLoadingGroups, + refetch, + request, + response, + setQuery: setAlertsQuery, + } = useQueryAlerts<{}, GroupingAggregation>({ + query: queryGroups, + indexName: signalIndexName, + queryName: ALERTS_QUERY_NAMES.ALERTS_GROUPING, + skip: isNoneGroup([selectedGroup]), + }); + + useEffect(() => { + if (!isNoneGroup([selectedGroup])) { + setAlertsQuery(queryGroups); + } + }, [queryGroups, selectedGroup, setAlertsQuery]); + + const { deleteQuery, setQuery } = useGlobalTime(); + // create a unique, but stable (across re-renders) query id + const uniqueQueryId = useMemo(() => `${ALERTS_GROUPING_ID}-${uuidv4()}`, []); + + useInspectButton({ + deleteQuery, + loading: isLoadingGroups, + refetch, + request, + response, + setQuery, + uniqueQueryId, + }); + + const inspect = useMemo( + () => ( + + ), + [uniqueQueryId] + ); + + const takeActionItems = useGroupTakeActionsItems({ + indexName: indexPattern.title, + currentStatus: currentAlertStatusFilterValue, + showAlertStatusActions: hasIndexWrite && hasIndexMaintenance, + }); + + const getTakeActionItems = useCallback( + (groupFilters: Filter[], groupNumber: number) => + takeActionItems({ + groupNumber, + query: getGlobalQuery([...(defaultFilters ?? []), ...groupFilters])?.filterQuery, + selectedGroup, + tableId, + }), + [defaultFilters, getGlobalQuery, selectedGroup, tableId, takeActionItems] + ); + + return useMemo( + () => + getGrouping({ + activePage: pageIndex, + data: alertsGroupsData?.aggregations, + groupingLevel, + inspectButton: inspect, + isLoading: loading || isLoadingGroups, + itemsPerPage: pageSize, + onChangeGroupsItemsPerPage: (size: number) => setPageSize(size), + onChangeGroupsPage: (index) => setPageIndex(index), + renderChildComponent, + onGroupClose, + selectedGroup, + takeActionItems: getTakeActionItems, + }), + [ + alertsGroupsData?.aggregations, + getGrouping, + getTakeActionItems, + groupingLevel, + inspect, + isLoadingGroups, + loading, + pageIndex, + pageSize, + renderChildComponent, + onGroupClose, + selectedGroup, + setPageIndex, + setPageSize, + ] + ); +}; + +export const GroupedSubLevel = React.memo(GroupedSubLevelComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.test.tsx index f84305dcb3b37..d663b2abc2c61 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.test.tsx @@ -30,7 +30,7 @@ describe('useGroupTakeActionsItems', () => { groupNumber: 0, selectedGroup: 'test', }; - it('returns array take actions items available for alerts table if showAlertStatusActions is true', async () => { + it('returns all take actions items if showAlertStatusActions is true and currentStatus is undefined', async () => { await act(async () => { const { result, waitForNextUpdate } = renderHook( () => @@ -47,7 +47,106 @@ describe('useGroupTakeActionsItems', () => { }); }); - it('returns empty array of take actions items available for alerts table if showAlertStatusActions is false', async () => { + it('returns all take actions items if currentStatus is []', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useGroupTakeActionsItems({ + currentStatus: [], + indexName: '.alerts-security.alerts-default', + showAlertStatusActions: true, + }), + { + wrapper: wrapperContainer, + } + ); + await waitForNextUpdate(); + expect(result.current(getActionItemsParams).length).toEqual(3); + }); + }); + + it('returns all take actions items if currentStatus.length > 1', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useGroupTakeActionsItems({ + currentStatus: ['open', 'closed'], + indexName: '.alerts-security.alerts-default', + showAlertStatusActions: true, + }), + { + wrapper: wrapperContainer, + } + ); + await waitForNextUpdate(); + expect(result.current(getActionItemsParams).length).toEqual(3); + }); + }); + + it('returns acknowledged & closed take actions items if currentStatus === ["open"]', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useGroupTakeActionsItems({ + currentStatus: ['open'], + indexName: '.alerts-security.alerts-default', + showAlertStatusActions: true, + }), + { + wrapper: wrapperContainer, + } + ); + await waitForNextUpdate(); + const currentParams = result.current(getActionItemsParams); + expect(currentParams.length).toEqual(2); + expect(currentParams[0].key).toEqual('acknowledge'); + expect(currentParams[1].key).toEqual('close'); + }); + }); + + it('returns open & acknowledged take actions items if currentStatus === ["closed"]', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useGroupTakeActionsItems({ + currentStatus: ['closed'], + indexName: '.alerts-security.alerts-default', + showAlertStatusActions: true, + }), + { + wrapper: wrapperContainer, + } + ); + await waitForNextUpdate(); + const currentParams = result.current(getActionItemsParams); + expect(currentParams.length).toEqual(2); + expect(currentParams[0].key).toEqual('open'); + expect(currentParams[1].key).toEqual('acknowledge'); + }); + }); + + it('returns open & closed take actions items if currentStatus === ["acknowledged"]', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useGroupTakeActionsItems({ + currentStatus: ['acknowledged'], + indexName: '.alerts-security.alerts-default', + showAlertStatusActions: true, + }), + { + wrapper: wrapperContainer, + } + ); + await waitForNextUpdate(); + const currentParams = result.current(getActionItemsParams); + expect(currentParams.length).toEqual(2); + expect(currentParams[0].key).toEqual('open'); + expect(currentParams[1].key).toEqual('close'); + }); + }); + + it('returns empty take actions items if showAlertStatusActions is false', async () => { await act(async () => { const { result, waitForNextUpdate } = renderHook( () => @@ -63,4 +162,20 @@ describe('useGroupTakeActionsItems', () => { expect(result.current(getActionItemsParams).length).toEqual(0); }); }); + it('returns array take actions items if showAlertStatusActions is true', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useGroupTakeActionsItems({ + indexName: '.alerts-security.alerts-default', + showAlertStatusActions: true, + }), + { + wrapper: wrapperContainer, + } + ); + await waitForNextUpdate(); + expect(result.current(getActionItemsParams).length).toEqual(3); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx index d2baadb99d124..5d151d2e4cc88 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo, useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { Status } from '../../../../../common/detection_engine/schemas/common'; @@ -30,8 +30,9 @@ import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import * as i18n from '../translations'; import { getTelemetryEvent, METRIC_TYPE, track } from '../../../../common/lib/telemetry'; import type { StartServices } from '../../../../types'; + export interface TakeActionsProps { - currentStatus?: Status; + currentStatus?: Status[]; indexName: string; showAlertStatusActions?: boolean; } @@ -182,7 +183,7 @@ export const useGroupTakeActionsItems = ({ ] ); - const items = useMemo(() => { + return useMemo(() => { const getActionItems = ({ query, tableId, @@ -196,61 +197,89 @@ export const useGroupTakeActionsItems = ({ }) => { const actionItems: JSX.Element[] = []; if (showAlertStatusActions) { - if (currentStatus !== FILTER_OPEN) { - actionItems.push( - - onClickUpdate({ - groupNumber, - query, - selectedGroup, - status: FILTER_OPEN as AlertWorkflowStatus, - tableId, - }) - } - > - {BULK_ACTION_OPEN_SELECTED} - - ); - } - if (currentStatus !== FILTER_ACKNOWLEDGED) { - actionItems.push( - - onClickUpdate({ - groupNumber, - query, - selectedGroup, - status: FILTER_ACKNOWLEDGED as AlertWorkflowStatus, - tableId, - }) - } - > - {BULK_ACTION_ACKNOWLEDGED_SELECTED} - - ); - } - if (currentStatus !== FILTER_CLOSED) { - actionItems.push( - - onClickUpdate({ - groupNumber, - query, - selectedGroup, - status: FILTER_CLOSED as AlertWorkflowStatus, - tableId, - }) - } - > - {BULK_ACTION_CLOSE_SELECTED} - + if (currentStatus && currentStatus.length === 1) { + const singleStatus = currentStatus[0]; + if (singleStatus !== FILTER_OPEN) { + actionItems.push( + + onClickUpdate({ + groupNumber, + query, + selectedGroup, + status: FILTER_OPEN as AlertWorkflowStatus, + tableId, + }) + } + > + {BULK_ACTION_OPEN_SELECTED} + + ); + } + if (singleStatus !== FILTER_ACKNOWLEDGED) { + actionItems.push( + + onClickUpdate({ + groupNumber, + query, + selectedGroup, + status: FILTER_ACKNOWLEDGED as AlertWorkflowStatus, + tableId, + }) + } + > + {BULK_ACTION_ACKNOWLEDGED_SELECTED} + + ); + } + if (singleStatus !== FILTER_CLOSED) { + actionItems.push( + + onClickUpdate({ + groupNumber, + query, + selectedGroup, + status: FILTER_CLOSED as AlertWorkflowStatus, + tableId, + }) + } + > + {BULK_ACTION_CLOSE_SELECTED} + + ); + } + } else { + const statusArr = { + [FILTER_OPEN]: BULK_ACTION_OPEN_SELECTED, + [FILTER_ACKNOWLEDGED]: BULK_ACTION_ACKNOWLEDGED_SELECTED, + [FILTER_CLOSED]: BULK_ACTION_CLOSE_SELECTED, + }; + Object.keys(statusArr).forEach((workflowStatus) => + actionItems.push( + + onClickUpdate({ + groupNumber, + query, + selectedGroup, + status: workflowStatus as AlertWorkflowStatus, + tableId, + }) + } + > + {statusArr[workflowStatus]} + + ) ); } } @@ -259,6 +288,4 @@ export const useGroupTakeActionsItems = ({ return getActionItems; }, [currentStatus, onClickUpdate, showAlertStatusActions]); - - return items; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts new file mode 100644 index 0000000000000..9e0b4e63715aa --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts @@ -0,0 +1,1736 @@ +/* + * 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 { mockAlertSearchResponse } from '../../../../common/components/alerts_treemap/lib/mocks/mock_alert_search_response'; + +export const groupingSearchResponse = { + ruleName: { + ...mockAlertSearchResponse, + hits: { + total: { + value: 6048, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + groupsCount: { + value: 32, + }, + groupByFields: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: ['critical hosts [Duplicate]', 'f'], + key_as_string: 'critical hosts [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical hosts [Duplicate] [Duplicate]', 'f'], + key_as_string: 'critical hosts [Duplicate] [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate]', 'f'], + key_as_string: 'high hosts [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate] [Duplicate]', 'f'], + key_as_string: 'high hosts [Duplicate] [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate]', 'f'], + key_as_string: 'low hosts [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate] [Duplicate]', 'f'], + key_as_string: 'low hosts [Duplicate] [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate]', 'f'], + key_as_string: 'medium hosts [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate] [Duplicate]', 'f'], + key_as_string: 'medium hosts [Duplicate] [Duplicate]|f', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical users [Duplicate]', 'f'], + key_as_string: 'critical users [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['critical users [Duplicate] [Duplicate]', 'f'], + key_as_string: 'critical users [Duplicate] [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['high users [Duplicate]', 'f'], + key_as_string: 'high users [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['high users [Duplicate] [Duplicate]', 'f'], + key_as_string: 'high users [Duplicate] [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['low users [Duplicate]', 'f'], + key_as_string: 'low users [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['low users [Duplicate] [Duplicate]', 'f'], + key_as_string: 'low users [Duplicate] [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['medium users [Duplicate]', 'f'], + key_as_string: 'medium users [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['medium users [Duplicate] [Duplicate]', 'f'], + key_as_string: 'medium users [Duplicate] [Duplicate]|f', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['critical hosts', 'f'], + key_as_string: 'critical hosts|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], + key_as_string: 'critical hosts [Duplicate] [Duplicate] [Duplicate]|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts', 'f'], + key_as_string: 'high hosts|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], + key_as_string: 'high hosts [Duplicate] [Duplicate] [Duplicate]|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts ', 'f'], + key_as_string: 'low hosts |f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], + key_as_string: 'low hosts [Duplicate] [Duplicate] [Duplicate]|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts', 'f'], + key_as_string: 'medium hosts|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], + key_as_string: 'medium hosts [Duplicate] [Duplicate] [Duplicate]|f', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical users [Duplicate] [Duplicate] [Duplicate]', 'f'], + key_as_string: 'critical users [Duplicate] [Duplicate] [Duplicate]|f', + doc_count: 91, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 91, + }, + { + key: 'rule', + doc_count: 91, + }, + ], + }, + unitsCount: { + value: 91, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 91, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + ], + }, + unitsCount: { + value: 6048, + }, + }, + }, + hostName: { + ...mockAlertSearchResponse, + hits: { + total: { + value: 900, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + groupsCount: { + value: 40, + }, + groupByFields: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-f0m6ngo8fo', + doc_count: 75, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 75, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 75, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 25, + }, + }, + { + key: 'Host-4aijlqggv8', + doc_count: 63, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 63, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 63, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 21, + }, + }, + { + key: 'Host-e50lhbdm91', + doc_count: 51, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 51, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 51, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 17, + }, + }, + { + key: 'sqp', + doc_count: 42, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 42, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 42, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'sUl', + doc_count: 33, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 33, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 33, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'vLJ', + doc_count: 30, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 30, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 30, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'Host-n28uwmsqmd', + doc_count: 27, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 27, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 27, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 9, + }, + }, + { + key: 'JaE', + doc_count: 27, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 27, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 27, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'CUA', + doc_count: 24, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 24, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 24, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'FWT', + doc_count: 24, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 24, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 24, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'ZqT', + doc_count: 24, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 24, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 24, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'mmn', + doc_count: 24, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 24, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 24, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'xRS', + doc_count: 24, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 24, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 24, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'HiC', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'Host-d7zbfvl3zz', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 7, + }, + }, + { + key: 'Nnc', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'OqH', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'Vaw', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'XPg', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'qBS', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'rwt', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'xVJ', + doc_count: 21, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 21, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 21, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'Bxg', + doc_count: 18, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 18, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 18, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'efP', + doc_count: 18, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 18, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 18, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: 'qcb', + doc_count: 18, + rulesCountAggregation: { + value: 3, + }, + unitsCount: { + value: 18, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 18, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + ], + }, + unitsCount: { + value: 900, + }, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 624b343c14cf9..921a8d3e3d43f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -42,8 +42,8 @@ export const getAlertsGroupingQuery = ({ getGroupingQuery({ additionalFilters, from, - groupByFields: !isNoneGroup(selectedGroup) ? getGroupFields(selectedGroup) : [], - statsAggregations: !isNoneGroup(selectedGroup) + groupByFields: !isNoneGroup([selectedGroup]) ? getGroupFields(selectedGroup) : [], + statsAggregations: !isNoneGroup([selectedGroup]) ? getAggregationsByGroupField(selectedGroup) : [], pageNumber: pageIndex * pageSize, @@ -51,7 +51,7 @@ export const getAlertsGroupingQuery = ({ { unitsCount: { value_count: { field: selectedGroup } }, }, - ...(!isNoneGroup(selectedGroup) + ...(!isNoneGroup([selectedGroup]) ? [{ groupsCount: { cardinality: { field: selectedGroup } } }] : []), ], diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx deleted file mode 100644 index 346e4b51df72d..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx +++ /dev/null @@ -1,261 +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 { render } from '@testing-library/react'; -import type { Filter } from '@kbn/es-query'; -import useResizeObserver from 'use-resize-observer/polyfilled'; - -import '../../../common/mock/match_media'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, - TestProviders, -} from '../../../common/mock'; -import type { AlertsTableComponentProps } from './alerts_grouping'; -import { GroupedAlertsTableComponent } from './alerts_grouping'; -import { TableId } from '@kbn/securitysolution-data-table'; -import { useSourcererDataView } from '../../../common/containers/sourcerer'; -import type { UseFieldBrowserOptionsProps } from '../../../timelines/components/fields_browser'; -import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context'; -import { mockTimelines } from '../../../common/mock/mock_timelines_plugin'; -import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; -import type { State } from '../../../common/store'; -import { createStore } from '../../../common/store'; -import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; - -jest.mock('@kbn/securitysolution-grouping'); - -jest.mock('../../../common/containers/sourcerer'); -jest.mock('../../../common/containers/use_global_time', () => ({ - useGlobalTime: jest.fn().mockReturnValue({ - from: '2020-07-07T08:20:18.966Z', - isInitializing: false, - to: '2020-07-08T08:20:18.966Z', - setQuery: jest.fn(), - }), -})); - -jest.mock('./grouping_settings', () => ({ - getAlertsGroupingQuery: jest.fn(), - getDefaultGroupingOptions: () => [ - { label: 'ruleName', key: 'kibana.alert.rule.name' }, - { label: 'userName', key: 'user.name' }, - { label: 'hostName', key: 'host.name' }, - { label: 'sourceIP', key: 'source.ip' }, - ], - getSelectedGroupBadgeMetrics: jest.fn(), - getSelectedGroupButtonContent: jest.fn(), - getSelectedGroupCustomMetrics: jest.fn(), - useGroupTakeActionsItems: jest.fn(), -})); - -const mockDispatch = jest.fn(); -jest.mock('react-redux', () => { - const original = jest.requireActual('react-redux'); - - return { - ...original, - useDispatch: () => mockDispatch, - }; -}); -jest.mock('../../../common/utils/normalize_time_range'); - -const mockUseFieldBrowserOptions = jest.fn(); -jest.mock('../../../timelines/components/fields_browser', () => ({ - useFieldBrowserOptions: (props: UseFieldBrowserOptionsProps) => mockUseFieldBrowserOptions(props), -})); - -const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; -jest.mock('use-resize-observer/polyfilled'); -mockUseResizeObserver.mockImplementation(() => ({})); - -const mockFilterManager = createFilterManagerMock(); - -const mockKibanaServices = createStartServicesMock(); - -jest.mock('../../../common/lib/kibana', () => { - const original = jest.requireActual('../../../common/lib/kibana'); - - return { - ...original, - useUiSetting$: jest.fn().mockReturnValue([]), - useKibana: () => ({ - services: { - ...mockKibanaServices, - application: { - navigateToUrl: jest.fn(), - capabilities: { - siem: { crud_alerts: true, read_alerts: true }, - }, - }, - cases: { - ui: { getCasesContext: mockCasesContext }, - }, - uiSettings: { - get: jest.fn(), - }, - timelines: { ...mockTimelines }, - data: { - query: { - filterManager: mockFilterManager, - }, - }, - docLinks: { - links: { - siem: { - privileges: 'link', - }, - }, - }, - storage: { - get: jest.fn(), - set: jest.fn(), - }, - triggerActionsUi: { - getAlertsStateTable: jest.fn(() => <>), - alertsTableConfigurationRegistry: {}, - }, - }, - }), - useToasts: jest.fn().mockReturnValue({ - addError: jest.fn(), - addSuccess: jest.fn(), - addWarning: jest.fn(), - remove: jest.fn(), - }), - }; -}); -const state: State = { - ...mockGlobalState, -}; -const { storage } = createSecuritySolutionStorageMock(); -const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - -const groupingStore = createStore( - { - ...state, - groups: { - groupSelector: <>, - selectedGroup: 'host.name', - }, - }, - SUB_PLUGINS_REDUCER, - kibanaObservable, - storage -); - -jest.mock('./timeline_actions/use_add_bulk_to_timeline', () => ({ - useAddBulkToTimelineAction: jest.fn(() => {}), -})); - -const sourcererDataView = { - indicesExist: true, - loading: false, - indexPattern: { - fields: [], - }, - browserFields: {}, -}; -const renderChildComponent = (groupingFilters: Filter[]) =>

    ; - -const testProps: AlertsTableComponentProps = { - defaultFilters: [], - from: '2020-07-07T08:20:18.966Z', - globalFilters: [], - globalQuery: { - query: 'query', - language: 'language', - }, - hasIndexMaintenance: true, - hasIndexWrite: true, - loading: false, - renderChildComponent, - runtimeMappings: {}, - signalIndexName: 'test', - tableId: TableId.test, - to: '2020-07-08T08:20:18.966Z', -}; - -const resetPagination = jest.fn(); - -describe('GroupedAlertsTable', () => { - const getGrouping = jest.fn().mockReturnValue(); - beforeEach(() => { - jest.clearAllMocks(); - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - selectedPatterns: ['myFakebeat-*'], - }); - (isNoneGroup as jest.Mock).mockReturnValue(true); - (useGrouping as jest.Mock).mockReturnValue({ - groupSelector: <>, - getGrouping, - selectedGroup: 'host.name', - pagination: { pageSize: 1, pageIndex: 0, reset: resetPagination }, - }); - }); - - it('calls the proper initial dispatch actions for groups', () => { - render( - - - - ); - expect(mockDispatch).toHaveBeenCalledTimes(2); - expect(mockDispatch.mock.calls[0][0].type).toEqual( - 'x-pack/security_solution/groups/UPDATE_GROUP_SELECTOR' - ); - expect(mockDispatch.mock.calls[1][0].type).toEqual( - 'x-pack/security_solution/groups/UPDATE_SELECTED_GROUP' - ); - }); - - it('renders grouping table', async () => { - (isNoneGroup as jest.Mock).mockReturnValue(false); - - const { getByTestId } = render( - - - - ); - expect(getByTestId('grouping-table')).toBeInTheDocument(); - expect(getGrouping.mock.calls[0][0].isLoading).toEqual(false); - }); - - it('renders loading when expected', () => { - (isNoneGroup as jest.Mock).mockReturnValue(false); - render( - - - - ); - expect(getGrouping.mock.calls[0][0].isLoading).toEqual(true); - }); - - it('resets grouping pagination when global query updates', () => { - (isNoneGroup as jest.Mock).mockReturnValue(false); - const { rerender } = render( - - - - ); - // called on initial query definition - expect(resetPagination).toHaveBeenCalledTimes(1); - rerender( - - - - ); - expect(resetPagination).toHaveBeenCalledTimes(2); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx index 8bd48a222ed7c..8bf6b99b21b00 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx @@ -15,7 +15,6 @@ import { EuiIcon, EuiToolTip, EuiFlexGrid, - EuiBetaBadge, } from '@elastic/eui'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; @@ -37,7 +36,6 @@ import type { RequiredFieldArray, Threshold, } from '../../../../../common/detection_engine/rule_schema'; -import { minimumLicenseForSuppression } from '../../../../../common/detection_engine/rule_schema'; import * as i18n from './translations'; import type { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './types'; @@ -50,8 +48,9 @@ import type { import { GroupByOptions } from '../../../pages/detection_engine/rules/types'; import { defaultToEmptyTag } from '../../../../common/components/empty_value'; import { ThreatEuiFlexGroup } from './threat_description'; +import { TechnicalPreviewBadge } from './technical_preview_badge'; import type { LicenseService } from '../../../../../common/license'; - +import { AlertSuppressionMissingFieldsStrategy } from '../../../../../common/detection_engine/rule_schema'; const NoteDescriptionContainer = styled(EuiFlexItem)` height: 105px; overflow-y: hidden; @@ -535,21 +534,7 @@ export const buildAlertSuppressionDescription = ( ); - const title = ( - <> - {label} - - {!license.isAtLeast(minimumLicenseForSuppression) && ( - - - - )} - - ); + const title = ; return [ { title, @@ -569,21 +554,30 @@ export const buildAlertSuppressionWindowDescription = ( ? `${value.value}${value.unit}` : i18n.ALERT_SUPPRESSION_PER_RULE_EXECUTION; - const title = ( - <> - {label} - - {!license.isAtLeast(minimumLicenseForSuppression) && ( - - - - )} - - ); + const title = ; + return [ + { + title, + description, + }, + ]; +}; + +export const buildAlertSuppressionMissingFieldsDescription = ( + label: string, + value: AlertSuppressionMissingFieldsStrategy, + license: LicenseService +): ListItems[] => { + if (isEmpty(value)) { + return []; + } + + const description = + value === AlertSuppressionMissingFieldsStrategy.Suppress + ? i18n.ALERT_SUPPRESSION_SUPPRESS_ON_MISSING_FIELDS + : i18n.ALERT_SUPPRESSION_DO_NOT_SUPPRESS_ON_MISSING_FIELDS; + + const title = ; return [ { title, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx index fe49cbded8fb2..36e4706665547 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx @@ -46,6 +46,7 @@ import { buildRequiredFieldsDescription, buildAlertSuppressionDescription, buildAlertSuppressionWindowDescription, + buildAlertSuppressionMissingFieldsDescription, } from './helpers'; import { buildMlJobsDescription } from './build_ml_jobs_description'; import { buildActionsDescription } from './actions_description'; @@ -216,6 +217,13 @@ export const getDescriptionItem = ( } else { return []; } + } else if (field === 'suppressionMissingFields') { + if (get('groupByFields', data).length > 0) { + const value = get(field, data); + return buildAlertSuppressionMissingFieldsDescription(label, value, license); + } else { + return []; + } } else if (field === 'eqlOptions') { const eqlOptions: EqlOptionsSelected = get(field, data); return buildEqlOptionsDescription(eqlOptions); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/technical_preview_badge.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/technical_preview_badge.tsx new file mode 100644 index 0000000000000..605f471f8f3ae --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/technical_preview_badge.tsx @@ -0,0 +1,35 @@ +/* + * 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, EuiToolTip, EuiBetaBadge } from '@elastic/eui'; + +import type { LicenseService } from '../../../../../common/license'; +import { minimumLicenseForSuppression } from '../../../../../common/detection_engine/rule_schema'; + +import * as i18n from './translations'; + +interface TechnicalPreviewBadgeProps { + label: string; + license: LicenseService; +} + +export const TechnicalPreviewBadge = ({ label, license }: TechnicalPreviewBadgeProps) => ( + <> + {label} + + {!license.isAtLeast(minimumLicenseForSuppression) && ( + + + + )} + +); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.ts index 626a0648d7bac..8a9742eb78dc8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.ts @@ -147,3 +147,17 @@ export const ALERT_SUPPRESSION_PER_RULE_EXECUTION = i18n.translate( defaultMessage: 'One rule execution', } ); + +export const ALERT_SUPPRESSION_SUPPRESS_ON_MISSING_FIELDS = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.alertSuppressionSuppressOnMissingFieldsDescription', + { + defaultMessage: 'Suppress on missing field value', + } +); + +export const ALERT_SUPPRESSION_DO_NOT_SUPPRESS_ON_MISSING_FIELDS = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.alertSuppressionDoNotSuppressOnMissingFieldsDescription', + { + defaultMessage: 'Do not suppress', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx index ad9b3c751f59f..104ea748f106c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.integration.test.tsx @@ -56,8 +56,7 @@ describe('MlAdminJobDescription', () => { userEvent.click(screen.getByTestId('job-switch')); expect(enableDatafeedSpy).toHaveBeenCalledWith( securityJobNotStarted, - securityJobNotStarted.latestTimestampMs, - true + securityJobNotStarted.latestTimestampMs ); await waitFor(() => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx index 7d4616a004364..7f1236aef08cd 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/ml_jobs_description/admin/ml_admin_job_description.tsx @@ -25,14 +25,23 @@ const MlAdminJobDescriptionComponent: FC = ({ loading, refreshJob, }) => { - const { enableDatafeed, isLoading: isLoadingEnableDataFeed } = useEnableDataFeed(); + const { + enableDatafeed, + disableDatafeed, + isLoading: isLoadingEnableDataFeed, + } = useEnableDataFeed(); const handleJobStateChange = useCallback( async (_, latestTimestampMs: number, enable: boolean) => { - await enableDatafeed(job, latestTimestampMs, enable); + if (enable) { + await enableDatafeed(job, latestTimestampMs); + } else { + await disableDatafeed(job); + } + refreshJob(job); }, - [enableDatafeed, job, refreshJob] + [enableDatafeed, disableDatafeed, job, refreshJob] ); const switchComponent = useMemo( diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx index 02566a8302937..084be38391a45 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx @@ -16,7 +16,6 @@ import type { ActionVariables, NotifyWhenSelectOptions, } from '@kbn/triggers-actions-ui-plugin/public'; -import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import type { RuleAction, RuleActionAlertsFilterProperty, @@ -24,6 +23,7 @@ import type { } from '@kbn/alerting-plugin/common'; import { SecurityConnectorFeatureId } from '@kbn/actions-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; +import { NOTIFICATION_DEFAULT_FREQUENCY } from '../../../../../common/constants'; import type { FieldHook } from '../../../../shared_imports'; import { useFormContext } from '../../../../shared_imports'; import { useKibana } from '../../../../common/lib/kibana'; @@ -33,12 +33,6 @@ import { FORM_ON_ACTIVE_ALERT_OPTION, } from './translations'; -const DEFAULT_FREQUENCY = { - notifyWhen: RuleNotifyWhen.ACTIVE, - throttle: null, - summary: true, -}; - const NOTIFY_WHEN_OPTIONS: NotifyWhenSelectOptions[] = [ { isSummaryOption: true, @@ -193,12 +187,35 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = const setActionAlertsFilterProperty = useCallback( (key: string, value: RuleActionAlertsFilterProperty, index: number) => { + field.setValue((prevValue: RuleAction[]) => { + const updatedActions = [...prevValue]; + const { alertsFilter, ...rest } = updatedActions[index]; + const updatedAlertsFilter = { ...alertsFilter }; + + if (value) { + updatedAlertsFilter[key] = value; + } else { + delete updatedAlertsFilter[key]; + } + + updatedActions[index] = { + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), + }; + return updatedActions; + }); + }, + [field] + ); + + const setActionFrequency = useCallback( + (key: string, value: RuleActionParam, index: number) => { field.setValue((prevValue: RuleAction[]) => { const updatedActions = [...prevValue]; updatedActions[index] = { ...updatedActions[index], - alertsFilter: { - ...(updatedActions[index].alertsFilter ?? { query: null, timeframe: null }), + frequency: { + ...(updatedActions[index].frequency ?? NOTIFICATION_DEFAULT_FREQUENCY), [key]: value, }, }; @@ -217,22 +234,22 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = setActionIdByIndex, setActions: setAlertActionsProperty, setActionParamsProperty, - setActionFrequencyProperty: () => {}, + setActionFrequencyProperty: setActionFrequency, setActionAlertsFilterProperty, featureId: SecurityConnectorFeatureId, defaultActionMessage: DEFAULT_ACTION_MESSAGE, defaultSummaryMessage: DEFAULT_ACTION_MESSAGE, hideActionHeader: true, - hideNotifyWhen: true, hasSummary: true, notifyWhenSelectOptions: NOTIFY_WHEN_OPTIONS, - defaultRuleFrequency: DEFAULT_FREQUENCY, + defaultRuleFrequency: NOTIFICATION_DEFAULT_FREQUENCY, showActionAlertsFilter: true, }), [ actions, getActionForm, messageVariables, + setActionFrequency, setActionIdByIndex, setActionParamsProperty, setAlertActionsProperty, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx index f5345f42f810b..aca8fe27abd0f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx @@ -96,7 +96,13 @@ const RuleActionsOverflowComponent = ({ ids: [rule.id], duplicatePayload: { include_exceptions: - modalDuplicationConfirmationResult === DuplicateOptions.withExceptions, + modalDuplicationConfirmationResult === DuplicateOptions.withExceptions || + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions, + include_expired_exceptions: !( + modalDuplicationConfirmationResult === + DuplicateOptions.withExceptionsExcludeExpiredExceptions + ), }, }); 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 01062c45693ce..37cd412ca4b8f 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 @@ -7,6 +7,7 @@ import type { EuiButtonGroupOptionProps } from '@elastic/eui'; import { + EuiAccordion, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, @@ -88,7 +89,10 @@ import { defaultCustomQuery } from '../../../pages/detection_engine/rules/utils' import { getIsRulePreviewDisabled } from '../rule_preview/helpers'; import { GroupByFields } from '../group_by_fields'; import { useLicense } from '../../../../common/hooks/use_license'; -import { minimumLicenseForSuppression } from '../../../../../common/detection_engine/rule_schema'; +import { + minimumLicenseForSuppression, + AlertSuppressionMissingFieldsStrategy, +} from '../../../../../common/detection_engine/rule_schema'; import { DurationInput } from '../duration_input'; const CommonUseField = getUseField({ component: Field }); @@ -179,6 +183,7 @@ const StepDefineRuleComponent: FC = ({ 'groupByRadioSelection', 'groupByDuration.value', 'groupByDuration.unit', + 'suppressionMissingFields', ], onChange: (data: DefineStepRule) => { if (onRuleDataChange) { @@ -561,6 +566,34 @@ const StepDefineRuleComponent: FC = ({ [license, groupByFields] ); + const AlertsSuppressionMissingFields = useCallback( + ({ suppressionMissingFields }) => ( + { + suppressionMissingFields.setValue(id); + }} + data-test-subj="suppressionMissingFieldsOptions" + /> + ), + [license, groupByFields] + ); + const dataViewIndexPatternToggleButtonOptions: EuiButtonGroupOptionProps[] = useMemo( () => [ { @@ -868,41 +901,68 @@ const StepDefineRuleComponent: FC = ({ )} - + - - - - + + - {GroupByChildren} - - + + + + + + {GroupByChildren} + + + + + + {AlertsSuppressionMissingFields} + + + + <> diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx index edd50d5582fd5..b895378aa8308 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx @@ -622,6 +622,14 @@ export const schema: FormSchema = { value: {}, unit: {}, }, + suppressionMissingFields: { + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.suppressionMissingFieldsLabel', + { + defaultMessage: 'If “Suppress by” field does not exist', + } + ), + }, newTermsFields: { type: FIELD_TYPES.COMBO_BOX, label: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx index 4650da2ce603a..78e282729c75c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx @@ -149,3 +149,31 @@ export const RULE_PREVIEW_TITLE = i18n.translate( defaultMessage: 'Rule Preview', } ); + +export const ALERT_SUPPRESSION_ACCORDION_BUTTON = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.alertSuppressionAccordionButtonLabel', + { + defaultMessage: 'Suppression Configuration', + } +); + +export const ALERT_SUPPRESSION_MISSING_FIELDS_FORM_ROW_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.alertSuppressionMissingFieldsLabel', + { + defaultMessage: 'If “Suppress by” field does not exist', + } +); + +export const ALERT_SUPPRESSION_MISSING_FIELDS_SUPPRESS_OPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.alertSuppressionMissingFieldsSuppressLabel', + { + defaultMessage: 'Suppress on missing field value', + } +); + +export const ALERT_SUPPRESSION_MISSING_FIELDS_DO_NOT_SUPPRESS_OPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.alertSuppressionMissingFieldsDoNotSuppressLabel', + { + defaultMessage: 'Do not suppress', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts index 858578f8a5d38..f16cac0eb923a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; - import type { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; import { debouncedValidateRuleActionsField } from '../../../containers/detection_engine/rules/validate_rule_actions_field'; @@ -30,12 +28,4 @@ export const getSchema = ({ responseActions: {}, enabled: {}, kibanaSiemAppUrl: {}, - throttle: { - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel', - { - defaultMessage: 'Actions frequency', - } - ), - }, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 58dd95bcac0dd..47c33f9282572 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -15,7 +15,6 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { findIndex } from 'lodash/fp'; import type { FC } from 'react'; import React, { memo, useCallback, useEffect, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -23,28 +22,24 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { ActionVariables } from '@kbn/triggers-actions-ui-plugin/public'; import { UseArray } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; +import type { RuleObjectId } from '../../../../../common/detection_engine/rule_schema'; import { isQueryRule } from '../../../../../common/detection_engine/utils'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { ResponseActionsForm } from '../../../../detection_engine/rule_response_actions/response_actions_form'; import type { RuleStepProps, ActionsStepRule } from '../../../pages/detection_engine/rules/types'; import { RuleStep } from '../../../pages/detection_engine/rules/types'; import { StepRuleDescription } from '../description_step'; -import { Form, UseField, useForm, useFormData } from '../../../../shared_imports'; +import { Form, UseField, useForm } from '../../../../shared_imports'; import { StepContentWrapper } from '../step_content_wrapper'; -import { - ThrottleSelectField, - THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, - DEFAULT_THROTTLE_OPTION, -} from '../throttle_select_field'; import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; import { getSchema } from './get_schema'; import * as I18n from './translations'; import { APP_UI_ID } from '../../../../../common/constants'; -import { useManageCaseAction } from './use_manage_case_action'; -import { THROTTLE_FIELD_HELP_TEXT, THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY } from './translations'; +import { RuleSnoozeSection } from './rule_snooze_section'; interface StepRuleActionsProps extends RuleStepProps { + ruleId?: RuleObjectId; // Rule SO's id (not ruleId) defaultValues?: ActionsStepRule | null; actionMessageParams: ActionVariables; ruleType?: Type; @@ -55,23 +50,10 @@ export const stepActionsDefaultValue: ActionsStepRule = { actions: [], responseActions: [], kibanaSiemAppUrl: '', - throttle: DEFAULT_THROTTLE_OPTION.value, }; const GhostFormField = () => <>; -const getThrottleOptions = (throttle?: string | null) => { - // Add support for throttle options set by the API - if ( - throttle && - findIndex(['value', throttle], THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING) < 0 - ) { - return [...THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, { value: throttle, text: throttle }]; - } - - return THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING; -}; - const DisplayActionsHeader = () => { return ( <> @@ -89,6 +71,7 @@ const DisplayActionsHeader = () => { }; const StepRuleActionsComponent: FC = ({ + ruleId, addPadding = false, defaultValues, isReadOnlyView, @@ -99,7 +82,6 @@ const StepRuleActionsComponent: FC = ({ actionMessageParams, ruleType, }) => { - const [isLoadingCaseAction] = useManageCaseAction(); const { services: { application, @@ -127,11 +109,6 @@ const StepRuleActionsComponent: FC = ({ schema, }); const { getFields, getFormData, submit } = form; - const [{ throttle: formThrottle }] = useFormData({ - form, - watch: ['throttle'], - }); - const throttle = formThrottle || initialState.throttle; const handleSubmit = useCallback( (enabled: boolean) => { @@ -163,44 +140,20 @@ const StepRuleActionsComponent: FC = ({ }; }, [getData, setForm]); - const throttleOptions = useMemo(() => { - return getThrottleOptions(throttle); - }, [throttle]); - - const throttleFieldComponentProps = useMemo( - () => ({ - idAria: 'detectionEngineStepRuleActionsThrottle', - isDisabled: isLoading, - isLoading: isLoadingCaseAction, - dataTestSubj: 'detectionEngineStepRuleActionsThrottle', - hasNoInitialSelection: false, - helpText: isQueryRule(ruleType) - ? THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY - : THROTTLE_FIELD_HELP_TEXT, - euiFieldProps: { - options: throttleOptions, - }, - }), - [isLoading, isLoadingCaseAction, ruleType, throttleOptions] - ); - const displayActionsOptions = useMemo( - () => - throttle !== stepActionsDefaultValue.throttle ? ( - <> - - - - ) : ( - - ), - [throttle, actionMessageParams] + () => ( + <> + + + + ), + [actionMessageParams] ); const displayResponseActionsOptions = useMemo(() => { if (isQueryRule(ruleType)) { @@ -217,36 +170,23 @@ const StepRuleActionsComponent: FC = ({ return application.capabilities.actions.show ? ( <> - + {ruleId && } {displayActionsOptions} {responseActionsEnabled && displayResponseActionsOptions} - ) : ( <> {I18n.NO_ACTIONS_READ_PERMISSIONS} - - - - ); }, [ + ruleId, application.capabilities.actions.show, displayActionsOptions, displayResponseActionsOptions, responseActionsEnabled, - throttleFieldComponentProps, ]); if (isReadOnlyView) { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/rule_snooze_section.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/rule_snooze_section.tsx new file mode 100644 index 0000000000000..d9586f80f3e93 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/rule_snooze_section.tsx @@ -0,0 +1,42 @@ +/* + * 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 { css } from '@emotion/react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; +import type { RuleObjectId } from '../../../../../common/detection_engine/rule_schema'; +import { RuleSnoozeBadge } from '../../../../detection_engine/rule_management/components/rule_snooze_badge'; +import * as i18n from './translations'; + +interface RuleSnoozeSectionProps { + ruleId: RuleObjectId; // Rule SO's id (not ruleId) +} + +export function RuleSnoozeSection({ ruleId }: RuleSnoozeSectionProps): JSX.Element { + const { euiTheme } = useEuiTheme(); + + return ( +

    + {i18n.RULE_SNOOZE_DESCRIPTION} + + + + + + + {i18n.SNOOZED_ACTIONS_WARNING} + + + +
    + ); +} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index 15937883fb409..06368eadc30df 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -29,18 +29,17 @@ export const NO_ACTIONS_READ_PERMISSIONS = i18n.translate( } ); -export const THROTTLE_FIELD_HELP_TEXT = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText', +export const RULE_SNOOZE_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.snoozeDescription', { defaultMessage: 'Select when automated actions should be performed if a rule evaluates as true.', } ); -export const THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery', +export const SNOOZED_ACTIONS_WARNING = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.snoozedActionsWarning', { - defaultMessage: - 'Select when automated actions should be performed if a rule evaluates as true. This frequency does not apply to Response Actions.', + defaultMessage: 'Actions will not be preformed until it is unsnoozed.', } ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx deleted file mode 100644 index 57dd5670dba80..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useEffect, useRef, useState } from 'react'; -import { getAllConnectorsUrl, getCreateConnectorUrl } from '@kbn/cases-plugin/common'; -import { convertArrayToCamelCase, KibanaServices } from '../../../../common/lib/kibana'; - -interface CaseAction { - connectorTypeId: string; - id: string; - isPreconfigured: boolean; - name: string; - referencedByCount: number; -} - -const CASE_ACTION_NAME = 'Cases'; - -export const useManageCaseAction = () => { - const hasInit = useRef(true); - const [loading, setLoading] = useState(true); - const [hasError, setHasError] = useState(false); - - useEffect(() => { - const abortCtrl = new AbortController(); - const fetchActions = async () => { - try { - const actions = convertArrayToCamelCase( - await KibanaServices.get().http.fetch(getAllConnectorsUrl(), { - method: 'GET', - signal: abortCtrl.signal, - }) - ) as CaseAction[]; - - if (!actions.some((a) => a.connectorTypeId === '.case' && a.name === CASE_ACTION_NAME)) { - await KibanaServices.get().http.post(getCreateConnectorUrl(), { - method: 'POST', - body: JSON.stringify({ - connector_type_id: '.case', - config: {}, - name: CASE_ACTION_NAME, - secrets: {}, - }), - signal: abortCtrl.signal, - }); - } - setLoading(false); - } catch { - setLoading(false); - setHasError(true); - } - }; - if (hasInit.current) { - hasInit.current = false; - fetchActions(); - } - - return () => { - abortCtrl.abort(); - }; - }, []); - return [loading, hasError]; -}; diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx index 78d736e99c93e..92f77c3e2df91 100644 --- a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx @@ -7,7 +7,6 @@ import React, { useCallback, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { isNoneGroup } from '@kbn/securitysolution-grouping'; import { dataTableSelectors, tableDefaults, @@ -29,9 +28,6 @@ export const getPersistentControlsHook = (tableId: TableId) => { const getGroupSelector = groupSelectors.getGroupSelector(); const groupSelector = useSelector((state: State) => getGroupSelector(state)); - const getSelectedGroup = groupSelectors.getSelectedGroup(); - - const selectedGroup = useSelector((state: State) => getSelectedGroup(state)); const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); @@ -88,10 +84,10 @@ export const getPersistentControlsHook = (tableId: TableId) => { hasRightOffset={false} additionalFilters={additionalFiltersComponent} showInspect={false} - additionalMenuOptions={isNoneGroup(selectedGroup) ? [groupSelector] : []} + additionalMenuOptions={groupSelector != null ? [groupSelector] : []} /> ), - [tableView, handleChangeTableView, additionalFiltersComponent, groupSelector, selectedGroup] + [tableView, handleChangeTableView, additionalFiltersComponent, groupSelector] ); return { diff --git a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx index edca07bd3426f..210c700c2eb37 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alert_details/tabs/summary/host_panel/host_panel.test.tsx @@ -20,6 +20,28 @@ import { getTimelineEventData } from '../../../utils/get_timeline_event_data'; import { RiskSeverity } from '../../../../../../../common/search_strategy'; import { useRiskScore } from '../../../../../../explore/containers/risk_score'; +jest.mock('../../../../../../management/hooks', () => { + const Generator = jest.requireActual( + '../../../../../../../common/endpoint/data_generators/endpoint_metadata_generator' + ); + + return { + useGetEndpointDetails: jest.fn(() => { + return { + data: new Generator.EndpointMetadataGenerator('seed').generateHostInfo({ + metadata: { + Endpoint: { + state: { + isolation: true, + }, + }, + }, + }), + }; + }), + }; +}); + jest.mock('../../../../../../explore/containers/risk_score'); const mockUseRiskScore = useRiskScore as jest.Mock; @@ -76,7 +98,7 @@ describe('AlertDetailsPage - SummaryTab - HostPanel', () => { describe('Agent status', () => { it('should show healthy', () => { const { getByTestId } = render(); - expect(getByTestId('host-panel-agent-status')).toHaveTextContent('Healthy'); + expect(getByTestId('endpointHostAgentStatus').textContent).toEqual('HealthyIsolated'); }); }); 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 2d3a55c6b0cd4..6a7bdc526e750 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 @@ -10,7 +10,11 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; -import { ALERTS_PATH, SecurityPageName } from '../../../../common/constants'; +import { + ALERTS_PATH, + ALERT_DETAILS_REDIRECT_PATH, + SecurityPageName, +} from '../../../../common/constants'; import { NotFoundPage } from '../../../app/404'; import * as i18n from './translations'; import { DetectionEnginePage } from '../detection_engine/detection_engine'; @@ -31,7 +35,7 @@ const AlertsContainerComponent: React.FC = () => { {/* Redirect to the alerts page filtered for the given alert id */} - + ); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 64e489599dd05..13836e658eee7 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import React from 'react'; -import { mount } from 'enzyme'; +import React, { useEffect } from 'react'; +import { render, waitFor } from '@testing-library/react'; import { useParams } from 'react-router-dom'; -import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { createSecuritySolutionStorageMock, @@ -29,6 +28,10 @@ import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_cont import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { createStubDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { useListsConfig } from '../../containers/detection_engine/lists/use_lists_config'; +import type { FilterGroupProps } from '../../../common/components/filter_group/types'; +import { FilterGroup } from '../../../common/components/filter_group'; +import type { AlertsTableComponentProps } from '../../components/alerts_table/alerts_grouping'; // Test will fail because we will to need to mock some core services to make the test work // For now let's forget about SiemSearchBar and QueryBar @@ -38,6 +41,27 @@ jest.mock('../../../common/components/search_bar', () => ({ jest.mock('../../../common/components/query_bar', () => ({ QueryBar: () => null, })); +jest.mock('../../../common/hooks/use_space_id', () => ({ + useSpaceId: () => 'default', +})); +jest.mock('../../../common/components/filter_group'); + +const mockStatusCapture = jest.fn(); +const GroupedAlertsTable: React.FC = ({ + currentAlertStatusFilterValue, +}) => { + useEffect(() => { + if (currentAlertStatusFilterValue) { + mockStatusCapture(currentAlertStatusFilterValue); + } + }, [currentAlertStatusFilterValue]); + return ; +}; + +jest.mock('../../components/alerts_table/alerts_grouping', () => ({ + GroupedAlertsTable, +})); + jest.mock('../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../components/user_info'); jest.mock('../../../common/containers/sourcerer'); @@ -158,9 +182,11 @@ jest.mock('../../../common/components/page/use_refetch_by_session'); describe('DetectionEnginePageComponent', () => { beforeAll(() => { + (useListsConfig as jest.Mock).mockReturnValue({ loading: false, needsConfiguration: false }); (useParams as jest.Mock).mockReturnValue({}); (useUserData as jest.Mock).mockReturnValue([ { + loading: false, hasIndexRead: true, canUserREAD: true, }, @@ -170,10 +196,15 @@ describe('DetectionEnginePageComponent', () => { indexPattern: {}, browserFields: mockBrowserFields, }); + (FilterGroup as jest.Mock).mockImplementation(() => { + return ; + }); + }); + beforeEach(() => { + jest.clearAllMocks(); }); - it('renders correctly', async () => { - const wrapper = mount( + const { getByTestId } = render( @@ -181,12 +212,12 @@ describe('DetectionEnginePageComponent', () => { ); await waitFor(() => { - expect(wrapper.find('FiltersGlobal').exists()).toBe(true); + expect(getByTestId('filter-group__loading')).toBeInTheDocument(); }); }); it('renders the chart panels', async () => { - const wrapper = mount( + const { getByTestId } = render( @@ -195,7 +226,119 @@ describe('DetectionEnginePageComponent', () => { ); await waitFor(() => { - expect(wrapper.find('[data-test-subj="chartPanels"]').exists()).toBe(true); + expect(getByTestId('chartPanels')).toBeInTheDocument(); + }); + }); + + it('the pageFiltersUpdateHandler updates status when a multi status filter is passed', async () => { + (FilterGroup as jest.Mock).mockImplementationOnce(({ onFilterChange }: FilterGroupProps) => { + if (onFilterChange) { + // once with status + onFilterChange([ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.workflow_status', + params: ['open', 'acknowledged'], + }, + }, + ]); + } + return ; + }); + await waitFor(() => { + render( + + + + + + ); + }); + // when statusFilter updates, we call mockStatusCapture in test mocks + expect(mockStatusCapture).toHaveBeenNthCalledWith(1, []); + expect(mockStatusCapture).toHaveBeenNthCalledWith(2, ['open', 'acknowledged']); + }); + + it('the pageFiltersUpdateHandler updates status when a single status filter is passed', async () => { + (FilterGroup as jest.Mock).mockImplementationOnce(({ onFilterChange }: FilterGroupProps) => { + if (onFilterChange) { + // once with status + onFilterChange([ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.workflow_status', + disabled: false, + }, + query: { + match_phrase: { + 'kibana.alert.workflow_status': 'open', + }, + }, + }, + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.severity', + disabled: false, + }, + query: { + match_phrase: { + 'kibana.alert.severity': 'low', + }, + }, + }, + ]); + } + return ; + }); + await waitFor(() => { + render( + + + + + + ); + }); + // when statusFilter updates, we call mockStatusCapture in test mocks + expect(mockStatusCapture).toHaveBeenNthCalledWith(1, []); + expect(mockStatusCapture).toHaveBeenNthCalledWith(2, ['open']); + }); + + it('the pageFiltersUpdateHandler clears status when no status filter is passed', async () => { + (FilterGroup as jest.Mock).mockImplementationOnce(({ onFilterChange }: FilterGroupProps) => { + if (onFilterChange) { + // once with status + onFilterChange([ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.severity', + disabled: false, + }, + query: { + match_phrase: { + 'kibana.alert.severity': 'low', + }, + }, + }, + ]); + } + return ; + }); + await waitFor(() => { + render( + + + + + + ); }); + // when statusFilter updates, we call mockStatusCapture in test mocks + expect(mockStatusCapture).toHaveBeenNthCalledWith(1, []); + expect(mockStatusCapture).toHaveBeenNthCalledWith(2, []); }); }); 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 0eacecd46e8fc..1ccfaea4584ee 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 @@ -29,7 +29,6 @@ import { dataTableActions, dataTableSelectors, tableDefaults, - FILTER_OPEN, TableId, } from '@kbn/securitysolution-data-table'; import { ALERTS_TABLE_REGISTRY_CONFIG_IDS } from '../../../../common/constants'; @@ -139,7 +138,7 @@ const DetectionEnginePageComponent: React.FC = ({ const arePageFiltersEnabled = useIsExperimentalFeatureEnabled('alertsPageFiltersEnabled'); // when arePageFiltersEnabled === false - const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); + const [statusFilter, setStatusFilter] = useState([]); const updatedAt = useShallowEqualSelector( (state) => (getTable(state, TableId.alertsOnAlertsPage) ?? tableDefaults).updated @@ -177,8 +176,8 @@ const DetectionEnginePageComponent: React.FC = ({ if (arePageFiltersEnabled) { return detectionPageFilters; } - return buildAlertStatusFilter(filterGroup); - }, [filterGroup, detectionPageFilters, arePageFiltersEnabled]); + return buildAlertStatusFilter(statusFilter[0] ?? 'open'); + }, [statusFilter, detectionPageFilters, arePageFiltersEnabled]); useEffect(() => { if (!detectionPageFilterHandler) return; @@ -276,6 +275,19 @@ const DetectionEnginePageComponent: React.FC = ({ const pageFiltersUpdateHandler = useCallback((newFilters: Filter[]) => { setDetectionPageFilters(newFilters); + if (newFilters.length) { + const newStatusFilter = newFilters.find( + (filter) => filter.meta.key === 'kibana.alert.workflow_status' + ); + if (newStatusFilter) { + const status: Status[] = newStatusFilter.meta.params + ? (newStatusFilter.meta.params as Status[]) + : [newStatusFilter.query?.match_phrase['kibana.alert.workflow_status']]; + setStatusFilter(status); + } else { + setStatusFilter([]); + } + } }, []); // Callback for when open/closed filter changes @@ -284,9 +296,9 @@ const DetectionEnginePageComponent: React.FC = ({ const timelineId = TableId.alertsOnAlertsPage; clearEventsLoading({ id: timelineId }); clearEventsDeleted({ id: timelineId }); - setFilterGroup(newFilterGroup); + setStatusFilter([newFilterGroup]); }, - [clearEventsLoading, clearEventsDeleted, setFilterGroup] + [clearEventsLoading, clearEventsDeleted, setStatusFilter] ); const areDetectionPageFiltersLoading = useMemo(() => { @@ -317,7 +329,7 @@ const DetectionEnginePageComponent: React.FC = ({ @@ -352,7 +364,7 @@ const DetectionEnginePageComponent: React.FC = ({ [ arePageFiltersEnabled, dataViewId, - filterGroup, + statusFilter, filters, onFilterGroupChangedCallback, pageFiltersUpdateHandler, @@ -462,7 +474,7 @@ const DetectionEnginePageComponent: React.FC = ({ { moment.suppressDeprecationWarnings = true; @@ -123,6 +125,7 @@ describe('rule helpers', () => { groupByRadioSelection: 'per-rule-execution', newTermsFields: ['host.name'], historyWindowSize: '7d', + suppressionMissingFields: expect.any(String), }; const aboutRuleStepData: AboutStepRule = { @@ -146,7 +149,6 @@ describe('rule helpers', () => { const scheduleRuleStepData = { from: '0s', interval: '5m' }; const ruleActionsStepData = { enabled: true, - throttle: 'no_actions', actions: [], responseActions: undefined, }; @@ -224,13 +226,8 @@ describe('rule helpers', () => { describe('getDefineStepsData', () => { test('returns with saved_id if value exists on rule', () => { const result: DefineStepRule = getDefineStepsData(mockRule('test-id')); - const expected = { + const expected = expect.objectContaining({ ruleType: 'saved_query', - anomalyThreshold: 50, - dataSourceType: 'indexPatterns', - dataViewId: undefined, - machineLearningJobId: [], - index: ['auditbeat-*'], queryBar: { query: { query: '', @@ -239,41 +236,8 @@ describe('rule helpers', () => { filters: [], saved_id: "Garrett's IP", }, - relatedIntegrations: [], - requiredFields: [], - threshold: { - field: [], - value: '100', - }, - threatIndex: [], - threatMapping: [], - threatQueryBar: { - query: { - query: '', - language: '', - }, - filters: [], - saved_id: null, - }, - timeline: { - id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', - title: 'Untitled timeline', - }, - eqlOptions: { - timestampField: undefined, - eventCategoryField: undefined, - tiebreakerField: undefined, - }, - groupByFields: [], - groupByDuration: { - value: 5, - unit: 'm', - }, - groupByRadioSelection: 'per-rule-execution', - newTermsFields: [], - historyWindowSize: '7d', shouldLoadQueryDynamically: true, - }; + }); expect(result).toEqual(expected); }); @@ -284,13 +248,8 @@ describe('rule helpers', () => { }; delete mockedRule.saved_id; const result: DefineStepRule = getDefineStepsData(mockedRule); - const expected = { + const expected = expect.objectContaining({ ruleType: 'saved_query', - anomalyThreshold: 50, - dataSourceType: 'indexPatterns', - dataViewId: undefined, - machineLearningJobId: [], - index: ['auditbeat-*'], queryBar: { query: { query: '', @@ -299,41 +258,8 @@ describe('rule helpers', () => { filters: [], saved_id: null, }, - relatedIntegrations: [], - requiredFields: [], - threshold: { - field: [], - value: '100', - }, - threatIndex: [], - threatMapping: [], - threatQueryBar: { - query: { - query: '', - language: '', - }, - filters: [], - saved_id: null, - }, - timeline: { - id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', - title: 'Untitled timeline', - }, - eqlOptions: { - timestampField: undefined, - eventCategoryField: undefined, - tiebreakerField: undefined, - }, - groupByFields: [], - groupByDuration: { - value: 5, - unit: 'm', - }, - groupByRadioSelection: 'per-rule-execution', - newTermsFields: [], - historyWindowSize: '7d', shouldLoadQueryDynamically: false, - }; + }); expect(result).toEqual(expected); }); @@ -347,6 +273,32 @@ describe('rule helpers', () => { expect(result.timeline.id).toBeNull(); expect(result.timeline.title).toBeNull(); }); + + describe('suppression on missing fields', () => { + test('returns default suppress value in suppress strategy is missing', () => { + const result: DefineStepRule = getDefineStepsData(mockRule('test-id')); + const expected = expect.objectContaining({ + suppressionMissingFields: AlertSuppressionMissingFieldsStrategy.Suppress, + }); + + expect(result).toEqual(expected); + }); + + test('returns suppress value if rule is configured with missing_fields_strategy', () => { + const result: DefineStepRule = getDefineStepsData({ + ...mockRule('test-id'), + alert_suppression: { + group_by: [], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + }); + const expected = expect.objectContaining({ + suppressionMissingFields: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }); + + expect(result).toEqual(expected); + }); + }); }); describe('getHumanizedDuration', () => { @@ -410,16 +362,22 @@ describe('rule helpers', () => { describe('getActionsStepsData', () => { test('returns expected ActionsStepRule rule object', () => { + const actions: RuleAlertAction[] = [ + { + id: 'id', + group: 'group', + params: {}, + action_type_id: 'action_type_id', + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + ]; const mockedRule = { ...mockRule('test-id'), - actions: [ - { - id: 'id', - group: 'group', - params: {}, - action_type_id: 'action_type_id', - }, - ], + actions, }; const result: ActionsStepRule = getActionsStepsData(mockedRule); const expected = { @@ -429,11 +387,15 @@ describe('rule helpers', () => { group: 'group', params: {}, actionTypeId: 'action_type_id', + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, }, ], responseActions: undefined, enabled: mockedRule.enabled, - throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index a290ac92f6a66..9d7c89bbfac02 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -23,6 +23,7 @@ import type { Filter } from '@kbn/es-query'; import type { ActionVariables } from '@kbn/triggers-actions-ui-plugin/public'; import type { ResponseAction } from '../../../../../common/detection_engine/rule_response_actions/schemas'; import { normalizeThresholdField } from '../../../../../common/detection_engine/utils'; +import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../common/detection_engine/rule_schema'; import type { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { assertUnreachable } from '../../../../../common/utility_types'; import { @@ -76,12 +77,11 @@ export const getActionsStepsData = ( response_actions?: ResponseAction[]; } ): ActionsStepRule => { - const { enabled, throttle, meta, actions = [], response_actions: responseActions } = rule; + const { enabled, meta, actions = [], response_actions: responseActions } = rule; return { actions: actions?.map(transformRuleToAlertAction), responseActions: responseActions?.map(transformRuleToAlertResponseAction), - throttle, kibanaSiemAppUrl: meta?.kibana_siem_app_url, enabled, }; @@ -140,6 +140,8 @@ export const getDefineStepsData = (rule: Rule): DefineStepRule => ({ ? GroupByOptions.PerTimePeriod : GroupByOptions.PerRuleExecution, groupByDuration: rule.alert_suppression?.duration ?? { value: 5, unit: 'm' }, + suppressionMissingFields: + rule.alert_suppression?.missing_fields_strategy ?? DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY, }); const convertHistoryStartToSize = (relativeTime: string) => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index 9511138f78f34..487a6176969d1 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -553,6 +553,13 @@ export const COLUMN_ENABLE = i18n.translate( } ); +export const COLUMN_SNOOZE = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.snoozeTitle', + { + defaultMessage: 'Notify', + } +); + export const COLUMN_INDEXING_TIMES = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.indexingTimes', { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index edbb3b4ecbf97..05c707144a0c9 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -33,6 +33,7 @@ import type { RuleNameOverride, SetupGuide, TimestampOverride, + AlertSuppressionMissingFields, } from '../../../../../common/detection_engine/rule_schema'; import type { SortOrder } from '../../../../../common/detection_engine/schemas/common'; import type { EqlOptionsSelected } from '../../../../../common/search_strategy'; @@ -176,6 +177,7 @@ export interface DefineStepRule { groupByFields: string[]; groupByRadioSelection: GroupByOptions; groupByDuration: Duration; + suppressionMissingFields?: AlertSuppressionMissingFields; } export interface Duration { @@ -194,7 +196,6 @@ export interface ActionsStepRule { responseActions?: RuleResponseAction[]; enabled: boolean; kibanaSiemAppUrl?: string; - throttle?: string | null; } export interface DefineStepRuleJson { diff --git a/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx new file mode 100644 index 0000000000000..72ba17ad86620 --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx @@ -0,0 +1,109 @@ +/* + * 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 { ExceptionsListCard } from '.'; +import { useListDetailsView } from '../../hooks'; +import { useExceptionsListCard } from '../../hooks/use_exceptions_list.card'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { TestProviders } from '../../../common/mock'; + +jest.mock('../../hooks'); +jest.mock('../../hooks/use_exceptions_list.card'); + +const getMockUseExceptionsListCard = () => ({ + listId: 'my-list', + listName: 'Exception list', + listType: 'detection', + createdAt: '2023-02-01T10:20:30.000Z', + createdBy: 'elastic', + exceptions: [{ ...getExceptionListItemSchemaMock() }], + pagination: { pageIndex: 0, pageSize: 5, totalItemCount: 1 }, + ruleReferences: { + 'my-list': { + name: 'Exception list', + id: '345', + referenced_rules: [], + listId: 'my-list', + }, + }, + toggleAccordion: false, + openAccordionId: '123', + menuActionItems: [ + { + key: 'Export', + icon: 'exportAction', + label: 'Export', + onClick: jest.fn(), + }, + ], + listRulesCount: '5', + listDescription: 'My exception list description', + exceptionItemsCount: jest.fn(), + onEditExceptionItem: jest.fn(), + onDeleteException: jest.fn(), + onPaginationChange: jest.fn(), + setToggleAccordion: jest.fn(), + exceptionViewerStatus: '', + showAddExceptionFlyout: false, + showEditExceptionFlyout: false, + exceptionToEdit: undefined, + onAddExceptionClick: jest.fn(), + handleConfirmExceptionFlyout: jest.fn(), + handleCancelExceptionItemFlyout: jest.fn(), + goToExceptionDetail: jest.fn(), + emptyViewerTitle: 'Empty View', + emptyViewerBody: 'This is the empty view description.', + emptyViewerButtonText: 'Take action', + handleCancelExpiredExceptionsModal: jest.fn(), + handleConfirmExpiredExceptionsModal: jest.fn(), + showIncludeExpiredExceptionsModal: false, +}); +const getMockUseListDetailsView = () => ({ + linkedRules: [], + showManageRulesFlyout: false, + showManageButtonLoader: false, + disableManageButton: false, + onManageRules: jest.fn(), + onSaveManageRules: jest.fn(), + onCancelManageRules: jest.fn(), + onRuleSelectionChange: jest.fn(), +}); + +describe('ExceptionsListCard', () => { + beforeEach(() => { + (useExceptionsListCard as jest.Mock).mockReturnValue(getMockUseExceptionsListCard()); + (useListDetailsView as jest.Mock).mockReturnValue(getMockUseListDetailsView()); + }); + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + it('should display expired exception confirmation modal when "showIncludeExpiredExceptionsModal" is "true"', () => { + (useExceptionsListCard as jest.Mock).mockReturnValue({ + ...getMockUseExceptionsListCard(), + showIncludeExpiredExceptionsModal: true, + }); + + const wrapper = render( + + + + ); + expect(wrapper.getByTestId('includeExpiredExceptionsConfirmationModal')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx index eb945e18e7761..44370b274cd12 100644 --- a/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx @@ -33,7 +33,7 @@ import { ListExceptionItems } from '../list_exception_items'; import { useListDetailsView } from '../../hooks'; import { useExceptionsListCard } from '../../hooks/use_exceptions_list.card'; import { ManageRules } from '../manage_rules'; -import { ExportExceptionsListModal } from '../export_exceptions_list_modal'; +import { IncludeExpiredExceptionsModal } from '../expired_exceptions_list_items_modal'; interface ExceptionsListCardProps { exceptionsList: ExceptionListInfo; @@ -59,6 +59,17 @@ interface ExceptionsListCardProps { name: string; namespaceType: NamespaceType; }) => () => Promise; + handleDuplicate: ({ + includeExpiredExceptions, + listId, + name, + namespaceType, + }: { + includeExpiredExceptions: boolean; + listId: string; + name: string; + namespaceType: NamespaceType; + }) => () => Promise; readOnly: boolean; } const buttonCss = css` @@ -78,7 +89,7 @@ const ListHeaderContainer = styled(EuiFlexGroup)` text-align: initial; `; export const ExceptionsListCard = memo( - ({ exceptionsList, handleDelete, handleExport, readOnly }) => { + ({ exceptionsList, handleDelete, handleExport, handleDuplicate, readOnly }) => { const { linkedRules, showManageRulesFlyout, @@ -102,7 +113,6 @@ export const ExceptionsListCard = memo( toggleAccordion, openAccordionId, menuActionItems, - listRulesCount, listDescription, exceptionItemsCount, onEditExceptionItem, @@ -120,13 +130,14 @@ export const ExceptionsListCard = memo( emptyViewerTitle, emptyViewerBody, emptyViewerButtonText, - handleCancelExportModal, - handleConfirmExportModal, - showExportModal, + handleCancelExpiredExceptionsModal, + handleConfirmExpiredExceptionsModal, + showIncludeExpiredExceptionsModal, } = useExceptionsListCard({ exceptionsList, handleExport, handleDelete, + handleDuplicate, handleManageRules: onManageRules, }); @@ -184,8 +195,11 @@ export const ExceptionsListCard = memo( - - + + ( } + data-test-subj={`exceptionsManagementListCard-${listId}`} > ( onRuleSelectionChange={onRuleSelectionChange} /> ) : null} - {showExportModal ? ( - ) : null} diff --git a/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.tsx b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.tsx new file mode 100644 index 0000000000000..43ebd8197c8d5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.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 { IncludeExpiredExceptionsModal } from '.'; +import { fireEvent, render } from '@testing-library/react'; + +describe('IncludeExpiredExceptionsModal', () => { + const handleCloseModal = jest.fn(); + const onModalConfirm = jest.fn(); + + it('should call handleCloseModal on cancel click', () => { + const wrapper = render( + + ); + fireEvent.click(wrapper.getByTestId('confirmModalCancelButton')); + expect(handleCloseModal).toHaveBeenCalled(); + }); + + it('should call onModalConfirm on confirm click', () => { + const wrapper = render( + + ); + fireEvent.click(wrapper.getByTestId('confirmModalConfirmButton')); + expect(onModalConfirm).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.tsx new file mode 100644 index 0000000000000..17588e6b305a7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.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, { memo, useCallback, useState } from 'react'; + +import { EuiConfirmModal, EuiSpacer, EuiSwitch, EuiText } from '@elastic/eui'; +import * as i18n from '../../translations'; + +export const CHECK_EXCEPTION_TTL_ACTION_TYPES = { + DUPLICATE: 'duplicate', + EXPORT: 'export', +} as const; + +export type CheckExceptionTtlActionTypes = + typeof CHECK_EXCEPTION_TTL_ACTION_TYPES[keyof typeof CHECK_EXCEPTION_TTL_ACTION_TYPES]; + +interface IncludeExpiredExceptionsModalProps { + handleCloseModal: () => void; + onModalConfirm: (includeExpired: boolean) => void; + action: CheckExceptionTtlActionTypes; +} + +export const IncludeExpiredExceptionsModal = memo( + ({ handleCloseModal, onModalConfirm, action }) => { + const [includeExpired, setIncludeExpired] = useState(true); + + const handleSwitchChange = useCallback(() => { + setIncludeExpired(!includeExpired); + }, [setIncludeExpired, includeExpired]); + + const handleConfirm = useCallback(() => { + onModalConfirm(includeExpired); + handleCloseModal(); + }, [includeExpired, handleCloseModal, onModalConfirm]); + + return ( + + + {action === CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT + ? i18n.EXPIRED_EXCEPTIONS_MODAL_EXPORT_DESCRIPTION + : i18n.EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_DESCRIPTION} + + + + + ); + } +); + +IncludeExpiredExceptionsModal.displayName = 'IncludeExpiredExceptionsModal'; diff --git a/x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx deleted file mode 100644 index 7316925cc24cf..0000000000000 --- a/x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx +++ /dev/null @@ -1,50 +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, useState } from 'react'; - -import { EuiConfirmModal, EuiSwitch } from '@elastic/eui'; -import * as i18n from '../../translations'; - -interface ExportExceptionsListModalProps { - handleCloseModal: () => void; - onModalConfirm: (includeExpired: boolean) => void; -} - -export const ExportExceptionsListModal = memo( - ({ handleCloseModal, onModalConfirm }) => { - const [exportExpired, setExportExpired] = useState(true); - - const handleSwitchChange = useCallback(() => { - setExportExpired(!exportExpired); - }, [setExportExpired, exportExpired]); - - const handleConfirm = useCallback(() => { - onModalConfirm(exportExpired); - handleCloseModal(); - }, [exportExpired, handleCloseModal, onModalConfirm]); - - return ( - - - - ); - } -); - -ExportExceptionsListModal.displayName = 'ExportExceptionsListModal'; diff --git a/x-pack/plugins/security_solution/public/exceptions/components/manage_rules/index.tsx b/x-pack/plugins/security_solution/public/exceptions/components/manage_rules/index.tsx index 4a82c748da97e..6fd569022af59 100644 --- a/x-pack/plugins/security_solution/public/exceptions/components/manage_rules/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/components/manage_rules/index.tsx @@ -77,6 +77,7 @@ export const ManageRules: FC = memo( () => Promise; handleDelete: ({ id, listId, namespaceType }: ListAction) => () => Promise; + handleDuplicate: ({ + listId, + name, + namespaceType, + includeExpiredExceptions, + }: DuplicateListAction) => () => Promise; handleManageRules: () => void; }) => { const [viewerStatus, setViewerStatus] = useState(ViewerStatus.LOADING); const [exceptionToEdit, setExceptionToEdit] = useState(); const [showAddExceptionFlyout, setShowAddExceptionFlyout] = useState(false); const [showEditExceptionFlyout, setShowEditExceptionFlyout] = useState(false); - const [showExportModal, setShowExportModal] = useState(false); + const [showIncludeExpiredExceptionsModal, setShowIncludeExpiredExceptionsModal] = + useState(null); const { name: listName, @@ -135,10 +151,19 @@ export const useExceptionsListCard = ({ includeExpiredExceptions: true, })(); } else { - setShowExportModal(true); + setShowIncludeExpiredExceptionsModal(CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT); } }, }, + { + key: 'Duplicate', + icon: 'copy', + label: i18n.DUPLICATE_EXCEPTION_LIST, + disabled: listCannotBeEdited, + onClick: (_: React.MouseEvent) => { + setShowIncludeExpiredExceptionsModal(CHECK_EXCEPTION_TTL_ACTION_TYPES.DUPLICATE); + }, + }, { key: 'Delete', icon: 'trash', @@ -163,16 +188,15 @@ export const useExceptionsListCard = ({ }, ], [ + listCannotBeEdited, + listType, + handleExport, exceptionsList.id, exceptionsList.list_id, exceptionsList.name, exceptionsList.namespace_type, handleDelete, - setShowExportModal, - listCannotBeEdited, handleManageRules, - handleExport, - listType, ] ); @@ -197,24 +221,42 @@ export const useExceptionsListCard = ({ ); const onExportListClick = useCallback(() => { - setShowExportModal(true); - }, [setShowExportModal]); + setShowIncludeExpiredExceptionsModal(CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT); + }, [setShowIncludeExpiredExceptionsModal]); - const handleCancelExportModal = () => { - setShowExportModal(false); + const handleCancelExpiredExceptionsModal = () => { + setShowIncludeExpiredExceptionsModal(null); }; - const handleConfirmExportModal = useCallback( + const handleConfirmExpiredExceptionsModal = useCallback( (includeExpiredExceptions: boolean): void => { - handleExport({ - id: exceptionsList.id, - listId: exceptionsList.list_id, - name: exceptionsList.name, - namespaceType: exceptionsList.namespace_type, - includeExpiredExceptions, - })(); + if (showIncludeExpiredExceptionsModal === CHECK_EXCEPTION_TTL_ACTION_TYPES.EXPORT) { + handleExport({ + id: exceptionsList.id, + listId: exceptionsList.list_id, + name: exceptionsList.name, + namespaceType: exceptionsList.namespace_type, + includeExpiredExceptions, + })(); + } + if (showIncludeExpiredExceptionsModal === CHECK_EXCEPTION_TTL_ACTION_TYPES.DUPLICATE) { + handleDuplicate({ + listId: exceptionsList.list_id, + name: exceptionsList.name, + namespaceType: exceptionsList.namespace_type, + includeExpiredExceptions, + })(); + } }, - [handleExport, exceptionsList] + [ + showIncludeExpiredExceptionsModal, + handleExport, + exceptionsList.id, + exceptionsList.list_id, + exceptionsList.name, + exceptionsList.namespace_type, + handleDuplicate, + ] ); // routes to x-pack/plugins/security_solution/public/exceptions/routes.tsx @@ -255,9 +297,9 @@ export const useExceptionsListCard = ({ emptyViewerTitle, emptyViewerBody, emptyViewerButtonText, - showExportModal, + showIncludeExpiredExceptionsModal, onExportListClick, - handleCancelExportModal, - handleConfirmExportModal, + handleCancelExpiredExceptionsModal, + handleConfirmExpiredExceptionsModal, }; }; diff --git a/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts b/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts index d3cc8a7d221b7..99f3288379537 100644 --- a/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts +++ b/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts @@ -51,7 +51,7 @@ export const useListDetailsView = (exceptionListId: string) => { const { http, notifications } = services; const { navigateToApp } = services.application; - const { exportExceptionList, deleteExceptionList } = useApi(http); + const { exportExceptionList, deleteExceptionList, duplicateExceptionList } = useApi(http); const [{ loading: userInfoLoading, canUserCRUD, canUserREAD }] = useUserData(); @@ -190,6 +190,35 @@ export const useListDetailsView = (exceptionListId: string) => { [list, exportExceptionList, handleErrorStatus, toasts] ); + const onDuplicateList = useCallback( + async (includeExpiredExceptions: boolean) => { + try { + if (!list) return; + await duplicateExceptionList({ + listId: list.list_id, + includeExpiredExceptions, + namespaceType: list.namespace_type, + onError: (error: Error) => handleErrorStatus(error), + onSuccess: (newList: ExceptionListSchema) => { + toasts?.addSuccess(i18n.EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY(list.name)); + navigateToApp(APP_UI_ID, { + deepLinkId: SecurityPageName.exceptions, + path: `/details/${newList.list_id}`, + }); + }, + }); + } catch (error) { + handleErrorStatus( + error, + undefined, + i18n.EXCEPTION_DUPLICATE_ERROR, + i18n.EXCEPTION_DUPLICATE_ERROR_DESCRIPTION + ); + } + }, + [list, duplicateExceptionList, handleErrorStatus, toasts, navigateToApp] + ); + const handleOnDownload = useCallback(() => { setExportedList(undefined); }, []); @@ -340,6 +369,9 @@ export const useListDetailsView = (exceptionListId: string) => { i18n.EXCEPTION_MANAGE_RULES_ERROR_DESCRIPTION ); setShowManageButtonLoader(false); + }) + .finally(() => { + initializeList(); }); } catch (err) { handleErrorStatus(err); @@ -348,10 +380,11 @@ export const useListDetailsView = (exceptionListId: string) => { list, getRulesToAdd, getRulesToRemove, - exceptionListId, resetManageRulesAfterSaving, - handleErrorStatus, + exceptionListId, invalidateFetchRuleByIdQuery, + handleErrorStatus, + initializeList, ]); const onCancelManageRules = useCallback(() => { setShowManageRulesFlyout(false); @@ -380,6 +413,7 @@ export const useListDetailsView = (exceptionListId: string) => { refreshExceptions, disableManageButton, handleDelete, + onDuplicateList, onEditListDetails, onExportList, onDeleteList, diff --git a/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx b/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx index 1e5b0af5768a4..023ebe69ab2fe 100644 --- a/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx @@ -25,7 +25,8 @@ import { AutoDownload } from '../../../common/components/auto_download/auto_down import { ListWithSearch, ManageRules, ListDetailsLinkAnchor } from '../../components'; import { useListDetailsView } from '../../hooks'; import * as i18n from '../../translations'; -import { ExportExceptionsListModal } from '../../components/export_exceptions_list_modal'; +import type { CheckExceptionTtlActionTypes } from '../../components/expired_exceptions_list_items_modal'; +import { IncludeExpiredExceptionsModal } from '../../components/expired_exceptions_list_items_modal'; export const ListsDetailViewComponent: FC = () => { const { detailName: exceptionListId } = useParams<{ @@ -52,6 +53,7 @@ export const ListsDetailViewComponent: FC = () => { refreshExceptions, disableManageButton, onEditListDetails, + onDuplicateList, onExportList, onManageRules, onSaveManageRules, @@ -62,20 +64,33 @@ export const ListsDetailViewComponent: FC = () => { handleReferenceDelete, } = useListDetailsView(exceptionListId); - const [showExportModal, setShowExportModal] = useState(false); + const [showIncludeExpiredExceptionItemsModal, setShowIncludeExpiredExceptionItemsModal] = + useState(null); - const onModalClose = useCallback(() => setShowExportModal(false), [setShowExportModal]); + const onModalClose = useCallback( + () => setShowIncludeExpiredExceptionItemsModal(null), + [setShowIncludeExpiredExceptionItemsModal] + ); - const onModalOpen = useCallback(() => setShowExportModal(true), [setShowExportModal]); + const onModalOpen = useCallback( + (actionType: CheckExceptionTtlActionTypes) => { + setShowIncludeExpiredExceptionItemsModal(actionType); + }, + [setShowIncludeExpiredExceptionItemsModal] + ); const handleExportList = useCallback(() => { if (list?.type === ExceptionListTypeEnum.ENDPOINT) { onExportList(true); } else { - onModalOpen(); + onModalOpen('export'); } }, [onModalOpen, list, onExportList]); + const handleDuplicateList = useCallback(() => { + onModalOpen('duplicate'); + }, [onModalOpen]); + const detailsViewContent = useMemo(() => { if (viewerStatus === ViewerStatus.ERROR) return ; @@ -99,6 +114,7 @@ export const ListsDetailViewComponent: FC = () => { onExportList={handleExportList} onDeleteList={handleDelete} onManageRules={onManageRules} + onDuplicateList={handleDuplicateList} dataTestSubj="exceptionListManagement" /> @@ -125,47 +141,52 @@ export const ListsDetailViewComponent: FC = () => { onRuleSelectionChange={onRuleSelectionChange} /> ) : null} - {showExportModal && ( - )} ); }, [ - canUserEditList, - disableManageButton, - exportedList, - handleOnDownload, - headerBackOptions, - invalidListId, - isLoading, + viewerStatus, isReadOnly, - linkedRules, + isLoading, + invalidListId, + listName, list, listDescription, listId, - listName, + linkedRules, + canUserEditList, + headerBackOptions, + onEditListDetails, + handleExportList, + handleDelete, + onManageRules, + handleDuplicateList, + exportedList, + handleOnDownload, + refreshExceptions, referenceModalState.contentText, referenceModalState.rulesReferences, - refreshExceptions, - showManageButtonLoader, - showManageRulesFlyout, + handleCloseReferenceErrorModal, + handleReferenceDelete, showReferenceErrorModal, - showExportModal, - viewerStatus, + showManageRulesFlyout, + showManageButtonLoader, + disableManageButton, + onSaveManageRules, onCancelManageRules, - onEditListDetails, - onExportList, - onManageRules, onRuleSelectionChange, - onSaveManageRules, - handleCloseReferenceErrorModal, - handleDelete, - handleReferenceDelete, + showIncludeExpiredExceptionItemsModal, + onExportList, + onDuplicateList, onModalClose, - handleExportList, ]); return ( <> diff --git a/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx b/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx index 4fe0596941294..22b41e88bfb85 100644 --- a/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx @@ -93,7 +93,7 @@ export const SharedLists = React.memo(() => { application: { navigateToApp }, }, } = useKibana(); - const { exportExceptionList, deleteExceptionList } = useApi(http); + const { exportExceptionList, deleteExceptionList, duplicateExceptionList } = useApi(http); const [showReferenceErrorModal, setShowReferenceErrorModal] = useState(false); const [referenceModalState, setReferenceModalState] = useState( @@ -262,6 +262,45 @@ export const SharedLists = React.memo(() => { [] ); + const handleDuplicationError = useCallback( + (err: Error) => { + addError(err, { title: i18n.EXCEPTION_DUPLICATE_ERROR }); + }, + [addError] + ); + + const handleDuplicateSuccess = useCallback( + (name: string) => (): void => { + addSuccess(i18n.EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY(name)); + handleRefresh(); + }, + [addSuccess, handleRefresh] + ); + + const handleDuplicate = useCallback( + ({ + listId, + name, + namespaceType, + includeExpiredExceptions, + }: { + listId: string; + name: string; + namespaceType: NamespaceType; + includeExpiredExceptions: boolean; + }) => + async () => { + await duplicateExceptionList({ + includeExpiredExceptions, + listId, + namespaceType, + onError: handleDuplicationError, + onSuccess: handleDuplicateSuccess(name), + }); + }, + [duplicateExceptionList, handleDuplicateSuccess, handleDuplicationError] + ); + const handleCloseReferenceErrorModal = useCallback((): void => { setShowReferenceErrorModal(false); setReferenceModalState({ @@ -546,6 +585,7 @@ export const SharedLists = React.memo(() => { exceptionsList={excList} handleDelete={handleDelete} handleExport={handleExport} + handleDuplicate={handleDuplicate} /> ))}
    diff --git a/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts b/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts index 6d1f8c115fab3..e2688d804b3d2 100644 --- a/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts +++ b/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts @@ -167,3 +167,17 @@ export const EXCEPTION_EXPORT_ERROR_DESCRIPTION = i18n.translate( defaultMessage: 'An error occurred exporting a list', } ); + +export const DUPLICATE_EXCEPTION_LIST = i18n.translate( + 'xpack.securitySolution.exceptionsTable.duplicateExceptionList', + { + defaultMessage: 'Duplicate exception list', + } +); + +export const EXCEPTION_DUPLICATE_ERROR_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptionsTable.duplicateListDescription', + { + defaultMessage: 'An error occurred duplicating a list', + } +); diff --git a/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts b/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts index 6ab1ca6df8464..eb18283577369 100644 --- a/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts +++ b/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts @@ -125,6 +125,19 @@ export const EXCEPTION_EXPORT_ERROR = i18n.translate( } ); +export const EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY = (listName: string) => + i18n.translate('xpack.securitySolution.exceptions.list.duplicate_success', { + values: { listName }, + defaultMessage: 'Exception list "{listName}" duplicated successfully', + }); + +export const EXCEPTION_DUPLICATE_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.duplicateError', + { + defaultMessage: 'Exception list duplication error', + } +); + export const EXCEPTION_DELETE_ERROR = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.all.exceptions.deleteError', { @@ -371,29 +384,59 @@ export const SORT_BY_CREATE_AT = i18n.translate( } ); -export const EXPORT_MODAL_CANCEL_BUTTON = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalCancelButton', +export const EXPIRED_EXCEPTIONS_MODAL_CANCEL_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalCancelButton', { defaultMessage: 'Cancel', } ); -export const EXPORT_MODAL_TITLE = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalTitle', +export const EXPIRED_EXCEPTIONS_MODAL_EXPORT_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalExportTitle', { - defaultMessage: 'Export exception list', + defaultMessage: 'Export exception list?', } ); -export const EXPORT_MODAL_INCLUDE_SWITCH_LABEL = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel', +export const EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalDuplicateTitle', + { + defaultMessage: 'Duplicate exception list?', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeDuplicateDescription', + { + defaultMessage: + 'You’re duplicating an exception list. Switch the toggle off to exclude expired exceptions.', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_EXPORT_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeExportDescription', + { + defaultMessage: + 'You’re exporting an exception list. Switch the toggle off to exclude expired exceptions.', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_INCLUDE_SWITCH_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeSwitchLabel', { defaultMessage: 'Include expired exceptions', } ); -export const EXPORT_MODAL_CONFIRM_BUTTON = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalConfirmButton', +export const EXPIRED_EXCEPTIONS_MODAL_CONFIRM_DUPLICATE_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalConfirmDuplicateButton', + { + defaultMessage: 'Duplicate', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_CONFIRM_EXPORT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalConfirmExportButton', { defaultMessage: 'Export', } diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts b/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts index 7f2a8e37b94b0..d6147328d6be2 100644 --- a/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts +++ b/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts @@ -7,3 +7,7 @@ export const RISKY_HOSTS_DASHBOARD_TITLE = 'Current Risk Score for Hosts'; export const RISKY_USERS_DASHBOARD_TITLE = 'Current Risk Score for Users'; + +export const CELL_ACTIONS_TELEMETRY = { + component: 'RiskScoreTable', +}; diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx index f1a489ed83b73..d9b57d3da7c29 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx @@ -21,6 +21,7 @@ import type { RiskSeverity } from '../../../../../common/search_strategy'; import { RiskScoreFields, RiskScoreEntity } from '../../../../../common/search_strategy'; import { RiskScore } from '../../../components/risk_score/severity/common'; import { ENTITY_RISK_CLASSIFICATION } from '../../../components/risk_score/translations'; +import { CELL_ACTIONS_TELEMETRY } from '../../../components/risk_score/constants'; export const getHostRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -37,7 +38,7 @@ export const getHostRiskScoreColumns = ({ if (hostName != null && hostName.length > 0) { return ( diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx index 2c8eba08178d0..07b458c14760f 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx @@ -38,7 +38,7 @@ export const getHostsColumns = ( if (hostName != null && hostName.length > 0) { return ( [ return ( { title={ diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx index cf122adc0c397..e6af3c9a873f7 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx @@ -22,6 +22,7 @@ import { RiskScoreEntity, RiskScoreFields } from '../../../../../common/search_s import { UserDetailsLink } from '../../../../common/components/links'; import { UsersTableType } from '../../store/model'; import { ENTITY_RISK_CLASSIFICATION } from '../../../components/risk_score/translations'; +import { CELL_ACTIONS_TELEMETRY } from '../../../components/risk_score/constants'; export const getUserRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -40,7 +41,7 @@ export const getUserRiskScoreColumns = ({ return ( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/analyze.stories.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.stories.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/flyout/left/components/analyze.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.stories.tsx diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.tsx index 3cb908a849e4d..d0e476e724f9b 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.tsx @@ -24,7 +24,7 @@ export const ANALYZE_GRAPH_ID = 'analyze_graph'; */ export const AnalyzeGraph: FC = () => { const { eventId } = useLeftPanelContext(); - const scopeId = 'fly-out'; + const scopeId = 'flyout'; // TO-DO: update to use context const { from, to, shouldUpdate, selectedPatterns } = useTimelineDataFilters( isActiveTimeline(scopeId) ); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx new file mode 100644 index 0000000000000..40b9b7e7a5b18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx @@ -0,0 +1,44 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { SessionView } from './session_view'; +import type { LeftPanelContext } from '../context'; +import { LeftFlyoutContext } from '../context'; + +export default { + component: SessionView, + title: 'Flyout/SessionView', +}; + +// TODO to get this working, we need to spent some time getting all the foundation items for storybook +// (ReduxStoreProvider, CellActionsProvider...) similarly to how it was done for the TestProvidersComponent +// see ticket https://github.com/elastic/security-team/issues/6223 +// export const Default: Story = () => { +// const contextValue = { +// getFieldsData: () => {}, +// } as unknown as LeftPanelContext; +// +// return ( +// +// +// +// ); +// }; + +export const Error: Story = () => { + const contextValue = { + getFieldsData: () => {}, + } as unknown as LeftPanelContext; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx new file mode 100644 index 0000000000000..fbdef60088857 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx @@ -0,0 +1,99 @@ +/* + * 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 '@testing-library/jest-dom'; +import type { LeftPanelContext } from '../context'; +import { LeftFlyoutContext } from '../context'; +import { TestProviders } from '../../../common/mock'; +import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids'; +import { + SessionView, + SESSION_ENTITY_ID, + SESSION_START_TIME, + KIBANA_ANCESTOR_INDEX, +} from './session_view'; + +interface MockData { + [key: string]: string; +} + +const mockData: MockData = { + [SESSION_ENTITY_ID]: 'id', + [SESSION_START_TIME]: '2023-04-25T04:33:23.676Z', + [KIBANA_ANCESTOR_INDEX]: '.ds-logs-endpoint.events.process-default', +}; + +const mockFieldsData = (prop: string) => { + return mockData[prop]; +}; + +jest.mock('../../../common/lib/kibana', () => { + const originalModule = jest.requireActual('../../../common/lib/kibana'); + return { + ...originalModule, + useKibana: jest.fn().mockReturnValue({ + services: { + sessionView: { + getSessionView: jest.fn().mockReturnValue(
    ), + }, + }, + }), + }; +}); + +describe('', () => { + it('renders session view correctly', () => { + const contextValue = { + getFieldsData: mockFieldsData, + indexName: '.ds-logs-endpoint.events.process-default', + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('renders session view from an alert correctly', () => { + const contextValue = { + getFieldsData: mockFieldsData, + indexName: '.alerts-security', // it should prioritize KIBANA_ANCESTOR_INDEX above indexName + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('should render error message on null eventId', () => { + const contextValue = { + getFieldsData: () => {}, + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_ERROR_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getByText('Unable to display session view')).toBeInTheDocument(); + expect(wrapper.getByText('There was an error displaying session view')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx index e62745b905640..6d708f962d368 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx @@ -7,16 +7,55 @@ import type { FC } from 'react'; import React from 'react'; -import { EuiText } from '@elastic/eui'; -import { SESSION_VIEW_TEST_ID } from './test_ids'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { getField } from '../../shared/utils'; +import { ERROR_MESSAGE, ERROR_TITLE } from '../../shared/translations'; +import { SESSION_VIEW_ERROR_MESSAGE } from './translations'; +import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids'; +import { useKibana } from '../../../common/lib/kibana'; +import { useLeftPanelContext } from '../context'; +import { getSessionViewProcessIndex } from '../../../common/components/header_actions/helpers'; export const SESSION_VIEW_ID = 'session_view'; +export const SESSION_ENTITY_ID = 'process.entry_leader.entity_id'; +export const SESSION_START_TIME = 'process.entry_leader.start'; +export const KIBANA_ANCESTOR_INDEX = 'kibana.alert.ancestors.index'; /** * Session view displayed in the document details expandable flyout left section under the Visualize tab */ export const SessionView: FC = () => { - return {'Session view'}; + const { sessionView } = useKibana().services; + const { getFieldsData, indexName } = useLeftPanelContext(); + + const processIndex = getSessionViewProcessIndex( + getField(getFieldsData(KIBANA_ANCESTOR_INDEX)) || indexName + ); + const sessionEntityId = getField(getFieldsData(SESSION_ENTITY_ID)); + const sessionStartTime = getField(getFieldsData(SESSION_START_TIME)); + + if (!processIndex || !sessionEntityId || !sessionStartTime) { + return ( + {ERROR_TITLE(SESSION_VIEW_ERROR_MESSAGE)}} + body={

    {ERROR_MESSAGE(SESSION_VIEW_ERROR_MESSAGE)}

    } + data-test-subj={SESSION_VIEW_ERROR_TEST_ID} + /> + ); + } + + return ( +
    + {sessionView.getSessionView({ + processIndex, + sessionEntityId, + sessionStartTime, + isFullScreen: true, + })} +
    + ); }; SessionView.displayName = 'SessionView'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts index 8b2804fae3e9f..40cf67fddb180 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts @@ -9,6 +9,7 @@ export const ANALYZER_GRAPH_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnal export const ANALYZE_GRAPH_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnalyzerGraphError'; export const SESSION_VIEW_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionView'; +export const SESSION_VIEW_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionViewError'; export const ENTITIES_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesDetails'; export const THREAT_INTELLIGENCE_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutThreatIntelligenceDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts index 8c59c8a101fb0..f82d34c859ddf 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts @@ -13,3 +13,10 @@ export const ANALYZER_ERROR_MESSAGE = i18n.translate( defaultMessage: 'analyzer', } ); + +export const SESSION_VIEW_ERROR_MESSAGE = i18n.translate( + 'xpack.securitySolution.flyout.sessionViewErrorTitle', + { + defaultMessage: 'session view', + } +); diff --git a/x-pack/plugins/security_solution/public/flyout/left/context.tsx b/x-pack/plugins/security_solution/public/flyout/left/context.tsx index 9b564666ea487..bf6adb5cd6a07 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/context.tsx @@ -6,6 +6,16 @@ */ import React, { createContext, useContext, useMemo } from 'react'; +import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { SecurityPageName } from '../../../common/constants'; +import { SourcererScopeName } from '../../common/store/sourcerer/model'; +import { useSourcererDataView } from '../../common/containers/sourcerer'; +import { useTimelineEventsDetails } from '../../timelines/containers/details'; +import { useGetFieldsData } from '../../common/hooks/use_get_fields_data'; +import { useRouteSpy } from '../../common/utils/route/use_route_spy'; +import { useSpaceId } from '../../common/hooks/use_space_id'; +import { getAlertIndexAlias } from '../../timelines/components/side_panel/event_details/helpers'; import type { LeftPanelProps } from '.'; export interface LeftPanelContext { @@ -17,6 +27,10 @@ export interface LeftPanelContext { * Name of the index used in the parent's page */ indexName: string; + /** + * Retrieves searchHit values for the provided field + */ + getFieldsData: (field: string) => unknown | unknown[]; } export const LeftFlyoutContext = createContext(undefined); @@ -29,11 +43,40 @@ export type LeftPanelProviderProps = { } & Partial; export const LeftPanelProvider = ({ id, indexName, children }: LeftPanelProviderProps) => { + const currentSpaceId = useSpaceId(); + const eventIndex = indexName ? getAlertIndexAlias(indexName, currentSpaceId) ?? indexName : ''; + const [{ pageName }] = useRouteSpy(); + const sourcererScope = + pageName === SecurityPageName.detections + ? SourcererScopeName.detections + : SourcererScopeName.default; + const sourcererDataView = useSourcererDataView(sourcererScope); + const [loading, _, searchHit] = useTimelineEventsDetails({ + indexName: eventIndex, + eventId: id ?? '', + runtimeMappings: sourcererDataView.runtimeMappings, + skip: !id, + }); + const getFieldsData = useGetFieldsData(searchHit?.fields); + const contextValue = useMemo( - () => (id && indexName ? { eventId: id, indexName } : undefined), - [id, indexName] + () => (id && indexName ? { eventId: id, indexName, getFieldsData } : undefined), + [id, indexName, getFieldsData] ); + if (loading) { + return ( + + + + ); + } + return {children}; }; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_preview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_preview.test.tsx new file mode 100644 index 0000000000000..9b21c296584bc --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_preview.test.tsx @@ -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. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { TestProviders } from '../../../common/mock'; +import { useAlertPrevalenceFromProcessTree } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; +import { mockContextValue, mockDataFormattedForFieldBrowser } from '../mocks/mock_context'; +import { RightPanelContext } from '../context'; +import { AnalyzerPreview } from './analyzer_preview'; +import { ANALYZER_PREVIEW_TEST_ID, ANALYZER_TREE_TEST_ID } from './test_ids'; +import * as mock from '../mocks/mock_analyzer_data'; + +jest.mock('../../../common/containers/alerts/use_alert_prevalence_from_process_tree', () => ({ + useAlertPrevalenceFromProcessTree: jest.fn(), +})); +const mockUseAlertPrevalenceFromProcessTree = useAlertPrevalenceFromProcessTree as jest.Mock; + +const contextValue = { + ...mockContextValue, + dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, +}; + +const contextValueEmpty = { + ...mockContextValue, + dataFormattedForFieldBrowser: [ + { + category: 'kibana', + field: 'kibana.alert.rule.uuid', + values: ['rule-uuid'], + originalValue: ['rule-uuid'], + isObjectArray: false, + }, + ], +}; + +describe('', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('shows analyzer preview correctly when documentid and index are present', () => { + mockUseAlertPrevalenceFromProcessTree.mockReturnValue({ + loading: false, + error: false, + alertIds: ['alertid'], + statsNodes: mock.mockStatsNodes, + }); + const wrapper = render( + + + + + + ); + + expect(mockUseAlertPrevalenceFromProcessTree).toHaveBeenCalledWith({ + processEntityId: '', + isActiveTimeline: false, + documentId: 'ancestors-id', + indices: ['rule-parameters-index'], + }); + expect(wrapper.getByTestId(ANALYZER_PREVIEW_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getByTestId(ANALYZER_TREE_TEST_ID)).toBeInTheDocument(); + }); + + it('does not show analyzer preview when documentid and index are not present', () => { + mockUseAlertPrevalenceFromProcessTree.mockReturnValue({ + loading: false, + error: false, + alertIds: undefined, + statsNodes: undefined, + }); + const { queryByTestId } = render( + + + + + + ); + + expect(mockUseAlertPrevalenceFromProcessTree).toHaveBeenCalledWith({ + processEntityId: '', + isActiveTimeline: false, + documentId: '', + indices: [], + }); + expect(queryByTestId(ANALYZER_TREE_TEST_ID)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_preview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_preview.tsx new file mode 100644 index 0000000000000..4042612bef06f --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_preview.tsx @@ -0,0 +1,59 @@ +/* + * 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 } from 'react'; +import { find } from 'lodash/fp'; +import { ANALYZER_PREVIEW_TEST_ID } from './test_ids'; +import { useRightPanelContext } from '../context'; +import { useAlertPrevalenceFromProcessTree } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; +import type { StatsNode } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; +import { AnalyzerTree } from './analyzer_tree'; +import { isActiveTimeline } from '../../../helpers'; + +/** + * Cache that stores fetched stats nodes + */ +interface Cache { + statsNodes: StatsNode[]; +} + +/** + * Analyzer preview under Overview, Visualizations. It shows a tree representation of analyzer. + */ +export const AnalyzerPreview: React.FC = () => { + const [cache, setCache] = useState>({}); + const { dataFormattedForFieldBrowser: data, scopeId } = useRightPanelContext(); + + const documentId = find({ category: 'kibana', field: 'kibana.alert.ancestors.id' }, data); + const processDocumentId = + documentId && Array.isArray(documentId.values) ? documentId.values[0] : ''; + + const index = find({ category: 'kibana', field: 'kibana.alert.rule.parameters.index' }, data); + const indices = index?.values ?? []; + + const { loading, error, statsNodes } = useAlertPrevalenceFromProcessTree({ + processEntityId: '', + isActiveTimeline: isActiveTimeline(scopeId), + documentId: processDocumentId, + indices, + }); + + useEffect(() => { + if (statsNodes && statsNodes.length !== 0) { + setCache({ statsNodes }); + } + }, [statsNodes, setCache]); + + return ( +
    + {documentId && index && ( + + )} +
    + ); +}; + +AnalyzerPreview.displayName = 'AnalyzerPreview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_tree.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_tree.test.tsx new file mode 100644 index 0000000000000..b07fe9b17bc2d --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_tree.test.tsx @@ -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; 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 { + ANALYZER_TREE_TEST_ID, + ANALYZER_TREE_LOADING_TEST_ID, + ANALYZER_TREE_ERROR_TEST_ID, +} from './test_ids'; +import { ANALYZER_PREVIEW_TITLE } from './translations'; +import * as mock from '../mocks/mock_analyzer_data'; +import { AnalyzerTree } from './analyzer_tree'; + +const defaultProps = { + statsNodes: mock.mockStatsNodes, + loading: false, + error: false, +}; +describe('', () => { + it('should render the component when data is passed', () => { + const { getByTestId, getByText } = render(); + expect(getByText(ANALYZER_PREVIEW_TITLE)).toBeInTheDocument(); + expect(getByTestId(ANALYZER_TREE_TEST_ID)).toBeInTheDocument(); + }); + + it('should render blank when data is not passed', () => { + const { queryByTestId, queryByText } = render( + + ); + expect(queryByText(ANALYZER_PREVIEW_TITLE)).not.toBeInTheDocument(); + expect(queryByTestId(ANALYZER_TREE_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should render loading spinner when loading is true', () => { + const { getByTestId } = render(); + expect(getByTestId(ANALYZER_TREE_LOADING_TEST_ID)).toBeInTheDocument(); + }); + + it('should display error message when error is true', () => { + const { getByTestId, getByText } = render(); + expect(getByText('Unable to display analyzer preview.')).toBeInTheDocument(); + expect(getByTestId(ANALYZER_TREE_ERROR_TEST_ID)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_tree.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_tree.tsx new file mode 100644 index 0000000000000..99d5924083a12 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/analyzer_tree.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { + EuiPanel, + EuiButtonEmpty, + EuiTreeView, + EuiLoadingSpinner, + EuiEmptyPrompt, +} from '@elastic/eui'; +import { ANALYZER_PREVIEW_TITLE, ANALYZER_PREVIEW_TEXT } from './translations'; +import { + ANALYZER_TREE_TEST_ID, + ANALYZER_TREE_LOADING_TEST_ID, + ANALYZER_TREE_ERROR_TEST_ID, +} from './test_ids'; +import type { StatsNode } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; +import { getTreeNodes } from '../utils/analyzer_helpers'; +import { ERROR_TITLE, ERROR_MESSAGE } from '../../shared/translations'; + +export interface AnalyzerTreeProps { + /** + * statsNode data from resolver tree api + */ + statsNodes?: StatsNode[]; + /** + * Boolean value of whether data is in loading + */ + loading: boolean; + /** + * Boolean value of whether there is error in data fetching + */ + error: boolean; +} + +/** + * Analyzer tree that represent a summary view of analyzer. It shows current process, and its parent and child processes + */ +export const AnalyzerTree: React.FC = ({ statsNodes, loading, error }) => { + const items = useMemo(() => getTreeNodes(statsNodes ?? []), [statsNodes]); + + if (loading) { + return ; + } + + if (error) { + return ( + {ERROR_TITLE(ANALYZER_PREVIEW_TEXT)}} + body={

    {ERROR_MESSAGE(ANALYZER_PREVIEW_TEXT)}

    } + data-test-subj={ANALYZER_TREE_ERROR_TEST_ID} + /> + ); + } + + if (items && items.length !== 0) { + return ( + + + {}}> + {ANALYZER_PREVIEW_TITLE} + + + + + ); + } + return null; +}; + +AnalyzerTree.displayName = 'AnalyzerTree'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/anayzer_tree.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/anayzer_tree.stories.tsx new file mode 100644 index 0000000000000..29bb82b9551ef --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/anayzer_tree.stories.tsx @@ -0,0 +1,61 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { AnalyzerTree } from './analyzer_tree'; +import * as mock from '../mocks/mock_analyzer_data'; + +export default { + component: AnalyzerTree, + title: 'Flyout/AnalyzerTree', +}; + +const defaultProps = { + loading: false, + error: false, +}; + +export const Default: Story = () => { + return ; +}; + +export const HasGrandparent: Story = () => { + return ; +}; + +export const HasParent: Story = () => { + return ; +}; + +export const HasChildren: Story = () => { + return ; +}; + +export const HasMoreThanThreeChildren: Story = () => { + return ; +}; + +export const HasGrandChildren: Story = () => { + return ; +}; + +export const SingleNode: Story = () => { + return ; +}; + +export const Loading: Story = () => { + return ; +}; + +export const Error: Story = () => { + return ; +}; + +export const Empty: Story = () => { + return ; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx index 9d42ddef6ddec..b8ebe27ddd7c4 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx @@ -28,7 +28,7 @@ const HOST_ICON = 'storage'; * Entities section under Insights section, overview tab. It contains a preview of host and user information. */ export const EntitiesOverview: React.FC = () => { - const { eventId, getFieldsData, indexName } = useRightPanelContext(); + const { eventId, getFieldsData, indexName, scopeId } = useRightPanelContext(); const { openLeftPanel } = useExpandableFlyoutContext(); const hostName = getField(getFieldsData('host.name')); const userName = getField(getFieldsData('user.name')); @@ -40,9 +40,10 @@ export const EntitiesOverview: React.FC = () => { params: { id: eventId, indexName, + scopeId, }, }); - }, [eventId, openLeftPanel, indexName]); + }, [eventId, openLeftPanel, indexName, scopeId]); if (!eventId || (!userName && !hostName)) { return null; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx index 52a85b66a3108..0045f30cecebc 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx @@ -54,7 +54,7 @@ describe('', () => { browserFields: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -62,11 +62,7 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if browserFields is null', () => { @@ -78,7 +74,7 @@ describe('', () => { browserFields: null, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -86,11 +82,7 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if eventId is null', () => { @@ -102,7 +94,7 @@ describe('', () => { browserFields: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -110,10 +102,6 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx index 1c0ee677d9e80..eaae42100ded3 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx @@ -17,7 +17,7 @@ import { RightPanelKey, RightPanelTableTabPath } from '..'; export const HighlightedFields: FC = () => { const { openRightPanel } = useExpandableFlyoutContext(); - const { eventId, indexName, dataFormattedForFieldBrowser, browserFields } = + const { eventId, indexName, dataFormattedForFieldBrowser, browserFields, scopeId } = useRightPanelContext(); const goToTableTab = useCallback(() => { @@ -27,12 +27,13 @@ export const HighlightedFields: FC = () => { params: { id: eventId, indexName, + scopeId, }, }); - }, [eventId, indexName, openRightPanel]); + }, [eventId, indexName, openRightPanel, scopeId]); if (!dataFormattedForFieldBrowser || !browserFields || !eventId) { - return <>; + return null; } return ( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx index 8409676b610b0..9efb979ba7a26 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; import { INSIGHTS_TEST_ID } from './test_ids'; import { INSIGHTS_TITLE } from './translations'; import { EntitiesOverview } from './entities_overview'; @@ -25,6 +26,7 @@ export const InsightsSection: React.FC = ({ expanded = fal return ( + ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx new file mode 100644 index 0000000000000..c1fc9dfe8a7f8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx @@ -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 React from 'react'; +import type { Story } from '@storybook/react'; +import { InsightsSubSection } from './insights_subsection'; + +export default { + component: InsightsSubSection, + title: 'Flyout/InsightsSubSection', +}; + +const title = 'Title'; +const children =
    {'hello'}
    ; + +export const Basic: Story = () => { + return {children}; +}; + +export const Loading: Story = () => { + return ( + + {null} + + ); +}; + +export const NoTitle: Story = () => { + return {children}; +}; + +export const NoChildren: Story = () => { + return {null}; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx new file mode 100644 index 0000000000000..271953c8e8105 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx @@ -0,0 +1,67 @@ +/* + * 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 { InsightsSubSection } from './insights_subsection'; + +const title = 'Title'; +const dataTestSubj = 'test'; +const children =
    {'hello'}
    ; + +describe('', () => { + it('should render children component', () => { + const { getByTestId } = render( + + {children} + + ); + + const titleDataTestSubj = `${dataTestSubj}Title`; + const contentDataTestSubj = `${dataTestSubj}Content`; + + expect(getByTestId(titleDataTestSubj)).toHaveTextContent(title); + expect(getByTestId(contentDataTestSubj)).toBeInTheDocument(); + }); + + it('should render loading component', () => { + const { getByTestId } = render( + + {children} + + ); + + const loadingDataTestSubj = `${dataTestSubj}Loading`; + expect(getByTestId(loadingDataTestSubj)).toBeInTheDocument(); + }); + + it('should render null if error', () => { + const { container } = render( + + {children} + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null if no title', () => { + const { container } = render({children}); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null if no children', () => { + const { container } = render( + + {null} + + ); + + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx new file mode 100644 index 0000000000000..4b5c1a541e316 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer, EuiTitle } from '@elastic/eui'; + +export interface InsightsSectionProps { + /** + * Renders a loading spinner if true + */ + loading?: boolean; + /** + * Returns a null component if true + */ + error?: boolean; + /** + * Title at the top of the component + */ + title: string; + /** + * Content of the component + */ + children: React.ReactNode; + /** + * Prefix data-test-subj to use for the elements + */ + ['data-test-subj']?: string; +} + +/** + * Presentational component to handle loading and error in the subsections of the Insights section. + * Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results + */ +export const InsightsSubSection: React.FC = ({ + loading = false, + error = false, + title, + 'data-test-subj': dataTestSubj, + children, +}) => { + const loadingDataTestSubj = `${dataTestSubj}Loading`; + // showing the loading in this component instead of SummaryPanel because we're hiding the entire section if no data + + if (loading) { + return ( + + + + + + ); + } + + // hide everything + if (error || !title || !children) { + return null; + } + + const titleDataTestSubj = `${dataTestSubj}Title`; + const contentDataTestSubj = `${dataTestSubj}Content`; + + return ( + <> + +
    {title}
    +
    + + + {children} + + + ); +}; + +InsightsSubSection.displayName = 'InsightsSubSection'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx new file mode 100644 index 0000000000000..5637d3c036860 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx @@ -0,0 +1,156 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { css } from '@emotion/react'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; + +export default { + component: InsightsSummaryPanel, + title: 'Flyout/InsightsSummaryPanel', +}; + +export const Default: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + { + icon: 'warning', + value: 2, + text: 'this is test for orange', + color: 'rgb(255,126,98)', + }, + { + icon: 'warning', + value: 3, + text: 'this is test for yellow', + color: 'rgb(241,216,11)', + }, + ]; + + return ( +
    + +
    + ); +}; + +export const InvalidColor: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for an invalid color (abc)', + color: 'abc', + }, + ]; + + return ( +
    + +
    + ); +}; + +export const NoColor: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + }, + { + icon: 'warning', + value: 2, + text: 'this is test for orange', + }, + { + icon: 'warning', + value: 3, + text: 'this is test for yellow', + }, + ]; + + return ( +
    + +
    + ); +}; + +export const LongText: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is an extremely long text to verify it is properly cut off and and we show three dots at the end', + color: 'abc', + }, + ]; + + return ( +
    + +
    + ); +}; +export const LongNumber: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is an extremely long value to verify it is properly cut off and and we show three dots at the end', + color: 'abc', + }, + ]; + + return ( +
    + +
    + ); +}; + +export const NoData: Story = () => { + const data: InsightsSummaryPanelData[] = []; + + return ( +
    + +
    + ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx new file mode 100644 index 0000000000000..9ecbbcc7fc0a5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx @@ -0,0 +1,90 @@ +/* + * 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 { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { + INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID, +} from './test_ids'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; + +describe('', () => { + it('should render by default', () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + ]; + + const { getByTestId } = render( + + + + ); + + const iconTestId = `${INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID}0`; + const valueTestId = `${INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID}0`; + const colorTestId = `${INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID}0`; + expect(getByTestId(iconTestId)).toBeInTheDocument(); + expect(getByTestId(valueTestId)).toHaveTextContent('1 this is a test for red'); + expect(getByTestId(colorTestId)).toBeInTheDocument(); + }); + + it('should only render null when data is null', () => { + const data = null as unknown as InsightsSummaryPanelData[]; + + const { container } = render(); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should handle big number in a compact notation', () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + ]; + + const { getByTestId } = render( + + + + ); + + const valueTestId = `${INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID}0`; + expect(getByTestId(valueTestId)).toHaveTextContent('160k this is a test for red'); + }); + + it(`should not show the colored dot if color isn't provided`, () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is a test for no color', + }, + ]; + + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId(INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx new file mode 100644 index 0000000000000..306eaa101b804 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { VFC } from 'react'; +import React from 'react'; +import { css } from '@emotion/react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiHealth, EuiPanel } from '@elastic/eui'; +import { FormattedCount } from '../../../common/components/formatted_number'; + +export interface InsightsSummaryPanelData { + /** + * Icon to display on the left side of each row + */ + icon: string; + /** + * Number of results/entries found + */ + value: number; + /** + * Text corresponding of the number of results/entries + */ + text: string; + /** + * Optional parameter for now, will be used to display a dot on the right side + * (corresponding to some sort of severity?) + */ + color?: string; // TODO remove optional when we have guidance on what the colors will actually be +} + +export interface InsightsSummaryPanelProps { + /** + * Array of data to display in each row + */ + data: InsightsSummaryPanelData[]; + /** + * Prefix data-test-subj because this component will be used in multiple places + */ + ['data-test-subj']?: string; +} + +/** + * Panel showing summary information as an icon, a count and text as well as a severity colored dot. + * Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results components under the Insights section. + * The colored dot is currently optional but will ultimately be mandatory (waiting on PM and UIUX). + */ +export const InsightsSummaryPanel: VFC = ({ + data, + 'data-test-subj': dataTestSubj, +}) => { + if (!data || data.length === 0) { + return null; + } + + const iconDataTestSubj = `${dataTestSubj}Icon`; + const valueDataTestSubj = `${dataTestSubj}Value`; + const colorDataTestSubj = `${dataTestSubj}Color`; + + return ( + + + {data.map((row, index) => ( + + + + + + {row.text} + + {row.color && ( + + + + )} + + ))} + + + ); +}; + +InsightsSummaryPanel.displayName = 'InsightsSummaryPanel'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx index f0bc9bd993f66..24e52e4ba5915 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx @@ -33,16 +33,12 @@ describe('', () => { }, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx index d2a7c90011d68..b7050d1df0fa0 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx @@ -37,17 +37,13 @@ describe('', () => { dataAsNestedObject: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render null if dataAsNestedObject is null', () => { @@ -55,17 +51,13 @@ describe('', () => { dataFormattedForFieldBrowser: [], } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render null if renderer is null', () => { const panelContextValue = { @@ -73,16 +65,12 @@ describe('', () => { dataFormattedForFieldBrowser: [], } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx index 3b9364df1dd04..554b6c90db32a 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx @@ -38,17 +38,13 @@ describe('', () => { getFieldsData: jest.fn(), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid', () => { @@ -56,16 +52,12 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => 123), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx index e329459c6cbc1..92c8b0a382638 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx @@ -38,17 +38,13 @@ describe('', () => { getFieldsData: jest.fn(), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid array', () => { @@ -56,17 +52,13 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => ['abc']), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid string', () => { @@ -74,16 +66,12 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => 'abc'), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
    - - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts index 65850272b2289..9ee38ba2940cb 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts @@ -52,6 +52,9 @@ export const HIGHLIGHTED_FIELDS_DETAILS_TEST_ID = export const HIGHLIGHTED_FIELDS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutHighlightedFields'; export const HIGHLIGHTED_FIELDS_HEADER_EXPAND_ICON_TEST_ID = 'query-toggle-header'; export const HIGHLIGHTED_FIELDS_GO_TO_TABLE_LINK = 'summary-view-go-to-table-link'; + +/* Insights section*/ + export const INSIGHTS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInsights'; export const INSIGHTS_HEADER_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInsightsHeader'; export const ENTITIES_HEADER_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesHeader'; @@ -80,3 +83,24 @@ export const ENTITIES_HOST_OVERVIEW_IP_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverviewIP'; export const ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverviewRiskLevel'; + +/* Insights Threat Intelligence */ + +export const INSIGHTS_THREAT_INTELLIGENCE_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsThreatIntelligence'; +export const INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Title`; +export const INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Content`; +export const INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}ViewAllButton`; +export const INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Loading`; +export const INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Icon`; +export const INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Value`; +export const INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Color`; + +/* Visualizations section*/ +export const VISUALIZATIONS_SECTION_TEST_ID = 'securitySolutionDocumentDetailsVisualizationsTitle'; +export const VISUALIZATIONS_SECTION_HEADER_TEST_ID = + 'securitySolutionDocumentDetailsVisualizationsTitleHeader'; +export const ANALYZER_PREVIEW_TEST_ID = 'securitySolutionDocumentDetailsAnalayzerPreview'; +export const ANALYZER_TREE_TEST_ID = 'securitySolutionDocumentDetailsAnalayzerTree'; +export const ANALYZER_TREE_LOADING_TEST_ID = 'securitySolutionDocumentDetailsAnalayzerTreeLoading'; +export const ANALYZER_TREE_ERROR_TEST_ID = 'securitySolutionDocumentDetailsAnalayzerTreeError'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx new file mode 100644 index 0000000000000..ecf17f2c7e822 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx @@ -0,0 +1,195 @@ +/* + * 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 { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; +import { RightPanelContext } from '../context'; +import { + INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID, +} from './test_ids'; +import { TestProviders } from '../../../common/mock'; +import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; +import { LeftPanelInsightsTabPath, LeftPanelKey } from '../../left'; +import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; + +jest.mock('../hooks/use_fetch_threat_intelligence'); + +const panelContextValue = { + eventId: 'event id', + indexName: 'indexName', + dataFormattedForFieldBrowser: [], +} as unknown as RightPanelContext; + +const renderThreatIntelligenceOverview = (contextValue: RightPanelContext) => ( + + + + + +); + +describe('', () => { + it('should render 1 match detected and 1 field enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID)).toHaveTextContent( + 'Threat Intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '1 threat match detected' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '1 field enriched with threat intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('should render 2 matches detected and 2 fields enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 2, + threatEnrichmentsCount: 2, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID)).toHaveTextContent( + 'Threat Intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '2 threat matches detected' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '2 fields enriched with threat intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('should render 0 field enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 0, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '0 field enriched with threat intelligence' + ); + }); + + it('should render 0 match detected', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 0, + threatEnrichmentsCount: 2, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '0 threat match detected' + ); + }); + + it('should render loading', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: true, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID)).toBeInTheDocument(); + }); + + it('should render null when eventId is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + }); + const contextValue = { + ...panelContextValue, + eventId: null, + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null when dataFormattedForFieldBrowser is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + error: true, + }); + const contextValue = { + ...panelContextValue, + dataFormattedForFieldBrowser: null, + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null when no enrichment found is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 0, + threatEnrichmentsCount: 0, + }); + const contextValue = { + ...panelContextValue, + dataFormattedForFieldBrowser: [], + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should navigate to left section Insights tab when clicking on button', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, + }); + const flyoutContextValue = { + openLeftPanel: jest.fn(), + } as unknown as ExpandableFlyoutContext; + + const { getByTestId } = render( + + + + + + + + ); + + getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID).click(); + expect(flyoutContextValue.openLeftPanel).toHaveBeenCalledWith({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: panelContextValue.eventId, + indexName: panelContextValue.indexName, + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx new file mode 100644 index 0000000000000..63f0862a68b3a --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx @@ -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 React, { useCallback } from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; +import { InsightsSubSection } from './insights_subsection'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; +import { useRightPanelContext } from '../context'; +import { INSIGHTS_THREAT_INTELLIGENCE_TEST_ID } from './test_ids'; +import { + VIEW_ALL, + THREAT_INTELLIGENCE_TITLE, + THREAT_INTELLIGENCE_TEXT, + THREAT_MATCH_DETECTED, + THREAT_ENRICHMENT, + THREAT_MATCHES_DETECTED, + THREAT_ENRICHMENTS, +} from './translations'; +import { LeftPanelKey, LeftPanelInsightsTabPath } from '../../left'; + +/** + * Threat Intelligence section under Insights section, overview tab. + * The component fetches the necessary data, then pass it down to the InsightsSubSection component for loading and error state, + * and the SummaryPanel component for data rendering. + */ +export const ThreatIntelligenceOverview: React.FC = () => { + const { eventId, indexName, dataFormattedForFieldBrowser } = useRightPanelContext(); + const { openLeftPanel } = useExpandableFlyoutContext(); + + const goToThreatIntelligenceTab = useCallback(() => { + openLeftPanel({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: eventId, + indexName, + }, + }); + }, [eventId, openLeftPanel, indexName]); + + const { loading, threatMatchesCount, threatEnrichmentsCount } = useFetchThreatIntelligence({ + dataFormattedForFieldBrowser, + }); + + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: threatMatchesCount, + text: threatMatchesCount <= 1 ? THREAT_MATCH_DETECTED : THREAT_MATCHES_DETECTED, + }, + { + icon: 'warning', + value: threatEnrichmentsCount, + text: threatMatchesCount <= 1 ? THREAT_ENRICHMENT : THREAT_ENRICHMENTS, + }, + ]; + + const error: boolean = + !eventId || + !dataFormattedForFieldBrowser || + (threatMatchesCount === 0 && threatEnrichmentsCount === 0); + + return ( + + + + {VIEW_ALL(THREAT_INTELLIGENCE_TEXT)} + + + ); +}; + +ThreatIntelligenceOverview.displayName = 'ThreatIntelligenceOverview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index 169299e895e24..d5b9f0d1928b3 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -101,11 +101,18 @@ export const HIGHLIGHTED_FIELDS_TITLE = i18n.translate( { defaultMessage: 'Highlighted fields' } ); +/* Insights section */ + export const ENTITIES_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.entitiesTitle', { defaultMessage: 'Entities' } ); +export const THREAT_INTELLIGENCE_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.threatIntelligenceTitle', + { defaultMessage: 'Threat Intelligence' } +); + export const INSIGHTS_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.insightsTitle', { defaultMessage: 'Insights' } @@ -131,8 +138,59 @@ export const ENTITIES_TEXT = i18n.translate( } ); +export const THREAT_INTELLIGENCE_TEXT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligenceText', + { + defaultMessage: 'fields of threat intelligence', + } +); + +export const THREAT_MATCH_DETECTED = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatMatch', + { + defaultMessage: `threat match detected`, + } +); + +export const THREAT_MATCHES_DETECTED = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatMatches', + { + defaultMessage: `threat matches detected`, + } +); + +export const THREAT_ENRICHMENT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatEnrichment', + { + defaultMessage: `field enriched with threat intelligence`, + } +); + +export const THREAT_ENRICHMENTS = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatEnrichments', + { + defaultMessage: `fields enriched with threat intelligence`, + } +); + export const VIEW_ALL = (text: string) => i18n.translate('xpack.securitySolution.flyout.documentDetails.overviewTab.viewAllButton', { values: { text }, defaultMessage: 'View all {text}', }); +export const VISUALIZATIONS_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.visualizationsTitle', + { defaultMessage: 'Visualizations' } +); + +export const ANALYZER_PREVIEW_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.analyzerPreviewTitle', + { defaultMessage: 'Analyzer preview' } +); + +export const ANALYZER_PREVIEW_TEXT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.analyzerPreviewText', + { + defaultMessage: 'analyzer preview.', + } +); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/visualizations_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/visualizations_section.test.tsx new file mode 100644 index 0000000000000..2b447967ca80b --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/visualizations_section.test.tsx @@ -0,0 +1,62 @@ +/* + * 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 { VISUALIZATIONS_SECTION_HEADER_TEST_ID } from './test_ids'; +import { TestProviders } from '../../../common/mock'; +import { VisualizationsSection } from './visualizations_section'; +import { mockContextValue, mockDataFormattedForFieldBrowser } from '../mocks/mock_context'; +import { RightPanelContext } from '../context'; +import { useAlertPrevalenceFromProcessTree } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; + +jest.mock('../../../common/containers/alerts/use_alert_prevalence_from_process_tree', () => ({ + useAlertPrevalenceFromProcessTree: jest.fn(), +})); +const mockUseAlertPrevalenceFromProcessTree = useAlertPrevalenceFromProcessTree as jest.Mock; + +const contextValue = { + ...mockContextValue, + dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, +}; + +describe('', () => { + beforeEach(() => { + mockUseAlertPrevalenceFromProcessTree.mockReturnValue({ + loading: false, + error: false, + alertIds: undefined, + statsNodes: undefined, + }); + }); + + it('should render visualizations component', () => { + const { getByTestId, getAllByRole } = render( + + + + ); + + expect(getByTestId(VISUALIZATIONS_SECTION_HEADER_TEST_ID)).toBeInTheDocument(); + expect(getAllByRole('button')[0]).toHaveAttribute('aria-expanded', 'false'); + expect(getAllByRole('button')[0]).not.toHaveAttribute('disabled'); + }); + + it('should render visualization component as expanded when expanded is true', () => { + const { getByTestId, getAllByRole } = render( + + + + + + ); + + expect(getByTestId(VISUALIZATIONS_SECTION_HEADER_TEST_ID)).toBeInTheDocument(); + expect(getAllByRole('button')[0]).toHaveAttribute('aria-expanded', 'true'); + expect(getAllByRole('button')[0]).not.toHaveAttribute('disabled'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/visualizations_section.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/visualizations_section.tsx new file mode 100644 index 0000000000000..c6c84a6a6b34d --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/visualizations_section.tsx @@ -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 React from 'react'; +import { ExpandableSection } from './expandable_section'; +import { VISUALIZATIONS_SECTION_TEST_ID } from './test_ids'; +import { VISUALIZATIONS_TITLE } from './translations'; +import { AnalyzerPreview } from './analyzer_preview'; + +export interface VisualizatioinsSectionProps { + /** + * Boolean to allow the component to be expanded or collapsed on first render + */ + expanded?: boolean; +} + +/** + * Visualizations section in overview. It contains analyzer preview and session view preview. + */ +export const VisualizationsSection: React.FC = ({ + expanded = false, +}) => { + return ( + + + + ); +}; + +VisualizationsSection.displayName = 'VisualizationsSection'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/context.tsx b/x-pack/plugins/security_solution/public/flyout/right/context.tsx index 282c224b5a15f..2da844946cbbd 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/context.tsx @@ -19,6 +19,7 @@ import { SecurityPageName } from '../../../common/constants'; import { SourcererScopeName } from '../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import type { RightPanelProps } from '.'; +import type { GetFieldsData } from '../../common/hooks/use_get_fields_data'; import { useGetFieldsData } from '../../common/hooks/use_get_fields_data'; export interface RightPanelContext { @@ -57,7 +58,7 @@ export interface RightPanelContext { /** * Retrieves searchHit values for the provided field */ - getFieldsData: (field: string) => unknown | unknown[]; + getFieldsData: GetFieldsData; } export const RightPanelContext = createContext(undefined); diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx new file mode 100644 index 0000000000000..ab57dcebe32af --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx @@ -0,0 +1,216 @@ +/* + * 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 { RenderHookResult } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; +import type { + UseThreatIntelligenceParams, + UseThreatIntelligenceValue, +} from './use_fetch_threat_intelligence'; +import { useFetchThreatIntelligence } from './use_fetch_threat_intelligence'; +import { useInvestigationTimeEnrichment } from '../../../common/containers/cti/event_enrichment'; + +jest.mock('../../../common/containers/cti/event_enrichment'); + +const dataFormattedForFieldBrowser = [ + { + category: 'kibana', + field: 'kibana.alert.rule.uuid', + isObjectArray: false, + originalValue: ['uuid'], + values: ['uuid'], + }, + { + category: 'threat', + field: 'threat.enrichments', + isObjectArray: true, + originalValue: ['{"indicator.file.hash.sha256":["sha256"]}'], + values: ['{"indicator.file.hash.sha256":["sha256"]}'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.file.hash.sha256', + isObjectArray: false, + originalValue: ['sha256'], + values: ['sha256'], + }, +]; + +describe('useFetchThreatIntelligence', () => { + let hookResult: RenderHookResult; + + it('should render 1 match detected and 1 field enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 2, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(1); + expect(hookResult.result.current.threatMatchesCount).toEqual(1); + expect(hookResult.result.current.threatEnrichments).toHaveLength(1); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(1); + }); + + it('should render 2 matches detected and 2 fields enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.3'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.4'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 4, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(2); + expect(hookResult.result.current.threatMatchesCount).toEqual(2); + expect(hookResult.result.current.threatEnrichments).toHaveLength(2); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(2); + }); + + it('should render 0 field enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + ], + totalCount: 1, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(1); + expect(hookResult.result.current.threatMatchesCount).toEqual(1); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); + + it('should render 0 match detected', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 1, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toHaveLength(1); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(1); + }); + + it('should return loading true', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: undefined, + loading: true, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(true); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); + + it('should return error true', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [], + totalCount: 0, + }, + loading: false, + }); + + hookResult = renderHook(() => + useFetchThreatIntelligence({ dataFormattedForFieldBrowser: null }) + ); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(true); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts new file mode 100644 index 0000000000000..4f3d23b082664 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts @@ -0,0 +1,108 @@ +/* + * 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 { useMemo } from 'react'; +import { groupBy } from 'lodash'; +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; +import type { CtiEnrichment } from '../../../../common/search_strategy'; +import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; +import { + filterDuplicateEnrichments, + getEnrichmentFields, + parseExistingEnrichments, + timelineDataToEnrichment, +} from '../../../common/components/event_details/cti_details/helpers'; +import { useInvestigationTimeEnrichment } from '../../../common/containers/cti/event_enrichment'; +import { ENRICHMENT_TYPES } from '../../../../common/cti/constants'; + +export interface UseThreatIntelligenceParams { + /** + * An array of field objects with category and value + */ + dataFormattedForFieldBrowser: TimelineEventsDetailsItem[] | null; +} + +export interface UseThreatIntelligenceValue { + /** + * Returns true while the threat intelligence data is being queried + */ + loading: boolean; + /** + * Returns true if the dataFormattedForFieldBrowser property is null + */ + error: boolean; + /** + * Threat matches (from an indicator match rule) + */ + threatMatches: CtiEnrichment[]; + /** + * Threat matches count + */ + threatMatchesCount: number; + /** + * Threat enrichments (from the real time query) + */ + threatEnrichments: CtiEnrichment[]; + /** + * Threat enrichments count + */ + threatEnrichmentsCount: number; +} + +/** + * Hook to retrieve threat intelligence data for the expandable flyout right and left sections. + */ +export const useFetchThreatIntelligence = ({ + dataFormattedForFieldBrowser, +}: UseThreatIntelligenceParams): UseThreatIntelligenceValue => { + const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); + + // retrieve the threat enrichment fields with value for the current document + // (see https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/common/cti/constants.ts#L35) + const eventFields = useMemo( + () => getEnrichmentFields(dataFormattedForFieldBrowser || []), + [dataFormattedForFieldBrowser] + ); + + // retrieve existing enrichment fields and their value + const existingEnrichments = useMemo( + () => + isAlert + ? parseExistingEnrichments(dataFormattedForFieldBrowser || []).map((enrichmentData) => + timelineDataToEnrichment(enrichmentData) + ) + : [], + [dataFormattedForFieldBrowser, isAlert] + ); + + // api call to retrieve all documents that match the eventFields + const { result: response, loading } = useInvestigationTimeEnrichment(eventFields); + + // combine existing enrichment and enrichment from the api response + // also removes the investigation-time enrichments if the exact indicator already exists + const allEnrichments = useMemo(() => { + if (loading || !response?.enrichments) { + return existingEnrichments; + } + return filterDuplicateEnrichments([...existingEnrichments, ...response.enrichments]); + }, [loading, response, existingEnrichments]); + + // separate threat matches (from indicator-match rule) from threat enrichments (realtime query) + const { + [ENRICHMENT_TYPES.IndicatorMatchRule]: threatMatches, + [ENRICHMENT_TYPES.InvestigationTime]: threatEnrichments, + } = groupBy(allEnrichments, 'matched.type'); + + return { + loading, + error: !dataFormattedForFieldBrowser, + threatMatches, + threatMatchesCount: (threatMatches || []).length, + threatEnrichments, + threatEnrichmentsCount: (threatEnrichments || []).length, + }; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_analyzer_data.ts b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_analyzer_data.ts new file mode 100644 index 0000000000000..1ea775b607baa --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_analyzer_data.ts @@ -0,0 +1,278 @@ +/* + * 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 { StatsNode } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; + +const mockNode: StatsNode = { + id: '70e19mhyda', + name: 'mimikatz.exe', + parent: '92ogx18xdh', + data: {}, + stats: { + total: 2, + byCategory: { alerts: 2 }, + }, +}; + +const mockParentNodeNoGrandparent: StatsNode = { + id: '92ogx18xdh', + name: 'explorer.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, +}; + +const mockParentNode: StatsNode = { + ...mockParentNodeNoGrandparent, + parent: 'skq89ren5b', +}; + +const mockGrandparentNode: StatsNode = { + id: 'skq89ren5b', + name: 'notepad.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, +}; + +export const mockChildrenNodes: StatsNode[] = [ + { + id: '6b4ffkdr0r', + parent: '70e19mhyda', + name: 'lsass.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, + { + id: 'gimoywlglz', + parent: '70e19mhyda', + name: 'iexlorer.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, + { + id: 'p6t1v7jum9', + parent: '70e19mhyda', + name: 'notepad.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, +]; + +const mockExtraChildrenNodes: StatsNode[] = [ + { + id: 'rxccek8vqu', + parent: '70e19mhyda', + name: 'powershell.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, + { + id: 'mq0n2g7093', + parent: '70e19mhyda', + name: 'exlorer.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, +]; + +const mockGrandchildrenNodes: StatsNode[] = [ + { + id: 't037f0qec3', + parent: 'gimoywlglz', + name: 'powershell.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, + { + id: '8bxu0crntu', + parent: 'gimoywlglz', + name: 'lsass.exe', + data: {}, + stats: { + total: 0, + byCategory: {}, + }, + }, +]; + +export const mockStatsNodesSingleNode: StatsNode[] = [mockNode]; + +export const mockStatsNodesHasParent: StatsNode[] = [mockNode, mockParentNodeNoGrandparent]; + +export const mockStatsNodesHasGrandparent: StatsNode[] = [ + mockNode, + mockParentNode, + mockGrandparentNode, +]; + +export const mockStatsNodesHasChildren: StatsNode[] = [mockNode, ...mockChildrenNodes]; + +export const mockStatsNodesMoreThanThreeChildren: StatsNode[] = [ + mockNode, + ...mockChildrenNodes, + ...mockExtraChildrenNodes, +]; + +export const mockStatsNodesHasGrandchildren: StatsNode[] = [ + mockNode, + ...mockChildrenNodes, + ...mockGrandchildrenNodes, +]; + +export const mockStatsNodes: StatsNode[] = [ + mockNode, + mockParentNode, + mockGrandparentNode, + ...mockChildrenNodes, + ...mockGrandchildrenNodes, +]; + +export const mockTreeNodesSingleNode = [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [], + }, +]; + +export const mockTreeNodesHasParent = [ + { + label: '--> explorer.exe', + id: '92ogx18xdh', + isExpanded: true, + children: [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [], + }, + ], + }, +]; + +export const mockTreeNodesHasGrandparent = [ + { + label: '...', + id: 'grandparent', + isExpanded: true, + children: [ + { + label: '--> explorer.exe', + id: '92ogx18xdh', + isExpanded: true, + children: [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [], + }, + ], + }, + ], + }, +]; + +export const mockTreeNodesHasChildren = [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [ + { label: '--> lsass.exe', id: '6b4ffkdr0r', isExpanded: true, children: [] }, + { label: '--> iexlorer.exe', id: 'gimoywlglz', isExpanded: true, children: [] }, + { label: '--> notepad.exe', id: 'p6t1v7jum9', isExpanded: true, children: [] }, + ], + }, +]; + +export const mockTreeNodesHasFourChildren = [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [ + { label: '--> lsass.exe', id: '6b4ffkdr0r', isExpanded: true, children: [] }, + { label: '--> iexlorer.exe', id: 'gimoywlglz', isExpanded: true, children: [] }, + { label: '--> notepad.exe', id: 'p6t1v7jum9', isExpanded: true, children: [] }, + { label: '--> powershell.exe', id: 'rxccek8vqu', isExpanded: true, children: [] }, + ], + }, +]; + +export const mockTreeNodesHasGrandchildren = [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [ + { label: '--> lsass.exe', id: '6b4ffkdr0r', isExpanded: true, children: [] }, + { label: '--> iexlorer.exe', id: 'gimoywlglz', isExpanded: true, children: [] }, + { label: '--> notepad.exe', id: 'p6t1v7jum9', isExpanded: true, children: [] }, + ], + }, + + { label: '...', id: 'grandchild', isExpanded: true, children: [] }, +]; + +export const mockTreeNodes = [ + { + label: '...', + id: 'grandparent', + isExpanded: true, + children: [ + { + label: '--> explorer.exe', + id: '92ogx18xdh', + isExpanded: true, + children: [ + { + label: '--> (Analyzed Event) mimikatz.exe', + id: '70e19mhyda', + isExpanded: true, + children: [ + { label: '--> lsass.exe', id: '6b4ffkdr0r', isExpanded: true, children: [] }, + { label: '--> iexlorer.exe', id: 'gimoywlglz', isExpanded: true, children: [] }, + { label: '--> notepad.exe', id: 'p6t1v7jum9', isExpanded: true, children: [] }, + ], + }, + ], + }, + ], + }, + { label: '...', id: 'grandchild', isExpanded: true, children: [] }, +]; + +export const mockChildrenTreeNodes = [ + { label: 'lsass.exe', id: '6b4ffkdr0r', isExpanded: true, children: [] }, + { label: 'iexlorer.exe', id: 'gimoywlglz', isExpanded: true, children: [] }, + { label: 'notepad.exe', id: 'p6t1v7jum9', isExpanded: true, children: [] }, +]; diff --git a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts index a4d01bda6bc21..034e18350b1cb 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts @@ -6,6 +6,7 @@ */ import { ALERT_RISK_SCORE, ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import type { RightPanelContext } from '../context'; /** * Returns mocked data for field (mock this method: x-pack/plugins/security_solution/public/common/hooks/use_get_fields_data.ts) @@ -59,6 +60,27 @@ export const mockDataFormattedForFieldBrowser = [ originalValue: ['rule-description'], isObjectArray: false, }, + { + category: 'kibana', + field: 'kibana.alert.ancestors.id', + values: ['ancestors-id'], + originalValue: ['ancestors-id'], + isObjectArray: false, + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.index', + values: ['rule-parameters-index'], + originalValue: ['rule-parameters-index'], + isObjectArray: false, + }, + { + category: 'process', + field: 'process.entity_id', + values: ['process-entity_id'], + originalValue: ['process-entity_id'], + isObjectArray: false, + }, ]; /** @@ -115,3 +137,18 @@ export const mockSearchHit = { ], }, }; + +/** + * Mock contextValue for right panel context + */ +export const mockContextValue: RightPanelContext = { + eventId: 'eventId', + indexName: 'index', + scopeId: 'scopeId', + getFieldsData: mockGetFieldsData, + dataFormattedForFieldBrowser: null, + browserFields: null, + dataAsNestedObject: null, + searchHit: undefined, + refetchFlyoutData: jest.fn(), +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx b/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx index 0c9e57f99d22f..73131f67a661a 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx @@ -11,6 +11,7 @@ import { EuiHorizontalRule } from '@elastic/eui'; import { InvestigationSection } from '../components/investigation_section'; import { DescriptionSection } from '../components/description_section'; import { InsightsSection } from '../components/insights_section'; +import { VisualizationsSection } from '../components/visualizations_section'; /** * Overview view displayed in the document details expandable flyout right section @@ -23,6 +24,8 @@ export const OverviewTab: FC = memo(() => { + + ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/utils/analyzer_helpers.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/utils/analyzer_helpers.test.tsx new file mode 100644 index 0000000000000..c3c8fbd6824ac --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/utils/analyzer_helpers.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { getTreeNodes, hasGrandchildren, getTreeNode } from './analyzer_helpers'; +import * as mock from '../mocks/mock_analyzer_data'; + +it('test hasGrandchildren', () => { + expect(hasGrandchildren([], [])).toBe(false); + expect(hasGrandchildren([], mock.mockChildrenNodes)).toBe(false); + expect(hasGrandchildren(mock.mockStatsNodesSingleNode, [])).toBe(false); + expect(hasGrandchildren(mock.mockStatsNodesHasChildren, mock.mockChildrenNodes)).toBe(false); + expect(hasGrandchildren(mock.mockStatsNodesHasGrandchildren, mock.mockChildrenNodes)).toBe(true); +}); + +it('test getTreeNode', () => { + const mockTreeNode = { + label: 'process name', + id: 'test id', + isExpanded: true, + children: mock.mockChildrenTreeNodes, + }; + const mockTreeNodeChildrenIsEmpty = { + label: 'process name', + id: 'test id', + isExpanded: true, + children: [], + }; + + expect(getTreeNode('test id', 'process name', mock.mockChildrenTreeNodes)).toStrictEqual( + mockTreeNode + ); + expect(getTreeNode('test id', 'process name', [])).toStrictEqual(mockTreeNodeChildrenIsEmpty); +}); + +describe('test getTreeNodes', () => { + it('should return the correct tree nodes', () => { + expect(getTreeNodes(mock.mockStatsNodes)).toStrictEqual(mock.mockTreeNodes); + expect(getTreeNodes(mock.mockStatsNodesHasGrandparent)).toStrictEqual( + mock.mockTreeNodesHasGrandparent + ); + expect(getTreeNodes(mock.mockStatsNodesHasParent)).toStrictEqual(mock.mockTreeNodesHasParent); + + expect(getTreeNodes(mock.mockStatsNodesHasChildren)).toStrictEqual( + mock.mockTreeNodesHasChildren + ); + expect(getTreeNodes(mock.mockStatsNodesHasGrandchildren)).toStrictEqual( + mock.mockTreeNodesHasGrandchildren + ); + expect(getTreeNodes(mock.mockStatsNodesSingleNode)).toStrictEqual(mock.mockTreeNodesSingleNode); + }); + + it('should return the correct tree nodes with 3 children when child count limit is not passed', () => { + expect(getTreeNodes(mock.mockStatsNodesMoreThanThreeChildren)).toStrictEqual( + mock.mockTreeNodesHasChildren + ); + }); + it('should return the correct number of children tree nodes when child count limit is passed', () => { + expect(getTreeNodes(mock.mockStatsNodesMoreThanThreeChildren, 4)).toStrictEqual( + mock.mockTreeNodesHasFourChildren + ); + expect(getTreeNodes(mock.mockStatsNodesMoreThanThreeChildren, 0)).toStrictEqual( + mock.mockTreeNodesSingleNode + ); + }); + expect(getTreeNodes([])).toStrictEqual([]); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/utils/analyzer_helpers.ts b/x-pack/plugins/security_solution/public/flyout/right/utils/analyzer_helpers.ts new file mode 100644 index 0000000000000..6e4740463ffbd --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/utils/analyzer_helpers.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 type { Node } from '@elastic/eui/src/components/tree_view/tree_view'; +import type { StatsNode } from '../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; + +/** + * Helper function to check if a node has grandchildren + * @param statsNodes type StatsNode array + * @param children type StatsNode array + * @return a boolean of wheather a bode has grandchildren + */ +export const hasGrandchildren = (statsNodes: StatsNode[], children: StatsNode[]) => { + return children.some((child) => statsNodes.some((node) => node.parent === child.id)); +}; + +/** + * Helper function to create a tree node from id, name and children + * @param id type string + * @param name type string + * @param children children nodes + * @return a node of Node type + */ +export const getTreeNode = (id: string, name: string, children: Node[]): Node => { + return { label: name, id, isExpanded: true, children }; +}; + +/** + * Helper function to create tree nodes based on statsNode list from resolver api + * @param statsNodes type StatsNode[] + * @param childrenCountLimit optional parameter to limit the number of children displayed, default to 3 + * @return a node list for EuiTreeView + */ + +export const getTreeNodes = (statsNodes: StatsNode[], childCountLimit: number = 3): Node[] => { + if (statsNodes.length === 0) { + return []; + } + const node = statsNodes[0]; + const nodeList = []; + const currentNode = getTreeNode(node.id, `--> (Analyzed Event) ${node.name}`, []); + + const children = statsNodes.filter((item) => item.parent === node.id); + if (children && children.length !== 0) { + children.forEach((child, idx) => { + if (idx < childCountLimit) { + currentNode.children?.push(getTreeNode(child.id, `--> ${child.name}`, [])); + } + }); + } + + const parent = statsNodes.find((item) => item.id === node.parent); + if (parent) { + const parentNode = getTreeNode(parent.id, `--> ${parent.name}`, [currentNode]); + if (parent?.parent) { + nodeList.push(getTreeNode('grandparent', '...', [parentNode])); + } else { + nodeList.push(parentNode); + } + } else { + nodeList.push(currentNode); + } + + if (hasGrandchildren(statsNodes, children)) { + nodeList.push(getTreeNode('grandchild', '...', [])); + } + + return nodeList; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx new file mode 100644 index 0000000000000..984b2a2e223dc --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { useSyncToUrl } from '@kbn/url-state'; +import { renderHook } from '@testing-library/react-hooks'; +import { useSyncFlyoutStateWithUrl } from './use_sync_flyout_state_with_url'; + +jest.mock('@kbn/url-state'); + +describe('useSyncFlyoutStateWithUrl', () => { + it('should return an array containing flyoutApi ref and handleFlyoutChanges function', () => { + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [flyoutApi, handleFlyoutChanges] = result.current; + + expect(flyoutApi.current).toBeNull(); + expect(typeof handleFlyoutChanges).toBe('function'); + }); + + it('should open flyout when relevant url state is detected in the query string', () => { + jest.useFakeTimers(); + + jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { + setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); + return jest.fn(); + }); + + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [flyoutApi, handleFlyoutChanges] = result.current; + + const flyoutApiMock: ExpandableFlyoutApi = { + openFlyout: jest.fn(), + getState: () => ({ left: undefined, right: undefined, preview: [] }), + }; + + expect(typeof handleFlyoutChanges).toBe('function'); + expect(flyoutApi.current).toBeNull(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (flyoutApi as any).current = flyoutApiMock; + + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + + expect(flyoutApiMock.openFlyout).toHaveBeenCalledTimes(1); + expect(flyoutApiMock.openFlyout).toHaveBeenCalledWith({ mocked: { flyout: 'state' } }); + }); + + it('should sync flyout state to url whenever handleFlyoutChanges is called by the consumer', () => { + const syncStateToUrl = jest.fn(); + jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { + setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); + return syncStateToUrl; + }); + + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [_flyoutApi, handleFlyoutChanges] = result.current; + + handleFlyoutChanges(); + + expect(syncStateToUrl).toHaveBeenCalledTimes(1); + expect(syncStateToUrl).toHaveBeenLastCalledWith(undefined); + + handleFlyoutChanges({ left: undefined, right: undefined, preview: [] }); + + expect(syncStateToUrl).toHaveBeenLastCalledWith({ + left: undefined, + right: undefined, + preview: undefined, + }); + expect(syncStateToUrl).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx new file mode 100644 index 0000000000000..be1b28147f63d --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useRef } from 'react'; +import type { ExpandableFlyoutApi, ExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useSyncToUrl } from '@kbn/url-state'; +import last from 'lodash/last'; + +const URL_KEY = 'eventFlyout' as const; + +type FlyoutState = Parameters[0]; + +/** + * Sync flyout state with the url and open it when relevant url state is detected in the query string + * @returns [ref, flyoutChangesHandler] + */ +export const useSyncFlyoutStateWithUrl = () => { + const flyoutApi = useRef(null); + + const syncStateToUrl = useSyncToUrl(URL_KEY, (data) => { + flyoutApi.current?.openFlyout(data); + }); + + // This should be bound to flyout changed and closed events. + // When flyout is closed, url state is cleared + const handleFlyoutChanges = useCallback( + (state?: ExpandableFlyoutContext['panels']) => { + if (!state) { + return syncStateToUrl(undefined); + } + + return syncStateToUrl({ + ...state, + preview: last(state.preview), + }); + }, + [syncStateToUrl] + ); + + return [flyoutApi, handleFlyoutChanges] as const; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/endpoint_agent_and_isolation_status.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/endpoint_agent_and_isolation_status.test.tsx deleted file mode 100644 index 742f5515e7ade..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/endpoint_agent_and_isolation_status.test.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 type { AppContextTestRender } from '../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; -import type { EndpointAgentAndIsolationStatusProps } from './endpoint_agent_and_isolation_status'; -import { EndpointAgentAndIsolationStatus } from './endpoint_agent_and_isolation_status'; -import { HostStatus } from '../../../../common/endpoint/types'; -import React from 'react'; - -describe('When using the EndpointAgentAndIsolationStatus component', () => { - let render: () => ReturnType; - let renderResult: ReturnType; - let renderProps: EndpointAgentAndIsolationStatusProps; - - beforeEach(() => { - const appTestContext = createAppRootMockRenderer(); - - renderProps = { - status: HostStatus.HEALTHY, - 'data-test-subj': 'test', - pendingActions: {}, - }; - - render = () => { - renderResult = appTestContext.render(); - return renderResult; - }; - }); - - it('should display host status only when `isIsolated` is undefined', () => { - render(); - - expect(renderResult.queryByTestId('test-isolationStatus')).toBeNull(); - }); - - it('should display pending status and pending counts', () => { - renderProps.isIsolated = true; - render(); - - expect(renderResult.getByTestId('test-isolationStatus')).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/endpoint_agent_and_isolation_status.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/endpoint_agent_and_isolation_status.tsx deleted file mode 100644 index 3e80d57d2d70b..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_agent_and_isolation_status/endpoint_agent_and_isolation_status.tsx +++ /dev/null @@ -1,61 +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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import styled from 'styled-components'; -import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; -import type { HostStatus } from '../../../../common/endpoint/types'; -import { AgentStatus } from '../../../common/components/endpoint/agent_status'; -import type { EndpointHostIsolationStatusProps } from '../../../common/components/endpoint/host_isolation'; -import { EndpointHostIsolationStatus } from '../../../common/components/endpoint/host_isolation'; - -const EuiFlexGroupStyled = styled(EuiFlexGroup)` - .isolation-status { - margin-left: ${({ theme }) => theme.eui.euiSizeS}; - } -`; - -export interface EndpointAgentAndIsolationStatusProps - extends Pick { - status: HostStatus; - /** - * If defined with a boolean, then the isolation status will be shown along with the agent status. - * The `pendingIsolate` and `pendingUnIsolate` props will only be used when this prop is set to a - * `boolean` - */ - isIsolated?: boolean; - 'data-test-subj'?: string; -} - -export const EndpointAgentAndIsolationStatus = memo( - ({ status, isIsolated, pendingActions, 'data-test-subj': dataTestSubj }) => { - const getTestId = useTestIdGenerator(dataTestSubj); - return ( - - - - - {isIsolated !== undefined && ( - - - - )} - - ); - } -); -EndpointAgentAndIsolationStatus.displayName = 'EndpointAgentAndIsolationStatus'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/execute_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/execute_action.test.tsx index c3f94871c8f67..8d5d8907924f6 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/execute_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/execute_action.test.tsx @@ -23,6 +23,8 @@ import { getEndpointAuthzInitialStateMock } from '../../../../../../common/endpo import type { EndpointPrivileges } from '../../../../../../common/endpoint/types'; import { INSUFFICIENT_PRIVILEGES_FOR_COMMAND } from '../../../../../common/translations'; import type { HttpFetchOptionsWithPath } from '@kbn/core-http-browser'; +import { endpointActionResponseCodes } from '../../lib/endpoint_action_response_codes'; +import { EndpointActionGenerator } from '../../../../../../common/endpoint/data_generators/endpoint_action_generator'; jest.mock('../../../../../common/components/user_privileges'); jest.mock('../../../../../common/experimental_features_service'); @@ -180,4 +182,36 @@ describe('When using execute action from response actions console', () => { ); }); }); + + it.each( + Object.keys(endpointActionResponseCodes).filter((key) => key.startsWith('ra_execute_error')) + )('should display known error message for response failure: %s', async (errorCode) => { + apiMocks.responseProvider.actionDetails.mockReturnValue({ + data: new EndpointActionGenerator('seed').generateActionDetails({ + command: 'execute', + errors: ['some error happen in endpoint'], + wasSuccessful: false, + outputs: { + 'agent-a': { + content: { + code: errorCode, + }, + }, + }, + }), + }); + + const { getByTestId } = await render(); + enterConsoleCommand(renderResult, 'execute --command="ls -l"'); + + await waitFor(() => { + expect(apiMocks.responseProvider.actionDetails).toHaveBeenCalled(); + }); + + await waitFor(() => { + expect(getByTestId('execute-actionFailure')).toHaveTextContent( + endpointActionResponseCodes[errorCode] + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx new file mode 100644 index 0000000000000..d229b297f239f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx @@ -0,0 +1,148 @@ +/* + * 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 { AppContextTestRender } from '../../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint'; +import { + ConsoleManagerTestComponent, + getConsoleManagerMockRenderResultQueriesAndActions, +} from '../../../console/components/console_manager/mocks'; +import React from 'react'; +import { getEndpointConsoleCommands } from '../../lib/console_commands_definition'; +import { enterConsoleCommand } from '../../../console/mocks'; +import { getEndpointAuthzInitialState } from '../../../../../../common/endpoint/service/authz'; +import type { EndpointCapabilities } from '../../../../../../common/endpoint/service/response_actions/constants'; +import { ENDPOINT_CAPABILITIES } from '../../../../../../common/endpoint/service/response_actions/constants'; +import { useGetEndpointPendingActionsSummary } from '../../../../hooks/response_actions/use_get_endpoint_pending_actions_summary'; +import { useGetEndpointDetails } from '../../../../hooks'; +import { EndpointActionGenerator } from '../../../../../../common/endpoint/data_generators/endpoint_action_generator'; +import { EndpointMetadataGenerator } from '../../../../../../common/endpoint/data_generators/endpoint_metadata_generator'; + +jest.mock('../../../../hooks/response_actions/use_get_endpoint_pending_actions_summary'); +jest.mock('../../../../hooks'); + +const useGetEndpointPendingActionsSummaryMock = useGetEndpointPendingActionsSummary as jest.Mock; +const useGetEndpointDetailsMock = useGetEndpointDetails as jest.Mock; + +describe('When using processes action from response actions console', () => { + let render: ( + capabilities?: EndpointCapabilities[] + ) => Promise>; + let renderResult: ReturnType; + let consoleManagerMockAccess: ReturnType< + typeof getConsoleManagerMockRenderResultQueriesAndActions + >; + const agentId = 'a.b.c'; + + const pendingActionsMock = () => { + useGetEndpointPendingActionsSummaryMock.mockReturnValue({ + data: { + data: [ + new EndpointActionGenerator('seed').generateAgentPendingActionsSummary({ + agent_id: agentId, + pending_actions: { + isolate: 0, + }, + }), + ], + }, + }); + }; + + const endpointDetailsMock = () => { + const endpointMetadata = new EndpointMetadataGenerator('seed').generateHostInfo({ + metadata: { + '@timestamp': new Date('2023-04-20T09:37:40.309Z').getTime(), + agent: { + id: agentId, + version: '8.8.0', + }, + elastic: { + agent: { id: agentId }, + }, + Endpoint: { + state: { + isolation: false, + }, + }, + }, + }); + useGetEndpointDetailsMock.mockReturnValue({ + data: endpointMetadata, + isFetching: false, + isFetched: true, + }); + }; + + beforeEach(() => { + const mockedContext = createAppRootMockRenderer(); + + render = async (capabilities: EndpointCapabilities[] = [...ENDPOINT_CAPABILITIES]) => { + renderResult = mockedContext.render( + { + return { + consoleProps: { + 'data-test-subj': 'test', + commands: getEndpointConsoleCommands({ + endpointAgentId: 'a.b.c', + endpointCapabilities: [...capabilities], + endpointPrivileges: { + ...getEndpointAuthzInitialState(), + loading: false, + }, + }), + }, + }; + }} + /> + ); + + consoleManagerMockAccess = getConsoleManagerMockRenderResultQueriesAndActions(renderResult); + + await consoleManagerMockAccess.clickOnRegisterNewConsole(); + await consoleManagerMockAccess.openRunningConsole(); + + return renderResult; + }; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should show expected status output', async () => { + pendingActionsMock(); + endpointDetailsMock(); + await render(); + enterConsoleCommand(renderResult, 'status'); + const statusResults = renderResult.getByTestId('agent-status-console-output'); + + expect( + Array.from(statusResults.querySelectorAll('dt')).map((term) => term.textContent) + ).toEqual([ + 'Agent status', + 'Platform', + 'Version', + 'Policy status', + 'Policy version', + 'Policy name', + 'Last active', + ]); + + expect( + Array.from(statusResults.querySelectorAll('dd')).map((detail) => detail.textContent) + ).toEqual([ + 'Healthy', + 'Windows Server 2012R2', + '8.8.0', + 'Success', + 'v3', + 'With Eventing', + 'Apr 20, 2023 @ 09:37:40.309', + ]); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx index 94b7aa9dd5160..e901e9b1a116d 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx @@ -12,7 +12,6 @@ import { i18n } from '@kbn/i18n'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { HostInfo, PendingActionsResponse } from '../../../../../common/endpoint/types'; import type { EndpointCommandDefinitionMeta } from '../types'; -import type { EndpointHostIsolationStatusProps } from '../../../../common/components/endpoint/host_isolation'; import { useGetEndpointPendingActionsSummary } from '../../../hooks/response_actions/use_get_endpoint_pending_actions_summary'; import { FormattedDate } from '../../../../common/components/formatted_date'; import { useGetEndpointDetails } from '../../../hooks'; @@ -53,12 +52,10 @@ export const EndpointStatusActionResult = memo< queryKey: [queryKey, endpointId], }); - const pendingIsolationActions = useMemo< - Pick< - Required, - 'pendingIsolate' | 'pendingUnIsolate' - > - >(() => { + const pendingIsolationActions = useMemo<{ + pendingIsolate: number; + pendingUnIsolate: number; + }>(() => { if (endpointPendingActions?.data.length) { const pendingActions = endpointPendingActions.data[0].pending_actions; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx index 2ffcf76b8ba01..b56746e7890a6 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; +import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, @@ -17,8 +17,7 @@ import { import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react'; import { useGetEndpointDetails } from '../../../hooks/endpoint/use_get_endpoint_details'; -import type { EndpointHostIsolationStatusProps } from '../../../../common/components/endpoint/host_isolation'; -import { EndpointAgentAndIsolationStatus } from '../../endpoint_agent_and_isolation_status'; +import { EndpointAgentStatus } from '../../../../common/components/endpoint/endpoint_agent_status'; import { useGetEndpointPendingActionsSummary } from '../../../hooks/response_actions/use_get_endpoint_pending_actions_summary'; import type { Platform } from './platforms'; import { PlatformIcon } from './platforms'; @@ -42,21 +41,6 @@ export const HeaderEndpointInfo = memo(({ endpointId }) refetchInterval: 10000, }); - const pendingActionRequests = useMemo< - Pick, 'pendingActions'> - >(() => { - const pendingActions = endpointPendingActions?.data?.[0].pending_actions; - return { - pendingActions: { - pendingIsolate: pendingActions?.isolate ?? 0, - pendingUnIsolate: pendingActions?.unisolate ?? 0, - pendingKillProcess: pendingActions?.['kill-process'] ?? 0, - pendingSuspendProcess: pendingActions?.['suspend-process'] ?? 0, - pendingRunningProcesses: pendingActions?.['running-processes'] ?? 0, - }, - }; - }, [endpointPendingActions?.data]); - if (isFetching && endpointPendingActions === undefined) { return ; } @@ -90,10 +74,8 @@ export const HeaderEndpointInfo = memo(({ endpointId }) - diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/endpoint_action_response_codes.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/endpoint_action_response_codes.ts index 746b9201cd200..c2595c625b7fa 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/endpoint_action_response_codes.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/endpoint_action_response_codes.ts @@ -123,6 +123,97 @@ const CODES = Object.freeze({ 'xpack.securitySolution.endpointActionResponseCodes.killProcess.notPermittedSuccess', { defaultMessage: 'The provided process cannot be killed' } ), + + // ----------------------------------------------------------------- + // EXECUTE CODES + // ----------------------------------------------------------------- + + // Dev: + // Something interrupted preparing the zip: file read error, zip error. I think these should be rare, + // and should succeed on retry by the user or result in file-not-found. We might implement some retries + // internally but I'm leaning to the opinion that we should rather quickly send the feedback to the + // user to let them decide. + ra_execute_error_processing: i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.processingError', + { + defaultMessage: 'Unable to create execution output zip file.', + } + ), + + // Dev: + // Executing timeout has been reached, the command was killed. + 'ra_execute_error_processing-timeout': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.processingTimeout', + { defaultMessage: 'Command execution was terminated. It exceeded the provided timeout.' } + ), + + // Dev: + // Execution was interrupted, for example: system shutdown, endpoint service stop/restart. + 'ra_execute_error_processing-interrupted': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.processingInterrupted', + { + defaultMessage: 'Command execution was absolutely interrupted.', + } + ), + + // Dev: + // Too many active execute actions, limit 10. Execute actions are allowed to run in parallel, we must + // take into account resource use impact on endpoint as customers are piky about CPU/MEM utilization. + 'ra_execute_error_to-many-requests': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.toManyRequests', + { + defaultMessage: 'Too many concurrent command execution actions.', + } + ), + + // Dev: + // generic failure (rare corner case, software bug, etc) + ra_execute_error_failure: i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.failure', + { defaultMessage: 'Unknown failure while executing command.' } + ), + + // Dev: + // Max pending response zip uploads has been reached, limit 10. Endpoint can't use unlimited disk space. + 'ra_execute_error_disk-quota': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.diskQuotaError', + { + defaultMessage: 'Too many pending command execution output zip files.', + } + ), + + // Dev: + // The fleet upload API was unreachable (not just busy). This may mean policy misconfiguration, in which + // case health status in Kibana should indicate degraded, or maybe network configuration problems, or fleet + // server problems HTTP 500. This excludes offline status, where endpoint should just wait for network connection. + 'ra_execute_error_upload-api-unreachable': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.uploadApiUnreachable', + { + defaultMessage: + 'Failed to upload command execution output zip file. Unable to reach Fleet Server upload API.', + } + ), + + // Dev: + // Perhaps internet connection was too slow or unstable to upload all chunks before unique + // upload-id expired. Endpoint will re-try a bit, max 3 times. + 'ra_execute_error_upload-timeout': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.outputUploadTimeout', + { + defaultMessage: 'Failed to upload command execution output zip file. Upload timed out', + } + ), + + // DEV: + // Upload API could be busy, endpoint should periodically re-try (2 days = 192 x 15min, assuming + // that with 1Mbps 15min is enough to upload 100MB) + 'ra_execute_error_queue-timeout': i18n.translate( + 'xpack.securitySolution.endpointActionResponseCodes.execute.queueTimeout', + { + defaultMessage: + 'Failed to upload command execution output zip file. Timed out while queued waiting for Fleet Server', + } + ), }); /** diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx index 2ea008296b12e..6fc62dd6cf851 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx @@ -652,10 +652,14 @@ describe('Response actions history', () => { }); it('should contain expected output accordions for `execute` action WITH execute operation privilege', async () => { - const actionDetails = await getActionListMock({ actionCount: 1, commands: ['execute'] }); + const actionListApiResponse = await getActionListMock({ + actionCount: 1, + agentIds: ['agent-a'], + commands: ['execute'], + }); useGetEndpointActionListMock.mockReturnValue({ ...getBaseMockedActionList(), - data: actionDetails, + data: actionListApiResponse, }); mockUseGetFileInfo = { @@ -669,17 +673,7 @@ describe('Response actions history', () => { isFetched: true, error: null, data: { - data: { - ...apiMocks.responseProvider.actionDetails({ - path: `/api/endpoint/action/${actionDetails.data[0].id}`, - }).data, - outputs: { - [actionDetails.data[0].agents[0]]: { - content: {}, - type: 'json', - }, - }, - }, + data: actionListApiResponse.data[0], }, }; @@ -714,7 +708,11 @@ describe('Response actions history', () => { }); useGetEndpointActionListMock.mockReturnValue({ ...getBaseMockedActionList(), - data: await getActionListMock({ actionCount: 1, commands: ['execute'] }), + data: await getActionListMock({ + actionCount: 1, + commands: ['execute'], + agentIds: ['agent-a'], + }), }); render(); @@ -723,10 +721,7 @@ describe('Response actions history', () => { const expandButton = getByTestId(`${testPrefix}-expand-button`); userEvent.click(expandButton); - const executeAccordions = getByTestId( - `${testPrefix}-actionsLogTray-executeResponseOutput-output` - ); - expect(executeAccordions).toBeTruthy(); + expect(getByTestId(`${testPrefix}-actionsLogTray-executeResponseOutput-output`)); }); it('should not contain full output download link in expanded row for `execute` action WITHOUT Actions Log privileges', async () => { @@ -796,6 +791,10 @@ describe('Response actions history', () => { }); describe('Action status ', () => { + beforeEach(() => { + apiMocks = responseActionsHttpMocks(mockedContext.coreStart.http); + }); + const expandRows = () => { const { getAllByTestId } = renderResult; @@ -805,58 +804,84 @@ describe('Response actions history', () => { return outputs; }; - it('shows completed status badge for successfully completed actions', async () => { - useGetEndpointActionListMock.mockReturnValue({ - ...getBaseMockedActionList(), - data: await getActionListMock({ actionCount: 2 }), - }); - render(); - - const outputs = expandRows(); - expect(outputs.map((n) => n.textContent)).toEqual([ - 'isolate completed successfully', - 'isolate completed successfully', - ]); - expect( - renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent) - ).toEqual(['Successful', 'Successful']); - }); + it.each(RESPONSE_ACTION_API_COMMANDS_NAMES)( + 'shows completed status badge for successfully completed %s actions', + async (command) => { + useGetEndpointActionListMock.mockReturnValue({ + ...getBaseMockedActionList(), + data: await getActionListMock({ actionCount: 2, commands: [command] }), + }); + if (command === 'get-file' || command === 'execute') { + mockUseGetFileInfo = { + isFetching: false, + error: null, + data: apiMocks.responseProvider.fileInfo(), + }; + } - it('shows Failed status badge for failed actions', async () => { - useGetEndpointActionListMock.mockReturnValue({ - ...getBaseMockedActionList(), - data: await getActionListMock({ actionCount: 2, wasSuccessful: false, status: 'failed' }), - }); - render(); + render(); - const outputs = expandRows(); - expect(outputs.map((n) => n.textContent)).toEqual(['isolate failed', 'isolate failed']); - expect( - renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent) - ).toEqual(['Failed', 'Failed']); - }); + const outputs = expandRows(); + expect(outputs.map((n) => n.textContent)).toEqual([ + expect.stringContaining(`${command} completed successfully`), + expect.stringContaining(`${command} completed successfully`), + ]); + expect( + renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent) + ).toEqual(['Successful', 'Successful']); + } + ); + + it.each(RESPONSE_ACTION_API_COMMANDS_NAMES)( + 'shows Failed status badge for failed %s actions', + async (command) => { + useGetEndpointActionListMock.mockReturnValue({ + ...getBaseMockedActionList(), + data: await getActionListMock({ + actionCount: 2, + commands: [command], + wasSuccessful: false, + status: 'failed', + }), + }); + render(); - it('shows Failed status badge for expired actions', async () => { - useGetEndpointActionListMock.mockReturnValue({ - ...getBaseMockedActionList(), - data: await getActionListMock({ - actionCount: 2, - isCompleted: false, - isExpired: true, - status: 'failed', - }), - }); - render(); + const outputs = expandRows(); + expect(outputs.map((n) => n.textContent)).toEqual([ + `${command} failed`, + `${command} failed`, + ]); + expect( + renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent) + ).toEqual(['Failed', 'Failed']); + } + ); + + it.each(RESPONSE_ACTION_API_COMMANDS_NAMES)( + 'shows Failed status badge for expired %s actions', + async (command) => { + useGetEndpointActionListMock.mockReturnValue({ + ...getBaseMockedActionList(), + data: await getActionListMock({ + actionCount: 2, + commands: [command], + isCompleted: false, + isExpired: true, + status: 'failed', + }), + }); + render(); - const outputs = expandRows(); - expect(outputs.map((n) => n.textContent)).toEqual([ - 'isolate failed: action expired', - 'isolate failed: action expired', - ]); - expect( - renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent) - ).toEqual(['Failed', 'Failed']); - }); + const outputs = expandRows(); + expect(outputs.map((n) => n.textContent)).toEqual([ + `${command} failed: action expired`, + `${command} failed: action expired`, + ]); + expect( + renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent) + ).toEqual(['Failed', 'Failed']); + } + ); it('shows Pending status badge for pending actions', async () => { useGetEndpointActionListMock.mockReturnValue({ diff --git a/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/index.ts b/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/index.ts new file mode 100644 index 0000000000000..c580eeda82d82 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/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 { MissingEncryptionKeyCallout } from './missing_encryption_key_callout'; diff --git a/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/missing_encryption_key_callout.test.tsx b/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/missing_encryption_key_callout.test.tsx new file mode 100644 index 0000000000000..eeff557fb0131 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/missing_encryption_key_callout.test.tsx @@ -0,0 +1,88 @@ +/* + * 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 { ACTION_STATE_ROUTE } from '../../../../common/endpoint/constants'; +import { act, fireEvent } from '@testing-library/react'; +import React from 'react'; +import type { AppContextTestRender } from '../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; +import { policyListApiPathHandlers } from '../../pages/policy/store/test_mock_utils'; +import { MissingEncryptionKeyCallout } from './missing_encryption_key_callout'; + +describe('Missing encryption key callout', () => { + let render: () => ReturnType; + let renderResult: ReturnType; + let mockedContext: AppContextTestRender; + let asyncActions: Promise = Promise.resolve(); + const sleep = (ms = 100) => new Promise((wakeup) => setTimeout(wakeup, ms)); + + const policyListApiHandlers = policyListApiPathHandlers(); + + const apiReturnsEncryptionKeyIsSet = (canEncrypt: boolean) => { + mockedContext.coreStart.http.get.mockImplementation((...args) => { + const [path] = args; + if (typeof path === 'string') { + // GET datasouce + if (path === ACTION_STATE_ROUTE) { + asyncActions = asyncActions.then(async (): Promise => sleep()); + return Promise.resolve({ + data: { canEncrypt }, + }); + } + + // Get action state + // Used in tests that route back to the list + if (policyListApiHandlers[path]) { + asyncActions = asyncActions.then(async () => sleep()); + return Promise.resolve(policyListApiHandlers[path]()); + } + } + + return Promise.reject(new Error(`unknown API call (not MOCKED): ${path}`)); + }); + }; + + beforeEach(() => { + mockedContext = createAppRootMockRenderer(); + render = () => (renderResult = mockedContext.render()); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should be visible when encryption key not set', async () => { + apiReturnsEncryptionKeyIsSet(false); + render(); + await asyncActions; + const callout = renderResult.queryByTestId('missingEncryptionKeyCallout'); + expect(callout).toBeTruthy(); + }); + + it('should not be visible when encryption key is set', async () => { + apiReturnsEncryptionKeyIsSet(true); + render(); + await asyncActions; + const callout = renderResult.queryByTestId('missingEncryptionKeyCallout'); + expect(callout).toBeFalsy(); + }); + + it('should be able to dismiss when visible', async () => { + apiReturnsEncryptionKeyIsSet(false); + render(); + await asyncActions; + let callout = renderResult.queryByTestId('missingEncryptionKeyCallout'); + expect(callout).toBeTruthy(); + + act(() => { + fireEvent.click(renderResult.getByTestId('dismissEncryptionKeyCallout')); + }); + + callout = renderResult.queryByTestId('missingEncryptionKeyCallout'); + expect(callout).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/missing_encryption_key_callout.tsx b/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/missing_encryption_key_callout.tsx new file mode 100644 index 0000000000000..6524f81f23c8b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/missing_encryption_key_callout/missing_encryption_key_callout.tsx @@ -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 React, { memo, useState, useCallback } from 'react'; +import { EuiCallOut, EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { useGetActionState } from '../../hooks'; + +export const MissingEncryptionKeyCallout = memo(() => { + const { data: encryptionKeyState } = useGetActionState(); + const [calloutDismiss, setCalloutDismiss] = useState(false); + + const onClickDismissButton = useCallback(() => setCalloutDismiss(true), []); + + if (!encryptionKeyState) { + return null; + } + + if (calloutDismiss || encryptionKeyState.data.canEncrypt === true) { + return null; + } + + return ( + <> + +
    + +
    + + + + +
    + + + ); +}); + +MissingEncryptionKeyCallout.displayName = 'MissingEncryptionKeyCallout'; diff --git a/x-pack/plugins/security_solution/public/management/cypress/cypress.d.ts b/x-pack/plugins/security_solution/public/management/cypress/cypress.d.ts index a48498a7ee43b..51cd74de62f67 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/cypress.d.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/cypress.d.ts @@ -10,9 +10,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { CasePostRequest } from '@kbn/cases-plugin/common/api'; +import type { DeleteAllEndpointDataResponse } from '../../../scripts/endpoint/common/delete_all_endpoint_data'; import type { IndexedEndpointPolicyResponse } from '../../../common/endpoint/data_loaders/index_endpoint_policy_response'; -import type { HostPolicyResponse } from '../../../common/endpoint/types'; -import type { IndexEndpointHostsCyTaskOptions } from './types'; +import type { + HostPolicyResponse, + LogsEndpointActionResponse, +} from '../../../common/endpoint/types'; +import type { IndexEndpointHostsCyTaskOptions, HostActionResponse } from './types'; import type { DeleteIndexedFleetEndpointPoliciesResponse, IndexedFleetEndpointPolicyResponse, @@ -53,6 +57,20 @@ declare global { ...args: Parameters['find']> ): Chainable>; + /** + * Continuously call provided callback function until it either return `true` + * or fail if `timeout` is reached. + * @param fn + * @param options + */ + waitUntil( + fn: (subject?: any) => boolean | Promise | Chainable, + options?: Partial<{ + interval: number; + timeout: number; + }> + ): Chainable; + task( name: 'indexFleetEndpointPolicy', arg: { @@ -115,6 +133,18 @@ declare global { arg: IndexedEndpointPolicyResponse, options?: Partial ): Chainable; + + task( + name: 'sendHostActionResponse', + arg: HostActionResponse, + options?: Partial + ): Chainable; + + task( + name: 'deleteAllEndpointData', + arg: { endpointAgentIds: string[] }, + options?: Partial + ): Chainable; } } } diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoint_alerts.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoint_alerts.cy.ts new file mode 100644 index 0000000000000..8163e74db17b1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoint_alerts.cy.ts @@ -0,0 +1,107 @@ +/* + * 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 { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; +import { getAlertsTableRows, navigateToAlertsList } from '../../screens/alerts'; +import { waitForEndpointAlerts } from '../../tasks/alerts'; +import { request } from '../../tasks/common'; +import { getEndpointIntegrationVersion } from '../../tasks/fleet'; +import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; +import type { PolicyData, ResponseActionApiResponse } from '../../../../../common/endpoint/types'; +import type { CreateAndEnrollEndpointHostResponse } from '../../../../../scripts/endpoint/common/endpoint_host_services'; +import { login } from '../../tasks/login'; +import { EXECUTE_ROUTE } from '../../../../../common/endpoint/constants'; +import { waitForActionToComplete } from '../../tasks/response_actions'; + +describe('Endpoint generated alerts', () => { + let indexedPolicy: IndexedFleetEndpointPolicyResponse; + let policy: PolicyData; + let createdHost: CreateAndEnrollEndpointHostResponse; + + before(() => { + getEndpointIntegrationVersion().then((version) => { + const policyName = `alerts test ${Math.random().toString(36).substring(2, 7)}`; + + cy.task('indexFleetEndpointPolicy', { + policyName, + endpointPackageVersion: version, + agentPolicyName: policyName, + }).then((data) => { + indexedPolicy = data; + policy = indexedPolicy.integrationPolicies[0]; + + return enableAllPolicyProtections(policy.id).then(() => { + // Create and enroll a new Endpoint host + return cy + .task( + 'createEndpointHost', + { + agentPolicyId: policy.policy_id, + }, + { timeout: 180000 } + ) + .then((host) => { + createdHost = host as CreateAndEnrollEndpointHostResponse; + }); + }); + }); + }); + }); + + after(() => { + if (createdHost) { + cy.task('destroyEndpointHost', createdHost).then(() => {}); + } + + if (indexedPolicy) { + cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy); + } + + if (createdHost) { + deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] }); + } + }); + + beforeEach(() => { + login(); + }); + + it('should create a Detection Engine alert from an endpoint alert', () => { + // Triggers a Malicious Behaviour alert on Linux system (`grep *` was added only to identify this specific alert) + const executeMaliciousCommand = `bash -c cat /dev/tcp/foo | grep ${Math.random() + .toString(16) + .substring(2)}`; + + // Send `execute` command that triggers malicious behaviour using the `execute` response action + request({ + method: 'POST', + url: EXECUTE_ROUTE, + body: { + endpoint_ids: [createdHost.agentId], + parameters: { + command: executeMaliciousCommand, + }, + }, + }) + .then((response) => waitForActionToComplete(response.body.data.id)) + .then(() => { + return waitForEndpointAlerts(createdHost.agentId, [ + { + term: { 'process.group_leader.args': executeMaliciousCommand }, + }, + ]); + }) + .then(() => { + return navigateToAlertsList( + `query=(language:kuery,query:'agent.id: "${createdHost.agentId}" ')` + ); + }); + + getAlertsTableRows().should('have.length.greaterThan', 0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/isolate.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/isolate.cy.ts new file mode 100644 index 0000000000000..db486a6478e1a --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/isolate.cy.ts @@ -0,0 +1,286 @@ +/* + * 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 { Agent } from '@kbn/fleet-plugin/common'; +import { APP_CASES_PATH, APP_ENDPOINTS_PATH } from '../../../../../common/constants'; +import { closeAllToasts } from '../../tasks/close_all_toasts'; +import { + checkEndpointListForIsolatedHosts, + checkFlyoutEndpointIsolation, + createAgentPolicyTask, + filterOutEndpoints, + filterOutIsolatedHosts, + isolateHostWithComment, + openAlertDetails, + openCaseAlertDetails, + releaseHostWithComment, + toggleRuleOffAndOn, + visitRuleAlerts, + waitForReleaseOption, +} from '../../tasks/isolate'; +import { cleanupCase, cleanupRule, loadCase, loadRule } from '../../tasks/api_fixtures'; +import { ENDPOINT_VM_NAME } from '../../tasks/common'; +import { login } from '../../tasks/login'; +import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import { + getAgentByHostName, + getEndpointIntegrationVersion, + reassignAgentPolicy, +} from '../../tasks/fleet'; + +describe('Isolate command', () => { + const endpointHostname = Cypress.env(ENDPOINT_VM_NAME); + const isolateComment = `Isolating ${endpointHostname}`; + const releaseComment = `Releasing ${endpointHostname}`; + + beforeEach(() => { + login(); + }); + + describe('From manage', () => { + let response: IndexedFleetEndpointPolicyResponse; + let initialAgentData: Agent; + + before(() => { + getAgentByHostName(endpointHostname).then((agentData) => { + initialAgentData = agentData; + }); + + getEndpointIntegrationVersion().then((version) => { + createAgentPolicyTask(version, (data) => { + response = data; + }); + }); + }); + + after(() => { + if (initialAgentData?.policy_id) { + reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id); + } + if (response) { + cy.task('deleteIndexedFleetEndpointPolicies', response); + } + }); + + it('should allow filtering endpoint by Isolated status', () => { + cy.visit(APP_ENDPOINTS_PATH); + closeAllToasts(); + checkEndpointListForIsolatedHosts(false); + + filterOutIsolatedHosts(); + cy.contains('No items found'); + cy.getByTestSubj('adminSearchBar').click().type('{selectall}{backspace}'); + cy.getByTestSubj('querySubmitButton').click(); + cy.getByTestSubj('endpointTableRowActions').click(); + cy.getByTestSubj('isolateLink').click(); + + cy.contains(`Isolate host ${endpointHostname} from network.`); + cy.getByTestSubj('endpointHostIsolationForm'); + cy.getByTestSubj('host_isolation_comment').type(isolateComment); + cy.getByTestSubj('hostIsolateConfirmButton').click(); + cy.contains(`Isolation on host ${endpointHostname} successfully submitted`); + cy.getByTestSubj('euiFlyoutCloseButton').click(); + cy.getByTestSubj('rowHostStatus-actionStatuses').should('contain.text', 'Isolated'); + filterOutIsolatedHosts(); + + checkEndpointListForIsolatedHosts(); + + cy.getByTestSubj('endpointTableRowActions').click(); + cy.getByTestSubj('unIsolateLink').click(); + releaseHostWithComment(releaseComment, endpointHostname); + cy.contains('Confirm').click(); + cy.getByTestSubj('euiFlyoutCloseButton').click(); + cy.getByTestSubj('adminSearchBar').click().type('{selectall}{backspace}'); + cy.getByTestSubj('querySubmitButton').click(); + checkEndpointListForIsolatedHosts(false); + }); + }); + + describe('From alerts', () => { + let response: IndexedFleetEndpointPolicyResponse; + let initialAgentData: Agent; + let ruleId: string; + let ruleName: string; + + before(() => { + getAgentByHostName(endpointHostname).then((agentData) => { + initialAgentData = agentData; + }); + + getEndpointIntegrationVersion().then((version) => { + createAgentPolicyTask(version, (data) => { + response = data; + }); + }); + loadRule(false).then((data) => { + ruleId = data.id; + ruleName = data.name; + }); + }); + + after(() => { + if (initialAgentData?.policy_id) { + reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id); + } + if (response) { + cy.task('deleteIndexedFleetEndpointPolicies', response); + } + if (ruleId) { + cleanupRule(ruleId); + } + }); + + it('should have generated endpoint and rule', () => { + cy.visit(APP_ENDPOINTS_PATH); + cy.contains(endpointHostname).should('exist'); + + toggleRuleOffAndOn(ruleName); + }); + + it('should isolate and release host', () => { + visitRuleAlerts(ruleName); + + filterOutEndpoints(endpointHostname); + + closeAllToasts(); + openAlertDetails(); + + isolateHostWithComment(isolateComment, endpointHostname); + + cy.getByTestSubj('hostIsolateConfirmButton').click(); + cy.contains(`Isolation on host ${endpointHostname} successfully submitted`); + + cy.getByTestSubj('euiFlyoutCloseButton').click(); + openAlertDetails(); + + checkFlyoutEndpointIsolation(); + + releaseHostWithComment(releaseComment, endpointHostname); + cy.contains('Confirm').click(); + + cy.contains(`Release on host ${endpointHostname} successfully submitted`); + cy.getByTestSubj('euiFlyoutCloseButton').click(); + openAlertDetails(); + cy.getByTestSubj('event-field-agent.status').within(() => { + cy.get('[title="Isolated"]').should('not.exist'); + }); + }); + }); + + describe('From cases', () => { + let response: IndexedFleetEndpointPolicyResponse; + let initialAgentData: Agent; + let ruleId: string; + let ruleName: string; + let caseId: string; + + const caseOwner = 'securitySolution'; + + before(() => { + getAgentByHostName(endpointHostname).then((agentData) => { + initialAgentData = agentData; + }); + getEndpointIntegrationVersion().then((version) => { + createAgentPolicyTask(version, (data) => { + response = data; + }); + }); + + loadRule(false).then((data) => { + ruleId = data.id; + ruleName = data.name; + }); + loadCase(caseOwner).then((data) => { + caseId = data.id; + }); + }); + + beforeEach(() => { + login(); + }); + + after(() => { + if (initialAgentData?.policy_id) { + reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id); + } + if (response) { + cy.task('deleteIndexedFleetEndpointPolicies', response); + } + if (ruleId) { + cleanupRule(ruleId); + } + if (caseId) { + cleanupCase(caseId); + } + }); + + it('should have generated endpoint and rule', () => { + cy.visit(APP_ENDPOINTS_PATH); + cy.contains(endpointHostname).should('exist'); + + toggleRuleOffAndOn(ruleName); + }); + + it('should isolate and release host', () => { + visitRuleAlerts(ruleName); + filterOutEndpoints(endpointHostname); + closeAllToasts(); + + openAlertDetails(); + + cy.getByTestSubj('add-to-existing-case-action').click(); + cy.getByTestSubj(`cases-table-row-select-${caseId}`).click(); + cy.contains(`An alert was added to \"Test ${caseOwner} case`); + + cy.intercept('GET', `/api/cases/${caseId}/user_actions/_find*`).as('case'); + cy.visit(`${APP_CASES_PATH}/${caseId}`); + cy.wait('@case', { timeout: 30000 }).then(({ response: res }) => { + const caseAlertId = res?.body.userActions[1].id; + + closeAllToasts(); + openCaseAlertDetails(caseAlertId); + isolateHostWithComment(isolateComment, endpointHostname); + cy.getByTestSubj('hostIsolateConfirmButton').click(); + + cy.getByTestSubj('euiFlyoutCloseButton').click(); + + cy.getByTestSubj('user-actions-list').within(() => { + cy.contains(isolateComment); + cy.get('[aria-label="lock"]').should('exist'); + cy.get('[aria-label="lockOpen"]').should('not.exist'); + }); + + waitForReleaseOption(caseAlertId); + + releaseHostWithComment(releaseComment, endpointHostname); + + cy.contains('Confirm').click(); + + cy.contains(`Release on host ${endpointHostname} successfully submitted`); + cy.getByTestSubj('euiFlyoutCloseButton').click(); + + cy.getByTestSubj('user-actions-list').within(() => { + cy.contains(releaseComment); + cy.contains(isolateComment); + cy.get('[aria-label="lock"]').should('exist'); + cy.get('[aria-label="lockOpen"]').should('exist'); + }); + + openCaseAlertDetails(caseAlertId); + + cy.getByTestSubj('event-field-agent.status').then(($status) => { + if ($status.find('[title="Isolated"]').length > 0) { + cy.getByTestSubj('euiFlyoutCloseButton').click(); + cy.getByTestSubj(`comment-action-show-alert-${caseAlertId}`).click(); + cy.getByTestSubj('take-action-dropdown-btn').click(); + } + cy.get('[title="Isolated"]').should('not.exist'); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts index 35b931675a6c2..d5c946af96c14 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts @@ -5,18 +5,31 @@ * 2.0. */ +import type { ReturnTypeFromChainable } from '../../types'; +import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts'; import { login } from '../../tasks/login'; -import { runEndpointLoaderScript } from '../../tasks/run_endpoint_loader'; describe('Endpoints page', () => { + let endpointData: ReturnTypeFromChainable; + before(() => { - runEndpointLoaderScript(); + indexEndpointHosts().then((indexEndpoints) => { + endpointData = indexEndpoints; + }); }); beforeEach(() => { login(); }); + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + }); + it('Loads the endpoints page', () => { cy.visit('/app/security/administration/endpoints'); cy.contains('Hosts running Elastic Defend').should('exist'); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts new file mode 100644 index 0000000000000..416987432fce3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts @@ -0,0 +1,312 @@ +/* + * 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 { getEndpointListPath } from '../../../common/routing'; +import { + checkEndpointListForIsolatedHosts, + checkFlyoutEndpointIsolation, + filterOutIsolatedHosts, + interceptActionRequests, + isolateHostWithComment, + openAlertDetails, + openCaseAlertDetails, + releaseHostWithComment, + sendActionResponse, + waitForReleaseOption, +} from '../../tasks/isolate'; +import type { ActionDetails } from '../../../../../common/endpoint/types'; +import { closeAllToasts } from '../../tasks/close_all_toasts'; +import type { ReturnTypeFromChainable } from '../../types'; +import { addAlertsToCase } from '../../tasks/add_alerts_to_case'; +import { APP_ALERTS_PATH, APP_CASES_PATH, APP_PATH } from '../../../../../common/constants'; +import { login } from '../../tasks/login'; +import { indexNewCase } from '../../tasks/index_new_case'; +import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts'; +import { indexEndpointRuleAlerts } from '../../tasks/index_endpoint_rule_alerts'; + +describe('Isolate command', () => { + describe('from Manage', () => { + let endpointData: ReturnTypeFromChainable; + let isolatedEndpointData: ReturnTypeFromChainable; + + before(() => { + indexEndpointHosts({ + count: 2, + withResponseActions: false, + isolation: false, + }).then((indexEndpoints) => { + endpointData = indexEndpoints; + }); + + indexEndpointHosts({ + count: 2, + withResponseActions: false, + isolation: true, + }).then((indexEndpoints) => { + isolatedEndpointData = indexEndpoints; + }); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + + if (isolatedEndpointData) { + isolatedEndpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + isolatedEndpointData = undefined; + } + }); + beforeEach(() => { + login(); + }); + it('should allow filtering endpoint by Isolated status', () => { + cy.visit(APP_PATH + getEndpointListPath({ name: 'endpointList' })); + closeAllToasts(); + filterOutIsolatedHosts(); + cy.contains('Showing 2 endpoints'); + checkEndpointListForIsolatedHosts(); + }); + }); + + describe('from Alerts', () => { + let endpointData: ReturnTypeFromChainable; + let alertData: ReturnTypeFromChainable; + let hostname: string; + + before(() => { + indexEndpointHosts({ withResponseActions: false, isolation: false }) + .then((indexEndpoints) => { + endpointData = indexEndpoints; + hostname = endpointData.data.hosts[0].host.name; + }) + .then(() => { + return indexEndpointRuleAlerts({ + endpointAgentId: endpointData.data.hosts[0].agent.id, + endpointHostname: endpointData.data.hosts[0].host.name, + endpointIsolated: false, + }); + }); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + + if (alertData) { + alertData.cleanup(); + // @ts-expect-error ignore setting to undefined + alertData = undefined; + } + }); + + beforeEach(() => { + login(); + }); + + it('should isolate and release host', () => { + const isolateComment = `Isolating ${hostname}`; + const releaseComment = `Releasing ${hostname}`; + let isolateRequestResponse: ActionDetails; + let releaseRequestResponse: ActionDetails; + + cy.visit(APP_ALERTS_PATH); + closeAllToasts(); + + cy.getByTestSubj('alertsTable').within(() => { + cy.getByTestSubj('expand-event') + .first() + .within(() => { + cy.get(`[data-is-loading="true"]`).should('exist'); + }); + cy.getByTestSubj('expand-event') + .first() + .within(() => { + cy.get(`[data-is-loading="true"]`).should('not.exist'); + }); + }); + + openAlertDetails(); + + isolateHostWithComment(isolateComment, hostname); + + interceptActionRequests((responseBody) => { + isolateRequestResponse = responseBody; + }, 'isolate'); + + cy.getByTestSubj('hostIsolateConfirmButton').click(); + + cy.wait('@isolate').then(() => { + sendActionResponse(isolateRequestResponse); + }); + + cy.contains(`Isolation on host ${hostname} successfully submitted`); + + cy.getByTestSubj('euiFlyoutCloseButton').click(); + cy.wait(1000); + openAlertDetails(); + + checkFlyoutEndpointIsolation(); + + releaseHostWithComment(releaseComment, hostname); + + interceptActionRequests((responseBody) => { + releaseRequestResponse = responseBody; + }, 'release'); + + cy.contains('Confirm').click(); + + cy.wait('@release').then(() => { + sendActionResponse(releaseRequestResponse); + }); + + cy.contains(`Release on host ${hostname} successfully submitted`); + cy.getByTestSubj('euiFlyoutCloseButton').click(); + openAlertDetails(); + cy.getByTestSubj('event-field-agent.status').within(() => { + cy.get('[title="Isolated"]').should('not.exist'); + }); + }); + }); + + describe('from Cases', () => { + let endpointData: ReturnTypeFromChainable; + let caseData: ReturnTypeFromChainable; + let alertData: ReturnTypeFromChainable; + let caseAlertActions: ReturnType; + let alertId: string; + let caseUrlPath: string; + let hostname: string; + + before(() => { + indexNewCase().then((indexCase) => { + caseData = indexCase; + caseUrlPath = `${APP_CASES_PATH}/${indexCase.data.id}`; + }); + + indexEndpointHosts({ withResponseActions: false, isolation: false }) + .then((indexEndpoints) => { + endpointData = indexEndpoints; + hostname = endpointData.data.hosts[0].host.name; + }) + .then(() => { + return indexEndpointRuleAlerts({ + endpointAgentId: endpointData.data.hosts[0].agent.id, + endpointHostname: endpointData.data.hosts[0].host.name, + endpointIsolated: false, + }).then((indexedAlert) => { + alertData = indexedAlert; + alertId = alertData.alerts[0]._id; + }); + }) + .then(() => { + caseAlertActions = addAlertsToCase({ + caseId: caseData.data.id, + alertIds: [alertId], + }); + }); + }); + + after(() => { + if (caseData) { + caseData.cleanup(); + // @ts-expect-error ignore setting to undefined + caseData = undefined; + } + + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + + if (alertData) { + alertData.cleanup(); + // @ts-expect-error ignore setting to undefined + alertData = undefined; + } + }); + + beforeEach(() => { + login(); + }); + + it('should isolate and release host', () => { + let isolateRequestResponse: ActionDetails; + let releaseRequestResponse: ActionDetails; + const isolateComment = `Isolating ${hostname}`; + const releaseComment = `Releasing ${hostname}`; + const caseAlertId = caseAlertActions.comments[alertId]; + + cy.visit(caseUrlPath); + closeAllToasts(); + openCaseAlertDetails(caseAlertId); + + isolateHostWithComment(isolateComment, hostname); + + interceptActionRequests((responseBody) => { + isolateRequestResponse = responseBody; + }, 'isolate'); + + cy.getByTestSubj('hostIsolateConfirmButton').click(); + + cy.wait('@isolate').then(() => { + sendActionResponse(isolateRequestResponse); + }); + + cy.contains(`Isolation on host ${hostname} successfully submitted`); + + cy.getByTestSubj('euiFlyoutCloseButton').click(); + + cy.getByTestSubj('user-actions-list').within(() => { + cy.contains(isolateComment); + cy.get('[aria-label="lock"]').should('exist'); + cy.get('[aria-label="lockOpen"]').should('not.exist'); + }); + + waitForReleaseOption(caseAlertId); + + releaseHostWithComment(releaseComment, hostname); + + interceptActionRequests((responseBody) => { + releaseRequestResponse = responseBody; + }, 'release'); + + cy.contains('Confirm').click(); + + cy.wait('@release').then(() => { + sendActionResponse(releaseRequestResponse); + }); + + cy.contains(`Release on host ${hostname} successfully submitted`); + cy.getByTestSubj('euiFlyoutCloseButton').click(); + + cy.getByTestSubj('user-actions-list').within(() => { + cy.contains(releaseComment); + cy.contains(isolateComment); + cy.get('[aria-label="lock"]').should('exist'); + cy.get('[aria-label="lockOpen"]').should('exist'); + }); + + openCaseAlertDetails(caseAlertId); + cy.getByTestSubj('event-field-agent.status').then(($status) => { + if ($status.find('[title="Isolated"]').length > 0) { + cy.getByTestSubj('euiFlyoutCloseButton').click(); + cy.getByTestSubj(`comment-action-show-alert-${caseAlertId}`).click(); + cy.getByTestSubj('take-action-dropdown-btn').click(); + } + cy.get('[title="Isolated"]').should('not.exist'); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/public/management/cypress/screens/alerts.ts new file mode 100644 index 0000000000000..48f0747464bf8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/screens/alerts.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { APP_ALERTS_PATH } from '../../../../common/constants'; + +export const navigateToAlertsList = (urlQueryParams: string = '') => { + cy.visit(`${APP_ALERTS_PATH}${urlQueryParams ? `?${urlQueryParams}` : ''}`); +}; + +export const clickAlertListRefreshButton = (): Cypress.Chainable => { + return cy.getByTestSubj('querySubmitButton').click().should('be.enabled'); +}; + +/** + * Waits until the Alerts list has alerts data and return the number of rows that are currently displayed + * @param timeout + */ +export const getAlertsTableRows = (timeout?: number): Cypress.Chainable> => { + let $rows: JQuery = Cypress.$(); + + return cy + .waitUntil( + () => { + clickAlertListRefreshButton(); + + return cy + .getByTestSubj('alertsTable') + .find('.euiDataGridRow') + .then(($rowsFound) => { + $rows = $rowsFound; + return Boolean($rows); + }); + }, + { timeout } + ) + .then(() => $rows); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.ts b/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.ts index 32a12168aadb0..da2c278b8e30d 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/screens/endpoints.ts @@ -6,7 +6,7 @@ */ import { APP_PATH } from '../../../../common/constants'; -import { getEndpointDetailsPath } from '../../common/routing'; +import { getEndpointDetailsPath, getEndpointListPath } from '../../common/routing'; export const AGENT_HOSTNAME_CELL = 'hostnameCellLink'; export const AGENT_POLICY_CELL = 'policyNameCellLink'; @@ -21,3 +21,7 @@ export const navigateToEndpointPolicyResponse = ( getEndpointDetailsPath({ name: 'endpointPolicyResponse', selected_endpoint: endpointAgentId }) ); }; + +export const navigateToEndpointList = (): Cypress.Chainable => { + return cy.visit(APP_PATH + getEndpointListPath({ name: 'endpointList' })); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts index b31ee2ec25874..ffcca01a6f1e9 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts @@ -8,12 +8,24 @@ // / import type { CasePostRequest } from '@kbn/cases-plugin/common/api'; +import { sendEndpointActionResponse } from '../../../../scripts/endpoint/agent_emulator/services/endpoint_response_actions'; +import type { DeleteAllEndpointDataResponse } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; +import { deleteAllEndpointData } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; +import { waitForEndpointToStreamData } from '../../../../scripts/endpoint/common/endpoint_metadata_services'; +import type { + CreateAndEnrollEndpointHostOptions, + CreateAndEnrollEndpointHostResponse, +} from '../../../../scripts/endpoint/common/endpoint_host_services'; import type { IndexedEndpointPolicyResponse } from '../../../../common/endpoint/data_loaders/index_endpoint_policy_response'; import { deleteIndexedEndpointPolicyResponse, indexEndpointPolicyResponse, } from '../../../../common/endpoint/data_loaders/index_endpoint_policy_response'; -import type { HostPolicyResponse } from '../../../../common/endpoint/types'; +import type { + ActionDetails, + HostPolicyResponse, + LogsEndpointActionResponse, +} from '../../../../common/endpoint/types'; import type { IndexEndpointHostsCyTaskOptions } from '../types'; import type { IndexedEndpointRuleAlerts, @@ -34,6 +46,10 @@ import { deleteIndexedEndpointRuleAlerts, indexEndpointRuleAlerts, } from '../../../../common/endpoint/data_loaders/index_endpoint_rule_alerts'; +import { + createAndEnrollEndpointHost, + destroyEndpointHost, +} from '../../../../scripts/endpoint/common/endpoint_host_services'; /** * Cypress plugin for adding data loading related `task`s @@ -95,12 +111,14 @@ export const dataLoaders = ( indexEndpointHosts: async (options: IndexEndpointHostsCyTaskOptions = {}) => { const { kbnClient, esClient } = await stackServicesPromise; - const { count: numHosts, version, os } = options; + const { count: numHosts, version, os, isolation, withResponseActions } = options; return cyLoadEndpointDataHandler(esClient, kbnClient, { numHosts, version, os, + isolation, + withResponseActions, }); }, @@ -140,5 +158,55 @@ export const dataLoaders = ( const { esClient } = await stackServicesPromise; return deleteIndexedEndpointPolicyResponse(esClient, indexedData).then(() => null); }, + + sendHostActionResponse: async (data: { + action: ActionDetails; + state: { state?: 'success' | 'failure' }; + }): Promise => { + const { esClient } = await stackServicesPromise; + return sendEndpointActionResponse(esClient, data.action, { state: data.state.state }); + }, + + deleteAllEndpointData: async ({ + endpointAgentIds, + }: { + endpointAgentIds: string[]; + }): Promise => { + const { esClient } = await stackServicesPromise; + return deleteAllEndpointData(esClient, endpointAgentIds); + }, + }); +}; + +export const dataLoadersForRealEndpoints = ( + on: Cypress.PluginEvents, + config: Cypress.PluginConfigOptions +): void => { + const stackServicesPromise = createRuntimeServices({ + kibanaUrl: config.env.KIBANA_URL, + elasticsearchUrl: config.env.ELASTICSEARCH_URL, + username: config.env.ELASTICSEARCH_USERNAME, + password: config.env.ELASTICSEARCH_PASSWORD, + asSuperuser: true, + }); + + on('task', { + createEndpointHost: async ( + options: Omit + ): Promise => { + const { kbnClient, log } = await stackServicesPromise; + return createAndEnrollEndpointHost({ ...options, log, kbnClient }).then((newHost) => { + return waitForEndpointToStreamData(kbnClient, newHost.agentId, 120000).then(() => { + return newHost; + }); + }); + }, + + destroyEndpointHost: async ( + createdHost: CreateAndEnrollEndpointHostResponse + ): Promise => { + const { kbnClient } = await stackServicesPromise; + return destroyEndpointHost(kbnClient, createdHost).then(() => null); + }, }); }; diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts b/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts index 0ccb00e8d5e63..12c236f481791 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/support/e2e.ts @@ -50,4 +50,42 @@ Cypress.Commands.addQuery<'findByTestSubj'>( } ); +Cypress.Commands.add( + 'waitUntil', + { prevSubject: 'optional' }, + (subject, fn, { interval = 500, timeout = 30000 } = {}) => { + let attempts = Math.floor(timeout / interval); + + const completeOrRetry = (result: boolean) => { + if (result) { + return result; + } + if (attempts < 1) { + throw new Error(`Timed out while retrying, last result was: {${result}}`); + } + cy.wait(interval, { log: false }).then(() => { + attempts--; + return evaluate(); + }); + }; + + const evaluate = () => { + const result = fn(subject); + + if (typeof result === 'boolean') { + return completeOrRetry(result); + } else if ('then' in result) { + // @ts-expect-error + return result.then(completeOrRetry); + } else { + throw new Error( + `Unknown return type from callback: ${Object.prototype.toString.call(result)}` + ); + } + }; + + return evaluate(); + } +); + Cypress.on('uncaught:exception', () => false); diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/plugin_handlers/endpoint_data_loader.ts b/x-pack/plugins/security_solution/public/management/cypress/support/plugin_handlers/endpoint_data_loader.ts index cfff2e9a2a4b0..9d0f5ac135d5d 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/support/plugin_handlers/endpoint_data_loader.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/support/plugin_handlers/endpoint_data_loader.ts @@ -36,6 +36,9 @@ export interface CyLoadEndpointDataOptions enableFleetIntegration: boolean; generatorSeed: string; waitUntilTransformed: boolean; + withResponseActions: boolean; + isolation: boolean; + bothIsolatedAndNormalEndpoints?: boolean; } /** @@ -58,10 +61,12 @@ export const cyLoadEndpointDataHandler = async ( waitUntilTransformed = true, version = kibanaPackageJson.version, os, + withResponseActions, + isolation, } = options; const DocGenerator = EndpointDocGenerator.custom({ - CustomMetadataGenerator: EndpointMetadataGenerator.custom({ version, os }), + CustomMetadataGenerator: EndpointMetadataGenerator.custom({ version, os, isolation }), }); if (waitUntilTransformed) { @@ -85,7 +90,8 @@ export const cyLoadEndpointDataHandler = async ( alertsPerHost, enableFleetIntegration, undefined, - DocGenerator + DocGenerator, + withResponseActions ); if (waitUntilTransformed) { diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/alerts.ts new file mode 100644 index 0000000000000..a78b1c6742afa --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/alerts.ts @@ -0,0 +1,170 @@ +/* + * 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 { estypes } from '@elastic/elasticsearch'; +import type { Rule } from '../../../detection_engine/rule_management/logic'; +import { + DETECTION_ENGINE_QUERY_SIGNALS_URL, + DETECTION_ENGINE_RULES_BULK_ACTION, + DETECTION_ENGINE_RULES_URL, +} from '../../../../common/constants'; +import { ELASTIC_SECURITY_RULE_ID } from '../../../../common'; +import { request } from './common'; +import { ENDPOINT_ALERTS_INDEX } from '../../../../scripts/endpoint/common/constants'; +const ES_URL = Cypress.env('ELASTICSEARCH_URL'); + +/** + * Continuously check for any alert to have been received by the given endpoint. + * + * NOTE: This is tno the same as the alerts that populate the Alerts list. To check for + * those types of alerts, use `waitForDetectionAlerts()` + */ +export const waitForEndpointAlerts = ( + endpointAgentId: string, + additionalFilters?: object[], + timeout = 120000 +): Cypress.Chainable => { + return cy + .waitUntil( + () => { + return request({ + method: 'GET', + url: `${ES_URL}/${ENDPOINT_ALERTS_INDEX}/_search`, + body: { + query: { + match: { + 'agent.id': endpointAgentId, + }, + }, + size: 1, + _source: false, + }, + }).then(({ body: streamedAlerts }) => { + return (streamedAlerts.hits.total as estypes.SearchTotalHits).value > 0; + }); + }, + { timeout } + ) + .then(() => { + // Stop/start Endpoint rule so that it can pickup and create Detection alerts + cy.log( + `Received endpoint alerts for agent [${endpointAgentId}] in index [${ENDPOINT_ALERTS_INDEX}]` + ); + + return stopStartEndpointDetectionsRule(); + }) + .then(() => { + // wait until the Detection alert shows up in the API + return waitForDetectionAlerts(getEndpointDetectionAlertsQueryForAgentId(endpointAgentId)); + }); +}; + +export const fetchEndpointSecurityDetectionRule = (): Cypress.Chainable => { + return request({ + method: 'GET', + url: DETECTION_ENGINE_RULES_URL, + qs: { + rule_id: ELASTIC_SECURITY_RULE_ID, + }, + }).then(({ body }) => { + return body; + }); +}; + +export const stopStartEndpointDetectionsRule = (): Cypress.Chainable => { + return fetchEndpointSecurityDetectionRule() + .then((endpointRule) => { + // Disabled it + return request({ + method: 'POST', + url: DETECTION_ENGINE_RULES_BULK_ACTION, + body: { + action: 'disable', + ids: [endpointRule.id], + }, + }).then(() => { + return endpointRule; + }); + }) + .then((endpointRule) => { + cy.log(`Endpoint rule id [${endpointRule.id}] has been disabled`); + + // Re-enable it + return request({ + method: 'POST', + url: DETECTION_ENGINE_RULES_BULK_ACTION, + body: { + action: 'enable', + ids: [endpointRule.id], + }, + }).then(() => endpointRule); + }) + .then((endpointRule) => { + cy.log(`Endpoint rule id [${endpointRule.id}] has been re-enabled`); + return cy.wrap(endpointRule); + }); +}; + +/** + * Waits for alerts to have been loaded by continuously calling the detections engine alerts + * api until data shows up + * @param query + * @param timeout + */ +export const waitForDetectionAlerts = ( + /** The ES query. Defaults to `{ match_all: {} }` */ + query: object = { match_all: {} }, + timeout?: number +): Cypress.Chainable => { + return cy.waitUntil( + () => { + return request({ + method: 'POST', + url: DETECTION_ENGINE_QUERY_SIGNALS_URL, + body: { + query, + size: 1, + }, + }).then(({ body: alertsResponse }) => { + return Boolean((alertsResponse.hits.total as estypes.SearchTotalHits)?.value ?? 0); + }); + }, + { timeout } + ); +}; + +/** + * Builds and returns the ES `query` object for use in querying for Endpoint Detection Engine + * alerts. Can be used in ES searches or with the Detection Engine query signals (alerts) url. + * @param endpointAgentId + */ +export const getEndpointDetectionAlertsQueryForAgentId = (endpointAgentId: string) => { + return { + bool: { + filter: [ + { + bool: { + should: [{ match_phrase: { 'agent.type': 'endpoint' } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ match_phrase: { 'agent.id': endpointAgentId } }], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [{ exists: { field: 'kibana.alert.rule.uuid' } }], + minimum_should_match: 1, + }, + }, + ], + }, + }; +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts index 8d8df6318d215..3b8b7cfae6340 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/api_fixtures.ts @@ -5,10 +5,8 @@ * 2.0. */ -import type { - RuleCreateProps, - RuleResponse, -} from '../../../../common/detection_engine/rule_schema'; +import type { CaseResponse } from '@kbn/cases-plugin/common'; +import type { RuleResponse } from '../../../../common/detection_engine/rule_schema'; import { request } from './common'; export const generateRandomStringName = (length: number) => @@ -18,9 +16,10 @@ export const cleanupRule = (id: string) => { request({ method: 'DELETE', url: `/api/detection_engine/rules?id=${id}` }); }; -export const loadRule = () => +export const loadRule = (includeResponseActions = true) => request({ method: 'POST', + url: `/api/detection_engine/rules`, body: { type: 'query', index: [ @@ -56,9 +55,35 @@ export const loadRule = () => actions: [], enabled: true, throttle: 'no_actions', - response_actions: [ - { params: { command: 'isolate', comment: 'Isolate host' }, action_type_id: '.endpoint' }, - ], - } as RuleCreateProps, - url: `/api/detection_engine/rules`, + ...(includeResponseActions + ? { + response_actions: [ + { + params: { command: 'isolate', comment: 'Isolate host' }, + action_type_id: '.endpoint', + }, + ], + } + : {}), + }, }).then((response) => response.body); + +export const loadCase = (owner: string) => + request({ + method: 'POST', + url: '/api/cases', + body: { + title: `Test ${owner} case ${generateRandomStringName(1)[0]}`, + tags: [], + severity: 'low', + description: 'Test security case', + assignees: [], + connector: { id: 'none', name: 'none', type: '.none', fields: null }, + settings: { syncAlerts: true }, + owner, + }, + }).then((response) => response.body); + +export const cleanupCase = (id: string) => { + request({ method: 'DELETE', url: '/api/cases', qs: { ids: JSON.stringify([id]) } }); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/delete_all_endpoint_data.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/delete_all_endpoint_data.ts new file mode 100644 index 0000000000000..761cde513ad52 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/delete_all_endpoint_data.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. + */ + +import type { DeleteAllEndpointDataResponse } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; + +export const deleteAllLoadedEndpointData = (options: { + endpointAgentIds: string[]; +}): Cypress.Chainable => { + return cy.task('deleteAllEndpointData', options); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/endpoint_policy.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/endpoint_policy.ts new file mode 100644 index 0000000000000..134fc470b412b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/endpoint_policy.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + GetOnePackagePolicyResponse, + UpdatePackagePolicy, + UpdatePackagePolicyResponse, +} from '@kbn/fleet-plugin/common'; +import { packagePolicyRouteService } from '@kbn/fleet-plugin/common'; +import { request } from './common'; +import { ProtectionModes } from '../../../../common/endpoint/types'; + +/** + * Updates the given Endpoint policy and enables all of the policy protections + * @param endpointPolicyId + */ +export const enableAllPolicyProtections = ( + endpointPolicyId: string +): Cypress.Chainable> => { + return request({ + method: 'GET', + url: packagePolicyRouteService.getInfoPath(endpointPolicyId), + }).then(({ body: { item: endpointPolicy } }) => { + const { + created_by: _createdBy, + created_at: _createdAt, + updated_at: _updatedAt, + updated_by: _updatedBy, + id, + version, + revision, + ...restOfPolicy + } = endpointPolicy; + + const updatedEndpointPolicy: UpdatePackagePolicy = restOfPolicy; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const policy = updatedEndpointPolicy!.inputs[0]!.config!.policy.value; + + policy.mac.malware.mode = ProtectionModes.prevent; + policy.windows.malware.mode = ProtectionModes.prevent; + policy.linux.malware.mode = ProtectionModes.prevent; + + policy.mac.memory_protection.mode = ProtectionModes.prevent; + policy.windows.memory_protection.mode = ProtectionModes.prevent; + policy.linux.memory_protection.mode = ProtectionModes.prevent; + + policy.mac.behavior_protection.mode = ProtectionModes.prevent; + policy.windows.behavior_protection.mode = ProtectionModes.prevent; + policy.linux.behavior_protection.mode = ProtectionModes.prevent; + + policy.windows.ransomware.mode = ProtectionModes.prevent; + + return request({ + method: 'PUT', + url: packagePolicyRouteService.getUpdatePath(endpointPolicyId), + body: updatedEndpointPolicy, + }); + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/index_endpoint_rule_alerts.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/index_endpoint_rule_alerts.ts index 498c105d0da49..21ebd75fa6329 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/index_endpoint_rule_alerts.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/index_endpoint_rule_alerts.ts @@ -12,6 +12,8 @@ import type { export const indexEndpointRuleAlerts = (options: { endpointAgentId: string; + endpointHostname?: string; + endpointIsolated?: boolean; count?: number; }): Cypress.Chainable< Pick & { diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts new file mode 100644 index 0000000000000..4644faaca2abf --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.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 type { IndexedFleetEndpointPolicyResponse } from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import type { ActionDetails } from '../../../../common/endpoint/types'; + +const API_ENDPOINT_ACTION_PATH = '/api/endpoint/action/*'; +export const interceptActionRequests = ( + cb: (responseBody: ActionDetails) => void, + alias: string +): void => { + cy.intercept('POST', API_ENDPOINT_ACTION_PATH, (req) => { + req.continue((res) => { + const { + body: { action, data }, + } = res; + + cb({ action, ...data }); + }); + }).as(alias); +}; + +export const sendActionResponse = (action: ActionDetails): void => { + cy.task('sendHostActionResponse', { + action, + state: { state: 'success' }, + }); +}; + +export const isolateHostWithComment = (comment: string, hostname: string): void => { + cy.getByTestSubj('isolate-host-action-item').click(); + cy.contains(`Isolate host ${hostname} from network.`); + cy.getByTestSubj('endpointHostIsolationForm'); + cy.getByTestSubj('host_isolation_comment').type(comment); +}; + +export const releaseHostWithComment = (comment: string, hostname: string): void => { + cy.contains(`${hostname} is currently isolated.`); + cy.getByTestSubj('endpointHostIsolationForm'); + cy.getByTestSubj('host_isolation_comment').type(comment); +}; + +export const openAlertDetails = (): void => { + cy.getByTestSubj('expand-event').first().click(); + cy.getByTestSubj('take-action-dropdown-btn').click(); +}; + +export const openCaseAlertDetails = (alertId: string): void => { + cy.getByTestSubj(`comment-action-show-alert-${alertId}`).click(); + cy.getByTestSubj('take-action-dropdown-btn').click(); +}; + +export const waitForReleaseOption = (alertId: string): void => { + openCaseAlertDetails(alertId); + cy.getByTestSubj('event-field-agent.status').then(($status) => { + if ($status.find('[title="Isolated"]').length > 0) { + cy.contains('Release host').click(); + } else { + cy.getByTestSubj('euiFlyoutCloseButton').click(); + openCaseAlertDetails(alertId); + cy.getByTestSubj('event-field-agent.status').within(() => { + cy.contains('Isolated'); + }); + cy.contains('Release host').click(); + } + }); +}; + +export const visitRuleAlerts = (ruleName: string) => { + cy.visit('/app/security/rules'); + cy.contains(ruleName).click(); +}; +export const checkFlyoutEndpointIsolation = (): void => { + cy.getByTestSubj('event-field-agent.status').then(($status) => { + if ($status.find('[title="Isolated"]').length > 0) { + cy.contains('Release host').click(); + } else { + cy.getByTestSubj('euiFlyoutCloseButton').click(); + cy.wait(5000); + openAlertDetails(); + cy.getByTestSubj('event-field-agent.status').within(() => { + cy.contains('Isolated'); + }); + cy.contains('Release host').click(); + } + }); +}; + +export const toggleRuleOffAndOn = (ruleName: string): void => { + cy.visit('/app/security/rules'); + cy.wait(2000); + cy.contains(ruleName) + .parents('tr') + .within(() => { + cy.getByTestSubj('ruleSwitch').should('have.attr', 'aria-checked', 'true'); + cy.getByTestSubj('ruleSwitch').click(); + cy.getByTestSubj('ruleSwitch').should('have.attr', 'aria-checked', 'false'); + cy.getByTestSubj('ruleSwitch').click(); + cy.getByTestSubj('ruleSwitch').should('have.attr', 'aria-checked', 'true'); + }); +}; + +export const filterOutEndpoints = (endpointHostname: string): void => { + cy.getByTestSubj('filters-global-container').within(() => { + cy.getByTestSubj('queryInput').click().type(`host.hostname : "${endpointHostname}"`); + cy.getByTestSubj('querySubmitButton').click(); + }); +}; + +export const createAgentPolicyTask = ( + version: string, + cb: (response: IndexedFleetEndpointPolicyResponse) => void +) => { + const policyName = `Reassign ${Math.random().toString(36).substring(2, 7)}`; + + cy.task('indexFleetEndpointPolicy', { + policyName, + endpointPackageVersion: version, + agentPolicyName: policyName, + }).then(cb); +}; + +export const filterOutIsolatedHosts = (): void => { + cy.getByTestSubj('adminSearchBar').click().type('united.endpoint.Endpoint.state.isolation: true'); + cy.getByTestSubj('querySubmitButton').click(); +}; + +export const checkEndpointListForIsolatedHosts = (expectIsolated = true): void => { + const chainer = expectIsolated ? 'contain.text' : 'not.contain.text'; + cy.getByTestSubj('endpointListTable').within(() => { + cy.get('tbody tr').each(($tr) => { + cy.wrap($tr).within(() => { + cy.get('td').eq(1).should(chainer, 'Isolated'); + }); + }); + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts index c888e7dce1254..13829f8d3378c 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts @@ -5,6 +5,10 @@ * 2.0. */ +import { request } from './common'; +import { resolvePathVariables } from '../../../common/utils/resolve_path_variables'; +import { ACTION_DETAILS_ROUTE } from '../../../../common/endpoint/constants'; +import type { ActionDetails, ActionDetailsApiResponse } from '../../../../common/endpoint/types'; import { ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS } from '../../../../common/endpoint/service/response_actions/constants'; export const validateAvailableCommands = () => { @@ -59,3 +63,40 @@ export const tryAddingDisabledResponseAction = (itemNumber = 0) => { }); cy.getByTestSubj(`response-actions-list-item-${itemNumber}`).should('not.exist'); }; + +/** + * Continuously checks an Response Action until it completes (or timeout is reached) + * @param actionId + * @param timeout + */ +export const waitForActionToComplete = ( + actionId: string, + timeout = 60000 +): Cypress.Chainable => { + let action: ActionDetails | undefined; + + return cy + .waitUntil( + () => { + return request({ + method: 'GET', + url: resolvePathVariables(ACTION_DETAILS_ROUTE, { action_id: actionId || 'undefined' }), + }).then((response) => { + if (response.body.data.isCompleted) { + action = response.body.data; + return true; + } + + return false; + }); + }, + { timeout } + ) + .then(() => { + if (!action) { + throw new Error(`Failed to retrieve completed action`); + } + + return action; + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/cypress/types.ts b/x-pack/plugins/security_solution/public/management/cypress/types.ts index 0741f7fab1ad0..97d635a3b6840 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/types.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/types.ts @@ -7,6 +7,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { ActionDetails } from '../../../common/endpoint/types'; import type { CyLoadEndpointDataOptions } from './support/plugin_handlers/endpoint_data_loader'; type PossibleChainable = @@ -41,5 +42,15 @@ export type ReturnTypeFromChainable = C extends Cyp : never; export type IndexEndpointHostsCyTaskOptions = Partial< - { count: number } & Pick + { count: number; withResponseActions: boolean } & Pick< + CyLoadEndpointDataOptions, + 'version' | 'os' | 'isolation' + > >; + +export interface HostActionResponse { + data: { + action: ActionDetails; + state: { state?: 'success' | 'failure' }; + }; +} diff --git a/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts b/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts index 8975599350fe2..50a9d8f1f5356 100644 --- a/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts +++ b/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts @@ -7,7 +7,7 @@ import { defineCypressConfig } from '@kbn/cypress-config'; // eslint-disable-next-line @kbn/imports/no_boundary_crossing -import { dataLoaders } from './cypress/support/data_loaders'; +import { dataLoaders, dataLoadersForRealEndpoints } from './cypress/support/data_loaders'; // eslint-disable-next-line import/no-default-export export default defineCypressConfig({ @@ -40,7 +40,9 @@ export default defineCypressConfig({ specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}', experimentalRunAllSpecs: true, setupNodeEvents: (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => { - return dataLoaders(on, config); + dataLoaders(on, config); + // Data loaders specific to "real" Endpoint testing + dataLoadersForRealEndpoints(on, config); }, }, }); diff --git a/x-pack/plugins/security_solution/public/management/hooks/index.ts b/x-pack/plugins/security_solution/public/management/hooks/index.ts index b439bcffb8874..5afa3cd5457f0 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/index.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/index.ts @@ -8,3 +8,4 @@ export { useGetEndpointDetails } from './endpoint/use_get_endpoint_details'; export { useWithShowEndpointResponder } from './use_with_show_endpoint_responder'; export { useGetEndpointActionList } from './response_actions/use_get_endpoint_action_list'; +export { useGetActionState } from './use_get_action_state'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/use_get_action_state.ts b/x-pack/plugins/security_solution/public/management/hooks/use_get_action_state.ts new file mode 100644 index 0000000000000..9b737abfc7ece --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/hooks/use_get_action_state.ts @@ -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 type { UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { ACTION_STATE_ROUTE } from '../../../common/endpoint/constants'; +import { useHttp } from '../../common/lib/kibana'; + +interface GetActionStateResponse { + data: { + canEncrypt?: boolean; + }; +} +/** + * Get info for action state + */ +export const useGetActionState = (): UseQueryResult => { + const http = useHttp(); + + return useQuery({ + queryKey: ['get-action-state'], + queryFn: () => { + return http.get(ACTION_STATE_ROUTE); + }, + }); +}; diff --git a/x-pack/plugins/security_solution/public/management/hooks/use_with_show_endpoint_responder.tsx b/x-pack/plugins/security_solution/public/management/hooks/use_with_show_endpoint_responder.tsx index c5dd32bacfb8f..26a0763ae6124 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/use_with_show_endpoint_responder.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/use_with_show_endpoint_responder.tsx @@ -16,6 +16,7 @@ import { } from '../components/endpoint_responder'; import { useConsoleManager } from '../components/console'; import type { HostMetadata } from '../../../common/endpoint/types'; +import { MissingEncryptionKeyCallout } from '../components/missing_encryption_key_callout'; type ShowEndpointResponseActionsConsole = (endpointMetadata: HostMetadata) => void; @@ -58,7 +59,12 @@ export const useWithShowEndpointResponder = (): ShowEndpointResponseActionsConso TitleComponent: () => , }, PageTitleComponent: () => <>{RESPONDER_PAGE_TITLE}, - PageBodyComponent: () => , + PageBodyComponent: () => ( + <> + + + + ), ActionComponents: endpointPrivileges.canReadActionsLogManagement ? [ActionLogButton] : undefined, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts index 39bd07d071974..7030f13bdd0f9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts @@ -45,7 +45,7 @@ import { fleetGetPackagePoliciesListHttpMock, } from '../../mocks'; -type EndpointMetadataHttpMocksInterface = ResponseProvidersInterface<{ +export type EndpointMetadataHttpMocksInterface = ResponseProvidersInterface<{ metadataList: () => MetadataListResponse; metadataDetails: () => HostInfo; }>; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index b72d4fa30777d..40abffc508fab 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -256,15 +256,15 @@ describe('endpoint list middleware', () => { query: { agent_ids: [ '0dc3661d-6e67-46b0-af39-6f12b025fcb0', - '34634c58-24b4-4448-80f4-107fb9918494', - '5a1298e3-e607-4bc0-8ef6-6d6a811312f2', - '78c54b13-596d-4891-95f4-80092d04454b', - '445f1fd2-5f81-4ddd-bdb6-f0d1bf2efe90', - 'd77a3fc6-3096-4852-a6ee-f6b09278fbc6', - '892fcccf-1bd8-45a2-a9cc-9a7860a3cb81', - '693a3110-5ba0-4284-a264-5d78301db08c', - '554db084-64fa-4e4a-ba47-2ba713f9932b', - 'c217deb6-674d-4f97-bb1d-a3a04238e6d7', + 'fe16dda9-7f34-434c-9824-b4844880f410', + 'f412728b-929c-48d5-bdb6-5a1298e3e607', + 'd0405ddc-1e7c-48f0-93d7-d55f954bd745', + '46d78dd2-aedf-4d3f-b3a9-da445f1fd25f', + '5aafa558-26b8-4bb4-80e2-ac0644d77a3f', + 'edac2c58-1748-40c3-853c-8fab48c333d7', + '06b7223a-bb2a-428a-9021-f1c0d2267ada', + 'b8daa43b-7f73-4684-9221-dbc8b769405e', + 'fbc06310-7d41-46b8-a5ea-ceed8a993b1a', ], }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index 29f0d81b96a97..8ad781c60dd20 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -113,6 +113,7 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta ...state, endpointDetails: { ...state.endpointDetails, + hostInfo: action.payload, hostDetails: { ...state.endpointDetails.hostDetails, details: action.payload.metadata, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 6431bde743483..1b4c716c37462 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -11,7 +11,7 @@ import { createSelector } from 'reselect'; import { matchPath } from 'react-router-dom'; import { decode } from '@kbn/rison'; import type { Query } from '@kbn/es-query'; -import type { Immutable, HostMetadata } from '../../../../../common/endpoint/types'; +import type { Immutable, EndpointPendingActions } from '../../../../../common/endpoint/types'; import { HostStatus } from '../../../../../common/endpoint/types'; import type { EndpointState, EndpointIndexUIQueryParams } from '../types'; import { extractListPaginationParams } from '../../../common/routing'; @@ -29,7 +29,6 @@ import { import type { ServerApiError } from '../../../../common/types'; import { isEndpointHostIsolated } from '../../../../common/utils/validators'; -import type { EndpointHostIsolationStatusProps } from '../../../../common/components/endpoint/host_isolation'; import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs'; export const listData = (state: Immutable) => state.hosts; @@ -47,6 +46,9 @@ export const listError = (state: Immutable) => state.error; export const detailsData = (state: Immutable) => state.endpointDetails.hostDetails.details; +export const fullDetailsHostInfo = (state: Immutable) => + state.endpointDetails.hostInfo; + export const detailsLoading = (state: Immutable): boolean => state.endpointDetails.hostDetails.detailsLoading; @@ -266,53 +268,32 @@ export const getEndpointPendingActionsState = ( return state.endpointPendingActions; }; +export const getMetadataTransformStats = (state: Immutable) => + state.metadataTransformStats; + +export const metadataTransformStats = (state: Immutable) => + isLoadedResourceState(state.metadataTransformStats) ? state.metadataTransformStats.data : []; + +export const isMetadataTransformStatsLoading = (state: Immutable) => + isLoadingResourceState(state.metadataTransformStats); + /** - * Returns a function (callback) that can be used to retrieve the props for the `EndpointHostIsolationStatus` - * component for a given Endpoint + * Returns a function (callback) that can be used to retrieve the list of pending actions against + * an endpoint currently displayed in the endpoint list */ -export const getEndpointHostIsolationStatusPropsCallback: ( +export const getEndpointPendingActionsCallback: ( state: Immutable -) => (endpoint: HostMetadata) => EndpointHostIsolationStatusProps = createSelector( +) => (endpointId: string) => EndpointPendingActions['pending_actions'] = createSelector( getEndpointPendingActionsState, (pendingActionsState) => { - return (endpoint: HostMetadata) => { - let pendingIsolate = 0; - let pendingUnIsolate = 0; - let pendingKillProcess = 0; - let pendingSuspendProcess = 0; - let pendingRunningProcesses = 0; + return (endpointId: string) => { + let response: EndpointPendingActions['pending_actions'] = {}; if (isLoadedResourceState(pendingActionsState)) { - const endpointPendingActions = pendingActionsState.data.get(endpoint.elastic.agent.id); - - if (endpointPendingActions) { - pendingIsolate = endpointPendingActions?.isolate ?? 0; - pendingUnIsolate = endpointPendingActions?.unisolate ?? 0; - pendingKillProcess = endpointPendingActions?.['kill-process'] ?? 0; - pendingSuspendProcess = endpointPendingActions?.['suspend-process'] ?? 0; - pendingRunningProcesses = endpointPendingActions?.['running-processes'] ?? 0; - } + response = pendingActionsState.data.get(endpointId) ?? {}; } - return { - isIsolated: isEndpointHostIsolated(endpoint), - pendingActions: { - pendingIsolate, - pendingUnIsolate, - pendingKillProcess, - pendingSuspendProcess, - pendingRunningProcesses, - }, - }; + return response; }; } ); - -export const getMetadataTransformStats = (state: Immutable) => - state.metadataTransformStats; - -export const metadataTransformStats = (state: Immutable) => - isLoadedResourceState(state.metadataTransformStats) ? state.metadataTransformStats.data : []; - -export const isMetadataTransformStatsLoading = (state: Immutable) => - isLoadingResourceState(state.metadataTransformStats); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index 8d7e6b0c4d10b..cdd5020226697 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -36,6 +36,9 @@ export interface EndpointState { /** api error from retrieving host list */ error?: ServerApiError; endpointDetails: { + // Adding `hostInfo` to store full API response in order to support the + // refactoring effort with AgentStatus component + hostInfo?: HostInfo; hostDetails: { /** details data for a specific host */ details?: Immutable; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx deleted file mode 100644 index c4270e8736e83..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx +++ /dev/null @@ -1,90 +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 type { AppContextTestRender } from '../../../../../common/mock/endpoint'; -import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint'; -import { endpointPageHttpMock } from '../../mocks'; -import { act } from '@testing-library/react'; -import type { EndpointAgentStatusProps } from './endpoint_agent_status'; -import { EndpointAgentStatus } from './endpoint_agent_status'; -import type { HostMetadata } from '../../../../../../common/endpoint/types'; -import { HostStatus } from '../../../../../../common/endpoint/types'; -import { isLoadedResourceState } from '../../../../state'; -import { KibanaServices } from '../../../../../common/lib/kibana'; - -jest.mock('../../../../../common/lib/kibana'); - -describe('When using the EndpointAgentStatus component', () => { - let render: ( - props: EndpointAgentStatusProps - ) => Promise>; - let waitForAction: AppContextTestRender['middlewareSpy']['waitForAction']; - let renderResult: ReturnType; - let httpMocks: ReturnType; - let endpointMeta: HostMetadata; - - beforeEach(() => { - const mockedContext = createAppRootMockRenderer(); - - (KibanaServices.get as jest.Mock).mockReturnValue(mockedContext.startServices); - httpMocks = endpointPageHttpMock(mockedContext.coreStart.http); - waitForAction = mockedContext.middlewareSpy.waitForAction; - endpointMeta = httpMocks.responseProvider.metadataList().data[0].metadata; - render = async (props: EndpointAgentStatusProps) => { - renderResult = mockedContext.render(); - return renderResult; - }; - - act(() => { - mockedContext.history.push('/administration/endpoints'); - }); - }); - - it.each([ - ['Healthy', 'healthy'], - ['Unhealthy', 'unhealthy'], - ['Updating', 'updating'], - ['Offline', 'offline'], - ['Inactive', 'inactive'], - ['Unhealthy', 'someUnknownValueHere'], - ])('should show agent status of %s', async (expectedLabel, hostStatus) => { - await render({ hostStatus: hostStatus as HostStatus, endpointMetadata: endpointMeta }); - expect(renderResult.getByTestId('rowHostStatus').textContent).toEqual(expectedLabel); - }); - - // FIXME: un-skip test once Islation pending statuses are supported - describe.skip('and host is isolated or pending isolation', () => { - beforeEach(async () => { - // Ensure pending action api sets pending action for the test endpoint metadata - const pendingActionsResponseProvider = - httpMocks.responseProvider.pendingActions.getMockImplementation(); - httpMocks.responseProvider.pendingActions.mockImplementation((...args) => { - const response = pendingActionsResponseProvider!(...args); - response.data.some((pendingAction) => { - if (pendingAction.agent_id === endpointMeta.elastic.agent.id) { - pendingAction.pending_actions.isolate = 1; - return true; - } - return false; - }); - return response; - }); - - const loadingPendingActions = waitForAction('endpointPendingActionsStateChanged', { - validate: (action) => isLoadedResourceState(action.payload), - }); - - await render({ hostStatus: HostStatus.HEALTHY, endpointMetadata: endpointMeta }); - await loadingPendingActions; - }); - - it('should show host pending action', () => { - expect(renderResult.getByTestId('rowIsolationStatus').textContent).toEqual('Isolating'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx deleted file mode 100644 index 494545b237052..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx +++ /dev/null @@ -1,49 +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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import styled from 'styled-components'; -import type { HostInfo, HostMetadata } from '../../../../../../common/endpoint/types'; -import { EndpointHostIsolationStatus } from '../../../../../common/components/endpoint/host_isolation'; -import { useEndpointSelector } from '../hooks'; -import { getEndpointHostIsolationStatusPropsCallback } from '../../store/selectors'; -import { AgentStatus } from '../../../../../common/components/endpoint/agent_status'; - -const EuiFlexGroupStyled = styled(EuiFlexGroup)` - .isolation-status { - margin-left: ${({ theme }) => theme.eui.euiSizeS}; - } -`; - -export interface EndpointAgentStatusProps { - hostStatus: HostInfo['host_status']; - endpointMetadata: HostMetadata; -} -export const EndpointAgentStatus = memo( - ({ endpointMetadata, hostStatus }) => { - const getEndpointIsolationStatusProps = useEndpointSelector( - getEndpointHostIsolationStatusPropsCallback - ); - - return ( - - - - - - - - - ); - } -); - -EndpointAgentStatus.displayName = 'EndpointAgentStatus'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx index d142e1385e80d..b33f98078b9fb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx @@ -17,17 +17,23 @@ import { } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { EndpointAgentStatus } from '../../../../../common/components/endpoint/endpoint_agent_status'; import { isPolicyOutOfDate } from '../../utils'; import type { HostInfo, HostMetadata, HostStatus } from '../../../../../../common/endpoint/types'; import { useEndpointSelector } from '../hooks'; -import { nonExistingPolicies, policyResponseStatus, uiQueryParams } from '../../store/selectors'; +import { + fullDetailsHostInfo, + getEndpointPendingActionsCallback, + nonExistingPolicies, + policyResponseStatus, + uiQueryParams, +} from '../../store/selectors'; import { POLICY_STATUS_TO_BADGE_COLOR } from '../host_constants'; import { FormattedDate } from '../../../../../common/components/formatted_date'; import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { getEndpointDetailsPath } from '../../../../common/routing'; import { EndpointPolicyLink } from '../../../../components/endpoint_policy_link'; import { OutOfDate } from '../components/out_of_date'; -import { EndpointAgentStatus } from '../components/endpoint_agent_status'; const EndpointDetailsContentStyled = styled.div` dl dt { @@ -63,8 +69,9 @@ export const EndpointDetailsContent = memo( const policyStatus = useEndpointSelector( policyResponseStatus ) as keyof typeof POLICY_STATUS_TO_BADGE_COLOR; - + const getHostPendingActions = useEndpointSelector(getEndpointPendingActionsCallback); const missingPolicies = useEndpointSelector(nonExistingPolicies); + const hostInfo = useEndpointSelector(fullDetailsHostInfo); const policyResponseRoutePath = useMemo(() => { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -101,7 +108,14 @@ export const EndpointDetailsContent = memo( /> ), - description: , + description: hostInfo ? ( + + ) : ( + <> + ), }, { title: ( @@ -214,7 +228,15 @@ export const EndpointDetailsContent = memo( ), }, ]; - }, [details, hostStatus, policyStatus, policyStatusClickHandler, policyInfo, missingPolicies]); + }, [ + details, + getHostPendingActions, + hostInfo, + missingPolicies, + policyInfo, + policyStatus, + policyStatusClickHandler, + ]); return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 095f6ce65c9b3..95f63266d4778 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -29,6 +29,7 @@ import type { CreatePackagePolicyRouteState, AgentPolicyDetailsDeployAgentAction, } from '@kbn/fleet-plugin/public'; +import { EndpointAgentStatus } from '../../../../common/components/endpoint/endpoint_agent_status'; import { EndpointDetailsFlyout } from './details'; import * as selectors from '../store/selectors'; import { useEndpointSelector } from './hooks'; @@ -60,7 +61,6 @@ import { AdminSearchBar } from './components/search_bar'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; import { TableRowActions } from './components/table_row_actions'; -import { EndpointAgentStatus } from './components/endpoint_agent_status'; import { CallOut } from '../../../../common/components/callouts'; import { metadataTransformPrefix } from '../../../../../common/endpoint/constants'; import { WARNING_TRANSFORM_STATES, APP_UI_ID } from '../../../../../common/constants'; @@ -69,6 +69,7 @@ import { BackToExternalAppButton } from '../../../components/back_to_external_ap import { ManagementEmptyStateWrapper } from '../../../components/management_empty_state_wrapper'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { useKibana } from '../../../../common/lib/kibana'; +import { getEndpointPendingActionsCallback } from '../store/selectors'; const MAX_PAGINATED_ITEM = 9999; const TRANSFORM_URL = '/data/transform'; @@ -127,6 +128,7 @@ export const EndpointList = () => { patternsError, metadataTransformStats, } = useEndpointSelector(selector); + const getHostPendingActions = useEndpointSelector(getEndpointPendingActionsCallback); const { canReadEndpointList, canAccessFleet, @@ -370,7 +372,11 @@ export const EndpointList = () => { }), render: (hostStatus: HostInfo['host_status'], endpointInfo) => { return ( - + ); }, }, @@ -536,7 +542,15 @@ export const EndpointList = () => { ], }, ]; - }, [queryParams, search, getAppUrl, canReadPolicyManagement, backToEndpointList, PAD_LEFT]); + }, [ + queryParams, + search, + getAppUrl, + getHostPendingActions, + canReadPolicyManagement, + backToEndpointList, + PAD_LEFT, + ]); const renderTableOrEmptyState = useMemo(() => { if (endpointsExist) { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx index 24062bf00ef46..4921353317a64 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx @@ -60,7 +60,10 @@ export const AntivirusRegistrationForm = memo(() => { supportedOss={[OperatingSystem.WINDOWS]} osRestriction={i18n.translate( 'xpack.securitySolution.endpoint.policy.details.av.windowsServerNotSupported', - { defaultMessage: 'Windows Server operating systems unsupported' } + { + defaultMessage: + 'Windows Server operating systems unsupported because Antivirus registration requires Windows Security Center, which is not included in Windows Server operating systems.', + } )} > {TRANSLATIONS.description} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx index 23db0cd14bf07..b5f46c91dade4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx @@ -90,7 +90,7 @@ export const ConfigForm: FC = memo( - + diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx index 26e3d866ff343..8928d3aaf312b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx @@ -20,6 +20,7 @@ import { } from '@elastic/eui'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -33,6 +34,10 @@ import * as i18n from '../translations'; import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; import type { HostAlertsItem } from './use_host_alerts_items'; import { useHostAlertsItems } from './use_host_alerts_items'; +import { + SecurityCellActions, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; interface HostAlertsTableProps { signalIndexName: string | null; @@ -143,13 +148,27 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.ALERTS_TEXT, 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', render: (totalAlerts: number, { hostName }) => ( - handleClick({ hostName })} + - - + handleClick({ hostName })} + > + + + ), }, { @@ -157,13 +176,30 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_CRITICAL_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'critical' })} + - - + handleClick({ hostName, severity: 'critical' })} + > + + + ), }, @@ -172,9 +208,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_HIGH_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'high' })}> - - + + handleClick({ hostName, severity: 'high' })} + > + + + ), }, @@ -183,12 +239,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_MEDIUM_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'medium' })} + - - + handleClick({ hostName, severity: 'medium' })} + > + + + ), }, @@ -197,9 +270,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_LOW_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'low' })}> - - + + handleClick({ hostName, severity: 'low' })} + > + + + ), }, diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index 8b0f38d0e479f..6c60bfc727b46 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -21,6 +21,8 @@ import { import { FormattedRelative } from '@kbn/i18n-react'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActionsTrigger } from '../../../../actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -36,6 +38,7 @@ import { HoverVisibilityContainer } from '../../../../common/components/hover_vi import { BUTTON_CLASS as INSPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; import { FormattedCount } from '../../../../common/components/formatted_number'; +import { SecurityCellActions } from '../../../../common/components/cell_actions'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -95,13 +98,27 @@ export const getTableColumns: GetTableColumns = ({ name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, 'data-test-subj': 'severityRuleAlertsTable-alertCount', render: (alertCount: number, { name }) => ( - openRuleInAlertsPage(name)} + - - + openRuleInAlertsPage(name)} + > + + + ), }, { diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx index ddaf75cba0f8a..914c3c93ff240 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx @@ -20,6 +20,8 @@ import { } from '@elastic/eui'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActionsTrigger } from '../../../../actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -32,6 +34,7 @@ import * as i18n from '../translations'; import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; import type { UserAlertsItem } from './use_user_alerts_items'; import { useUserAlertsItems } from './use_user_alerts_items'; +import { SecurityCellActions } from '../../../../common/components/cell_actions'; interface UserAlertsTableProps { signalIndexName: string | null; @@ -142,13 +145,27 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.ALERTS_TEXT, 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', render: (totalAlerts: number, { userName }) => ( - handleClick({ userName })} + - - + handleClick({ userName })} + > + + + ), }, { @@ -156,13 +173,30 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_CRITICAL_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'critical' })} + - - + handleClick({ userName, severity: 'critical' })} + > + + + ), }, @@ -171,9 +205,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_HIGH_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'high' })}> - - + + handleClick({ userName, severity: 'high' })} + > + + + ), }, @@ -182,12 +236,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_MEDIUM_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'medium' })} + - - + handleClick({ userName, severity: 'medium' })} + > + + + ), }, @@ -196,9 +267,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_LOW_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'low' })}> - - + + handleClick({ userName, severity: 'low' })} + > + + + ), }, diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx new file mode 100644 index 0000000000000..4ce4b6810ec98 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search'; +import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock'; +import { TestProviders } from '../../../../common/mock'; +import { AnomaliesCountLink } from './anomalies_count_link'; + +const mockedTelemetry = createTelemetryServiceMock(); +jest.mock('../../../../common/lib/kibana', () => { + const original = jest.requireActual('../../../../common/lib/kibana'); + + return { + ...original, + useKibana: () => ({ + services: { + telemetry: mockedTelemetry, + }, + }), + }; +}); + +describe('AnomaliesCountLink', () => { + it('reports telemetry when clicked', () => { + const count = 10; + const jobId = 'test-job-id'; + + const { getByRole } = render( + , + { wrapper: TestProviders } + ); + + fireEvent.click(getByRole('button')); + + expect(mockedTelemetry.reportAnomaliesCountClicked).toHaveBeenLastCalledWith({ jobId, count }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx new file mode 100644 index 0000000000000..529f197c62e44 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useDispatch } from 'react-redux'; +import React, { useCallback } from 'react'; +import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search'; +import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; +import { SecurityPageName } from '../../../../app/types'; +import { usersActions } from '../../../../explore/users/store'; +import { hostsActions } from '../../../../explore/hosts/store'; +import { HostsType } from '../../../../explore/hosts/store/model'; +import { UsersType } from '../../../../explore/users/store/model'; + +import { useKibana } from '../../../../common/lib/kibana'; + +export const AnomaliesCountLink = ({ + count, + jobId, + entity, +}: { + count: number; + jobId?: string; + entity: AnomalyEntity; +}) => { + const dispatch = useDispatch(); + const { telemetry } = useKibana().services; + + const deepLinkId = + entity === AnomalyEntity.User + ? SecurityPageName.usersAnomalies + : SecurityPageName.hostsAnomalies; + + const onClick = useCallback(() => { + if (!jobId) return; + + telemetry.reportAnomaliesCountClicked({ + jobId, + count, + }); + + if (entity === AnomalyEntity.User) { + dispatch( + usersActions.updateUsersAnomaliesJobIdFilter({ + jobIds: [jobId], + usersType: UsersType.page, + }) + ); + + dispatch( + usersActions.updateUsersAnomaliesInterval({ + interval: 'second', + usersType: UsersType.page, + }) + ); + } else { + dispatch( + hostsActions.updateHostsAnomaliesJobIdFilter({ + jobIds: [jobId], + hostsType: HostsType.page, + }) + ); + + dispatch( + hostsActions.updateHostsAnomaliesInterval({ + interval: 'second', + hostsType: HostsType.page, + }) + ); + } + }, [jobId, telemetry, count, entity, dispatch]); + + return ( + + {count} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx index 92f8751782fa6..4ef5d2811b5c2 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx @@ -4,28 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import type { EuiBasicTableColumn } from '@elastic/eui'; -import { EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; -import { useDispatch } from 'react-redux'; - import * as i18n from './translations'; -import type { AnomaliesCount } from '../../../../common/components/ml/anomaly/use_anomalies_search'; -import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search'; - -import { LinkAnchor, SecuritySolutionLinkAnchor } from '../../../../common/components/links'; -import { SecurityPageName } from '../../../../app/types'; -import { usersActions } from '../../../../explore/users/store'; -import { hostsActions } from '../../../../explore/hosts/store'; -import { HostsType } from '../../../../explore/hosts/store/model'; -import { UsersType } from '../../../../explore/users/store/model'; import type { SecurityJob } from '../../../../common/components/ml_popover/types'; -import { - isJobFailed, - isJobStarted, - isJobLoading, -} from '../../../../../common/machine_learning/helpers'; +import { isJobStarted } from '../../../../../common/machine_learning/helpers'; + +import { TotalAnomalies } from './components/total_anomalies'; +import type { AnomaliesCount } from '../../../../common/components/ml/anomaly/use_anomalies_search'; type AnomaliesColumns = Array>; @@ -35,7 +22,8 @@ const MediumShadeText = styled.span` export const useAnomaliesColumns = ( loading: boolean, - onJobStateChange: (job: SecurityJob) => Promise + onJobEnabled: (job: SecurityJob) => void, + recentlyEnabledJobIds: string[] ): AnomaliesColumns => { const columns: AnomaliesColumns = useMemo( () => [ @@ -74,97 +62,20 @@ export const useAnomaliesColumns = ( 'data-test-subj': 'anomalies-table-column-count', render: (count, { entity, job }) => { if (!job) return ''; - - if (count > 0 || isJobStarted(job.jobState, job.datafeedState)) { - return ; - } else if (isJobFailed(job.jobState, job.datafeedState)) { - return i18n.JOB_STATUS_FAILED; - } else if (job.isCompatible) { - return ; - } else { - return ; - } + return ( + + ); }, }, ], - [loading, onJobStateChange] + [loading, onJobEnabled, recentlyEnabledJobIds] ); return columns; }; - -const EnableJob = ({ - job, - isLoading, - onJobStateChange, -}: { - job: SecurityJob; - isLoading: boolean; - onJobStateChange: (job: SecurityJob) => Promise; -}) => { - const handleChange = useCallback(() => onJobStateChange(job), [job, onJobStateChange]); - - return isLoading || isJobLoading(job.jobState, job.datafeedState) ? ( - - ) : ( - - {i18n.RUN_JOB} - - ); -}; - -const AnomaliesTabLink = ({ - count, - jobId, - entity, -}: { - count: number; - jobId?: string; - entity: AnomalyEntity; -}) => { - const dispatch = useDispatch(); - - const deepLinkId = - entity === AnomalyEntity.User - ? SecurityPageName.usersAnomalies - : SecurityPageName.hostsAnomalies; - - const onClick = useCallback(() => { - if (!jobId) return; - - if (entity === AnomalyEntity.User) { - dispatch( - usersActions.updateUsersAnomaliesJobIdFilter({ - jobIds: [jobId], - usersType: UsersType.page, - }) - ); - - dispatch( - usersActions.updateUsersAnomaliesInterval({ - interval: 'second', - usersType: UsersType.page, - }) - ); - } else { - dispatch( - hostsActions.updateHostsAnomaliesJobIdFilter({ - jobIds: [jobId], - hostsType: HostsType.page, - }) - ); - - dispatch( - hostsActions.updateHostsAnomaliesInterval({ - interval: 'second', - hostsType: HostsType.page, - }) - ); - } - }, [jobId, dispatch, entity]); - - return ( - - {count} - - ); -}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/anomalies_tab_link.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/anomalies_tab_link.tsx new file mode 100644 index 0000000000000..80f78ee50331e --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/anomalies_tab_link.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 React, { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { SecuritySolutionLinkAnchor } from '../../../../../common/components/links'; +import { SecurityPageName } from '../../../../../app/types'; +import { usersActions } from '../../../../../explore/users/store'; +import { hostsActions } from '../../../../../explore/hosts/store'; +import { HostsType } from '../../../../../explore/hosts/store/model'; +import { UsersType } from '../../../../../explore/users/store/model'; +import { AnomalyEntity } from '../../../../../common/components/ml/anomaly/use_anomalies_search'; + +export const AnomaliesTabLink = ({ + count, + jobId, + entity, +}: { + count: number; + jobId?: string; + entity: AnomalyEntity; +}) => { + const dispatch = useDispatch(); + + const deepLinkId = + entity === AnomalyEntity.User + ? SecurityPageName.usersAnomalies + : SecurityPageName.hostsAnomalies; + + const onClick = useCallback(() => { + if (!jobId) return; + + if (entity === AnomalyEntity.User) { + dispatch( + usersActions.updateUsersAnomaliesJobIdFilter({ + jobIds: [jobId], + usersType: UsersType.page, + }) + ); + + dispatch( + usersActions.updateUsersAnomaliesInterval({ + interval: 'second', + usersType: UsersType.page, + }) + ); + } else { + dispatch( + hostsActions.updateHostsAnomaliesJobIdFilter({ + jobIds: [jobId], + hostsType: HostsType.page, + }) + ); + + dispatch( + hostsActions.updateHostsAnomaliesInterval({ + interval: 'second', + hostsType: HostsType.page, + }) + ); + } + }, [jobId, dispatch, entity]); + + return ( + + {count} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx new file mode 100644 index 0000000000000..c5c199b79df09 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx @@ -0,0 +1,66 @@ +/* + * 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, fireEvent, waitFor } from '@testing-library/react'; +import { useEnableDataFeed } from '../../../../../common/components/ml_popover/hooks/use_enable_data_feed'; +import type { SecurityJob } from '../../../../../common/components/ml_popover/types'; +import { EnableJob } from './enable_job'; + +jest.mock('../../../../../common/components/ml_popover/hooks/use_enable_data_feed', () => ({ + useEnableDataFeed: jest.fn(() => ({ enableDatafeed: jest.fn(), isLoading: false })), +})); + +describe('EnableJob', () => { + const job = { id: 'job-1', latestTimestampMs: 123456789 } as SecurityJob; + + it('renders loading spinner when isLoading is true', () => { + const { queryByTestId } = render( + + ); + expect(queryByTestId('job-switch-loader')).toBeInTheDocument(); + }); + + it('renders enable job when isLoading is false', () => { + const { queryByTestId } = render( + + ); + expect(queryByTestId('job-switch-loader')).not.toBeInTheDocument(); + }); + + it('calls enableDatafeed and onJobEnabled when enable job is clicked', async () => { + const enableDatafeedMock = jest.fn(() => ({ enabled: true })); + const onJobEnabledMock = jest.fn(); + (useEnableDataFeed as jest.Mock).mockReturnValueOnce({ + enableDatafeed: enableDatafeedMock, + isLoading: false, + }); + const { getByText } = render( + + ); + fireEvent.click(getByText('Run job')); + + await waitFor(() => { + expect(enableDatafeedMock).toHaveBeenCalledWith(job, job.latestTimestampMs); + expect(onJobEnabledMock).toHaveBeenCalledWith(job); + }); + }); + + it('renders loading spinner when enabling data feed', async () => { + const enableDatafeedMock = jest.fn(() => ({ enabled: true })); + const onJobEnabledMock = jest.fn(); + (useEnableDataFeed as jest.Mock).mockReturnValueOnce({ + enableDatafeed: enableDatafeedMock, + isLoading: true, + }); + const { queryByTestId } = render( + + ); + + expect(queryByTestId('job-switch-loader')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx new file mode 100644 index 0000000000000..533a0eddcbc1a --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import type { SecurityJob } from '../../../../../common/components/ml_popover/types'; +import { LinkAnchor } from '../../../../../common/components/links'; +import * as i18n from '../translations'; +import { useEnableDataFeed } from '../../../../../common/components/ml_popover/hooks/use_enable_data_feed'; + +export const EnableJob = ({ + job, + isLoading, + onJobEnabled, +}: { + job: SecurityJob; + isLoading: boolean; + onJobEnabled: (job: SecurityJob) => void; +}) => { + const { enableDatafeed, isLoading: isEnabling } = useEnableDataFeed(); + + const handleChange = useCallback(async () => { + const result = await enableDatafeed(job, job.latestTimestampMs || 0); + + if (result.enabled) { + onJobEnabled(job); + } + }, [enableDatafeed, job, onJobEnabled]); + + return isLoading || isEnabling ? ( + + ) : ( + + {i18n.RUN_JOB} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.test.tsx new file mode 100644 index 0000000000000..3cd8e25869763 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.test.tsx @@ -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 React from 'react'; +import { AnomalyEntity } from '../../../../../common/components/ml/anomaly/use_anomalies_search'; +import type { SecurityJob } from '../../../../../common/components/ml_popover/types'; +import { render } from '@testing-library/react'; +import { TotalAnomalies } from './total_anomalies'; +import { TestProviders } from '../../../../../common/mock'; + +const defaultProps = { + count: 0, + job: { isInstalled: true, datafeedState: 'started', jobState: 'opened' } as SecurityJob, + entity: AnomalyEntity.User, + recentlyEnabledJobIds: [], + loading: false, + onJobEnabled: () => {}, +}; + +describe('TotalAnomalies', () => { + it('shows a waiting status when the job is loading', () => { + const loadingJob = { + isInstalled: false, + datafeedState: 'starting', + jobState: 'opening', + } as SecurityJob; + + const { container } = render(, { + wrapper: TestProviders, + }); + + expect(container).toHaveTextContent('Waiting'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.tsx new file mode 100644 index 0000000000000..8311b28177a08 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.tsx @@ -0,0 +1,51 @@ +/* + * 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 { EuiIcon } from '@elastic/eui'; +import React from 'react'; +import { + isJobFailed, + isJobLoading, + isJobStarted, +} from '../../../../../../common/machine_learning/helpers'; +import type { AnomalyEntity } from '../../../../../common/components/ml/anomaly/use_anomalies_search'; +import type { SecurityJob } from '../../../../../common/components/ml_popover/types'; +import * as i18n from '../translations'; +import { AnomaliesTabLink } from './anomalies_tab_link'; +import { EnableJob } from './enable_job'; + +export const TotalAnomalies = ({ + count, + job, + entity, + recentlyEnabledJobIds, + loading, + onJobEnabled, +}: { + count: number; + job: SecurityJob; + entity: AnomalyEntity; + recentlyEnabledJobIds: string[]; + loading: boolean; + onJobEnabled: (job: SecurityJob) => void; +}) => { + if (isJobLoading(job.jobState, job.datafeedState)) { + return <>{i18n.JOB_STATUS_WAITING}; + } else if (isJobFailed(job.jobState, job.datafeedState)) { + return <>{i18n.JOB_STATUS_FAILED}; + } else if ( + count > 0 || + isJobStarted(job.jobState, job.datafeedState) || + recentlyEnabledJobIds.includes(job.id) + ) { + return ; + } else if (job.isCompatible) { + return ; + } else { + return ; + } +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts index 29f227e800f7a..1396568384b17 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/config.ts @@ -5,18 +5,49 @@ * 2.0. */ -export const NOTABLE_ANOMALIES_IDS: NotableAnomaliesJobId[] = [ +export const NOTABLE_ANOMALIES_IDS = [ 'auth_rare_source_ip_for_a_user', 'packetbeat_dns_tunneling', 'packetbeat_rare_server_domain', 'packetbeat_rare_dns_question', 'suspicious_login_activity', 'v3_windows_anomalous_script', -]; -export type NotableAnomaliesJobId = - | 'auth_rare_source_ip_for_a_user' - | 'packetbeat_dns_tunneling' - | 'packetbeat_rare_server_domain' - | 'packetbeat_rare_dns_question' - | 'suspicious_login_activity' - | 'v3_windows_anomalous_script'; + 'high_count_network_denies', + 'v3_windows_anomalous_process_all_hosts', + 'v3_linux_rare_metadata_process', + 'packetbeat_rare_user_agent', + 'v3_linux_anomalous_process_all_hosts', + 'packetbeat_rare_urls', + 'v3_windows_anomalous_path_activity', + 'v3_windows_anomalous_process_creation', + 'v3_linux_system_process_discovery', + 'v3_linux_system_user_discovery', + 'high_count_by_destination_country', + 'auth_high_count_logon_events', + 'v3_linux_anomalous_user_name', + 'v3_rare_process_by_host_windows', + 'v3_linux_anomalous_network_activity', + 'auth_high_count_logon_fails', + 'auth_high_count_logon_events_for_a_source_ip', + 'v3_linux_rare_metadata_user', + 'rare_destination_country', + 'v3_linux_system_information_discovery', + 'v3_linux_rare_user_compiler', + 'v3_windows_anomalous_user_name', + 'v3_rare_process_by_host_linux', + 'v3_windows_anomalous_network_activity', + 'auth_rare_hour_for_a_user', + 'v3_windows_rare_metadata_user', + 'v3_windows_rare_user_type10_remote_login', + 'v3_linux_anomalous_network_port_activity', + 'v3_linux_rare_sudo_user', + 'v3_windows_anomalous_service', + 'v3_windows_rare_metadata_process', + 'v3_windows_rare_user_runas_event', + 'v3_linux_network_connection_discovery', + 'v3_linux_network_configuration_discovery', + 'auth_rare_user', + 'high_count_network_events', +] as const; + +export type NotableAnomaliesJobId = typeof NOTABLE_ANOMALIES_IDS[number]; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx index 12ef348fcb903..58f075ff76b21 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; import { EntityAnalyticsAnomalies } from '.'; import type { AnomaliesCount } from '../../../../common/components/ml/anomaly/use_anomalies_search'; @@ -14,6 +14,13 @@ import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anom import { TestProviders } from '../../../../common/mock'; import type { SecurityJob } from '../../../../common/components/ml_popover/types'; +jest.mock('../../../../common/components/ml_popover/hooks/use_enable_data_feed', () => ({ + useEnableDataFeed: () => ({ + loading: false, + enableDatafeed: jest.fn().mockResolvedValue({ enabled: true }), + }), +})); + // Query toggle only works if pageName.lenght > 0 jest.mock('../../../../common/utils/route/use_route_spy', () => ({ useRouteSpy: jest.fn().mockReturnValue([ @@ -162,6 +169,32 @@ describe('EntityAnalyticsAnomalies', () => { expect(getByTestId('enable-job')).toBeInTheDocument(); }); + it('renders recently installed jobs', async () => { + const jobCount: AnomaliesCount = { + job: { isInstalled: false, isCompatible: true } as SecurityJob, + name: 'v3_windows_anomalous_script', + count: 0, + + entity: AnomalyEntity.User, + }; + + mockUseNotableAnomaliesSearch.mockReturnValue({ + isLoading: false, + data: [jobCount], + refetch: jest.fn(), + }); + + const { getByTestId } = render(, { wrapper: TestProviders }); + + act(() => { + fireEvent.click(getByTestId('enable-job')); + }); + + await waitFor(() => { + expect(getByTestId('anomalies-table-column-count')).toHaveTextContent('0'); + }); + }); + it('renders failed jobs', () => { const jobCount: AnomaliesCount = { job: { diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx index 4f74100ff4888..44213690721ea 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx @@ -35,7 +35,6 @@ import { SecurityPageName } from '../../../../app/types'; import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users'; import { UsersTableType } from '../../../../explore/users/store/model'; import { useKibana } from '../../../../common/lib/kibana'; -import { useEnableDataFeed } from '../../../../common/components/ml_popover/hooks/use_enable_data_feed'; import type { SecurityJob } from '../../../../common/components/ml_popover/types'; const TABLE_QUERY_ID = 'entityAnalyticsDashboardAnomaliesTable'; @@ -50,6 +49,8 @@ const TABLE_SORTING = { export const ENTITY_ANALYTICS_ANOMALIES_PANEL = 'entity_analytics_anomalies'; export const EntityAnalyticsAnomalies = () => { + const [recentlyEnabledJobIds, setRecentlyEnabledJobIds] = useState([]); + const { services: { ml, http, docLinks }, } = useKibana(); @@ -60,7 +61,7 @@ export const EntityAnalyticsAnomalies = () => { const [updatedAt, setUpdatedAt] = useState(Date.now()); const { toggleStatus, setToggleStatus } = useQueryToggle(TABLE_QUERY_ID); - const { deleteQuery, setQuery, from, to } = useGlobalTime(false); + const { deleteQuery, setQuery, from, to } = useGlobalTime(); const { isLoading: isSearchLoading, data, @@ -70,21 +71,12 @@ export const EntityAnalyticsAnomalies = () => { from, to, }); - const { isLoading: isEnableDataFeedLoading, enableDatafeed } = useEnableDataFeed(); - - const handleJobStateChange = useCallback( - async (job: SecurityJob) => { - const result = await enableDatafeed(job, job.latestTimestampMs || 0, true); - refetch(); - return result; - }, - [refetch, enableDatafeed] - ); - const columns = useAnomaliesColumns( - isSearchLoading || isEnableDataFeedLoading, - handleJobStateChange - ); + const onJobEnabled = useCallback(async (job: SecurityJob) => { + setRecentlyEnabledJobIds((current) => [...current, job.id]); + }, []); + + const columns = useAnomaliesColumns(isSearchLoading, onJobEnabled, recentlyEnabledJobIds); const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps(); useEffect(() => { @@ -116,8 +108,12 @@ export const EntityAnalyticsAnomalies = () => { }, [getSecuritySolutionLinkProps]); const installedJobsIds = useMemo( - () => data.filter(({ job }) => !!job && job.isInstalled).map(({ job }) => job?.id ?? ''), - [data] + () => + data + .filter(({ job }) => !!job && job.isInstalled) + .map(({ job }) => job?.id ?? '') + .concat(recentlyEnabledJobIds), + [data, recentlyEnabledJobIds] ); const incompatibleJobCount = useMemo( @@ -192,7 +188,6 @@ export const EntityAnalyticsAnomalies = () => { />

    - )} @@ -201,6 +196,9 @@ export const EntityAnalyticsAnomalies = () => { responsive={false} items={data} columns={columns} + pagination={{ + showPerPageOptions: true, + }} loading={isSearchLoading} id={TABLE_QUERY_ID} sorting={TABLE_SORTING} diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts index fdef8b65baddf..d8dbcd8664c95 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts @@ -77,6 +77,13 @@ export const JOB_STATUS_FAILED = i18n.translate( } ); +export const JOB_STATUS_WAITING = i18n.translate( + 'xpack.securitySolution.entityAnalytics.anomalies.jobStatusLoading', + { + defaultMessage: 'Waiting', + } +); + export const MODULE_NOT_COMPATIBLE_TITLE = (incompatibleJobCount: number) => i18n.translate('xpack.securitySolution.entityAnalytics.anomalies.moduleNotCompatibleTitle', { values: { incompatibleJobCount }, diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx index 28e5c696d1f52..85cc8c0043897 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx @@ -41,7 +41,7 @@ const HOST_RISK_QUERY_ID = 'hostRiskScoreKpiQuery'; const USER_RISK_QUERY_ID = 'userRiskScoreKpiQuery'; export const EntityAnalyticsHeader = () => { - const { from, to } = useGlobalTime(false); + const { from, to } = useGlobalTime(); const timerange = useMemo( () => ({ from, diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx index 7cd355e967974..27b069c45de75 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx @@ -14,6 +14,7 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { HostDetailsLink, UserDetailsLink } from '../../../../common/components/links'; import { HostsTableType } from '../../../../explore/hosts/store/model'; import { RiskScore } from '../../../../explore/components/risk_score/severity/common'; +import { CELL_ACTIONS_TELEMETRY } from '../../../../explore/components/risk_score/constants'; import type { HostRiskScore, RiskSeverity, @@ -64,6 +65,9 @@ export const getRiskScoreColumns = ( SecurityCellActionType.FILTER, SecurityCellActionType.SHOW_TOP_N, ]} + metadata={{ + telemetry: CELL_ACTIONS_TELEMETRY, + }} /> ) : ( @@ -136,17 +140,31 @@ export const getRiskScoreColumns = ( truncateText: false, mobileOptions: { show: true }, render: (alertCount: number, risk) => ( - - openEntityOnAlertsPage( - riskEntity === RiskScoreEntity.host ? risk.host.name : risk.user.name - ) - } + - - + + openEntityOnAlertsPage( + riskEntity === RiskScoreEntity.host ? risk.host.name : risk.user.name + ) + } + > + + + ), }, ]; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx index 80a750a8bce14..7869a234ccd50 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../../common/mock'; import { EntityAnalyticsRiskScores } from '.'; -import type { UserRiskScore } from '../../../../../common/search_strategy'; import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy'; import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types'; import { useRiskScore, useRiskScoreKpi } from '../../../../explore/containers/risk_score'; @@ -146,17 +145,17 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( expect(queryByTestId('entity_analytics_content')).not.toBeInTheDocument(); }); - it('renders alerts count', () => { + it('renders alerts count', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); mockUseRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, loading: false, }); const alertsCount = 999; - const data: UserRiskScore[] = [ + const data = [ { '@timestamp': '1234567899', - user: { + [riskEntity]: { name: 'testUsermame', risk: { rule_risks: [], @@ -176,10 +175,12 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( ); - expect(queryByTestId('risk-score-alerts')).toHaveTextContent(alertsCount.toString()); + await waitFor(() => { + expect(queryByTestId('risk-score-alerts')).toHaveTextContent(alertsCount.toString()); + }); }); - it('navigates to alerts page with filters when alerts count is clicked', () => { + it('navigates to alerts page with filters when alerts count is clicked', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); mockUseRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, @@ -211,13 +212,15 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( fireEvent.click(getByTestId('risk-score-alerts')); - expect(mockOpenAlertsPageWithFilters.mock.calls[0][0]).toEqual([ - { - title: riskEntity === RiskScoreEntity.host ? 'Host' : 'User', - fieldName: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', - selectedOptions: [name], - }, - ]); + await waitFor(() => { + expect(mockOpenAlertsPageWithFilters.mock.calls[0][0]).toEqual([ + { + title: riskEntity === RiskScoreEntity.host ? 'Host' : 'User', + fieldName: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', + selectedOptions: [name], + }, + ]); + }); }); } ); diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx index d7591ed353fa9..cc24f8b0dbeba 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.test.tsx @@ -16,6 +16,7 @@ import { EndpointOverview } from '.'; import type { EndpointFields } from '../../../../../common/search_strategy/security_solution/hosts'; import { HostPolicyResponseActionStatus } from '../../../../../common/search_strategy/security_solution/hosts'; import { HostStatus } from '../../../../../common/endpoint/types'; +import { EndpointMetadataGenerator } from '../../../../../common/endpoint/data_generators/endpoint_metadata_generator'; jest.mock('../../../../common/lib/kibana'); @@ -44,6 +45,15 @@ describe('EndpointOverview Component', () => { isolation: false, elasticAgentStatus: HostStatus.HEALTHY, pendingActions: {}, + hostInfo: new EndpointMetadataGenerator('seed').generateHostInfo({ + metadata: { + Endpoint: { + state: { + isolation: true, + }, + }, + }, + }), }; }); @@ -52,7 +62,7 @@ describe('EndpointOverview Component', () => { expect(findData.at(0).text()).toEqual(endpointData.endpointPolicy); expect(findData.at(1).text()).toEqual(endpointData.policyStatus); expect(findData.at(2).text()).toContain(endpointData.sensorVersion); // contain because drag adds a space - expect(findData.at(3).text()).toEqual('Healthy'); + expect(findData.at(3).text()).toEqual('HealthyIsolated'); }); test('it renders with null data', () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.tsx index 43be986d78500..f62fa5627ebdf 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/endpoint_overview/index.tsx @@ -9,6 +9,7 @@ import { EuiHealth } from '@elastic/eui'; import { getOr } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; +import { EndpointAgentStatus } from '../../../../common/components/endpoint/endpoint_agent_status'; import { OverviewDescriptionList } from '../../../../common/components/overview_description_list'; import type { DescriptionList } from '../../../../../common/utility_types'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; @@ -16,8 +17,6 @@ import { DefaultFieldRenderer } from '../../../../timelines/components/field_ren import * as i18n from './translations'; import type { EndpointFields } from '../../../../../common/search_strategy/security_solution/hosts'; import { HostPolicyResponseActionStatus } from '../../../../../common/search_strategy/security_solution/hosts'; -import { AgentStatus } from '../../../../common/components/endpoint/agent_status'; -import { EndpointHostIsolationStatus } from '../../../../common/components/endpoint/host_isolation'; interface Props { contextID?: string; @@ -77,20 +76,11 @@ export const EndpointOverview = React.memo(({ contextID, data }) => { { title: i18n.FLEET_AGENT_STATUS, description: - data != null && data.elasticAgentStatus ? ( - <> - - - + data != null && data.hostInfo ? ( + ) : ( getEmptyTagValue() ), diff --git a/x-pack/plugins/security_solution/public/overview/links.ts b/x-pack/plugins/security_solution/public/overview/links.ts index 2d75eee57bece..07cc2a491cf02 100644 --- a/x-pack/plugins/security_solution/public/overview/links.ts +++ b/x-pack/plugins/security_solution/public/overview/links.ts @@ -103,7 +103,6 @@ export const ecsDataQualityDashboardLinks: LinkItem = { ), path: DATA_QUALITY_PATH, capabilities: [`${SERVER_APP_ID}.show`], - isBeta: true, globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.ecsDataQualityDashboard', { defaultMessage: 'Data Quality', diff --git a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx index 35a991e6ae7ad..4f7df91715247 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx @@ -33,11 +33,10 @@ import { SecurityPageName } from '../../app/types'; import { getGroupByFieldsOnClick } from '../../common/components/alerts_treemap/lib/helpers'; import { useTheme } from '../../common/components/charts/common'; import { HeaderPage } from '../../common/components/header_page'; -import type { BadgeOptions } from '../../common/components/header_page/types'; import { LandingPageComponent } from '../../common/components/landing_page'; import { useLocalStorage } from '../../common/components/local_storage'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; -import { DEFAULT_NUMBER_FORMAT } from '../../../common/constants'; +import { DEFAULT_BYTES_FORMAT, DEFAULT_NUMBER_FORMAT } from '../../../common/constants'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useGetUserCasesPermissions, @@ -51,11 +50,6 @@ import * as i18n from './translations'; const LOCAL_STORAGE_KEY = 'dataQualityDashboardLastChecked'; -const badgeOptions: BadgeOptions = { - beta: true, - text: i18n.BETA, -}; - const comboBoxStyle: React.CSSProperties = { width: '322px', }; @@ -141,6 +135,7 @@ const DataQualityComponent: React.FC = () => { }, [toasts] ); + const [defaultBytesFormat] = useUiSetting$(DEFAULT_BYTES_FORMAT); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); const labelInputId = useGeneratedHtmlId({ prefix: 'labelInput' }); const [selectedOptions, setSelectedOptions] = useState(defaultOptions); @@ -210,11 +205,7 @@ const DataQualityComponent: React.FC = () => { {indicesExist ? ( <> - + { ( { [timelineId]: { ...mockGlobalState.timeline.timelineById[timelineId], sessionViewConfig: { + processIndex: 'logs-endpoint.events.process*', sessionEntityId: 'testId', + sessionStartTime: '2021-10-14T08:05:34.853Z', }, }, }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap index 9717daac79baf..c5bc7821278b5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap @@ -49,18 +49,26 @@ Array [
    - +
    + +
    +
    ,
    +
    +
    +
    ( )} - {handleOnEventClosed && ( - - - - )} - {isAlert && ( - - {(copy) => ( - - {i18n.SHARE_ALERT} - + + + {handleOnEventClosed && ( + + + + )} + {isAlert && alertDetailsLink && ( + + {(copy) => ( + + {i18n.SHARE_ALERT} + + )} + )} - - )} + + ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/use_get_alert_details_flyout_link.ts b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/use_get_alert_details_flyout_link.ts index 1d2d1b5ea6213..9a074e16dc0b3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/use_get_alert_details_flyout_link.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/use_get_alert_details_flyout_link.ts @@ -6,9 +6,10 @@ */ import { useMemo } from 'react'; +import { DEFAULT_PREVIEW_INDEX } from '../../../../../common/constants'; +import { buildAlertDetailPath } from '../../../../../common/utils/alert_detail_path'; import { useAppUrl } from '../../../../common/lib/kibana/hooks'; -import { ALERTS_PATH } from '../../../../../common/constants'; export const useGetAlertDetailsFlyoutLink = ({ _id, @@ -20,13 +21,16 @@ export const useGetAlertDetailsFlyoutLink = ({ timestamp: string; }) => { const { getAppUrl } = useAppUrl(); + const alertDetailPath = buildAlertDetailPath({ alertId: _id, index: _index, timestamp }); + const isPreviewAlert = _index.includes(DEFAULT_PREVIEW_INDEX); + // getAppUrl accounts for the users selected space const alertDetailsLink = useMemo(() => { - const url = getAppUrl({ - path: `${ALERTS_PATH}/${_id}?index=${_index}×tamp=${timestamp}`, - }); + if (isPreviewAlert) return null; + const url = getAppUrl({ path: alertDetailPath }); + // We use window.location.origin instead of http.basePath as the http.basePath has to be configured in config dev yml return `${window.location.origin}${url}`; - }, [_id, _index, getAppUrl, timestamp]); + }, [isPreviewAlert, getAppUrl, alertDetailPath]); return alertDetailsLink; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx deleted file mode 100644 index 5ef421d057a4a..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { EndpointHostIsolationStatus } from '../../../../../common/components/endpoint/host_isolation'; -import { useHostIsolationStatus } from '../../../../../detections/containers/detection_engine/alerts/use_host_isolation_status'; -import { AgentStatus } from '../../../../../common/components/endpoint/agent_status'; -import { EMPTY_STATUS } from './translations'; - -export const AgentStatuses = React.memo( - ({ - fieldName, - contextId, - eventId, - fieldType, - isAggregatable, - isDraggable, - value, - }: { - fieldName: string; - fieldType: string; - contextId: string; - eventId: string; - isAggregatable: boolean; - isDraggable: boolean; - value: string; - }) => { - const { isIsolated, agentStatus, pendingIsolation, pendingUnisolation } = - useHostIsolationStatus({ agentId: value }); - return ( - - {agentStatus !== undefined ? ( - - - - ) : ( - -

    {EMPTY_STATUS}

    -
    - )} - - - -
    - ); - } -); - -AgentStatuses.displayName = 'AgentStatuses'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx index 9be0cd66bca7c..1d59656d07563 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.test.tsx @@ -6,14 +6,18 @@ */ import { TimelineId } from '../../../../../../../common/types'; -import { render } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; import React from 'react'; +import { get } from 'lodash'; import { getThreatMatchDetectionAlert, TestProviders } from '../../../../../../common/mock'; +import type { Fields } from '../../../../../../../common/search_strategy'; import { threatMatchRowRenderer } from './threat_match_row_renderer'; import { useKibana } from '../../../../../../common/lib/kibana'; import { mockTimelines } from '../../../../../../common/mock/mock_timelines_plugin'; +import { ENRICHMENT_DESTINATION_PATH } from '../../../../../../../common/constants'; +import type { ThreatEnrichmentEcs } from '@kbn/securitysolution-ecs/src/threat'; jest.mock('../../../../../../common/lib/kibana'); describe('threatMatchRowRenderer', () => { @@ -73,5 +77,36 @@ describe('threatMatchRowRenderer', () => { expect(getByTestId('threat-match-details')).toBeInTheDocument(); }); + + it('rendered when indicator matches are more than MAX rendered', async () => { + const NO_OF_MATCHES = 20; + const largeNoOfIndicatorMatches = new Array(NO_OF_MATCHES) + .fill({}) + .map(() => get(threatMatchData, ENRICHMENT_DESTINATION_PATH)[0] as Fields); + + const modThreatMatchData: typeof threatMatchData = { + ...threatMatchData, + threat: { + enrichments: largeNoOfIndicatorMatches as ThreatEnrichmentEcs[], + }, + }; + + const children = threatMatchRowRenderer.renderRow({ + data: modThreatMatchData, + isDraggable: true, + scopeId: TimelineId.test, + }); + const { getByTestId, queryAllByTestId, findAllByTestId, findByTestId } = render( + {children} + ); + expect(getByTestId('threat-match-row-show-all')).toBeVisible(); + expect(queryAllByTestId('threat-match-row').length).toBe(2); + + fireEvent.click(getByTestId('threat-match-row-show-all')); + + expect(await findByTestId('threat-match-row-modal')).toBeVisible(); + + expect((await findAllByTestId('threat-match-row')).length).toBe(NO_OF_MATCHES + 2); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.tsx index 9044370711d5a..007f52a5f70d6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_row_renderer.tsx @@ -8,10 +8,10 @@ import type { RowRenderer } from '../../../../../../../common/types/timeline'; import { RowRendererId } from '../../../../../../../common/types/timeline'; import { hasThreatMatchValue } from './helpers'; -import { ThreatMatchRows } from './threat_match_rows'; +import { renderThreatMatchRows } from './threat_match_rows'; export const threatMatchRowRenderer: RowRenderer = { id: RowRendererId.threat_match, isInstance: hasThreatMatchValue, - renderRow: ThreatMatchRows, + renderRow: renderThreatMatchRows, }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx index 307d3eaf59286..1f907eac582a3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/cti/threat_match_rows.tsx @@ -5,44 +5,140 @@ * 2.0. */ -import { EuiHorizontalRule } from '@elastic/eui'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, +} from '@elastic/eui'; import { get } from 'lodash'; -import React, { Fragment } from 'react'; +import type { FC, ReactElement } from 'react'; +import React, { Fragment, useState, useCallback } from 'react'; import styled from 'styled-components'; +import type { EcsSecurityExtension } from '@kbn/securitysolution-ecs'; import { ENRICHMENT_DESTINATION_PATH } from '../../../../../../../common/constants'; import type { RowRenderer } from '../../../../../../../common/types'; import type { Fields } from '../../../../../../../common/search_strategy'; import { ID_FIELD_NAME } from '../../../../../../common/components/event_details/event_id'; import { RowRendererContainer } from '../row_renderer'; import { ThreatMatchRow } from './threat_match_row'; +import { + ALL_INDICATOR_MATCHES_MODAL_CLOSE, + ALL_INDICATOR_MATCHES_MODAL_HEADER, + SHOW_ALL_INDICATOR_MATCHES, +} from '../translations'; const SpacedContainer = styled.div` margin: ${({ theme }) => theme.eui.euiSizeS} 0; `; -export const ThreatMatchRows: RowRenderer['renderRow'] = ({ data, isDraggable, scopeId }) => { +export const renderThreatMatchRows: RowRenderer['renderRow'] = ({ data, isDraggable, scopeId }) => { + return ; +}; + +interface ThreatMatchRowProps { + data: EcsSecurityExtension; + isDraggable: boolean; + scopeId: string; +} + +const MAX_INDICATOR_VISIBLE = 2; + +const ThreatMatchRowWrapper: FC = ({ data, isDraggable, scopeId }) => { const indicators = get(data, ENRICHMENT_DESTINATION_PATH) as Fields[]; const eventId = get(data, ID_FIELD_NAME); + const getThreatMatchRows = useCallback( + (mode: 'max' | 'all' = 'max') => { + const allIndicators = + mode === 'max' ? indicators.slice(0, MAX_INDICATOR_VISIBLE) : indicators; + + return ( + + + {allIndicators.map((indicator, index) => { + const contextId = `threat-match-row-${scopeId}-${eventId}-${index}`; + return ( + + + {index < indicators.length - 1 && } + + ); + })} + + + ); + }, + [indicators, eventId, isDraggable, scopeId] + ); + + const renderModalChildren = useCallback(() => getThreatMatchRows('all'), [getThreatMatchRows]); + + return ( + + {getThreatMatchRows()} + {indicators.length > MAX_INDICATOR_VISIBLE && ( + + + + )} + + ); +}; + +interface ThreatMatchRowModalProps { + title: string; + renderChildren: () => ReactElement; +} + +const ThreatMatchRowModal: FC = ({ title, renderChildren }) => { + const [isModalVisible, setShowModal] = useState(false); + const closeModal = () => setShowModal(false); + const showModal = () => setShowModal(true); + let modal; + + if (isModalVisible) { + modal = ( + + + {ALL_INDICATOR_MATCHES_MODAL_HEADER} + + {renderChildren()} + + + {ALL_INDICATOR_MATCHES_MODAL_CLOSE} + + + + ); + } + return ( - - - {indicators.map((indicator, index) => { - const contextId = `threat-match-row-${scopeId}-${eventId}-${index}`; - return ( - - - {index < indicators.length - 1 && } - - ); - })} - - +
    + + {title} + + {modal} +
    ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index 1fe2a6b658791..bf216c55f721f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -12,6 +12,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { isNumber, isEmpty } from 'lodash/fp'; import React from 'react'; +import { EndpointAgentStatusById } from '../../../../../common/components/endpoint/endpoint_agent_status'; import { INDICATOR_REFERENCE } from '../../../../../../common/cti/constants'; import { DefaultDraggable } from '../../../../../common/components/draggables'; import { Bytes, BYTES_FORMAT } from './bytes'; @@ -40,7 +41,6 @@ import { import { RenderRuleName, renderEventModule, renderUrl } from './formatted_field_helpers'; import { RuleStatus } from './rule_status'; import { HostName } from './host_name'; -import { AgentStatuses } from './agent_statuses'; import { UserName } from './user_name'; // simple black-list to prevent dragging and dropping fields such as message name @@ -240,14 +240,9 @@ const FormattedFieldValueComponent: React.FC<{ ); } else if (fieldName === AGENT_STATUS_FIELD_NAME) { return ( - ); } else if ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/translations.ts index d303fe45bba53..d5b231a33d27a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/translations.ts @@ -41,15 +41,22 @@ export const LINK_ELASTIC_ENDPOINT_SECURITY = i18n.translate( } ); -export const EMPTY_STATUS = i18n.translate( - 'xpack.securitySolution.hostIsolation.agentStatuses.empty', +export const SHOW_ALL_INDICATOR_MATCHES = (count: number) => + i18n.translate('xpack.securitySolution.event.summary.threat_indicator.showMatches', { + values: { count }, + defaultMessage: 'Show all {count} indicator match alerts', + }); + +export const ALL_INDICATOR_MATCHES_MODAL_HEADER = i18n.translate( + 'xpack.securitySolution.event.summary.threat_indicator.modal.allMatches', { - defaultMessage: '-', + defaultMessage: 'All Indicator Matches', } ); -export const REASON_RENDERER_TITLE = (eventRendererName: string) => - i18n.translate('xpack.securitySolution.event.reason.reasonRendererTitle', { - values: { eventRendererName }, - defaultMessage: 'Event renderer: {eventRendererName} ', - }); +export const ALL_INDICATOR_MATCHES_MODAL_CLOSE = i18n.translate( + 'xpack.securitySolution.event.summary.threat_indicator.modal.close', + { + defaultMessage: 'Close', + } +); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx index 8056a07fb39de..551003923a151 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/cell_rendering/default_cell_renderer.tsx @@ -16,6 +16,8 @@ import { getLinkColumnDefinition } from '../../../../common/lib/cell_actions/hel const StyledContent = styled.div<{ $isDetails: boolean }>` padding: ${({ $isDetails }) => ($isDetails ? '0 8px' : undefined)}; + width: 100%; + margin: 0 auto; `; export const DefaultCellRenderer: React.FC = ({ diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts index 2b3d73efac361..25c2e5f6327be 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts @@ -137,15 +137,21 @@ export const sendEndpointActionResponse = async ( message: 'Endpoint encountered an error and was unable to apply action to host', }; - if (endpointResponse.EndpointActions.data.command === 'get-file') { + if ( + endpointResponse.EndpointActions.data.command === 'get-file' && + endpointResponse.EndpointActions.data.output + ) { ( - endpointResponse.EndpointActions.data.output?.content as ResponseActionGetFileOutputContent + endpointResponse.EndpointActions.data.output.content as ResponseActionGetFileOutputContent ).code = endpointActionGenerator.randomGetFileFailureCode(); } - if (endpointResponse.EndpointActions.data.command === 'execute') { + if ( + endpointResponse.EndpointActions.data.command === 'execute' && + endpointResponse.EndpointActions.data.output + ) { ( - endpointResponse.EndpointActions.data.output?.content as ResponseActionExecuteOutputContent + endpointResponse.EndpointActions.data.output.content as ResponseActionExecuteOutputContent ).stderr = 'execute command timed out'; } } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/delete_all_endpoint_data.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/delete_all_endpoint_data.ts new file mode 100644 index 0000000000000..6382964fda643 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/delete_all_endpoint_data.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Client, estypes } from '@elastic/elasticsearch'; +import assert from 'assert'; +import { createEsClient } from './stack_services'; +import { createSecuritySuperuser } from './security_user_services'; + +export interface DeleteAllEndpointDataResponse { + count: number; + query: string; + response: estypes.DeleteByQueryResponse; +} + +/** + * Attempts to delete all data associated with the provided endpoint agent IDs. + * + * **NOTE:** This utility will create a new role and user that has elevated privileges and access to system indexes. + * + * @param esClient + * @param endpointAgentIds + */ +export const deleteAllEndpointData = async ( + esClient: Client, + endpointAgentIds: string[] +): Promise => { + assert(endpointAgentIds.length > 0, 'At least one endpoint agent id must be defined'); + + const unrestrictedUser = await createSecuritySuperuser(esClient, 'super_superuser'); + const esUrl = getEsUrlFromClient(esClient); + const esClientUnrestricted = createEsClient({ + url: esUrl, + username: unrestrictedUser.username, + password: unrestrictedUser.password, + }); + + const queryString = endpointAgentIds.map((id) => `(${id})`).join(' OR '); + + const deleteResponse = await esClientUnrestricted.deleteByQuery({ + index: '*,.*', + body: { + query: { + query_string: { + query: queryString, + }, + }, + }, + ignore_unavailable: true, + conflicts: 'proceed', + }); + + return { + count: deleteResponse.deleted ?? 0, + query: queryString, + response: deleteResponse, + }; +}; + +const getEsUrlFromClient = (esClient: Client) => { + const connection = esClient.connectionPool.connections.find((entry) => entry.status === 'alive'); + + if (!connection) { + throw new Error( + 'Unable to get esClient connection information. No connection found with status `alive`' + ); + } + + const url = new URL(connection.url.href); + url.username = ''; + url.password = ''; + + return url.href; +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts new file mode 100644 index 0000000000000..4bb03324f172e --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts @@ -0,0 +1,205 @@ +/* + * 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 { kibanaPackageJson } from '@kbn/repo-info'; +import type { KbnClient } from '@kbn/test'; +import type { ToolingLog } from '@kbn/tooling-log'; +import execa from 'execa'; +import assert from 'assert'; +import { + fetchAgentPolicyEnrollmentKey, + fetchFleetServerUrl, + getAgentDownloadUrl, + unEnrollFleetAgent, + waitForHostToEnroll, +} from './fleet_services'; + +export interface CreateAndEnrollEndpointHostOptions + extends Pick { + kbnClient: KbnClient; + log: ToolingLog; + /** The fleet Agent Policy ID to use for enrolling the agent */ + agentPolicyId: string; + /** version of the Agent to install. Defaults to stack version */ + version?: string; + /** The name for the host. Will also be the name of the VM */ + hostname?: string; +} + +export interface CreateAndEnrollEndpointHostResponse { + hostname: string; + agentId: string; +} + +/** + * Creates a new virtual machine (host) and enrolls that with Fleet + */ +export const createAndEnrollEndpointHost = async ({ + kbnClient, + log, + agentPolicyId, + cpus, + disk, + memory, + hostname, + version = kibanaPackageJson.version, +}: CreateAndEnrollEndpointHostOptions): Promise => { + const [vm, agentDownloadUrl, fleetServerUrl, enrollmentToken] = await Promise.all([ + createMultipassVm({ + vmName: hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`, + disk, + cpus, + memory, + }), + + getAgentDownloadUrl(version, true, log), + + fetchFleetServerUrl(kbnClient), + + fetchAgentPolicyEnrollmentKey(kbnClient, agentPolicyId), + ]); + + // Some validations before we proceed + assert(agentDownloadUrl, 'Missing agent download URL'); + assert(fleetServerUrl, 'Fleet server URL not set'); + assert(enrollmentToken, `No enrollment token for agent policy id [${agentPolicyId}]`); + + log.verbose(`Enrolling host [${vm.vmName}] + with fleet-server [${fleetServerUrl}] + using enrollment token [${enrollmentToken}]`); + + const { agentId } = await enrollHostWithFleet({ + kbnClient, + log, + fleetServerUrl, + agentDownloadUrl, + enrollmentToken, + vmName: vm.vmName, + }); + + return { + hostname: vm.vmName, + agentId, + }; +}; + +/** + * Destroys the Endpoint Host VM and un-enrolls the Fleet agent + * @param kbnClient + * @param createdHost + */ +export const destroyEndpointHost = async ( + kbnClient: KbnClient, + createdHost: CreateAndEnrollEndpointHostResponse +): Promise => { + await Promise.all([ + deleteMultipassVm(createdHost.hostname), + unEnrollFleetAgent(kbnClient, createdHost.agentId, true), + ]); +}; + +interface CreateMultipassVmOptions { + vmName: string; + /** Number of CPUs */ + cpus?: number; + /** Disk size */ + disk?: string; + /** Amount of memory */ + memory?: string; +} + +interface CreateMultipassVmResponse { + vmName: string; +} + +/** + * Creates a new VM using `multipass` + */ +const createMultipassVm = async ({ + vmName, + disk = '8G', + cpus = 1, + memory = '1G', +}: CreateMultipassVmOptions): Promise => { + await execa.command( + `multipass launch --name ${vmName} --disk ${disk} --cpus ${cpus} --memory ${memory}` + ); + + return { + vmName, + }; +}; + +const deleteMultipassVm = async (vmName: string): Promise => { + await execa.command(`multipass delete -p ${vmName}`); +}; + +interface EnrollHostWithFleetOptions { + kbnClient: KbnClient; + log: ToolingLog; + vmName: string; + agentDownloadUrl: string; + fleetServerUrl: string; + enrollmentToken: string; +} + +const enrollHostWithFleet = async ({ + kbnClient, + log, + vmName, + fleetServerUrl, + agentDownloadUrl, + enrollmentToken, +}: EnrollHostWithFleetOptions): Promise<{ agentId: string }> => { + const agentDownloadedFile = agentDownloadUrl.substring(agentDownloadUrl.lastIndexOf('/') + 1); + const vmDirName = agentDownloadedFile.replace(/\.tar\.gz$/, ''); + + await execa.command( + `multipass exec ${vmName} -- curl -L ${agentDownloadUrl} -o ${agentDownloadedFile}` + ); + await execa.command(`multipass exec ${vmName} -- tar -zxf ${agentDownloadedFile}`); + await execa.command(`multipass exec ${vmName} -- rm -f ${agentDownloadedFile}`); + + const agentInstallArguments = [ + 'exec', + + vmName, + + '--working-directory', + `/home/ubuntu/${vmDirName}`, + + '--', + + 'sudo', + + './elastic-agent', + + 'install', + + '--insecure', + + '--force', + + '--url', + fleetServerUrl, + + '--enrollment-token', + enrollmentToken, + ]; + + log.info(`Enrolling elastic agent with Fleet`); + log.verbose(`Command: multipass ${agentInstallArguments.join(' ')}`); + + await execa(`multipass`, agentInstallArguments); + + log.info(`Waiting for Agent to check-in with Fleet`); + const agent = await waitForHostToEnroll(kbnClient, vmName, 120000); + + return { + agentId: agent.id, + }; +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts index a1f21b80567d6..7823309aa0059 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts @@ -131,3 +131,46 @@ const fetchLastStreamedEndpointUpdate = async ( return queryResult.hits?.hits[0]?._source; }; + +/** + * Waits for an endpoint to have streamed data to ES and for that data to have made it to the + * Endpoint Details API (transform destination index) + * @param kbnClient + * @param endpointAgentId + * @param timeoutMs + */ +export const waitForEndpointToStreamData = async ( + kbnClient: KbnClient, + endpointAgentId: string, + timeoutMs: number = 60000 +): Promise => { + const started = new Date(); + const hasTimedOut = (): boolean => { + const elapsedTime = Date.now() - started.getTime(); + return elapsedTime > timeoutMs; + }; + let found: HostInfo | undefined; + + while (!found && !hasTimedOut()) { + found = await fetchEndpointMetadata(kbnClient, 'invalid-id-test').catch((error) => { + // Ignore `not found` (404) responses. Endpoint could be new and thus documents might not have + // been streamed yet. + if (error?.response?.status === 404) { + return undefined; + } + + throw error; + }); + + if (!found) { + // sleep and check again + await new Promise((r) => setTimeout(r, 2000)); + } + } + + if (!found) { + throw new Error(`Timed out waiting for Endpoint id [${endpointAgentId}] to stream data to ES`); + } + + return found; +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index 1f4d28cecc569..fca93d1848f4a 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -14,7 +14,12 @@ import type { GetAgentPoliciesResponse, GetAgentsResponse, } from '@kbn/fleet-plugin/common'; -import { AGENT_API_ROUTES, agentPolicyRouteService, AGENTS_INDEX } from '@kbn/fleet-plugin/common'; +import { + AGENT_API_ROUTES, + agentPolicyRouteService, + agentRouteService, + AGENTS_INDEX, +} from '@kbn/fleet-plugin/common'; import { ToolingLog } from '@kbn/tooling-log'; import type { KbnClient } from '@kbn/test'; import type { GetFleetServerHostsResponse } from '@kbn/fleet-plugin/common/types/rest_spec/fleet_server_hosts'; @@ -26,7 +31,10 @@ import type { EnrollmentAPIKey, GetAgentsRequest, GetEnrollmentAPIKeysResponse, + PostAgentUnenrollResponse, } from '@kbn/fleet-plugin/common/types'; +import nodeFetch from 'node-fetch'; +import semver from 'semver'; import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator'; const fleetGenerator = new FleetAgentGenerator(); @@ -236,3 +244,135 @@ export const getAgentVersionMatchingCurrentStack = async ( return version; }; + +interface ElasticArtifactSearchResponse { + manifest: { + 'last-update-time': string; + 'seconds-since-last-update': number; + }; + packages: { + [packageFileName: string]: { + architecture: string; + os: string[]; + type: string; + asc_url: string; + sha_url: string; + url: string; + }; + }; +} + +/** + * Retrieves the download URL to the Linux installation package for a given version of the Elastic Agent + * @param version + * @param closestMatch + * @param log + */ +export const getAgentDownloadUrl = async ( + version: string, + /** + * When set to true a check will be done to determine the latest version of the agent that + * is less than or equal to the `version` provided + */ + closestMatch: boolean = false, + log?: ToolingLog +): Promise => { + const agentVersion = closestMatch ? await getLatestAgentDownloadVersion(version, log) : version; + const downloadArch = + { arm64: 'arm64', x64: 'x86_64' }[process.arch] ?? `UNSUPPORTED_ARCHITECTURE_${process.arch}`; + const agentFile = `elastic-agent-${agentVersion}-linux-${downloadArch}.tar.gz`; + const artifactSearchUrl = `https://artifacts-api.elastic.co/v1/search/${agentVersion}/${agentFile}`; + + log?.verbose(`Retrieving elastic agent download URL from:\n ${artifactSearchUrl}`); + + const searchResult: ElasticArtifactSearchResponse = await nodeFetch(artifactSearchUrl).then( + (response) => { + if (!response.ok) { + throw new Error( + `Failed to search elastic's artifact repository: ${response.statusText} (HTTP ${response.status}) {URL: ${artifactSearchUrl})` + ); + } + + return response.json(); + } + ); + + log?.verbose(searchResult); + + if (!searchResult.packages[agentFile]) { + throw new Error(`Unable to find an Agent download URL for version [${agentVersion}]`); + } + + return searchResult.packages[agentFile].url; +}; + +/** + * Given a stack version number, function will return the closest Agent download version available + * for download. THis could be the actual version passed in or lower. + * @param version + */ +export const getLatestAgentDownloadVersion = async ( + version: string, + log?: ToolingLog +): Promise => { + const artifactsUrl = 'https://artifacts-api.elastic.co/v1/versions'; + const semverMatch = `<=${version}`; + const artifactVersionsResponse: { versions: string[] } = await nodeFetch(artifactsUrl).then( + (response) => { + if (!response.ok) { + throw new Error( + `Failed to retrieve list of versions from elastic's artifact repository: ${response.statusText} (HTTP ${response.status}) {URL: ${artifactsUrl})` + ); + } + + return response.json(); + } + ); + + const stackVersionToArtifactVersion: Record = + artifactVersionsResponse.versions.reduce((acc, artifactVersion) => { + const stackVersion = artifactVersion.split('-SNAPSHOT')[0]; + acc[stackVersion] = artifactVersion; + return acc; + }, {} as Record); + + log?.verbose( + `Versions found from [${artifactsUrl}]:\n${JSON.stringify( + stackVersionToArtifactVersion, + null, + 2 + )}` + ); + + const matchedVersion = semver.maxSatisfying( + Object.keys(stackVersionToArtifactVersion), + semverMatch + ); + + if (!matchedVersion) { + throw new Error(`Unable to find a semver version that meets ${semverMatch}`); + } + + return stackVersionToArtifactVersion[matchedVersion]; +}; + +/** + * Un-enrolls a Fleet agent + * + * @param kbnClient + * @param agentId + * @param force + */ +export const unEnrollFleetAgent = async ( + kbnClient: KbnClient, + agentId: string, + force = false +): Promise => { + const { data } = await kbnClient.request({ + method: 'POST', + path: agentRouteService.getUnenrollPath(agentId), + body: { revoke: force }, + }); + + return data; +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/security_user_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/security_user_services.ts index dab9e2b6abd27..f17bf7b514f21 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/security_user_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/security_user_services.ts @@ -17,12 +17,41 @@ export const createSecuritySuperuser = async ( throw new Error(`username and password require values.`); } + // Create a role which has full access to restricted indexes + await esClient.transport.request({ + method: 'POST', + path: '_security/role/superuser_restricted_indices', + body: { + cluster: ['all'], + indices: [ + { + names: ['*'], + privileges: ['all'], + allow_restricted_indices: true, + }, + { + names: ['*'], + privileges: ['monitor', 'read', 'view_index_metadata', 'read_cross_cluster'], + allow_restricted_indices: true, + }, + ], + applications: [ + { + application: '*', + privileges: ['*'], + resources: ['*'], + }, + ], + run_as: ['*'], + }, + }); + const addedUser = await esClient.transport.request>({ method: 'POST', path: `_security/user/${username}`, body: { password, - roles: ['superuser', 'kibana_system'], + roles: ['superuser', 'kibana_system', 'superuser_restricted_indices'], full_name: username, }, }); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts index 424f451c3fdc6..f7ba4c1a5b514 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts @@ -99,7 +99,11 @@ export const createRuntimeServices = async ({ }; }; -const buildUrlWithCredentials = (url: string, username: string, password: string): string => { +export const buildUrlWithCredentials = ( + url: string, + username: string, + password: string +): string => { const newUrl = new URL(url); newUrl.username = username; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts index e6de63c7ba510..68ff4951f77d4 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts @@ -57,7 +57,7 @@ export const enrollEndpointHost = async (): Promise => { try { const uniqueId = Math.random().toString().substring(2, 6); - const username = userInfo().username.toLowerCase(); + const username = userInfo().username.toLowerCase().replace('.', '-'); // Multipass doesn't like periods in username const policyId: string = policy || (await getOrCreateAgentPolicyId()); if (!policyId) { diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 489953a2d96d2..8e1855b2cd84c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -125,7 +125,8 @@ export class EndpointAppContextService { logger, licenseService, featureUsageService, - endpointMetadataService + endpointMetadataService, + cloud ) ); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/migrate_artifacts_to_fleet.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/migrate_artifacts_to_fleet.test.ts deleted file mode 100644 index 277772253b92e..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/migrate_artifacts_to_fleet.test.ts +++ /dev/null @@ -1,144 +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 { - loggingSystemMock, - savedObjectsClientMock, - elasticsearchServiceMock, -} from '@kbn/core/server/mocks'; -import type { - SavedObjectsClient, - Logger, - SavedObjectsFindResponse, - SavedObjectsFindResult, -} from '@kbn/core/server'; -import { migrateArtifactsToFleet } from './migrate_artifacts_to_fleet'; -import { createEndpointArtifactClientMock } from '../../services/artifacts/mocks'; -import type { InternalArtifactCompleteSchema } from '../../schemas'; -import { generateArtifactEsGetSingleHitMock } from '@kbn/fleet-plugin/server/services/artifacts/mocks'; -import type { NewArtifact } from '@kbn/fleet-plugin/server/services'; -import type { CreateRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - -describe('When migrating artifacts to fleet', () => { - let soClient: jest.Mocked; - let logger: jest.Mocked; - let artifactClient: ReturnType; - /** An artifact that was created prior to 7.14 */ - let soArtifactEntry: InternalArtifactCompleteSchema; - - const createSoFindResult = ( - soHits: SavedObjectsFindResult[] = [], - total: number = 15, - page: number = 1 - ): SavedObjectsFindResponse => { - return { - total, - page, - per_page: 10, - saved_objects: soHits, - }; - }; - - beforeEach(async () => { - soClient = savedObjectsClientMock.create() as unknown as jest.Mocked; - logger = loggingSystemMock.create().get() as jest.Mocked; - artifactClient = createEndpointArtifactClientMock(); - // pre-v7.14 artifact, which is compressed - soArtifactEntry = { - identifier: 'endpoint-exceptionlist-macos-v1', - compressionAlgorithm: 'zlib', - encryptionAlgorithm: 'none', - decodedSha256: 'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658', - encodedSha256: 'f8e6afa1d5662f5b37f83337af774b5785b5b7f1daee08b7b00c2d6813874cda', - decodedSize: 14, - encodedSize: 22, - body: 'eJyrVkrNKynKTC1WsoqOrQUAJxkFKQ==', - }; - - // Mock the esClient create response to include the artifact properties that were provide - // to it by fleet artifact client - artifactClient._esClient.create.mockImplementation((props: CreateRequest) => { - return elasticsearchServiceMock.createSuccessTransportRequestPromise({ - ...generateArtifactEsGetSingleHitMock({ - ...((props?.body ?? {}) as NewArtifact), - }), - _index: '.fleet-artifacts-7', - _id: `endpoint:endpoint-exceptionlist-macos-v1-${ - // @ts-expect-error TS2339 - props?.body?.decodedSha256 ?? 'UNKNOWN?' - }`, - _version: 1, - result: 'created', - _shards: { - total: 1, - successful: 1, - failed: 0, - }, - _seq_no: 0, - _primary_term: 1, - }); - }); - - soClient.find.mockResolvedValue(createSoFindResult([], 0)).mockResolvedValueOnce( - createSoFindResult([ - { - score: 1, - type: '', - id: 'abc123', - references: [], - attributes: soArtifactEntry, - }, - ]) - ); - }); - - it('should do nothing if there are no artifacts', async () => { - soClient.find.mockReset(); - soClient.find.mockResolvedValue(createSoFindResult([], 0)); - await migrateArtifactsToFleet(soClient, artifactClient, logger); - expect(soClient.find).toHaveBeenCalled(); - expect(artifactClient.createArtifact).not.toHaveBeenCalled(); - expect(soClient.delete).not.toHaveBeenCalled(); - }); - - it('should create new artifact via fleet client and delete prior SO one', async () => { - await migrateArtifactsToFleet(soClient, artifactClient, logger); - expect(artifactClient.createArtifact).toHaveBeenCalled(); - expect(soClient.delete).toHaveBeenCalled(); - }); - - it('should create artifact in fleet with attributes that match the SO version', async () => { - await migrateArtifactsToFleet(soClient, artifactClient, logger); - - await expect(artifactClient.createArtifact.mock.results[0].value).resolves.toEqual( - expect.objectContaining({ - ...soArtifactEntry, - compressionAlgorithm: 'zlib', - }) - ); - }); - - it('should ignore 404 responses for SO delete (multi-node kibana setup)', async () => { - const notFoundError: Error & { output?: { statusCode: number } } = new Error('not found'); - notFoundError.output = { statusCode: 404 }; - soClient.delete.mockRejectedValue(notFoundError); - await expect(migrateArtifactsToFleet(soClient, artifactClient, logger)).resolves.toEqual( - undefined - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Artifact Migration: Attempt to delete Artifact SO [abc123] returned 404' - ); - }); - - it('should Throw() and log error if migration fails', async () => { - const error = new Error('test: delete failed'); - soClient.delete.mockRejectedValue(error); - await expect(migrateArtifactsToFleet(soClient, artifactClient, logger)).rejects.toThrow( - 'Artifact SO migration failed' - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/migrate_artifacts_to_fleet.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/migrate_artifacts_to_fleet.ts deleted file mode 100644 index e015019fa8a5d..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/migrate_artifacts_to_fleet.ts +++ /dev/null @@ -1,96 +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 { inflate as _inflate } from 'zlib'; -import { promisify } from 'util'; -import type { SavedObjectsClient, Logger } from '@kbn/core/server'; -import type { EndpointArtifactClientInterface } from '../../services'; -import type { InternalArtifactCompleteSchema, InternalArtifactSchema } from '../../schemas'; -import { ArtifactConstants } from './common'; - -class ArtifactMigrationError extends Error { - constructor(message: string, public readonly meta?: unknown) { - super(message); - } -} - -const inflateAsync = promisify(_inflate); - -function isCompressed(artifact: InternalArtifactSchema) { - return artifact.compressionAlgorithm === 'zlib'; -} - -/** - * With v7.13, artifact storage was moved from a security_solution saved object to a fleet index - * in order to support Fleet Server. - */ -export const migrateArtifactsToFleet = async ( - soClient: SavedObjectsClient, - endpointArtifactClient: EndpointArtifactClientInterface, - logger: Logger -): Promise => { - let totalArtifactsMigrated = -1; - let hasMore = true; - - try { - while (hasMore) { - // Retrieve list of artifact records - const { saved_objects: artifactList, total } = - await soClient.find({ - type: ArtifactConstants.SAVED_OBJECT_TYPE, - page: 1, - perPage: 10, - }); - - if (totalArtifactsMigrated === -1) { - totalArtifactsMigrated = total; - if (total > 0) { - logger.info(`Migrating artifacts from SavedObject`); - } - } - - // If nothing else to process, then exit out - if (total === 0) { - hasMore = false; - if (totalArtifactsMigrated > 0) { - logger.info(`Total Artifacts migrated: ${totalArtifactsMigrated}`); - } - return; - } - - for (const artifact of artifactList) { - if (isCompressed(artifact.attributes)) { - artifact.attributes = { - ...artifact.attributes, - body: (await inflateAsync(Buffer.from(artifact.attributes.body, 'base64'))).toString( - 'base64' - ), - }; - } - - // Create new artifact in fleet index - await endpointArtifactClient.createArtifact(artifact.attributes); - // Delete old artifact from SO and if there are errors here, then ignore 404's - // since multiple kibana instances could be going at this - try { - await soClient.delete(ArtifactConstants.SAVED_OBJECT_TYPE, artifact.id); - } catch (e) { - if (e?.output?.statusCode !== 404) { - throw e; - } - logger.debug( - `Artifact Migration: Attempt to delete Artifact SO [${artifact.id}] returned 404` - ); - } - } - } - } catch (e) { - const error = new ArtifactMigrationError('Artifact SO migration failed', e); - logger.error(error); - throw error; - } -}; diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/saved_object_mappings.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/saved_object_mappings.ts index 9ce550859da4d..dba96c2c084de 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/saved_object_mappings.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/saved_object_mappings.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; import { ArtifactConstants, ManifestConstants } from './common'; @@ -13,82 +14,21 @@ import { migrations } from './migrations'; export const exceptionsArtifactSavedObjectType = ArtifactConstants.SAVED_OBJECT_TYPE; export const manifestSavedObjectType = ManifestConstants.SAVED_OBJECT_TYPE; -export const exceptionsArtifactSavedObjectMappings: SavedObjectsType['mappings'] = { - properties: { - identifier: { - type: 'keyword', - }, - compressionAlgorithm: { - type: 'keyword', - index: false, - }, - encryptionAlgorithm: { - type: 'keyword', - index: false, - }, - encodedSha256: { - type: 'keyword', - }, - encodedSize: { - type: 'long', - index: false, - }, - decodedSha256: { - type: 'keyword', - index: false, - }, - decodedSize: { - type: 'long', - index: false, - }, - created: { - type: 'date', - index: false, - }, - body: { - type: 'binary', - }, - }, -}; - export const manifestSavedObjectMappings: SavedObjectsType['mappings'] = { + dynamic: false, properties: { - created: { - type: 'date', - index: false, - }, schemaVersion: { type: 'keyword', }, - semanticVersion: { - type: 'keyword', - index: false, - }, artifacts: { type: 'nested', - properties: { - policyId: { - type: 'keyword', - index: false, - }, - artifactId: { - type: 'keyword', - index: false, - }, - }, }, }, }; -export const exceptionsArtifactType: SavedObjectsType = { - name: exceptionsArtifactSavedObjectType, - hidden: false, - namespaceType: 'agnostic', - mappings: exceptionsArtifactSavedObjectMappings, -}; - export const manifestType: SavedObjectsType = { name: manifestSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'agnostic', mappings: manifestSavedObjectMappings, diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts index dded85b559c6d..195c8509e60d5 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts @@ -17,7 +17,6 @@ import type { } from '@kbn/core/server'; import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { PackagePolicyClient } from '@kbn/fleet-plugin/server'; import type { ILicense } from '@kbn/licensing-plugin/common/types'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; @@ -35,19 +34,16 @@ export class PolicyWatcher { private policyService: PackagePolicyClient; private subscription: Subscription | undefined; private soStart: SavedObjectsServiceStart; - private cloud: CloudSetup; constructor( policyService: PackagePolicyClient, soStart: SavedObjectsServiceStart, esStart: ElasticsearchServiceStart, - cloud: CloudSetup, logger: Logger ) { this.policyService = policyService; this.esClient = esStart.client.asInternalUser; this.logger = logger; this.soStart = soStart; - this.cloud = cloud; } /** @@ -105,9 +101,6 @@ export class PolicyWatcher { for (const policy of response.items as PolicyData[]) { const updatePolicy = getPolicyDataForUpdate(policy); const policyConfig = updatePolicy.inputs[0].config.policy.value; - updatePolicy.inputs[0].config.policy.value.meta.license = license.type || ''; - // add cloud info to policy meta - updatePolicy.inputs[0].config.policy.value.meta.cloud = this.cloud?.isCloudEnabled; try { if (!isEndpointPolicyValidForLicense(policyConfig, license)) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts index 6bba9bc38c962..1f96cb4dff64a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts @@ -11,6 +11,7 @@ import { registerActionDetailsRoutes } from './details'; import type { SecuritySolutionPluginRouter } from '../../../types'; import type { EndpointAppContext } from '../../types'; import { registerActionStatusRoutes } from './status'; +import { registerActionStateRoutes } from './state'; import { registerActionAuditLogRoutes } from './audit_log'; import { registerActionListRoutes } from './list'; import { registerResponseActionRoutes } from './response_actions'; @@ -19,9 +20,11 @@ import { registerResponseActionRoutes } from './response_actions'; export function registerActionRoutes( router: SecuritySolutionPluginRouter, - endpointContext: EndpointAppContext + endpointContext: EndpointAppContext, + canEncrypt?: boolean ) { registerActionStatusRoutes(router, endpointContext); + registerActionStateRoutes(router, endpointContext, canEncrypt); registerActionAuditLogRoutes(router, endpointContext); registerActionListRoutes(router, endpointContext); registerActionDetailsRoutes(router, endpointContext); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.test.ts new file mode 100644 index 0000000000000..9612eab2c2821 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.test.ts @@ -0,0 +1,133 @@ +/* + * 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 { ScopedClusterClientMock } from '@kbn/core/server/mocks'; +import { loggingSystemMock, httpServerMock, httpServiceMock } from '@kbn/core/server/mocks'; +import type { KibanaResponseFactory, SavedObjectsClientContract } from '@kbn/core/server'; + +import { + createMockEndpointAppContextServiceStartContract, + createRouteHandlerContext, +} from '../../mocks'; +import type { EndpointAuthz } from '../../../../common/endpoint/types/authz'; + +import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__'; + +import { getEndpointAuthzInitialStateMock } from '../../../../common/endpoint/service/authz/mocks'; + +import { EndpointAppContextService } from '../../endpoint_app_context_services'; +import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; + +import { registerActionStateRoutes } from './state'; +import type { RouterMock } from '@kbn/core-http-router-server-mocks'; +import { ACTION_STATE_ROUTE } from '../../../../common/endpoint/constants'; + +interface CallRouteInterface { + authz?: Partial; +} + +describe('when calling the Action state route handler', () => { + let mockScopedEsClient: ScopedClusterClientMock; + let mockSavedObjectClient: jest.Mocked; + let mockResponse: jest.Mocked; + let callRoute: ( + routerMock: RouterMock, + routePrefix: string, + opts: CallRouteInterface, + indexExists?: { endpointDsExists: boolean } + ) => Promise; + + beforeEach(() => { + const startContract = createMockEndpointAppContextServiceStartContract(); + mockResponse = httpServerMock.createResponseFactory(); + // define a convenience function to execute an API call for a given route + callRoute = async ( + routerMock: RouterMock, + routePrefix: string, + { authz = {} }: CallRouteInterface + ): Promise => { + const superUser = { + username: 'superuser', + roles: ['superuser'], + }; + (startContract.security.authc.getCurrentUser as jest.Mock).mockImplementationOnce( + () => superUser + ); + + const ctx = createRouteHandlerContext(mockScopedEsClient, mockSavedObjectClient); + + ctx.securitySolution.getEndpointAuthz.mockResolvedValue( + getEndpointAuthzInitialStateMock(authz) + ); + + const mockRequest = httpServerMock.createKibanaRequest(); + const [, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => + path.startsWith(routePrefix) + )!; + + await routeHandler(ctx, mockRequest, mockResponse); + }; + }); + describe('with having right privileges', () => { + it.each([[true], [false]])( + 'when can encrypt is set to %s it returns proper value', + async (canEncrypt) => { + const routerMock: RouterMock = httpServiceMock.createRouter(); + const endpointAppContextService = new EndpointAppContextService(); + registerActionStateRoutes( + routerMock, + { + logFactory: loggingSystemMock.create(), + service: endpointAppContextService, + config: () => Promise.resolve(createMockConfig()), + experimentalFeatures: parseExperimentalConfigValue( + createMockConfig().enableExperimental + ), + }, + canEncrypt + ); + + await callRoute(routerMock, ACTION_STATE_ROUTE, { + authz: { canIsolateHost: true }, + }); + + expect(mockResponse.ok).toHaveBeenCalledWith({ body: { data: { canEncrypt } } }); + } + ); + }); + describe('without having right privileges', () => { + it('it returns unauthorized error', async () => { + const routerMock: RouterMock = httpServiceMock.createRouter(); + const endpointAppContextService = new EndpointAppContextService(); + registerActionStateRoutes( + routerMock, + { + logFactory: loggingSystemMock.create(), + service: endpointAppContextService, + config: () => Promise.resolve(createMockConfig()), + experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental), + }, + true + ); + + await callRoute(routerMock, ACTION_STATE_ROUTE, { + authz: { + canIsolateHost: false, + canUnIsolateHost: false, + canKillProcess: false, + canSuspendProcess: false, + canGetRunningProcesses: false, + canAccessResponseConsole: false, + canWriteExecuteOperations: false, + canWriteFileOperations: false, + }, + }); + + expect(mockResponse.forbidden).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.ts new file mode 100644 index 0000000000000..f461b144663ed --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.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 type { RequestHandler } from '@kbn/core/server'; +import { ACTION_STATE_ROUTE } from '../../../../common/endpoint/constants'; +import type { + SecuritySolutionPluginRouter, + SecuritySolutionRequestHandlerContext, +} from '../../../types'; +import type { EndpointAppContext } from '../../types'; +import { withEndpointAuthz } from '../with_endpoint_authz'; + +/** + * Registers routes for checking state of actions routes + */ +export function registerActionStateRoutes( + router: SecuritySolutionPluginRouter, + endpointContext: EndpointAppContext, + canEncrypt?: boolean +) { + router.get( + { + path: ACTION_STATE_ROUTE, + validate: false, + options: { authRequired: true, tags: ['access:securitySolution'] }, + }, + withEndpointAuthz( + { + any: [ + 'canIsolateHost', + 'canUnIsolateHost', + 'canKillProcess', + 'canSuspendProcess', + 'canGetRunningProcesses', + 'canAccessResponseConsole', + 'canWriteExecuteOperations', + 'canWriteFileOperations', + ], + }, + endpointContext.logFactory.get('actionState'), + getActionStateRequestHandler(canEncrypt) + ) + ); +} + +export const getActionStateRequestHandler = function ( + canEncrypt?: boolean +): RequestHandler { + return async (_, __, res) => { + return res.ok({ + body: { + data: { canEncrypt }, + }, + }); + }; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts index cd18a61368ef7..ca16294c552af 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts @@ -39,6 +39,7 @@ export function registerEndpointSuggestionsRoutes( { path: SUGGESTIONS_ROUTE, validate: EndpointSuggestionsSchema, + options: { authRequired: true, tags: ['access:securitySolution'] }, }, withEndpointAuthz( { any: ['canWriteEventFilters'] }, diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index 30c7e776e718e..c415fc287fbec 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -366,7 +366,8 @@ describe('ingest_integration tests ', () => { logger, licenseService, endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService + endpointAppContextMock.endpointMetadataService, + cloudService ); const policyConfig = generator.generatePolicyPackagePolicy(); policyConfig.inputs[0]!.config!.policy.value = mockPolicy; @@ -382,7 +383,8 @@ describe('ingest_integration tests ', () => { logger, licenseService, endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService + endpointAppContextMock.endpointMetadataService, + cloudService ); const policyConfig = generator.generatePolicyPackagePolicy(); policyConfig.inputs[0]!.config!.policy.value = mockPolicy; @@ -412,7 +414,8 @@ describe('ingest_integration tests ', () => { logger, licenseService, endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService + endpointAppContextMock.endpointMetadataService, + cloudService ); const policyConfig = generator.generatePolicyPackagePolicy(); policyConfig.inputs[0]!.config!.policy.value = mockPolicy; @@ -427,6 +430,66 @@ describe('ingest_integration tests ', () => { }); }); + describe('package policy update callback when meta fields should be updated', () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + beforeEach(() => { + licenseEmitter.next(Platinum); // set license level to platinum + }); + it('updates successfully when meta fields differ from services', async () => { + const mockPolicy = policyFactory(); + mockPolicy.meta.cloud = true; // cloud mock will return true + mockPolicy.meta.license = 'platinum'; // license is set to emit platinum + const logger = loggingSystemMock.create().get('ingest_integration.test'); + const callback = getPackagePolicyUpdateCallback( + logger, + licenseService, + endpointAppContextMock.featureUsageService, + endpointAppContextMock.endpointMetadataService, + cloudService + ); + const policyConfig = generator.generatePolicyPackagePolicy(); + // values should be updated + policyConfig.inputs[0]!.config!.policy.value.meta.cloud = false; + policyConfig.inputs[0]!.config!.policy.value.meta.license = 'gold'; + const updatedPolicyConfig = await callback( + policyConfig, + soClient, + esClient, + requestContextMock.convertContext(ctx), + req + ); + expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy); + }); + + it('meta fields stay the same where there is no difference', async () => { + const mockPolicy = policyFactory(); + mockPolicy.meta.cloud = true; // cloud mock will return true + mockPolicy.meta.license = 'platinum'; // license is set to emit platinum + const logger = loggingSystemMock.create().get('ingest_integration.test'); + const callback = getPackagePolicyUpdateCallback( + logger, + licenseService, + endpointAppContextMock.featureUsageService, + endpointAppContextMock.endpointMetadataService, + cloudService + ); + const policyConfig = generator.generatePolicyPackagePolicy(); + // values should be updated + policyConfig.inputs[0]!.config!.policy.value.meta.cloud = true; + policyConfig.inputs[0]!.config!.policy.value.meta.license = 'platinum'; + const updatedPolicyConfig = await callback( + policyConfig, + soClient, + esClient, + requestContextMock.convertContext(ctx), + req + ); + expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy); + }); + }); + describe('package policy delete callback', () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts index fd6f85af1a045..7e68c63f07593 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts @@ -44,6 +44,17 @@ const isEndpointPackagePolicy = ( return packagePolicy.package?.name === 'endpoint'; }; +const shouldUpdateMetaValues = ( + endpointPackagePolicy: PolicyConfig, + currentLicenseType: string, + currentCloudInfo: boolean +) => { + return ( + endpointPackagePolicy.meta.license !== currentLicenseType || + endpointPackagePolicy.meta.cloud !== currentCloudInfo + ); +}; + /** * Callback to handle creation of PackagePolicies in Fleet */ @@ -152,7 +163,8 @@ export const getPackagePolicyUpdateCallback = ( logger: Logger, licenseService: LicenseService, featureUsageService: FeatureUsageService, - endpointMetadataService: EndpointMetadataService + endpointMetadataService: EndpointMetadataService, + cloud: CloudSetup ): PutPackagePolicyUpdateCallback => { return async (newPackagePolicy: NewPackagePolicy): Promise => { if (!isEndpointPackagePolicy(newPackagePolicy)) { @@ -170,6 +182,22 @@ export const getPackagePolicyUpdateCallback = ( notifyProtectionFeatureUsage(newPackagePolicy, featureUsageService, endpointMetadataService); + const newEndpointPackagePolicy = newPackagePolicy.inputs[0].config?.policy + ?.value as PolicyConfig; + + if ( + newPackagePolicy.inputs[0].config?.policy?.value && + shouldUpdateMetaValues( + newEndpointPackagePolicy, + licenseService.getLicenseType(), + cloud?.isCloudEnabled + ) + ) { + newEndpointPackagePolicy.meta.license = licenseService.getLicenseType(); + newEndpointPackagePolicy.meta.cloud = cloud?.isCloudEnabled; + newPackagePolicy.inputs[0].config.policy.value = newEndpointPackagePolicy; + } + return newPackagePolicy; }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/saved_objects.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/saved_objects.ts index 8a168d3c43ba0..180aecfeb34d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/saved_objects.ts @@ -5,55 +5,29 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; export const signalsMigrationType = 'security-solution-signals-migration'; export const signalsMigrationMappings: SavedObjectsType['mappings'] = { + dynamic: false, properties: { sourceIndex: { type: 'keyword', }, - destinationIndex: { - type: 'keyword', - index: false, - }, - version: { - type: 'long', - }, - error: { - type: 'text', - index: false, - }, - taskId: { - type: 'keyword', - index: false, - }, - status: { - type: 'keyword', - index: false, - }, - created: { - type: 'date', - index: false, - }, - createdBy: { - type: 'text', - index: false, - }, updated: { type: 'date', - index: false, }, - updatedBy: { - type: 'text', - index: false, + version: { + type: 'long', }, }, }; export const type: SavedObjectsType = { name: signalsMigrationType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'single', mappings: signalsMigrationMappings, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts index 74b5d6fbffadd..be989326f6963 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_type.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; export const PREBUILT_RULE_ASSETS_SO_TYPE = 'security-rule'; @@ -23,6 +24,7 @@ const prebuiltRuleAssetMappings: SavedObjectsType['mappings'] = { export const prebuiltRuleAssetType: SavedObjectsType = { name: PREBUILT_RULE_ASSETS_SO_TYPE, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, management: { importableAndExportable: true, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts index 7df109deb6f3b..848a0933da542 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -65,7 +65,7 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({ severity_mapping: [], updated_by: 'elastic', tags: [], - throttle: 'no_actions', + throttle: undefined, threat: getThreatMock(), exceptions_list: getListArrayMock(), filters: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_saved_object_mappings.ts index 1868cb238b11e..3612a4bf744f0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_saved_object_mappings.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_saved_object_mappings.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; // eslint-disable-next-line no-restricted-imports import { legacyRuleActionsSavedObjectMigration } from './legacy_migrations'; @@ -61,6 +62,7 @@ const legacyRuleActionsSavedObjectMappings: SavedObjectsType['mappings'] = { export const legacyType: SavedObjectsType = { convertToMultiNamespaceTypeVersion: '8.0.0', name: legacyRuleActionsSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', mappings: legacyRuleActionsSavedObjectMappings, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts index 27b9111b0434d..974ea9cc7ecc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts @@ -432,8 +432,10 @@ export const performBulkActionRoute = ( } let shouldDuplicateExceptions = true; + let shouldDuplicateExpiredExceptions = true; if (body.duplicate !== undefined) { shouldDuplicateExceptions = body.duplicate.include_exceptions; + shouldDuplicateExpiredExceptions = body.duplicate.include_expired_exceptions; } const duplicateRuleToCreate = await duplicateRule({ @@ -449,6 +451,7 @@ export const performBulkActionRoute = ( ? await duplicateExceptions({ ruleId: rule.params.ruleId, exceptionLists: rule.params.exceptionsList, + includeExpiredExceptions: shouldDuplicateExpiredExceptions, exceptionsClient, }) : []; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts new file mode 100644 index 0000000000000..aeb593ec33fff --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts @@ -0,0 +1,163 @@ +/* + * 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 { duplicateExceptions } from './duplicate_exceptions'; +import { getExceptionListClientMock } from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client.mock'; +import type { List } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionListClient } from '@kbn/lists-plugin/server'; +import { getDetectionsExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; + +jest.mock('uuid', () => ({ + v4: jest.fn(), +})); + +describe('duplicateExceptions', () => { + let exceptionsClient: ExceptionListClient; + + beforeAll(() => { + exceptionsClient = getExceptionListClientMock(); + exceptionsClient.duplicateExceptionListAndItems = jest.fn(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it('returns empty array if no exceptions to duplicate', async () => { + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(result).toEqual([]); + }); + + it('returns array referencing the same shared exception lists if no rule default exceptions included', async () => { + const sharedExceptionListReference: List = { + type: ExceptionListTypeEnum.DETECTION, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [sharedExceptionListReference], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(exceptionsClient.duplicateExceptionListAndItems).not.toHaveBeenCalled(); + expect(result).toEqual([sharedExceptionListReference]); + }); + + it('duplicates rule default and shared exceptions', async () => { + const newDefaultRuleList = { + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list_dupe', + namespace_type: 'single', + id: '123-abc', + }; + + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list', + namespace_type: 'single', + id: '5678', + }); + exceptionsClient.duplicateExceptionListAndItems = jest + .fn() + .mockResolvedValue(newDefaultRuleList); + + const sharedExceptionListReference: List = { + type: ExceptionListTypeEnum.DETECTION, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list', + namespace_type: 'single', + id: '5678', + }; + + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [sharedExceptionListReference, ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(result).toEqual([ + sharedExceptionListReference, + { + type: newDefaultRuleList.type, + namespace_type: newDefaultRuleList.namespace_type, + id: newDefaultRuleList.id, + list_id: newDefaultRuleList.list_id, + }, + ]); + }); + + it('throws error if rule default list to duplicate not found', async () => { + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue(null); + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + await expect(() => + duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unable to duplicate rule default exceptions - unable to find their container with list_id: "my_list"]` + ); + }); + + it('throws error if list duplication returns null', async () => { + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }); + exceptionsClient.duplicateExceptionListAndItems = jest.fn().mockResolvedValue(null); + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + await expect(() => + duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unable to duplicate rule default exception items for rule_id: rule_123]` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts index 496a91ba55963..82f13adc4534c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts @@ -5,23 +5,42 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { RuleParams } from '../../../rule_schema'; +const ERROR_DUPLICATING = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.cloneExceptions.errorDuplicatingList', + { + defaultMessage: + 'Unable to duplicate rule default exceptions - unable to find their container with list_id:', + } +); + +const ERROR_DUPLICATING_ITEMS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.cloneExceptions.errorDuplicatingListItems', + { + defaultMessage: 'Unable to duplicate rule default exception items for rule_id:', + } +); + interface DuplicateExceptionsParams { ruleId: RuleParams['ruleId']; exceptionLists: RuleParams['exceptionsList']; exceptionsClient: ExceptionListClient | undefined; + includeExpiredExceptions: boolean; } export const duplicateExceptions = async ({ ruleId, exceptionLists, exceptionsClient, + includeExpiredExceptions, }: DuplicateExceptionsParams): Promise => { - if (exceptionLists == null) { + if (exceptionLists == null || !exceptionLists.length) { return []; } @@ -37,24 +56,36 @@ export const duplicateExceptions = async ({ // For rule_default list (exceptions that live only on a single rule), we need // to create a new rule_default list to assign to duplicated rule if (ruleDefaultList != null && exceptionsClient != null) { - const ruleDefaultExceptionList = await exceptionsClient.duplicateExceptionListAndItems({ + // fetch list container + const listToDuplicate = await exceptionsClient.getExceptionList({ + id: undefined, listId: ruleDefaultList.list_id, namespaceType: ruleDefaultList.namespace_type, }); - if (ruleDefaultExceptionList == null) { - throw new Error(`Unable to duplicate rule default exception items for rule_id: ${ruleId}`); - } + if (listToDuplicate == null) { + throw new Error(`${ERROR_DUPLICATING} "${ruleDefaultList.list_id}"`); + } else { + const ruleDefaultExceptionList = await exceptionsClient.duplicateExceptionListAndItems({ + list: listToDuplicate, + namespaceType: ruleDefaultList.namespace_type, + includeExpiredExceptions, + }); - return [ - ...sharedLists, - { - id: ruleDefaultExceptionList.id, - list_id: ruleDefaultExceptionList.list_id, - namespace_type: ruleDefaultExceptionList.namespace_type, - type: ruleDefaultExceptionList.type, - }, - ]; + if (ruleDefaultExceptionList == null) { + throw new Error(`${ERROR_DUPLICATING_ITEMS} ${ruleId}`); + } + + return [ + ...sharedLists, + { + id: ruleDefaultExceptionList.id, + list_id: ruleDefaultExceptionList.list_id, + namespace_type: ruleDefaultExceptionList.namespace_type, + type: ruleDefaultExceptionList.type, + }, + ]; + } } // If no rule_default list exists, we can just return diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts index b441d071c21c1..08a53c007dc06 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts @@ -109,8 +109,6 @@ describe('duplicateRule', () => { consumer: rule.consumer, schedule: rule.schedule, actions: rule.actions, - throttle: null, // TODO: fix? - notifyWhen: null, // TODO: fix? enabled: false, // covered in a separate test }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts index 4a99085123b41..315517504def4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts @@ -11,6 +11,7 @@ import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import type { InternalRuleCreate, RuleParams } from '../../../rule_schema'; +import { transformToActionFrequency } from '../../normalization/rule_actions'; const DUPLICATE_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.cloneRule.duplicateTitle', @@ -33,6 +34,7 @@ export const duplicateRule = async ({ rule }: DuplicateRuleParams): Promise - ({ - field: 'throttle', - operation: 'set', - value: transformToAlertThrottle(throttle), - } as const); - -const getNotifyWhenOperation = (throttle: string) => - ({ - field: 'notifyWhen', - operation: 'set', - value: transformToNotifyWhen(throttle), - } as const); +import { transformToActionFrequency } from '../../normalization/rule_actions'; /** * converts bulk edit action to format of rulesClient.bulkEdit operation @@ -70,10 +55,8 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'actions', operation: 'add', - value: action.value.actions, + value: transformToActionFrequency(action.value.actions, action.value.throttle), }, - getThrottleOperation(action.value.throttle), - getNotifyWhenOperation(action.value.throttle), ]; case BulkActionEditType.set_rule_actions: @@ -81,10 +64,8 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'actions', operation: 'set', - value: action.value.actions, + value: transformToActionFrequency(action.value.actions, action.value.throttle), }, - getThrottleOperation(action.value.throttle), - getNotifyWhenOperation(action.value.throttle), ]; // schedule actions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts index 6660ce730f84b..09d4eb691427d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts @@ -5,23 +5,13 @@ * 2.0. */ -import type { BulkOperationError, RulesClient } from '@kbn/alerting-plugin/server'; -import pMap from 'p-map'; -import { - MAX_RULES_TO_UPDATE_IN_PARALLEL, - NOTIFICATION_THROTTLE_NO_ACTIONS, -} from '../../../../../../common/constants'; +import type { RulesClient } from '@kbn/alerting-plugin/server'; -import type { - BulkActionEditPayload, - BulkActionEditPayloadRuleActions, -} from '../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { BulkActionEditType } from '../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; +import type { BulkActionEditPayload } from '../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; import type { MlAuthz } from '../../../../machine_learning/authz'; import { enrichFilterWithRuleTypeMapping } from '../search/enrich_filter_with_rule_type_mappings'; -import { readRules } from '../crud/read_rules'; import type { RuleAlertType } from '../../../rule_schema'; import { ruleParamsModifier } from './rule_params_modifier'; @@ -67,52 +57,5 @@ export const bulkEditRules = async ({ }, }); - // rulesClient bulkEdit currently doesn't support bulk mute/unmute. - // this is a workaround to mitigate this, - // until https://github.com/elastic/kibana/issues/139084 is resolved - // if rule actions has been applied, we go through each rule, unmute it if necessary and refetch it - // calling unmute needed only if rule was muted and throttle value is not NOTIFICATION_THROTTLE_NO_ACTIONS - const ruleActions = attributesActions.filter((rule): rule is BulkActionEditPayloadRuleActions => - [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].includes(rule.type) - ); - - // bulk edit actions are applied in historical order. - // So, we need to find a rule action that will be applied the last, to be able to check if rule should be muted/unmuted - const rulesAction = ruleActions.pop(); - - if (rulesAction) { - const unmuteErrors: BulkOperationError[] = []; - const rulesToUnmute = await pMap( - result.rules, - async (rule) => { - try { - if (rule.muteAll && rulesAction.value.throttle !== NOTIFICATION_THROTTLE_NO_ACTIONS) { - await rulesClient.unmuteAll({ id: rule.id }); - return (await readRules({ rulesClient, id: rule.id, ruleId: undefined })) ?? rule; - } - - return rule; - } catch (err) { - unmuteErrors.push({ - message: err.message, - rule: { - id: rule.id, - name: rule.name, - }, - }); - - return null; - } - }, - { concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL } - ); - - return { - ...result, - rules: rulesToUnmute.filter((rule): rule is RuleAlertType => rule != null), - errors: [...result.errors, ...unmuteErrors], - }; - } - return result; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts index 416edb039919e..5b9f3efa1ad52 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts @@ -9,7 +9,6 @@ import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import type { RulesClient } from '@kbn/alerting-plugin/server'; import type { RuleCreateProps } from '../../../../../../common/detection_engine/rule_schema'; -import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../../common/constants'; import { convertCreateAPIToInternalSchema } from '../../normalization/rule_converters'; import type { RuleParams } from '../../../rule_schema'; @@ -39,10 +38,5 @@ export const createRules = async ({ allowMissingConnectorSecrets, }); - // Mute the rule if it is first created with the explicit no actions - if (params.throttle === NOTIFICATION_THROTTLE_NO_ACTIONS) { - await rulesClient.muteAll({ id: rule.id }); - } - return rule; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts index cfb051273255a..6e4d573a880e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts @@ -123,6 +123,7 @@ describe('patchRules', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', }, group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }), @@ -158,6 +159,7 @@ describe('patchRules', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', }, group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts index 3ce61fcc9800b..643750ea78b6d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts @@ -10,7 +10,6 @@ import type { PartialRule, RulesClient } from '@kbn/alerting-plugin/server'; import type { PatchRuleRequestBody } from '../../../../../../common/detection_engine/rule_management'; import type { RuleAlertType, RuleParams } from '../../../rule_schema'; import { convertPatchAPIToInternalSchema } from '../../normalization/rule_converters'; -import { maybeMute } from '../rule_actions/muting'; export interface PatchRulesOptions { rulesClient: RulesClient; @@ -41,15 +40,6 @@ export const patchRules = async ({ shouldIncrementRevision: () => shouldIncrementRevision, }); - if (nextParams.throttle !== undefined) { - await maybeMute({ - rulesClient, - muteAll: existingRule.muteAll, - throttle: nextParams.throttle, - id: update.id, - }); - } - if (existingRule.enabled && nextParams.enabled === false) { await rulesClient.disable({ id: existingRule.id }); } else if (!existingRule.enabled && nextParams.enabled === true) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index d949fd63136ea..070a81692f3d5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -12,9 +12,8 @@ import type { RuleUpdateProps } from '../../../../../../common/detection_engine/ import { transformRuleToAlertAction } from '../../../../../../common/detection_engine/transform_actions'; import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; -import { transformToAlertThrottle, transformToNotifyWhen } from '../../normalization/rule_actions'; +import { transformToActionFrequency } from '../../normalization/rule_actions'; import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; -import { maybeMute } from '../rule_actions/muting'; export interface UpdateRulesOptions { rulesClient: RulesClient; @@ -31,6 +30,9 @@ export const updateRules = async ({ return null; } + const alertActions = ruleUpdate.actions?.map(transformRuleToAlertAction) ?? []; + const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); + const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); const enabled = ruleUpdate.enabled ?? true; const newInternalRule: InternalRuleUpdate = { @@ -71,9 +73,7 @@ export const updateRules = async ({ ...typeSpecificParams, }, schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions: ruleUpdate.actions != null ? ruleUpdate.actions.map(transformRuleToAlertAction) : [], - throttle: transformToAlertThrottle(ruleUpdate.throttle), - notifyWhen: transformToNotifyWhen(ruleUpdate.throttle), + actions, }; const update = await rulesClient.update({ @@ -81,13 +81,6 @@ export const updateRules = async ({ data: newInternalRule, }); - await maybeMute({ - rulesClient, - muteAll: existingRule.muteAll, - throttle: ruleUpdate.throttle, - id: update.id, - }); - if (existingRule.enabled && enabled === false) { await rulesClient.disable({ id: existingRule.id }); } else if (!existingRule.enabled && enabled === true) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 038dfbc9152d6..c86387f26f50a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -131,7 +131,6 @@ describe('getExportAll', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), @@ -275,6 +274,7 @@ describe('getExportAll', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.slack', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], building_block_type: 'default', @@ -313,7 +313,6 @@ describe('getExportAll', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'rule', note: '# Investigative notes', version: 1, revision: 0, @@ -416,6 +415,7 @@ describe('getExportAll', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.email', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index de76ef3b23d8b..35151f1b4b1d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -128,7 +128,6 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), @@ -284,6 +283,7 @@ describe('get_export_by_object_ids', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.slack', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], building_block_type: 'default', @@ -322,7 +322,6 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'rule', note: '# Investigative notes', version: 1, revision: 0, @@ -426,6 +425,7 @@ describe('get_export_by_object_ids', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.email', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }) @@ -508,7 +508,7 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', + throttle: undefined, note: '# Investigative notes', version: 1, revision: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_actions/muting.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_actions/muting.ts deleted file mode 100644 index 6c0c7d9686767..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_actions/muting.ts +++ /dev/null @@ -1,37 +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 { RulesClient } from '@kbn/alerting-plugin/server'; -import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../../common/constants'; -import type { RuleAlertType } from '../../../rule_schema'; - -/** - * Mutes, unmutes, or does nothing to the alert if no changed is detected - * @param id The id of the alert to (un)mute - * @param rulesClient the rules client - * @param muteAll If the existing alert has all actions muted - * @param throttle If the existing alert has a throttle set - */ -export const maybeMute = async ({ - id, - rulesClient, - muteAll, - throttle, -}: { - id: RuleAlertType['id']; - rulesClient: RulesClient; - muteAll: RuleAlertType['muteAll']; - throttle: string | null | undefined; -}): Promise => { - if (muteAll && throttle !== NOTIFICATION_THROTTLE_NO_ACTIONS) { - await rulesClient.unmuteAll({ id }); - } else if (!muteAll && throttle === NOTIFICATION_THROTTLE_NO_ACTIONS) { - await rulesClient.muteAll({ id }); - } else { - // Do nothing, no-operation - } -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts index d0d5edad970f0..33fcf0bd29c20 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { + NOTIFICATION_DEFAULT_FREQUENCY, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE, } from '../../../../../common/constants'; @@ -14,6 +16,7 @@ import type { RuleAlertType } from '../../rule_schema'; import { transformFromAlertThrottle, + transformToActionFrequency, transformToAlertThrottle, transformToNotifyWhen, } from './rule_actions'; @@ -151,4 +154,152 @@ describe('Rule actions normalization', () => { ).toEqual(NOTIFICATION_THROTTLE_RULE); }); }); + + describe('transformToActionFrequency', () => { + describe('actions without frequencies', () => { + const actionsWithoutFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + }, + ]; + + test.each([undefined, null, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE])( + `it sets each action's frequency attribute to default value when 'throttle' is '%s'`, + (throttle) => { + expect(transformToActionFrequency(actionsWithoutFrequencies, throttle)).toEqual( + actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })) + ); + } + ); + + test.each(['47s', '10m', '3h', '101d'])( + `it correctly transforms 'throttle = %s' and sets it as a frequency of each action`, + (throttle) => { + expect(transformToActionFrequency(actionsWithoutFrequencies, throttle)).toEqual( + actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })) + ); + } + ); + }); + + describe('actions with frequencies', () => { + const actionsWithFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + frequency: { + summary: false, + throttle: '1s', + notifyWhen: 'onThrottleInterval', + }, + }, + ]; + + test.each([ + undefined, + null, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '1h', + '1d', + ])(`it does not change actions frequency attributes when 'throttle' is '%s'`, (throttle) => { + expect(transformToActionFrequency(actionsWithFrequencies, throttle)).toEqual( + actionsWithFrequencies + ); + }); + }); + + describe('some actions with frequencies', () => { + const someActionsWithFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + frequency: { + summary: false, + throttle: '1s', + notifyWhen: 'onThrottleInterval', + }, + }, + { + group: 'group', + id: 'id-345', + actionTypeId: 'id-678', + params: {}, + }, + ]; + + test.each([undefined, null, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE])( + `it overrides each action's frequency attribute to default value when 'throttle' is '%s'`, + (throttle) => { + expect(transformToActionFrequency(someActionsWithFrequencies, throttle)).toEqual( + someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })) + ); + } + ); + + test.each(['47s', '10m', '3h', '101d'])( + `it correctly transforms 'throttle = %s' and overrides frequency attribute of each action`, + (throttle) => { + expect(transformToActionFrequency(someActionsWithFrequencies, throttle)).toEqual( + someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })) + ); + } + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts index 51dbe9d80f986..d0e909cc5f329 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import type { RuleActionFrequency, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; import { NOTIFICATION_THROTTLE_NO_ACTIONS, @@ -14,6 +14,38 @@ import { import type { RuleAlertType } from '../../rule_schema'; +export const transformToFrequency = (throttle: string | null | undefined): RuleActionFrequency => { + return { + summary: true, + notifyWhen: transformToNotifyWhen(throttle) ?? 'onActiveAlert', + throttle: transformToAlertThrottle(throttle), + }; +}; + +interface ActionWithFrequency { + frequency?: RuleActionFrequency; +} + +/** + * The action level `frequency` attribute should always take precedence over the rule level `throttle` + * Frequency's default value is `{ summary: true, throttle: null, notifyWhen: 'onActiveAlert' }` + * + * The transformation follows the next rules: + * - Both rule level `throttle` and all actions have `frequency` are set: we will ignore rule level `throttle` + * - Rule level `throttle` set and actions don't have `frequency` set: we will transform rule level `throttle` in action level `frequency` + * - All actions have `frequency` set: do nothing + * - Neither of them is set: we will set action level `frequency` to default value + * - Rule level `throttle` and some of the actions have `frequency` set: we will transform rule level `throttle` and set it to actions without the frequency attribute + * - Only some actions have `frequency` set and there is no rule level `throttle`: we will set default `frequency` to actions without frequency attribute + */ +export const transformToActionFrequency = ( + actions: T[], + throttle: string | null | undefined +): T[] => { + const defaultFrequency = transformToFrequency(throttle); + return actions.map((action) => ({ ...action, frequency: action.frequency ?? defaultFrequency })); +}; + /** * Given a throttle from a "security_solution" rule this will transform it into an "alerting" notifyWhen * on their saved object. diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index 7296e90bf73e7..66d873c2c0935 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -39,10 +39,10 @@ import { } from '../../../../../common/detection_engine/rule_schema'; import { + transformAlertToRuleAction, transformAlertToRuleResponseAction, transformRuleToAlertAction, transformRuleToAlertResponseAction, - transformAlertToRuleAction, } from '../../../../../common/detection_engine/transform_actions'; import { @@ -73,11 +73,7 @@ import type { NewTermsRuleParams, NewTermsSpecificRuleParams, } from '../../rule_schema'; -import { - transformFromAlertThrottle, - transformToAlertThrottle, - transformToNotifyWhen, -} from './rule_actions'; +import { transformFromAlertThrottle, transformToActionFrequency } from './rule_actions'; import { convertAlertSuppressionToCamel, convertAlertSuppressionToSnake } from '../utils/utils'; import { createRuleExecutionSummary } from '../../rule_monitoring'; @@ -399,6 +395,11 @@ export const convertPatchAPIToInternalSchema = ( ): InternalRuleUpdate => { const typeSpecificParams = patchTypeSpecificSnakeToCamel(nextParams, existingRule.params); const existingParams = existingRule.params; + + const alertActions = nextParams.actions?.map(transformRuleToAlertAction) ?? existingRule.actions; + const throttle = nextParams.throttle ?? transformFromAlertThrottle(existingRule); + const actions = transformToActionFrequency(alertActions, throttle); + return { name: nextParams.name ?? existingRule.name, tags: nextParams.tags ?? existingRule.tags, @@ -438,15 +439,7 @@ export const convertPatchAPIToInternalSchema = ( ...typeSpecificParams, }, schedule: { interval: nextParams.interval ?? existingRule.schedule.interval }, - actions: nextParams.actions - ? nextParams.actions.map(transformRuleToAlertAction) - : existingRule.actions, - throttle: nextParams.throttle - ? transformToAlertThrottle(nextParams.throttle) - : existingRule.throttle ?? null, - notifyWhen: nextParams.throttle - ? transformToNotifyWhen(nextParams.throttle) - : existingRule.notifyWhen ?? null, + actions, }; }; @@ -462,6 +455,10 @@ export const convertCreateAPIToInternalSchema = ( ): InternalRuleCreate => { const typeSpecificParams = typeSpecificSnakeToCamel(input); const newRuleId = input.rule_id ?? uuidv4(); + + const alertActions = input.actions?.map(transformRuleToAlertAction) ?? []; + const actions = transformToActionFrequency(alertActions, input.throttle); + return { name: input.name, tags: input.tags ?? [], @@ -502,9 +499,7 @@ export const convertCreateAPIToInternalSchema = ( }, schedule: { interval: input.interval ?? '5m' }, enabled: input.enabled ?? defaultEnabled, - actions: input.actions?.map(transformRuleToAlertAction) ?? [], - throttle: transformToAlertThrottle(input.throttle), - notifyWhen: transformToNotifyWhen(input.throttle), + actions, }; }; @@ -651,6 +646,10 @@ export const internalRuleToAPIResponse = ( const isResolvedRule = (obj: unknown): obj is ResolvedSanitizedRule => (obj as ResolvedSanitizedRule).outcome != null; + const alertActions = rule.actions.map(transformAlertToRuleAction); + const throttle = transformFromAlertThrottle(rule); + const actions = transformToActionFrequency(alertActions, throttle); + return { // saved object properties outcome: isResolvedRule(rule) ? rule.outcome : undefined, @@ -672,8 +671,8 @@ export const internalRuleToAPIResponse = ( // Type specific security solution rule params ...typeSpecificCamelToSnake(rule.params), // Actions - throttle: transformFromAlertThrottle(rule), - actions: rule.actions.map(transformAlertToRuleAction), + throttle: undefined, + actions, // Execution summary execution_summary: executionSummary ?? undefined, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts index 9d455e0fad519..0e8d52e4ce84e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts @@ -370,6 +370,7 @@ export const convertAlertSuppressionToCamel = ( ? { groupBy: input.group_by, duration: input.duration, + missingFieldsStrategy: input.missing_fields_strategy, } : undefined; @@ -380,5 +381,6 @@ export const convertAlertSuppressionToSnake = ( ? { group_by: input.groupBy, duration: input.duration, + missing_fields_strategy: input.missingFieldsStrategy, } : undefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index bae75363ff49a..373842fe31860 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -43,7 +43,7 @@ export const ruleOutput = (): RuleResponse => ({ tags: [], to: 'now', type: 'query', - throttle: 'no_actions', + throttle: undefined, threat: getThreatMock(), version: 1, revision: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts index c0cb0cefa234e..c68eab1baec36 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts @@ -40,6 +40,7 @@ const ruleExecutionLogForExecutorsMock = { ruleId: context.ruleId ?? 'some rule id', ruleUuid: context.ruleUuid ?? 'some rule uuid', ruleName: context.ruleName ?? 'Some rule', + ruleRevision: context.ruleRevision ?? 0, ruleType: context.ruleType ?? 'some rule type', spaceId: context.spaceId ?? 'some space id', }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts index a0cbe754d6b3c..a8d61aa7dda84 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts @@ -50,7 +50,7 @@ export const createClientForExecutors = ( const baseLogSuffix = baseCorrelationIds.getLogSuffix(); const baseLogMeta = baseCorrelationIds.getLogMeta(); - const { executionId, ruleId, ruleUuid, ruleName, ruleType, spaceId } = context; + const { executionId, ruleId, ruleUuid, ruleName, ruleRevision, ruleType, spaceId } = context; const client: IRuleExecutionLogForExecutors = { get context() { @@ -140,6 +140,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, @@ -202,6 +203,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, @@ -213,6 +215,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts index cb65b42d50b69..54cad1f72be07 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts @@ -92,6 +92,11 @@ export interface RuleExecutionContext { */ ruleName: string; + /** + * Current revision of the rule being execution (rule.revision) + */ + ruleRevision: number; + /** * Alerting Framework's rule type id of the rule being executed. */ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts index aa1fcf36aba68..ceed2f1d3a739 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts @@ -31,6 +31,7 @@ export interface BaseArgs { ruleId: string; ruleUuid: string; ruleName: string; + ruleRevision: number; ruleType: string; spaceId: string; executionId: string; @@ -83,6 +84,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL execution: { uuid: args.executionId, }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], @@ -126,6 +128,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL status: args.newStatus, status_order: ruleExecutionStatusToNumber(args.newStatus), }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], @@ -167,6 +170,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL uuid: args.executionId, metrics: args.metrics, }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index 10a213a5d8b2f..44fa44ee01892 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -213,10 +213,10 @@ export const getRuleConfigMock = (type: string = 'rule-type'): SanitizedRuleConf consumer: 'sample consumer', notifyWhen: null, producer: 'sample producer', + revision: 0, ruleTypeId: `${type}-id`, ruleTypeName: type, muteAll: false, - revision: 0, snoozeSchedule: [], }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index e204aeb7bc50f..0fbddc2a2236c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -14,6 +14,7 @@ import { RiskScore, RiskScoreMapping, RuleActionArrayCamel, + RuleActionNotifyWhen, RuleActionThrottle, RuleIntervalFrom, RuleIntervalTo, @@ -259,13 +260,6 @@ export interface CompleteRule { ruleConfig: SanitizedRuleConfig; } -export const notifyWhen = t.union([ - t.literal('onActionGroupChange'), - t.literal('onActiveAlert'), - t.literal('onThrottleInterval'), - t.null, -]); - export const allRuleTypes = t.union([ t.literal(SIGNALS_ID), t.literal(EQL_RULE_TYPE_ID), @@ -291,7 +285,7 @@ const internalRuleCreateRequired = t.type({ }); const internalRuleCreateOptional = t.partial({ throttle: t.union([RuleActionThrottle, t.null]), - notifyWhen, + notifyWhen: t.union([RuleActionNotifyWhen, t.null]), }); export const internalRuleCreate = t.intersection([ internalRuleCreateOptional, @@ -310,7 +304,7 @@ const internalRuleUpdateRequired = t.type({ }); const internalRuleUpdateOptional = t.partial({ throttle: t.union([RuleActionThrottle, t.null]), - notifyWhen, + notifyWhen: t.union([RuleActionNotifyWhen, t.null]), }); export const internalRuleUpdate = t.intersection([ internalRuleUpdateOptional, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/es_results.ts index 9593dd5624c22..f852bfff48873 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/es_results.ts @@ -50,6 +50,7 @@ import { ALERT_SEVERITY, ALERT_STATUS, ALERT_STATUS_ACTIVE, + ALERT_URL, ALERT_UUID, ALERT_WORKFLOW_STATUS, EVENT_KIND, @@ -318,6 +319,7 @@ export const sampleAlertDocAADNoSortId = ( }, ], }, + [ALERT_URL]: 'http://example.com/docID', }, fields: { someKey: ['someValue'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts index 15f82e84155cb..399a80f4b9101 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/threshold.ts @@ -13,6 +13,7 @@ import { ALERT_STATUS_ACTIVE, ALERT_WORKFLOW_STATUS, ALERT_RULE_NAMESPACE, + ALERT_URL, ALERT_UUID, ALERT_RULE_TYPE_ID, ALERT_RULE_PRODUCER, @@ -125,6 +126,7 @@ export const sampleThresholdAlert = { interval: '5m', exceptions_list: getListArrayMock(), }) as TypeOfFieldMap), + [ALERT_URL]: 'http://example.com/docID', 'kibana.alert.depth': 1, }, }; 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 a22a95adf307b..990523d6ad5f9 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 @@ -65,7 +65,16 @@ export const securityRuleTypeFieldMap = { /* eslint-disable complexity */ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = - ({ lists, logger, config, ruleDataClient, ruleExecutionLoggerFactory, version, isPreview }) => + ({ + lists, + logger, + config, + publicBaseUrl, + ruleDataClient, + ruleExecutionLoggerFactory, + version, + isPreview, + }) => (type) => { const { alertIgnoreFields: ignoreFields, alertMergeStrategy: mergeStrategy } = config; const persistenceRuleType = createPersistenceRuleTypeWrapper({ ruleDataClient, logger }); @@ -124,6 +133,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ruleId: rule.id, ruleUuid: params.ruleId, ruleName: rule.name, + ruleRevision: rule.revision, ruleType: rule.ruleTypeId, spaceId, }, @@ -318,6 +328,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = spaceId, indicesToQuery: inputIndex, alertTimestampOverride, + publicBaseUrl, ruleExecutionLogger, }); @@ -327,6 +338,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = mergeStrategy, completeRule, spaceId, + publicBaseUrl, indicesToQuery: inputIndex, alertTimestampOverride, }); @@ -370,6 +382,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = alertTimestampOverride, alertWithSuppression, refreshOnIndexingAlerts: refresh, + publicBaseUrl, }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts index 98c5637b59d7a..b3cf8f3ed1675 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ALERT_RULE_CONSUMER } from '@kbn/rule-data-utils'; +import { ALERT_RULE_CONSUMER, ALERT_URL } from '@kbn/rule-data-utils'; import { sampleDocNoSortId, sampleRuleGuid } from '../__mocks__/es_results'; import { @@ -25,6 +25,7 @@ import { } from '../../../../../common/field_maps/field_names'; const SPACE_ID = 'space'; +const PUBLIC_BASE_URL = 'http://testkibanabaseurl.com'; const ruleExecutionLoggerMock = ruleExecutionLogMock.forExecutors.create(); @@ -54,7 +55,8 @@ describe('buildAlert', () => { SPACE_ID, jest.fn(), completeRule.ruleParams.index as string[], - undefined + undefined, + PUBLIC_BASE_URL ); expect(alertGroup.length).toEqual(3); expect(alertGroup[0]).toEqual( @@ -74,6 +76,9 @@ describe('buildAlert', () => { }), }) ); + expect(alertGroup[0]._source[ALERT_URL]).toContain( + 'http://testkibanabaseurl.com/s/space/app/security/alerts/redirect/f2db3574eaf8450e3f4d1cf4f416d70b110b035ae0a7a00026242df07f0a6c90?index=.alerts-security.alerts-space' + ); expect(alertGroup[1]).toEqual( expect.objectContaining({ _source: expect.objectContaining({ @@ -91,6 +96,9 @@ describe('buildAlert', () => { }), }) ); + expect(alertGroup[1]._source[ALERT_URL]).toContain( + 'http://testkibanabaseurl.com/s/space/app/security/alerts/redirect/1dbc416333244efbda833832eb83f13ea5d980a33c2f981ca8d2b35d82a045da?index=.alerts-security.alerts-space' + ); expect(alertGroup[2]).toEqual( expect.objectContaining({ _source: expect.objectContaining({ @@ -128,7 +136,9 @@ describe('buildAlert', () => { }), }) ); - + expect(alertGroup[2]._source[ALERT_URL]).toContain( + 'http://testkibanabaseurl.com/s/space/app/security/alerts/redirect/1b7d06954e74257140f3bf73f139078483f9658fe829fd806cc307fc0388fb23?index=.alerts-security.alerts-space' + ); const groupIds = alertGroup.map((alert) => alert._source[ALERT_GROUP_ID]); for (const groupId of groupIds) { expect(groupId).toEqual(groupIds[0]); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts index f03d185c19996..92c8e4d749a7d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/build_alert_group_from_sequence.ts @@ -5,8 +5,10 @@ * 2.0. */ -import { ALERT_UUID } from '@kbn/rule-data-utils'; +import { ALERT_URL, ALERT_UUID } from '@kbn/rule-data-utils'; +import { getAlertDetailsUrl } from '../../../../../common/utils/alert_detail_path'; +import { DEFAULT_ALERTS_INDEX } from '../../../../../common/constants'; import type { ConfigType } from '../../../../config'; import type { Ancestor, SignalSource, SignalSourceHit } from '../types'; import { buildAlert, buildAncestors, generateAlertId } from '../factories/utils/build_alert'; @@ -43,7 +45,8 @@ export const buildAlertGroupFromSequence = ( spaceId: string | null | undefined, buildReasonMessage: BuildReasonMessage, indicesToQuery: string[], - alertTimestampOverride: Date | undefined + alertTimestampOverride: Date | undefined, + publicBaseUrl?: string ): Array> => { const ancestors: Ancestor[] = sequence.events.flatMap((event) => buildAncestors(event)); if (ancestors.some((ancestor) => ancestor?.rule === completeRule.alertId)) { @@ -65,7 +68,9 @@ export const buildAlertGroupFromSequence = ( buildReasonMessage, indicesToQuery, alertTimestampOverride, - ruleExecutionLogger + ruleExecutionLogger, + 'placeholder-alert-uuid', // This is overriden below + publicBaseUrl ) ); } catch (error) { @@ -96,7 +101,8 @@ export const buildAlertGroupFromSequence = ( spaceId, buildReasonMessage, indicesToQuery, - alertTimestampOverride + alertTimestampOverride, + publicBaseUrl ); const sequenceAlert: WrappedFieldsLatest = { _id: shellAlert[ALERT_UUID], @@ -106,15 +112,26 @@ export const buildAlertGroupFromSequence = ( // Finally, we have the group id from the shell alert so we can convert the BaseFields into EqlBuildingBlocks const wrappedBuildingBlocks = wrappedBaseFields.map( - (block, i): WrappedFieldsLatest => ({ - ...block, - _source: { - ...block._source, - [ALERT_BUILDING_BLOCK_TYPE]: 'default', - [ALERT_GROUP_ID]: shellAlert[ALERT_GROUP_ID], - [ALERT_GROUP_INDEX]: i, - }, - }) + (block, i): WrappedFieldsLatest => { + const alertUrl = getAlertDetailsUrl({ + alertId: block._id, + index: `${DEFAULT_ALERTS_INDEX}-${spaceId}`, + timestamp: block._source['@timestamp'], + basePath: publicBaseUrl, + spaceId, + }); + + return { + ...block, + _source: { + ...block._source, + [ALERT_BUILDING_BLOCK_TYPE]: 'default', + [ALERT_GROUP_ID]: shellAlert[ALERT_GROUP_ID], + [ALERT_GROUP_INDEX]: i, + [ALERT_URL]: alertUrl, + }, + }; + } ); return [...wrappedBuildingBlocks, sequenceAlert]; @@ -126,7 +143,8 @@ export const buildAlertRoot = ( spaceId: string | null | undefined, buildReasonMessage: BuildReasonMessage, indicesToQuery: string[], - alertTimestampOverride: Date | undefined + alertTimestampOverride: Date | undefined, + publicBaseUrl?: string ): EqlShellFieldsLatest => { const mergedAlerts = objectArrayIntersection(wrappedBuildingBlocks.map((alert) => alert._source)); const reason = buildReasonMessage({ @@ -140,14 +158,25 @@ export const buildAlertRoot = ( spaceId, reason, indicesToQuery, + 'placeholder-uuid', // These will be overriden below + publicBaseUrl, // Not necessary now, but when the ID is created ahead of time this can be passed alertTimestampOverride ); const alertId = generateAlertId(doc); + const alertUrl = getAlertDetailsUrl({ + alertId, + index: `${DEFAULT_ALERTS_INDEX}-${spaceId}`, + timestamp: doc['@timestamp'], + basePath: publicBaseUrl, + spaceId, + }); + return { ...mergedAlerts, ...doc, [ALERT_UUID]: alertId, [ALERT_GROUP_ID]: alertId, + [ALERT_URL]: alertUrl, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts index 650cfb21a71c9..6c608da5cb5cb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/wrap_sequences_factory.ts @@ -21,6 +21,7 @@ export const wrapSequencesFactory = completeRule, ignoreFields, mergeStrategy, + publicBaseUrl, spaceId, indicesToQuery, alertTimestampOverride, @@ -32,6 +33,7 @@ export const wrapSequencesFactory = spaceId: string | null | undefined; indicesToQuery: string[]; alertTimestampOverride: Date | undefined; + publicBaseUrl: string | undefined; }): WrapSequences => (sequences, buildReasonMessage) => sequences.reduce( @@ -45,7 +47,8 @@ export const wrapSequencesFactory = spaceId, buildReasonMessage, indicesToQuery, - alertTimestampOverride + alertTimestampOverride, + publicBaseUrl ), ], [] diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts index 2b6702f591ab4..8525c63ce8c87 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -17,6 +17,7 @@ import { ALERT_SEVERITY, ALERT_STATUS, ALERT_STATUS_ACTIVE, + ALERT_URL, ALERT_UUID, ALERT_WORKFLOW_STATUS, EVENT_ACTION, @@ -31,7 +32,7 @@ import { sampleDocNoSortIdWithTimestamp } from '../../__mocks__/es_results'; import { buildAlert, buildParent, buildAncestors, additionalAlertFields } from './build_alert'; import type { Ancestor, SignalSourceHit } from '../../types'; import { getListArrayMock } from '../../../../../../common/detection_engine/schemas/types/lists.mock'; -import { SERVER_APP_ID } from '../../../../../../common/constants'; +import { DEFAULT_ALERTS_INDEX, SERVER_APP_ID } from '../../../../../../common/constants'; import { EVENT_DATASET } from '../../../../../../common/cti/constants'; import { ALERT_ANCESTORS, @@ -48,6 +49,9 @@ type SignalDoc = SignalSourceHit & { }; const SPACE_ID = 'space'; +const reason = 'alert reasonable reason'; +const publicBaseUrl = 'testKibanaBasePath.com'; +const alertUuid = 'test-uuid'; describe('buildAlert', () => { beforeEach(() => { @@ -58,7 +62,6 @@ describe('buildAlert', () => { const doc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); delete doc._source.event; const completeRule = getCompleteRuleMock(getQueryRuleParams()); - const reason = 'alert reasonable reason'; const alert = { ...buildAlert( [doc], @@ -66,11 +69,14 @@ describe('buildAlert', () => { SPACE_ID, reason, completeRule.ruleParams.index as string[], + alertUuid, + publicBaseUrl, undefined ), ...additionalAlertFields(doc), }; const timestamp = alert[TIMESTAMP]; + const expectedAlertUrl = `${publicBaseUrl}/s/${SPACE_ID}/app/security/alerts/redirect/${alertUuid}?index=${DEFAULT_ALERTS_INDEX}-${SPACE_ID}×tamp=${timestamp}`; const expected = { [TIMESTAMP]: timestamp, [EVENT_KIND]: 'signal', @@ -222,6 +228,8 @@ describe('buildAlert', () => { timeline_title: 'some-timeline-title', }), [ALERT_DEPTH]: 1, + [ALERT_URL]: expectedAlertUrl, + [ALERT_UUID]: alertUuid, }; expect(alert).toEqual(expected); }); @@ -239,7 +247,6 @@ describe('buildAlert', () => { }, }; const completeRule = getCompleteRuleMock(getQueryRuleParams()); - const reason = 'alert reasonable reason'; const alert = { ...buildAlert( [doc], @@ -247,12 +254,14 @@ describe('buildAlert', () => { SPACE_ID, reason, completeRule.ruleParams.index as string[], + alertUuid, + publicBaseUrl, undefined ), ...additionalAlertFields(doc), }; const timestamp = alert[TIMESTAMP]; - + const expectedAlertUrl = `${publicBaseUrl}/s/${SPACE_ID}/app/security/alerts/redirect/${alertUuid}?index=${DEFAULT_ALERTS_INDEX}-${SPACE_ID}×tamp=${timestamp}`; const expected = { [TIMESTAMP]: timestamp, [EVENT_KIND]: 'signal', @@ -410,6 +419,8 @@ describe('buildAlert', () => { timeline_title: 'some-timeline-title', }), [ALERT_DEPTH]: 1, + [ALERT_URL]: expectedAlertUrl, + [ALERT_UUID]: alertUuid, }; expect(alert).toEqual(expected); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index d206fa06704f0..5d53380a736f5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -34,6 +34,8 @@ import { ALERT_SEVERITY, ALERT_STATUS, ALERT_STATUS_ACTIVE, + ALERT_URL, + ALERT_UUID, ALERT_WORKFLOW_STATUS, EVENT_KIND, SPACE_IDS, @@ -43,6 +45,7 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import { createHash } from 'crypto'; +import { getAlertDetailsUrl } from '../../../../../../common/utils/alert_detail_path'; import type { BaseSignalHit, SimpleHit } from '../../types'; import type { ThresholdResult } from '../../threshold/types'; import { @@ -51,7 +54,7 @@ import { isWrappedDetectionAlert, isWrappedSignalHit, } from '../../utils/utils'; -import { SERVER_APP_ID } from '../../../../../../common/constants'; +import { DEFAULT_ALERTS_INDEX, SERVER_APP_ID } from '../../../../../../common/constants'; import type { SearchTypes } from '../../../../telemetry/types'; import { ALERT_ANCESTORS, @@ -137,6 +140,8 @@ export const buildAlert = ( spaceId: string | null | undefined, reason: string, indicesToQuery: string[], + alertUuid: string, + publicBaseUrl: string | undefined, alertTimestampOverride: Date | undefined, overrides?: { nameOverride: string; @@ -180,8 +185,18 @@ export const buildAlert = ( primaryTimestamp: TIMESTAMP, }); + const timestamp = alertTimestampOverride?.toISOString() ?? new Date().toISOString(); + + const alertUrl = getAlertDetailsUrl({ + alertId: alertUuid, + index: `${DEFAULT_ALERTS_INDEX}-${spaceId}`, + timestamp, + basePath: publicBaseUrl, + spaceId, + }); + return { - [TIMESTAMP]: alertTimestampOverride?.toISOString() ?? new Date().toISOString(), + [TIMESTAMP]: timestamp, [SPACE_IDS]: spaceId != null ? [spaceId] : [], [EVENT_KIND]: 'signal', [ALERT_ORIGINAL_TIME]: originalTime?.toISOString(), @@ -229,6 +244,8 @@ export const buildAlert = ( [ALERT_RULE_UPDATED_BY]: updatedBy ?? '', [ALERT_RULE_UUID]: completeRule.alertId, [ALERT_RULE_VERSION]: params.version, + [ALERT_URL]: alertUrl, + [ALERT_UUID]: alertUuid, ...flattenWithPrefix(ALERT_RULE_META, params.meta), // These fields don't exist in the mappings, but leaving here for now to limit changes to the alert building logic 'kibana.alert.rule.risk_score': params.riskScore, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts index cd7351362db0b..1963837d64bc7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts @@ -55,7 +55,9 @@ export const buildBulkBody = ( buildReasonMessage: BuildReasonMessage, indicesToQuery: string[], alertTimestampOverride: Date | undefined, - ruleExecutionLogger: IRuleExecutionLogForExecutors + ruleExecutionLogger: IRuleExecutionLogForExecutors, + alertUuid: string, + publicBaseUrl?: string ): BaseFieldsLatest => { const mergedDoc = getMergeStrategy(mergeStrategy)({ doc, ignoreFields }); @@ -111,6 +113,8 @@ export const buildBulkBody = ( spaceId, reason, indicesToQuery, + alertUuid, + publicBaseUrl, alertTimestampOverride, overrides ), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts index aae7501bf3798..a5b56303c603d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts @@ -6,8 +6,6 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { ALERT_UUID } from '@kbn/rule-data-utils'; - import type { ConfigType } from '../../../../config'; import type { SignalSource, SimpleHit } from '../types'; import type { CompleteRule, RuleParams } from '../../rule_schema'; @@ -28,6 +26,7 @@ export const wrapHitsFactory = spaceId, indicesToQuery, alertTimestampOverride, + publicBaseUrl, ruleExecutionLogger, }: { completeRule: CompleteRule; @@ -36,6 +35,7 @@ export const wrapHitsFactory = spaceId: string | null | undefined; indicesToQuery: string[]; alertTimestampOverride: Date | undefined; + publicBaseUrl: string | undefined; ruleExecutionLogger: IRuleExecutionLogForExecutors; }) => ( @@ -49,23 +49,27 @@ export const wrapHitsFactory = String(event._version), `${spaceId}:${completeRule.alertId}` ); + + const baseAlert = buildBulkBody( + spaceId, + completeRule, + event as SimpleHit, + mergeStrategy, + ignoreFields, + true, + buildReasonMessage, + indicesToQuery, + alertTimestampOverride, + ruleExecutionLogger, + id, + publicBaseUrl + ); + return { _id: id, _index: '', _source: { - ...buildBulkBody( - spaceId, - completeRule, - event as SimpleHit, - mergeStrategy, - ignoreFields, - true, - buildReasonMessage, - indicesToQuery, - alertTimestampOverride, - ruleExecutionLogger - ), - [ALERT_UUID]: id, + ...baseAlert, }, }; }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts index c4525de1b00b3..41c5420c748e9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/create_new_terms_alert_type.ts @@ -107,6 +107,7 @@ export const createNewTermsAlertType = ( exceptionFilter, unprocessedExceptions, alertTimestampOverride, + publicBaseUrl, }, services, params, @@ -300,6 +301,7 @@ export const createNewTermsAlertType = ( indicesToQuery: inputIndex, alertTimestampOverride, ruleExecutionLogger, + publicBaseUrl, }); const bulkCreateResult = await bulkCreate( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts index 69d0b90b45c29..67a3c69af9850 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ALERT_UUID } from '@kbn/rule-data-utils'; +import { ALERT_URL, ALERT_UUID } from '@kbn/rule-data-utils'; import { ALERT_NEW_TERMS } from '../../../../../common/field_maps/field_names'; import { getCompleteRuleMock, getNewTermsRuleParams } from '../../rule_schema/mocks'; import { ruleExecutionLogMock } from '../../rule_monitoring/mocks'; @@ -15,6 +15,7 @@ import { wrapNewTermsAlerts } from './wrap_new_terms_alerts'; const ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create(); const docId = 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71'; +const publicBaseUrl = 'http://somekibanabaseurl.com'; describe('wrapNewTermsAlerts', () => { test('should create an alert with the correct _id from a document', () => { const doc = sampleDocNoSortIdWithTimestamp(docId); @@ -27,11 +28,15 @@ describe('wrapNewTermsAlerts', () => { indicesToQuery: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], alertTimestampOverride: undefined, ruleExecutionLogger, + publicBaseUrl, }); expect(alerts[0]._id).toEqual('a36d9fe6fe4b2f65058fb1a487733275f811af58'); expect(alerts[0]._source[ALERT_UUID]).toEqual('a36d9fe6fe4b2f65058fb1a487733275f811af58'); expect(alerts[0]._source[ALERT_NEW_TERMS]).toEqual(['127.0.0.1']); + expect(alerts[0]._source[ALERT_URL]).toContain( + 'http://somekibanabaseurl.com/app/security/alerts/redirect/a36d9fe6fe4b2f65058fb1a487733275f811af58?index=.alerts-security.alerts-default' + ); }); test('should create an alert with a different _id if the space is different', () => { @@ -45,11 +50,15 @@ describe('wrapNewTermsAlerts', () => { indicesToQuery: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], alertTimestampOverride: undefined, ruleExecutionLogger, + publicBaseUrl, }); expect(alerts[0]._id).toEqual('f7877a31b1cc83373dbc9ba5939ebfab1db66545'); expect(alerts[0]._source[ALERT_UUID]).toEqual('f7877a31b1cc83373dbc9ba5939ebfab1db66545'); expect(alerts[0]._source[ALERT_NEW_TERMS]).toEqual(['127.0.0.1']); + expect(alerts[0]._source[ALERT_URL]).toContain( + 'http://somekibanabaseurl.com/s/otherSpace/app/security/alerts/redirect/f7877a31b1cc83373dbc9ba5939ebfab1db66545?index=.alerts-security.alerts-otherSpace' + ); }); test('should create an alert with a different _id if the newTerms array is different', () => { @@ -63,11 +72,15 @@ describe('wrapNewTermsAlerts', () => { indicesToQuery: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], alertTimestampOverride: undefined, ruleExecutionLogger, + publicBaseUrl, }); expect(alerts[0]._id).toEqual('75e5a507a4bc48bcd983820c7fd2d9621ff4e2ea'); expect(alerts[0]._source[ALERT_UUID]).toEqual('75e5a507a4bc48bcd983820c7fd2d9621ff4e2ea'); expect(alerts[0]._source[ALERT_NEW_TERMS]).toEqual(['127.0.0.2']); + expect(alerts[0]._source[ALERT_URL]).toContain( + 'http://somekibanabaseurl.com/s/otherSpace/app/security/alerts/redirect/75e5a507a4bc48bcd983820c7fd2d9621ff4e2ea?index=.alerts-security.alerts-otherSpace' + ); }); test('should create an alert with a different _id if the newTerms array contains multiple terms', () => { @@ -81,10 +94,14 @@ describe('wrapNewTermsAlerts', () => { indicesToQuery: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], alertTimestampOverride: undefined, ruleExecutionLogger, + publicBaseUrl, }); expect(alerts[0]._id).toEqual('86a216cfa4884767d9bb26d2b8db911cb4aa85ce'); expect(alerts[0]._source[ALERT_UUID]).toEqual('86a216cfa4884767d9bb26d2b8db911cb4aa85ce'); expect(alerts[0]._source[ALERT_NEW_TERMS]).toEqual(['127.0.0.1', '127.0.0.2']); + expect(alerts[0]._source[ALERT_URL]).toContain( + 'http://somekibanabaseurl.com/s/otherSpace/app/security/alerts/redirect/86a216cfa4884767d9bb26d2b8db911cb4aa85ce?index=.alerts-security.alerts-otherSpace' + ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts index 424c52273c30a..2a373edf7de6e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/new_terms/wrap_new_terms_alerts.ts @@ -7,7 +7,6 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import objectHash from 'object-hash'; -import { ALERT_UUID } from '@kbn/rule-data-utils'; import type { BaseFieldsLatest, NewTermsFieldsLatest, @@ -34,6 +33,7 @@ export const wrapNewTermsAlerts = ({ indicesToQuery, alertTimestampOverride, ruleExecutionLogger, + publicBaseUrl, }: { eventsAndTerms: EventsAndTerms[]; spaceId: string | null | undefined; @@ -42,6 +42,7 @@ export const wrapNewTermsAlerts = ({ indicesToQuery: string[]; alertTimestampOverride: Date | undefined; ruleExecutionLogger: IRuleExecutionLogForExecutors; + publicBaseUrl: string | undefined; }): Array> => { return eventsAndTerms.map((eventAndTerms) => { const id = objectHash([ @@ -61,15 +62,17 @@ export const wrapNewTermsAlerts = ({ buildReasonMessageForNewTermsAlert, indicesToQuery, alertTimestampOverride, - ruleExecutionLogger + ruleExecutionLogger, + id, + publicBaseUrl ); + return { _id: id, _index: '', _source: { ...baseAlert, [ALERT_NEW_TERMS]: eventAndTerms.newTerms, - [ALERT_UUID]: id, }, }; }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap index a46533db938f3..0952989be1040 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/build_group_by_field_aggregation.test.ts.snap @@ -35,7 +35,6 @@ Object { "host.name": Object { "terms": Object { "field": "host.name", - "missing_bucket": true, }, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap index 05674205e1e38..5ba53cf006db2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/__snapshots__/group_and_bulk_create.test.ts.snap @@ -28,6 +28,7 @@ Array [ }, }, ], + "must_not": Array [], }, }, Object { @@ -53,6 +54,7 @@ Array [ }, }, ], + "must_not": Array [], }, }, ], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.test.ts index 010a2ab50ffab..1d30138e4a886 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.test.ts @@ -16,7 +16,40 @@ describe('build_group_by_field_aggregation', () => { groupByFields, maxSignals, aggregatableTimestampField: 'kibana.combined_timestamp', + missingBucket: false, }); expect(agg).toMatchSnapshot(); }); + + it('should include missing bucket configuration for aggregation if configured', () => { + const groupByFields = ['host.name']; + const maxSignals = 100; + + const agg = buildGroupByFieldAggregation({ + groupByFields, + maxSignals, + aggregatableTimestampField: 'kibana.combined_timestamp', + missingBucket: true, + }); + expect(agg.eventGroups.composite.sources[0]['host.name'].terms).toEqual({ + field: 'host.name', + missing_bucket: true, + missing_order: 'last', + }); + }); + + it('should not include missing bucket configuration for aggregation if not configured', () => { + const groupByFields = ['host.name']; + const maxSignals = 100; + + const agg = buildGroupByFieldAggregation({ + groupByFields, + maxSignals, + aggregatableTimestampField: 'kibana.combined_timestamp', + missingBucket: false, + }); + expect(agg.eventGroups.composite.sources[0]['host.name'].terms).toEqual({ + field: 'host.name', + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.ts index af0821de31146..4f643e1b9a0c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/build_group_by_field_aggregation.ts @@ -5,16 +5,30 @@ * 2.0. */ +import type { ESSearchResponse } from '@kbn/es-types'; +import type { SignalSource } from '../../types'; + +export type EventGroupingMultiBucketAggregationResult = ESSearchResponse< + SignalSource, + { + body: { + aggregations: ReturnType; + }; + } +>; + interface GetGroupByFieldAggregationArgs { groupByFields: string[]; maxSignals: number; aggregatableTimestampField: string; + missingBucket: boolean; } export const buildGroupByFieldAggregation = ({ groupByFields, maxSignals, aggregatableTimestampField, + missingBucket, }: GetGroupByFieldAggregationArgs) => ({ eventGroups: { composite: { @@ -22,7 +36,9 @@ export const buildGroupByFieldAggregation = ({ [field]: { terms: { field, - missing_bucket: true, + ...(missingBucket + ? { missing_bucket: missingBucket, missing_order: 'last' as const } + : {}), }, }, })), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/bulk_create_unsuppressed_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/bulk_create_unsuppressed_alerts.ts new file mode 100644 index 0000000000000..2ebe36c099bc3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/bulk_create_unsuppressed_alerts.ts @@ -0,0 +1,90 @@ +/* + * 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 { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; + +import type { RunOpts, SearchAfterAndBulkCreateReturnType, RuleServices } from '../../types'; +import type { UnifiedQueryRuleParams } from '../../../rule_schema'; + +import type { BuildReasonMessage } from '../../utils/reason_formatters'; +import { searchAfterAndBulkCreate } from '../../utils/search_after_bulk_create'; +import type { ITelemetryEventsSender } from '../../../../telemetry/sender'; + +type BulkCreateUnsuppressedAlerts = (params: { + runOpts: RunOpts; + size: number; + groupByFields: string[]; + buildReasonMessage: BuildReasonMessage; + services: RuleServices; + filter: QueryDslQueryContainer; + eventsTelemetry: ITelemetryEventsSender | undefined; +}) => Promise; + +/** + * searches and bulk creates unsuppressed alerts if any exists + * @param param0 + * @returns + */ +export const bulkCreateUnsuppressedAlerts: BulkCreateUnsuppressedAlerts = async ({ + size, + groupByFields, + buildReasonMessage, + runOpts, + filter, + services, + eventsTelemetry, +}) => { + const bulkCreatedResult = await searchAfterAndBulkCreate({ + tuple: { ...runOpts.tuple, maxSignals: size }, + exceptionsList: runOpts.unprocessedExceptions, + services, + listClient: runOpts.listClient, + ruleExecutionLogger: runOpts.ruleExecutionLogger, + eventsTelemetry, + inputIndexPattern: runOpts.inputIndex, + pageSize: runOpts.searchAfterSize, + filter, + buildReasonMessage, + bulkCreate: runOpts.bulkCreate, + wrapHits: runOpts.wrapHits, + runtimeMappings: runOpts.runtimeMappings, + primaryTimestamp: runOpts.primaryTimestamp, + secondaryTimestamp: runOpts.secondaryTimestamp, + additionalFilters: buildMissingFieldsFilter(groupByFields), + }); + + return bulkCreatedResult; +}; + +/** + * builds filter that returns only docs with at least one missing field from a list of groupByFields fields + * @param groupByFields + * @returns - Array<{@link QueryDslQueryContainer}> + */ +const buildMissingFieldsFilter = (groupByFields: string[]): QueryDslQueryContainer[] => { + if (groupByFields.length === 0) { + return []; + } + + return [ + { + bool: { + should: groupByFields.map((field) => ({ + bool: { + must_not: [ + { + exists: { + field, + }, + }, + ], + }, + })), + }, + }, + ]; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts index 99f1901aad9f8..c8d07a4c379a6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts @@ -9,24 +9,28 @@ import type moment from 'moment'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; -import type { ESSearchResponse } from '@kbn/es-types'; - import { withSecuritySpan } from '../../../../../utils/with_security_span'; import { buildTimeRangeFilter } from '../../utils/build_events_query'; -import type { - RuleServices, - RunOpts, - SearchAfterAndBulkCreateReturnType, - SignalSource, -} from '../../types'; -import { addToSearchAfterReturn, getUnprocessedExceptionsWarnings } from '../../utils/utils'; -import type { SuppressionBuckets } from './wrap_suppressed_alerts'; +import type { RuleServices, RunOpts, SearchAfterAndBulkCreateReturnType } from '../../types'; +import { + addToSearchAfterReturn, + getUnprocessedExceptionsWarnings, + mergeReturns, +} from '../../utils/utils'; +import type { SuppressionBucket } from './wrap_suppressed_alerts'; import { wrapSuppressedAlerts } from './wrap_suppressed_alerts'; import { buildGroupByFieldAggregation } from './build_group_by_field_aggregation'; +import type { EventGroupingMultiBucketAggregationResult } from './build_group_by_field_aggregation'; import { singleSearchAfter } from '../../utils/single_search_after'; import { bulkCreateWithSuppression } from './bulk_create_with_suppression'; import type { UnifiedQueryRuleParams } from '../../../rule_schema'; import type { BuildReasonMessage } from '../../utils/reason_formatters'; +import { + AlertSuppressionMissingFieldsStrategy, + DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY, +} from '../../../../../../common/detection_engine/rule_schema'; +import { bulkCreateUnsuppressedAlerts } from './bulk_create_unsuppressed_alerts'; +import type { ITelemetryEventsSender } from '../../../../telemetry/sender'; export interface BucketHistory { key: Record; @@ -41,6 +45,7 @@ export interface GroupAndBulkCreateParams { buildReasonMessage: BuildReasonMessage; bucketHistory?: BucketHistory[]; groupByFields: string[]; + eventsTelemetry: ITelemetryEventsSender | undefined; } export interface GroupAndBulkCreateReturnType extends SearchAfterAndBulkCreateReturnType { @@ -49,15 +54,6 @@ export interface GroupAndBulkCreateReturnType extends SearchAfterAndBulkCreateRe }; } -type EventGroupingMultiBucketAggregationResult = ESSearchResponse< - SignalSource, - { - body: { - aggregations: ReturnType; - }; - } ->; - /** * Builds a filter that excludes documents from existing buckets. */ @@ -80,22 +76,21 @@ export const buildBucketHistoryFilter = ({ bool: { must_not: bucketHistory.map((bucket) => ({ bool: { + must_not: Object.entries(bucket.key) + .filter(([_, value]) => value == null) + .map(([field, _]) => ({ + exists: { + field, + }, + })), filter: [ - ...Object.entries(bucket.key).map(([field, value]) => - value != null - ? { - term: { - [field]: value, - }, - } - : { - must_not: { - exists: { - field, - }, - }, - } - ), + ...Object.entries(bucket.key) + .filter(([_, value]) => value != null) + .map(([field, value]) => ({ + term: { + [field]: value, + }, + })), buildTimeRangeFilter({ to: bucket.endDate, from: from.toISOString(), @@ -128,6 +123,7 @@ export const groupAndBulkCreate = async ({ buildReasonMessage, bucketHistory, groupByFields, + eventsTelemetry, }: GroupAndBulkCreateParams): Promise => { return withSecuritySpan('groupAndBulkCreate', async () => { const tuple = runOpts.tuple; @@ -137,7 +133,7 @@ export const groupAndBulkCreate = async ({ fromDate: tuple.from.toDate(), }); - const toReturn: GroupAndBulkCreateReturnType = { + let toReturn: GroupAndBulkCreateReturnType = { success: true, warning: false, searchAfterTimes: [], @@ -170,13 +166,20 @@ export const groupAndBulkCreate = async ({ from: tuple.from, }); + // if we do not suppress alerts for docs with missing values, we will create aggregation for null missing buckets + const suppressOnMissingFields = + (runOpts.completeRule.ruleParams.alertSuppression?.missingFieldsStrategy ?? + DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY) === + AlertSuppressionMissingFieldsStrategy.Suppress; + const groupingAggregation = buildGroupByFieldAggregation({ groupByFields, maxSignals: tuple.maxSignals, aggregatableTimestampField: runOpts.aggregatableTimestampField, + missingBucket: suppressOnMissingFields, }); - const { searchResult, searchDuration, searchErrors } = await singleSearchAfter({ + const eventsSearchParams = { aggregations: groupingAggregation, searchAfterSortIds: undefined, index: runOpts.inputIndex, @@ -190,7 +193,11 @@ export const groupAndBulkCreate = async ({ secondaryTimestamp: runOpts.secondaryTimestamp, runtimeMappings: runOpts.runtimeMappings, additionalFilters: bucketHistoryFilter, - }); + }; + const { searchResult, searchDuration, searchErrors } = await singleSearchAfter( + eventsSearchParams + ); + toReturn.searchAfterTimes.push(searchDuration); toReturn.errors.push(...searchErrors); @@ -202,11 +209,26 @@ export const groupAndBulkCreate = async ({ const buckets = eventsByGroupResponseWithAggs.aggregations.eventGroups.buckets; + // we can create only as many unsuppressed alerts, as total number of alerts(suppressed and unsuppressed) does not exceeds maxSignals + const maxUnsuppressedCount = tuple.maxSignals - buckets.length; + if (suppressOnMissingFields === false && maxUnsuppressedCount > 0) { + const unsuppressedResult = await bulkCreateUnsuppressedAlerts({ + groupByFields, + size: maxUnsuppressedCount, + runOpts, + buildReasonMessage, + eventsTelemetry, + filter, + services, + }); + toReturn = { ...toReturn, ...mergeReturns([toReturn, unsuppressedResult]) }; + } + if (buckets.length === 0) { return toReturn; } - const suppressionBuckets: SuppressionBuckets[] = buckets.map((bucket) => ({ + const suppressionBuckets: SuppressionBucket[] = buckets.map((bucket) => ({ event: bucket.topHits.hits.hits[0], count: bucket.doc_count, start: bucket.min_timestamp.value_as_string @@ -224,6 +246,7 @@ export const groupAndBulkCreate = async ({ completeRule: runOpts.completeRule, mergeStrategy: runOpts.mergeStrategy, indicesToQuery: runOpts.inputIndex, + publicBaseUrl: runOpts.publicBaseUrl, buildReasonMessage, alertTimestampOverride: runOpts.alertTimestampOverride, ruleExecutionLogger: runOpts.ruleExecutionLogger, @@ -242,6 +265,7 @@ export const groupAndBulkCreate = async ({ alertTimestampOverride: runOpts.alertTimestampOverride, }); addToSearchAfterReturn({ current: toReturn, next: bulkCreateResult }); + runOpts.ruleExecutionLogger.debug(`created ${bulkCreateResult.createdItemsCount} signals`); } else { const bulkCreateResult = await runOpts.bulkCreate(wrappedAlerts); addToSearchAfterReturn({ current: toReturn, next: bulkCreateResult }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts index 42fe0954a1847..f3a294e77c16d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/wrap_suppressed_alerts.ts @@ -8,7 +8,6 @@ import objectHash from 'object-hash'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import { - ALERT_UUID, ALERT_SUPPRESSION_TERMS, ALERT_SUPPRESSION_DOCS_COUNT, ALERT_SUPPRESSION_END, @@ -27,7 +26,7 @@ import type { SignalSource } from '../../types'; import { buildBulkBody } from '../../factories/utils/build_bulk_body'; import type { BuildReasonMessage } from '../../utils/reason_formatters'; -export interface SuppressionBuckets { +export interface SuppressionBucket { event: estypes.SearchHit; count: number; start: Date; @@ -56,8 +55,9 @@ export const wrapSuppressedAlerts = ({ buildReasonMessage, alertTimestampOverride, ruleExecutionLogger, + publicBaseUrl, }: { - suppressionBuckets: SuppressionBuckets[]; + suppressionBuckets: SuppressionBucket[]; spaceId: string; completeRule: CompleteRule; mergeStrategy: ConfigType['alertMergeStrategy']; @@ -65,6 +65,7 @@ export const wrapSuppressedAlerts = ({ buildReasonMessage: BuildReasonMessage; alertTimestampOverride: Date | undefined; ruleExecutionLogger: IRuleExecutionLogForExecutors; + publicBaseUrl: string | undefined; }): Array> => { return suppressionBuckets.map((bucket) => { const id = objectHash([ @@ -91,8 +92,11 @@ export const wrapSuppressedAlerts = ({ buildReasonMessage, indicesToQuery, alertTimestampOverride, - ruleExecutionLogger + ruleExecutionLogger, + id, + publicBaseUrl ); + return { _id: id, _index: '', @@ -102,7 +106,6 @@ export const wrapSuppressedAlerts = ({ [ALERT_SUPPRESSION_START]: bucket.start, [ALERT_SUPPRESSION_END]: bucket.end, [ALERT_SUPPRESSION_DOCS_COUNT]: bucket.count - 1, - [ALERT_UUID]: id, [ALERT_INSTANCE_ID]: instanceId, }, }; 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 d3f8e8b42b44e..13c7e9e53df54 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 @@ -34,6 +34,7 @@ jest.mock('../utils/get_list_client', () => ({ describe('Custom Query Alerts', () => { const mocks = createRuleTypeMocks(); const licensing = licensingMock.createSetup(); + const publicBaseUrl = 'http://somekibanabaseurl.com'; const { dependencies, executor, services } = mocks; const { alerting, lists, logger, ruleDataClient } = dependencies; @@ -44,6 +45,7 @@ describe('Custom Query Alerts', () => { ruleDataClient, ruleExecutionLoggerFactory: () => Promise.resolve(ruleExecutionLogMock.forExecutors.create()), version: '8.3', + publicBaseUrl, }); const eventsTelemetry = createMockTelemetryEventsSender(true); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts index 764103151bf24..8e4314d01b65b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts @@ -74,6 +74,7 @@ export const queryExecutor = async ({ buildReasonMessage: buildReasonMessageForQueryAlert, bucketHistory, groupByFields: ruleParams.alertSuppression.groupBy, + eventsTelemetry, }) : { ...(await searchAfterAndBulkCreate({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 0dee5eba79cc4..42e33efe04ca4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -100,6 +100,7 @@ export interface RunOpts { alertTimestampOverride: Date | undefined; alertWithSuppression: SuppressedAlertService; refreshOnIndexingAlerts: RefreshTypes; + publicBaseUrl: string | undefined; } export type SecurityAlertType< @@ -129,6 +130,7 @@ export interface CreateSecurityRuleTypeWrapperProps { lists: SetupPlugins['lists']; logger: Logger; config: ConfigType; + publicBaseUrl: string | undefined; ruleDataClient: IRuleDataClient; ruleExecutionLoggerFactory: IRuleExecutionLogService['createClientForExecutors']; version: string; @@ -372,6 +374,7 @@ export interface SearchAfterAndBulkCreateParams { runtimeMappings: estypes.MappingRuntimeFields | undefined; primaryTimestamp: string; secondaryTimestamp?: string; + additionalFilters?: estypes.QueryDslQueryContainer[]; } export interface SearchAfterAndBulkCreateReturnType { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts index 43cd37aca396a..3d3dc8872f261 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts @@ -38,6 +38,7 @@ import { ALERT_SEVERITY, ALERT_STATUS, ALERT_STATUS_ACTIVE, + ALERT_URL, ALERT_UUID, ALERT_WORKFLOW_STATUS, EVENT_KIND, @@ -158,6 +159,7 @@ export const createAlert = ( [ALERT_RULE_UUID]: '2e051244-b3c6-4779-a241-e1b4f0beceb9', [ALERT_RULE_VERSION]: 1, [ALERT_UUID]: someUuid, + [ALERT_URL]: `http://kibanaurl.com/app/security/alerts/redirect/${someUuid}?index=myFakeSignalIndex×tamp=2020-04-20T21:27:45`, 'kibana.alert.rule.risk_score': 50, 'kibana.alert.rule.severity': 'high', 'kibana.alert.rule.building_block_type': undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts index 8e71a4dce49aa..31c1e38b08f91 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.test.ts @@ -112,6 +112,7 @@ describe('searchAfterAndBulkCreate', () => { indicesToQuery: inputIndexPattern, alertTimestampOverride: undefined, ruleExecutionLogger, + publicBaseUrl: 'http://testkibanabaseurl.com', }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts index 78bead8d70ff6..fba2b3faba93a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create.ts @@ -44,6 +44,7 @@ export const searchAfterAndBulkCreate = async ({ runtimeMappings, primaryTimestamp, secondaryTimestamp, + additionalFilters, }: SearchAfterAndBulkCreateParams): Promise => { return withSecuritySpan('searchAfterAndBulkCreate', async () => { let toReturn = createSearchAfterReturnType(); @@ -80,6 +81,7 @@ export const searchAfterAndBulkCreate = async ({ secondaryTimestamp, trackTotalHits, sortOrder, + additionalFilters, }); mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResult]); toReturn = mergeReturns([ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts index eea89c880e04f..b667791bfd28a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/single_search_after.ts @@ -18,7 +18,7 @@ import type { TimestampOverride } from '../../../../../common/detection_engine/r import { withSecuritySpan } from '../../../../utils/with_security_span'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; -interface SingleSearchAfterParams { +export interface SingleSearchAfterParams { aggregations?: Record; searchAfterSortIds: estypes.SortResults | undefined; index: string[]; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts index 9e5c383e825d1..631b67cd49601 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts @@ -26,6 +26,9 @@ describe('Security Telemetry filters', () => { 'event.provider': true, 'event.type': true, 'powershell.file.script_block_text': true, + 'kubernetes.pod.uid': true, + 'kubernetes.pod.name': true, + 'kubernetes.pod.ip': true, package_version: true, }; @@ -177,5 +180,23 @@ describe('Security Telemetry filters', () => { package_version: '3.4.1', }); }); + + it('copies over kubernetes fields', () => { + const event = { + not_event: 'much data, much wow', + 'event.id': '36857486973080746231799376445175633955031786243637182487', + 'event.ingested': 'May 17, 2022 @ 00:22:07.000', + 'kubernetes.pod.uid': '059a3767-7492-4fb5-92d4-93f458ddab44', + 'kubernetes.pod.name': 'kube-dns-6f4fd4zzz-7z7xj', + 'kubernetes.pod.ip': '10-245-0-5', + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + 'event.id': '36857486973080746231799376445175633955031786243637182487', + 'event.ingested': 'May 17, 2022 @ 00:22:07.000', + 'kubernetes.pod.uid': '059a3767-7492-4fb5-92d4-93f458ddab44', + 'kubernetes.pod.name': 'kube-dns-6f4fd4zzz-7z7xj', + 'kubernetes.pod.ip': '10-245-0-5', + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts index 225206cca4b0d..42235cae66574 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts @@ -215,6 +215,9 @@ export const prebuiltRuleAllowlistFields: AllowlistFields = { target_resources: true, }, }, + properties: { + category: true, + }, signinlogs: { properties: { app_display_name: true, @@ -253,6 +256,85 @@ export const prebuiltRuleAllowlistFields: AllowlistFields = { setting: { name: true, }, + application: { + name: true, + }, + old_value: true, + role: { + name: true, + }, + }, + event: { + type: true, + }, + }, + // kubernetes + kubernetes: { + audit: { + annotations: true, + verb: true, + user: { + groups: true, + }, + impersonatedUser: { + groups: true, + }, + objectRef: { + name: true, + namespace: true, + resource: true, + subresource: true, + }, + requestObject: { + spec: { + containers: { + image: true, + securityContext: { + allowPrivilegeEscalation: true, + capabilities: { + add: true, + }, + privileged: true, + procMount: true, + runAsGroup: true, + runAsUser: true, + }, + }, + hostIPC: true, + hostNetwork: true, + hostPID: true, + securityContext: { + runAsGroup: true, + runAsUser: true, + }, + serviceAccountName: true, + type: true, + volumes: { + hostPath: { + path: true, + }, + }, + }, + }, + requestURI: true, + responseObject: { + roleRef: { + kind: true, + resourceName: true, + }, + rules: true, + spec: { + containers: { + securityContext: { + allowPrivilegeEscalation: true, + }, + }, + }, + }, + responseStatus: { + code: true, + }, + userAgent: true, }, }, // office 360 @@ -275,6 +357,11 @@ export const prebuiltRuleAllowlistFields: AllowlistFields = { Enabled: true, ForwardAsAttachmentTo: true, ForwardTo: true, + ModifiedProperties: { + Role_DisplayName: { + NewValue: true, + }, + }, RedirectTo: true, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts index 4e3db8c657e04..e01eb21cbe68c 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts @@ -25,6 +25,7 @@ import { tlog, setIsElasticCloudDeployment, createTaskMetric, + processK8sUsernames, } from './helpers'; import type { ESClusterInfo, ESLicense, ExceptionListItem } from './types'; import type { PolicyConfig, PolicyData } from '../../../common/endpoint/types'; @@ -963,6 +964,7 @@ describe.skip('test create task metrics', () => { passed: true, }); }); + test('can succeed when error given', async () => { const stubTaskName = 'test'; const stubPassed = false; @@ -982,3 +984,95 @@ describe.skip('test create task metrics', () => { }); }); }); + +describe('Pii is removed from a kubernetes prebuilt rule alert', () => { + test('a document without the sensitive values is ignored', async () => { + const clusterUuid = '7c5f1d31-ce87-4090-8dbf-decaac0261ca'; + const testDocument = { + kubernetes: { + audit: {}, + pod: { + uid: 'test', + name: 'test', + ip: 'test', + labels: 'test', + annotations: 'test', + }, + }, + powershell: { + command_line: 'test', + module: 'test', + module_loaded: 'test', + module_version: 'test', + process_name: 'test', + }, + }; + + const ignoredDocument = processK8sUsernames(clusterUuid, testDocument); + expect(ignoredDocument).toEqual(testDocument); + }); + + test('kubernetes system usernames are not sanitized from a document', async () => { + const clusterUuid = '7c5f1d31-ce87-4090-8dbf-decaac0261ca'; + const testDocument = { + kubernetes: { + pod: { + uid: 'test', + name: 'test', + ip: 'test', + labels: 'test', + annotations: 'test', + }, + audit: { + user: { + username: 'system:serviceaccount:default:default', + groups: [ + 'system:serviceaccounts', + 'system:serviceaccounts:default', + 'system:authenticated', + ], + }, + impersonated_user: { + username: 'system:serviceaccount:default:default', + groups: [ + 'system:serviceaccounts', + 'system:serviceaccounts:default', + 'system:authenticated', + ], + }, + }, + }, + }; + + const sanitizedDocument = processK8sUsernames(clusterUuid, testDocument); + expect(sanitizedDocument).toEqual(testDocument); + }); + + test('kubernetes system usernames are sanitized from a document when not system users', async () => { + const clusterUuid = '7c5f1d31-ce87-4090-8dbf-decaac0261ca'; + const testDocument = { + kubernetes: { + pod: { + uid: 'test', + name: 'test', + ip: 'test', + labels: 'test', + annotations: 'test', + }, + audit: { + user: { + username: 'user1', + groups: ['group1', 'group2', 'group3'], + }, + impersonated_user: { + username: 'impersonatedUser1', + groups: ['group4', 'group5', 'group6'], + }, + }, + }, + }; + + const sanitizedDocument = processK8sUsernames(clusterUuid, testDocument); + expect(sanitizedDocument).toEqual(testDocument); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts index f03621899c800..f5d6bc41ee349 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts @@ -8,8 +8,9 @@ import moment from 'moment'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { PackagePolicy } from '@kbn/fleet-plugin/common/types/models/package_policy'; -import { merge } from 'lodash'; +import { merge, set } from 'lodash'; import type { Logger } from '@kbn/core/server'; +import { sha256 } from 'js-sha256'; import { copyAllowlistedFields, filterList } from './filterlists'; import type { PolicyConfig, PolicyData } from '../../../common/endpoint/types'; import type { @@ -300,3 +301,47 @@ export const createTaskMetric = ( error_message: errorMessage, }; }; + +function obfuscateString(clusterId: string, toHash: string): string { + const valueToObfuscate = toHash + clusterId; + return sha256.create().update(valueToObfuscate).hex(); +} + +function isAllowlistK8sUsername(username: string) { + return ( + username === 'edit' || + username === 'view' || + username === 'admin' || + username === 'elastic-agent' || + username === 'cluster-admin' || + username.startsWith('system') + ); +} + +export const processK8sUsernames = (clusterId: string, event: TelemetryEvent): TelemetryEvent => { + // if there is no kubernetes key, return the event as is + if (event.kubernetes === undefined && event.kubernetes === null) { + return event; + } + + const username = event?.kubernetes?.audit?.user?.username; + const impersonatedUser = event?.kubernetes?.audit?.impersonated_user?.username; + + if (username !== undefined && username !== null && !isAllowlistK8sUsername(username)) { + set(event, 'kubernetes.audit.user.username', obfuscateString(clusterId, username)); + } + + if ( + impersonatedUser !== undefined && + impersonatedUser !== null && + !isAllowlistK8sUsername(impersonatedUser) + ) { + set( + event, + 'kubernetes.audit.impersonated_user.username', + obfuscateString(clusterId, impersonatedUser) + ); + } + + return event; +}; 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 03614a4bfc0ac..9526ec07ea2a6 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -73,7 +73,7 @@ import { PREBUILT_RULES_PACKAGE_NAME } from '../../../common/detection_engine/co export interface ITelemetryReceiver { start( core?: CoreStart, - kibanaIndex?: string, + getIndexForType?: (type: string) => string, alertsIndex?: string, endpointContextService?: EndpointAppContextService, exceptionListClient?: ExceptionListClient, @@ -185,7 +185,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { private esClient?: ElasticsearchClient; private exceptionListClient?: ExceptionListClient; private soClient?: SavedObjectsClientContract; - private kibanaIndex?: string; + private getIndexForType?: (type: string) => string; private alertsIndex?: string; private clusterInfo?: ESClusterInfo; private processTreeFetcher?: Fetcher; @@ -198,13 +198,13 @@ export class TelemetryReceiver implements ITelemetryReceiver { public async start( core?: CoreStart, - kibanaIndex?: string, + getIndexForType?: (type: string) => string, alertsIndex?: string, endpointContextService?: EndpointAppContextService, exceptionListClient?: ExceptionListClient, packageService?: PackageService ) { - this.kibanaIndex = kibanaIndex; + this.getIndexForType = getIndexForType; this.alertsIndex = alertsIndex; this.agentClient = endpointContextService?.getInternalFleetServices().agent; this.agentPolicyService = endpointContextService?.getInternalFleetServices().agentPolicy; @@ -493,7 +493,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { const query: SearchRequest = { expand_wildcards: ['open' as const, 'hidden' as const], - index: `${this.kibanaIndex}*`, + index: this.getIndexForType?.('alert'), ignore_unavailable: true, body: { size: this.maxRecords, @@ -924,7 +924,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { }; const exceptionListQuery: SearchRequest = { expand_wildcards: ['open' as const, 'hidden' as const], - index: `${this.kibanaIndex}*`, + index: this.getIndexForType?.('exception-list'), ignore_unavailable: true, body: { size: 0, // no query results required - only aggregation quantity @@ -944,7 +944,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { }; const indicatorMatchRuleQuery: SearchRequest = { expand_wildcards: ['open' as const, 'hidden' as const], - index: `${this.kibanaIndex}*`, + index: this.getIndexForType?.('alert'), ignore_unavailable: true, body: { size: 0, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts index a7fab953dad38..0fdc6cf32a69c 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts @@ -11,7 +11,7 @@ import type { ITelemetryReceiver } from '../receiver'; import type { ESClusterInfo, ESLicense, TelemetryEvent } from '../types'; import type { TaskExecutionPeriod } from '../task'; import { TELEMETRY_CHANNEL_DETECTION_ALERTS, TASK_METRICS_CHANNEL } from '../constants'; -import { batchTelemetryRecords, tlog, createTaskMetric } from '../helpers'; +import { batchTelemetryRecords, createTaskMetric, processK8sUsernames, tlog } from '../helpers'; import { copyAllowlistedFields, filterList } from '../filterlists'; export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: number) { @@ -70,7 +70,12 @@ export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: n copyAllowlistedFields(filterList.prebuiltRulesAlerts, event) ); - const enrichedAlerts = processedAlerts.map( + const sanitizedAlerts = processedAlerts.map( + (event: TelemetryEvent): TelemetryEvent => + processK8sUsernames(clusterInfo?.cluster_uuid, event) + ); + + const enrichedAlerts = sanitizedAlerts.map( (event: TelemetryEvent): TelemetryEvent => ({ ...event, licence_id: licenseInfo?.uid, 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 ba61f6b85aaab..df3b571714b29 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts @@ -64,6 +64,19 @@ export interface TelemetryEvent { id?: string; kind?: string; }; + kubernetes?: { + audit?: { + user?: { + username?: string; + groups?: string[]; + }; + impersonated_user?: { + username?: string; + groups?: string[]; + }; + pod?: SearchTypes; + }; + }; } // EP Policy Response diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts index 98a965ea0c26b..0a8f59adfa19f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; import { notesMigrations } from './migrations/notes'; @@ -35,6 +36,7 @@ export const noteSavedObjectMappings: SavedObjectsType['mappings'] = { export const noteType: SavedObjectsType = { name: noteSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts index 0df23655c6af8..863f14b3ab39f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; import { pinnedEventsMigrations } from './migrations/pinned_events'; @@ -32,6 +33,7 @@ export const pinnedEventSavedObjectMappings: SavedObjectsType['mappings'] = { export const pinnedEventType: SavedObjectsType = { name: pinnedEventSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts index e58c7a70739ab..4f7df977662f4 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectsType } from '@kbn/core/server'; import { timelinesMigrations } from './migrations/timelines'; @@ -317,6 +318,7 @@ export const timelineSavedObjectMappings: SavedObjectsType['mappings'] = { export const timelineType: SavedObjectsType = { name: timelineSavedObjectType, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 2ec2db2394068..db1e35d316640 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -69,7 +69,6 @@ import type { ITelemetryReceiver } from './lib/telemetry/receiver'; import { TelemetryReceiver } from './lib/telemetry/receiver'; import { licenseService } from './lib/license'; import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; -import { migrateArtifactsToFleet } from './endpoint/lib/artifacts/migrate_artifacts_to_fleet'; import previewPolicy from './lib/detection_engine/routes/index/preview_policy.json'; import { createRuleExecutionLogService } from './lib/detection_engine/rule_monitoring'; import { getKibanaPrivilegesFeaturePrivileges, getCasesKibanaFeature } from './features'; @@ -128,7 +127,6 @@ export class Plugin implements ISecuritySolutionPlugin { private checkMetadataTransformsTask: CheckMetadataTransformsTask | undefined; private artifactsCache: LRU; private telemetryUsageCounter?: UsageCounter; - private kibanaIndex?: string; private endpointContext: EndpointAppContext; constructor(context: PluginInitializerContext) { @@ -159,7 +157,6 @@ export class Plugin implements ISecuritySolutionPlugin { const { appClientFactory, pluginContext, config, logger } = this; const experimentalFeatures = config.experimentalFeatures; - this.kibanaIndex = core.savedObjects.getKibanaIndex(); initSavedObjects(core.savedObjects); initUiSettings(core.uiSettings, experimentalFeatures); @@ -241,6 +238,7 @@ export class Plugin implements ISecuritySolutionPlugin { lists: plugins.lists, logger: this.logger, config: this.config, + publicBaseUrl: core.http.basePath.publicBaseUrl, ruleDataClient, ruleExecutionLoggerFactory: ruleExecutionLogService.createClientForExecutors, version: pluginContext.env.packageInfo.version, @@ -309,7 +307,11 @@ export class Plugin implements ISecuritySolutionPlugin { ); registerLimitedConcurrencyRoutes(core); registerPolicyRoutes(router, this.endpointContext); - registerActionRoutes(router, this.endpointContext); + registerActionRoutes( + router, + this.endpointContext, + plugins.encryptedSavedObjects?.canEncrypt === true + ); const ruleTypes = [ LEGACY_NOTIFICATIONS_ID, @@ -448,17 +450,15 @@ export class Plugin implements ISecuritySolutionPlugin { // Migrate artifacts to fleet and then start the minifest task after that is done plugins.fleet.fleetSetupCompleted().then(() => { - migrateArtifactsToFleet(savedObjectsClient, artifactClient, logger).finally(() => { - logger.info('Dependent plugin setup complete - Starting ManifestTask'); - - if (this.manifestTask) { - this.manifestTask.start({ - taskManager, - }); - } else { - logger.error(new Error('User artifacts task not available.')); - } - }); + logger.info('Dependent plugin setup complete - Starting ManifestTask'); + + if (this.manifestTask) { + this.manifestTask.start({ + taskManager, + }); + } else { + logger.error(new Error('User artifacts task not available.')); + } }); // License related start @@ -513,8 +513,7 @@ export class Plugin implements ISecuritySolutionPlugin { this.telemetryReceiver.start( core, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.kibanaIndex!, + (type: string) => core.savedObjects.getIndexForType(type), DEFAULT_ALERTS_INDEX, this.endpointAppContextService, exceptionListClient, diff --git a/x-pack/plugins/security_solution/server/saved_objects.ts b/x-pack/plugins/security_solution/server/saved_objects.ts index 394cab7f52455..bd6c21a4d489a 100644 --- a/x-pack/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/saved_objects.ts @@ -12,10 +12,7 @@ import { noteType, pinnedEventType, timelineType } from './lib/timeline/saved_ob import { legacyType as legacyRuleActionsType } from './lib/detection_engine/rule_actions_legacy'; import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules'; import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects'; -import { - exceptionsArtifactType, - manifestType, -} from './endpoint/lib/artifacts/saved_object_mappings'; +import { manifestType } from './endpoint/lib/artifacts/saved_object_mappings'; const types = [ noteType, @@ -23,7 +20,6 @@ const types = [ legacyRuleActionsType, prebuiltRuleAssetType, timelineType, - exceptionsArtifactType, manifestType, signalsMigrationType, ]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts index aa07acd20c896..66f36a9052bb5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts @@ -201,6 +201,7 @@ export const getHostEndpoint = async ( : {}; return { + hostInfo: endpointData, endpointPolicy: endpointData.metadata.Endpoint.policy.applied.name, policyStatus: endpointData.metadata.Endpoint.policy.applied.status, sensorVersion: endpointData.metadata.agent.version, diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index bfc45e92c36a5..748130f6be7b1 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -23,7 +23,9 @@ ], "kbn_references": [ "@kbn/core", - { "path": "../../../src/setup_node_env/tsconfig.json" }, + { + "path": "../../../src/setup_node_env/tsconfig.json" + }, "@kbn/data-plugin", "@kbn/embeddable-plugin", "@kbn/files-plugin", @@ -38,7 +40,6 @@ "@kbn/actions-plugin", "@kbn/alerting-plugin", "@kbn/cases-plugin", - "@kbn/cloud-defend-plugin", "@kbn/cloud-experiments-plugin", "@kbn/cloud-security-posture-plugin", "@kbn/encrypted-saved-objects-plugin", @@ -135,7 +136,6 @@ "@kbn/repo-info", "@kbn/storybook", "@kbn/controls-plugin", - "@kbn/shared-ux-utility", "@kbn/core-saved-objects-common", "@kbn/core-saved-objects-import-export-server-mocks", "@kbn/user-profile-components", @@ -145,6 +145,7 @@ "@kbn/shared-ux-router", "@kbn/charts-plugin", "@kbn/alerts-as-data-utils", + "@kbn/cloud-defend-plugin", "@kbn/expandable-flyout", "@kbn/securitysolution-grouping", "@kbn/securitysolution-data-table", @@ -154,5 +155,6 @@ "@kbn/security-solution-side-nav", "@kbn/core-lifecycle-browser", "@kbn/ecs", + "@kbn/url-state", ] } diff --git a/x-pack/plugins/session_view/common/constants.ts b/x-pack/plugins/session_view/common/constants.ts index e0ad59c4accfc..6dee67ff9d4b8 100644 --- a/x-pack/plugins/session_view/common/constants.ts +++ b/x-pack/plugins/session_view/common/constants.ts @@ -18,10 +18,6 @@ export const SECURITY_APP_ID = 'security'; export const POLICIES_PAGE_PATH = '/administration/policy'; // index patterns -const ENDPOINT_PROCESS_EVENTS_INDEX = - '*:logs-endpoint.events.process*,logs-endpoint.events.process*'; -const CLOUD_DEFEND_PROCESS_EVENTS_INDEX = '*:logs-cloud_defend.process*,logs-cloud_defend.process*'; -export const PROCESS_EVENTS_INDEX = `${ENDPOINT_PROCESS_EVENTS_INDEX},${CLOUD_DEFEND_PROCESS_EVENTS_INDEX}`; // match on both cross cluster and local indices export const PREVIEW_ALERTS_INDEX = '.preview.alerts-security.alerts-default'; // field properties diff --git a/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts b/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts index e82e4a2e54ea4..96abd0a93ec20 100644 --- a/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts +++ b/x-pack/plugins/session_view/common/mocks/constants/session_view_process.mock.ts @@ -17,6 +17,9 @@ import { ProcessEventAlertCategory, } from '../../types/process_tree'; +export const TEST_PROCESS_INDEX = 'logs-endpoint.events.process*'; +export const TEST_SESSION_START_TIME = '2021-10-14T08:05:34.853Z'; + export const mockEvents: ProcessEvent[] = [ { '@timestamp': '2021-11-23T15:25:04.210Z', diff --git a/x-pack/plugins/session_view/common/types/process_tree/index.ts b/x-pack/plugins/session_view/common/types/process_tree/index.ts index 264d1685908ae..c77b97ba5d85d 100644 --- a/x-pack/plugins/session_view/common/types/process_tree/index.ts +++ b/x-pack/plugins/session_view/common/types/process_tree/index.ts @@ -193,6 +193,7 @@ export interface ProcessEvent { kind?: EventKind; category?: string | string[]; action?: EventAction | EventAction[]; + type?: string | string[]; id?: string; }; file?: { @@ -289,6 +290,7 @@ export interface ProcessEventCloud { }; project?: { id?: string; + name?: string; }; provider?: string; region?: string; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts index f4da70e5927bc..854876df91800 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts @@ -132,6 +132,7 @@ export const getCloudData = (cloud: ProcessEventCloud | undefined): DetailPanelC }, project: { id: DASH, + name: DASH, }, provider: DASH, region: DASH, @@ -144,6 +145,7 @@ export const getCloudData = (cloud: ProcessEventCloud | undefined): DetailPanelC detailPanelCloud.instance.name = dataOrDash(cloud?.instance?.name).toString(); detailPanelCloud.account.id = dataOrDash(cloud?.account?.id).toString(); detailPanelCloud.project.id = dataOrDash(cloud?.project?.id).toString(); + detailPanelCloud.project.name = dataOrDash(cloud?.project?.name).toString(); detailPanelCloud.provider = dataOrDash(cloud?.provider).toString(); detailPanelCloud.region = dataOrDash(cloud?.region).toString(); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx index ba530805c80dc..955526ec92bff 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx @@ -136,7 +136,7 @@ describe('DetailPanelMetadataTab component', () => { expect(renderResult.queryByText(TEST_NAME)).toBeVisible(); // expand host os accordion - renderResult.queryByText('Host OS')?.click(); + renderResult.queryByText('OS')?.click(); expect(renderResult.queryByText('architecture')).toBeVisible(); expect(renderResult.queryByText('os.family')).toBeVisible(); expect(renderResult.queryByText('os.full')).toBeVisible(); @@ -182,7 +182,7 @@ describe('DetailPanelMetadataTab component', () => { expect(renderResult.queryAllByText('name').length).toBe(2); // expand host os accordion - renderResult.queryByText('Host OS')?.click(); + renderResult.queryByText('OS')?.click(); expect(renderResult.queryByText('architecture')).toBeVisible(); expect(renderResult.queryByText('os.family')).toBeVisible(); expect(renderResult.queryByText('os.full')).toBeVisible(); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx index 797f6c725dfb6..8333f6d29d2c8 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx @@ -50,33 +50,33 @@ export const DetailPanelMetadataTab = ({ <> hostname, + title: id, description: ( - {hostData.hostname} + {hostData.id} ), }, { - title: id, + title: hostname, description: ( - {hostData.id} + {hostData.hostname} ), @@ -133,7 +133,7 @@ export const DetailPanelMetadataTab = ({ ), }, + { + title: project.name, + description: ( + + + {cloudData.project.name} + + + ), + }, ]} /> diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx index ded1f20aef49e..bc7090599b3df 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx @@ -453,17 +453,6 @@ export const DetailPanelProcessTab = ({ selectedProcess }: DetailPanelProcessTab ), }, - { - title: user.id, - description: ( - - {userId} - - ), - }, { title: user.name, description: ( diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap b/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap index 39e1778dd1f4a..419237d35086f 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/__snapshots__/index.test.tsx.snap @@ -95,6 +95,9 @@ Object {
    +
    @@ -190,6 +193,9 @@ Object {
    +
    , diff --git a/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx index ad9ec57488671..9977393b9c1c6 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_alert/index.tsx @@ -15,6 +15,7 @@ import { EuiToolTip, EuiPanel, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { ALERT_ICONS } from '../../../common/constants'; import { ProcessEvent, @@ -78,6 +79,7 @@ export const ProcessTreeAlert = ({ const processEventAlertCategory = category ?? ProcessEventAlertCategory.process; const alertCategoryDetailDisplayText = getAlertCategoryDisplayText(alert, category); const alertIconTooltipContent = getAlertIconTooltipContent(processEventAlertCategory); + const eventType = Array.isArray(event?.type) ? event?.type?.[0] : event?.type; return (
    @@ -137,6 +139,13 @@ export const ProcessTreeAlert = ({ {event?.action} + + {eventType === 'denied' && ( + + + + )} +
    ); diff --git a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx index 6ffc4415fb47b..1cee6fbe498cc 100644 --- a/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx +++ b/x-pack/plugins/session_view/public/components/process_tree_node/index.tsx @@ -44,6 +44,10 @@ export const EXEC_USER_CHANGE = i18n.translate('xpack.sessionView.execUserChange defaultMessage: 'Exec user change', }); +export const COLLAPSE_ALL = i18n.translate('xpack.sessionView.collapseAll', { + defaultMessage: 'Collapse all', +}); + export interface ProcessDeps { process: Process; isSessionLeader?: boolean; @@ -263,7 +267,7 @@ export function ProcessTreeNode({ const shouldRenderChildren = isSessionLeader || (childrenExpanded && children?.length > 0); const childrenTreeDepth = depth + 1; - const showUserEscalation = !isSessionLeader && !!user?.name && user.name !== parent?.user?.name; + const showUserEscalation = !isSessionLeader && !!user?.id && user.id !== parent?.user?.id; const interactiveSession = !!tty; const sessionIcon = interactiveSession ? 'desktop' : 'gear'; const iconTestSubj = hasExec @@ -303,12 +307,11 @@ export function ProcessTreeNode({ {userName} - + + + + + ) : ( <> @@ -344,7 +347,7 @@ export function ProcessTreeNode({ css={buttonStyles.userChangedButton} aria-label={EXEC_USER_CHANGE} > - {EXEC_USER_CHANGE} :{user.name} + {EXEC_USER_CHANGE} ({userName}) )} {!isSessionLeader && children.length > 0 && ( diff --git a/x-pack/plugins/session_view/public/components/session_view/hooks.ts b/x-pack/plugins/session_view/public/components/session_view/hooks.ts index 2180228673fc0..3505f3b7ca455 100644 --- a/x-pack/plugins/session_view/public/components/session_view/hooks.ts +++ b/x-pack/plugins/session_view/public/components/session_view/hooks.ts @@ -28,14 +28,16 @@ import { } from '../../../common/constants'; export const useFetchSessionViewProcessEvents = ( + index: string, sessionEntityId: string, + sessionStartTime: string, jumpToCursor?: string ) => { const { http } = useKibana().services; const [currentJumpToCursor, setCurrentJumpToCursor] = useState(''); const cachingKeys = useMemo( - () => [QUERY_KEY_PROCESS_EVENTS, sessionEntityId, jumpToCursor], - [sessionEntityId, jumpToCursor] + () => [QUERY_KEY_PROCESS_EVENTS, index, sessionEntityId, jumpToCursor], + [index, sessionEntityId, jumpToCursor] ); const query = useInfiniteQuery( @@ -50,7 +52,9 @@ export const useFetchSessionViewProcessEvents = ( const res = await http.get(PROCESS_EVENTS_ROUTE, { query: { + index, sessionEntityId, + sessionStartTime, cursor, forward, }, @@ -126,6 +130,7 @@ export const useFetchSessionViewProcessEvents = ( export const useFetchSessionViewAlerts = ( sessionEntityId: string, + sessionStartTime: string, investigatedAlertId?: string ) => { const { http } = useKibana().services; @@ -139,6 +144,7 @@ export const useFetchSessionViewAlerts = ( const res = await http.get(ALERTS_ROUTE, { query: { sessionEntityId, + sessionStartTime, investigatedAlertId, cursor, }, @@ -210,15 +216,21 @@ export const useFetchAlertStatus = ( return query; }; -export const useFetchGetTotalIOBytes = (sessionEntityId: string) => { +export const useFetchGetTotalIOBytes = ( + index: string, + sessionEntityId: string, + sessionStartTime: string +) => { const { http } = useKibana().services; - const cachingKeys = [QUERY_KEY_GET_TOTAL_IO_BYTES, sessionEntityId]; + const cachingKeys = [QUERY_KEY_GET_TOTAL_IO_BYTES, index, sessionEntityId]; const query = useQuery<{ total: number }, Error>( cachingKeys, async () => { return http.get<{ total: number }>(GET_TOTAL_IO_BYTES_ROUTE, { query: { + index, sessionEntityId, + sessionStartTime, }, }); }, diff --git a/x-pack/plugins/session_view/public/components/session_view/index.test.tsx b/x-pack/plugins/session_view/public/components/session_view/index.test.tsx index 542d546b38707..7eaa1f6a93124 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.test.tsx @@ -7,6 +7,10 @@ import { waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import React from 'react'; +import { + TEST_PROCESS_INDEX, + TEST_SESSION_START_TIME, +} from '../../../common/mocks/constants/session_view_process.mock'; import { sessionViewProcessEventsMock } from '../../../common/mocks/responses/session_view_process_events.mock'; import { sessionViewProcessEventsMergedMock } from '../../../common/mocks/responses/session_view_process_events_merged.mock'; import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; @@ -48,7 +52,13 @@ describe('SessionView component', () => { mockedContext = createAppRootMockRenderer(); mockedApi = mockedContext.coreStart.http.get; render = () => - (renderResult = mockedContext.render()); + (renderResult = mockedContext.render( + + )); mockUseDateFormat.mockImplementation(() => 'MMM D, YYYY @ HH:mm:ss.SSS'); }); diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index 5f52f5ff8e935..86884e4683dd9 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -46,7 +46,9 @@ import { REFRESH_SESSION, TOGGLE_TTY_PLAYER, DETAIL_PANEL } from './translations * The main wrapper component for the session view. */ export const SessionView = ({ + processIndex, sessionEntityId, + sessionStartTime, height, isFullScreen = false, jumpToEntityId, @@ -129,7 +131,12 @@ export const SessionView = ({ fetchPreviousPage, hasPreviousPage, refetch, - } = useFetchSessionViewProcessEvents(sessionEntityId, currentJumpToCursor); + } = useFetchSessionViewProcessEvents( + processIndex, + sessionEntityId, + sessionStartTime, + currentJumpToCursor + ); const { data: alertsData, @@ -138,10 +145,13 @@ export const SessionView = ({ hasNextPage: hasNextPageAlerts, error: alertsError, refetch: refetchAlerts, - } = useFetchSessionViewAlerts(sessionEntityId, investigatedAlertId); + } = useFetchSessionViewAlerts(sessionEntityId, sessionStartTime, investigatedAlertId); - const { data: totalTTYOutputBytes, refetch: refetchTotalTTYOutput } = - useFetchGetTotalIOBytes(sessionEntityId); + const { data: totalTTYOutputBytes, refetch: refetchTotalTTYOutput } = useFetchGetTotalIOBytes( + processIndex, + sessionEntityId, + sessionStartTime + ); const hasTTYOutput = !!totalTTYOutputBytes?.total; const bytesOfOutput = useMemo(() => { const { unit, value } = byteSize(totalTTYOutputBytes?.total || 0); @@ -421,8 +431,10 @@ export const SessionView = ({ }} { +export const useFetchIOEvents = ( + index: string, + sessionEntityId: string, + sessionStartTime: string +) => { const { http } = useKibana().services; const cachingKeys = useMemo(() => [QUERY_KEY_IO_EVENTS, sessionEntityId], [sessionEntityId]); @@ -43,7 +47,9 @@ export const useFetchIOEvents = (sessionEntityId: string) => { const { cursor } = pageParam; const res = await http.get(IO_EVENTS_ROUTE, { query: { + index, sessionEntityId, + sessionStartTime, cursor, }, }); diff --git a/x-pack/plugins/session_view/public/components/tty_player/index.test.tsx b/x-pack/plugins/session_view/public/components/tty_player/index.test.tsx index ba56931b4a99b..56553fa721a05 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/tty_player/index.test.tsx @@ -7,6 +7,10 @@ import React from 'react'; import { waitFor, act } from '@testing-library/react'; +import { + TEST_PROCESS_INDEX, + TEST_SESSION_START_TIME, +} from '../../../common/mocks/constants/session_view_process.mock'; import { sessionViewIOEventsMock } from '../../../common/mocks/responses/session_view_io_events.mock'; import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; import { TTYPlayerDeps, TTYPlayer } from '.'; @@ -51,7 +55,9 @@ describe('TTYPlayer component', () => { props = { show: true, + index: TEST_PROCESS_INDEX, sessionEntityId: mockSessionEntityId, + sessionStartTime: TEST_SESSION_START_TIME, onClose: jest.fn(), onJumpToEvent: jest.fn(), isFullscreen: false, diff --git a/x-pack/plugins/session_view/public/components/tty_player/index.tsx b/x-pack/plugins/session_view/public/components/tty_player/index.tsx index 434805ac689db..8b89a5bfa0d06 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/index.tsx +++ b/x-pack/plugins/session_view/public/components/tty_player/index.tsx @@ -33,8 +33,10 @@ import { TTYPlayerControls } from '../tty_player_controls'; import { BETA, TOGGLE_TTY_PLAYER, DETAIL_PANEL } from '../session_view/translations'; export interface TTYPlayerDeps { - show: boolean; + index: string; sessionEntityId: string; + sessionStartTime: string; + show: boolean; onClose(): void; isFullscreen: boolean; onJumpToEvent(event: ProcessEvent): void; @@ -43,8 +45,10 @@ export interface TTYPlayerDeps { } export const TTYPlayer = ({ - show, + index, sessionEntityId, + sessionStartTime, + show, onClose, isFullscreen, onJumpToEvent, @@ -54,8 +58,11 @@ export const TTYPlayer = ({ const ref = useRef(null); const { ref: scrollRef, height: containerHeight = 1 } = useResizeObserver({}); - const { data, fetchNextPage, hasNextPage, isFetching, refetch } = - useFetchIOEvents(sessionEntityId); + const { data, fetchNextPage, hasNextPage, isFetching, refetch } = useFetchIOEvents( + index, + sessionEntityId, + sessionStartTime + ); const { lines, processStartMarkers } = useIOLines(data?.pages); const [fontSize, setFontSize] = useState(DEFAULT_TTY_FONT_SIZE); const [isPlaying, setIsPlaying] = useState(false); @@ -88,7 +95,7 @@ export const TTYPlayer = ({ useEffect(() => { if (show) { // refetch the most recent page when tty player is loaded - refetch({ refetchPage: (_page, index, allPages) => allPages.length - 1 === index }); + refetch({ refetchPage: (_page, i, allPages) => allPages.length - 1 === i }); } }, [refetch, show]); diff --git a/x-pack/plugins/session_view/public/types.ts b/x-pack/plugins/session_view/public/types.ts index 06f2a6d06b56e..31cdaf4f2f89f 100644 --- a/x-pack/plugins/session_view/public/types.ts +++ b/x-pack/plugins/session_view/public/types.ts @@ -10,8 +10,15 @@ import { CoreStart } from '@kbn/core/public'; export type SessionViewServices = CoreStart; export interface SessionViewDeps { + // we pass in the index of the session leader that spawned session_view, this avoids having to query multiple cross cluster indices + processIndex: string; + // the root node of the process tree to render. e.g process.entry.entity_id or process.session_leader.entity_id sessionEntityId: string; + + // start time is passed in order to scope session_view queries to the appropriate time range, and avoid querying data across all time. + sessionStartTime: string; + height?: number; isFullScreen?: boolean; // if provided, the session view will jump to and select the provided event if it belongs to the session leader @@ -132,6 +139,7 @@ export interface DetailPanelCloud { }; project: { id: string; + name: string; }; provider: string; region: string; diff --git a/x-pack/plugins/session_view/server/routes/alerts_route.ts b/x-pack/plugins/session_view/server/routes/alerts_route.ts index 09369a6ce5733..53d3cb2416d44 100644 --- a/x-pack/plugins/session_view/server/routes/alerts_route.ts +++ b/x-pack/plugins/session_view/server/routes/alerts_route.ts @@ -31,15 +31,15 @@ export const registerAlertsRoute = ( validate: { query: schema.object({ sessionEntityId: schema.string(), + sessionStartTime: schema.string(), investigatedAlertId: schema.maybe(schema.string()), cursor: schema.maybe(schema.string()), - range: schema.maybe(schema.arrayOf(schema.string())), }), }, }, async (_context, request, response) => { const client = await ruleRegistry.getRacClientWithRequest(request); - const { sessionEntityId, investigatedAlertId, range, cursor } = request.query; + const { sessionEntityId, sessionStartTime, investigatedAlertId, cursor } = request.query; try { const body = await searchAlerts( @@ -47,7 +47,7 @@ export const registerAlertsRoute = ( sessionEntityId, ALERTS_PER_PAGE, investigatedAlertId, - range, + [sessionStartTime], cursor ); diff --git a/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts b/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts index 081969b66ca43..07e50cc97059b 100644 --- a/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts +++ b/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts @@ -7,9 +7,9 @@ import { IRouter } from '@kbn/core/server'; import { EVENT_ACTION } from '@kbn/rule-data-utils'; import { GET_TOTAL_IO_BYTES_ROUTE, - PROCESS_EVENTS_INDEX, TOTAL_BYTES_CAPTURED_PROPERTY, ENTRY_SESSION_ENTITY_ID_PROPERTY, + TIMESTAMP_PROPERTY, } from '../../common/constants'; export const registerGetTotalIOBytesRoute = (router: IRouter) => { @@ -18,23 +18,33 @@ export const registerGetTotalIOBytesRoute = (router: IRouter) => { path: GET_TOTAL_IO_BYTES_ROUTE, validate: { query: schema.object({ + index: schema.string(), sessionEntityId: schema.string(), + sessionStartTime: schema.string(), }), }, }, async (context, request, response) => { const client = (await context.core).elasticsearch.client.asCurrentUser; - const { sessionEntityId } = request.query; + const { index, sessionEntityId, sessionStartTime } = request.query; try { const search = await client.search({ - index: [PROCESS_EVENTS_INDEX], + index: [index], body: { query: { bool: { must: [ { term: { [ENTRY_SESSION_ENTITY_ID_PROPERTY]: sessionEntityId } }, { term: { [EVENT_ACTION]: 'text_output' } }, + { + range: { + // optimization to prevent data before this session from being hit. + [TIMESTAMP_PROPERTY]: { + gte: sessionStartTime, + }, + }, + }, ], }, }, diff --git a/x-pack/plugins/session_view/server/routes/io_events_route.test.ts b/x-pack/plugins/session_view/server/routes/io_events_route.test.ts index 5ad0615665c64..efdcc7df22e15 100644 --- a/x-pack/plugins/session_view/server/routes/io_events_route.test.ts +++ b/x-pack/plugins/session_view/server/routes/io_events_route.test.ts @@ -8,6 +8,8 @@ import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { EventAction, EventKind } from '../../common/types/process_tree'; import { searchProcessWithIOEvents } from './io_events_route'; +const TEST_PROCESS_INDEX = 'logs-endpoint.events.process*'; + const getEmptyResponse = async () => { return { aggregations: { @@ -45,7 +47,7 @@ describe('io_events_route.ts', () => { describe('searchProcessWithIOEvents(client, sessionEntityId, range)', () => { it('should return an empty events array for a non existant entity_id', async () => { const esClient = elasticsearchServiceMock.createElasticsearchClient(getEmptyResponse()); - const body = await searchProcessWithIOEvents(esClient, 'asdf'); + const body = await searchProcessWithIOEvents(esClient, TEST_PROCESS_INDEX, 'asdf'); expect(body.length).toBe(0); }); @@ -53,7 +55,7 @@ describe('io_events_route.ts', () => { it('returns results for a particular session entity_id', async () => { const esClient = elasticsearchServiceMock.createElasticsearchClient(getResponse()); - const body = await searchProcessWithIOEvents(esClient, 'mockId'); + const body = await searchProcessWithIOEvents(esClient, TEST_PROCESS_INDEX, 'mockId'); expect(body.length).toBe(1); @@ -69,7 +71,10 @@ describe('io_events_route.ts', () => { const start = '2021-11-23T15:25:04.210Z'; const end = '2021-20-23T15:25:04.210Z'; - const body = await searchProcessWithIOEvents(esClient, 'mockId', [start, end]); + const body = await searchProcessWithIOEvents(esClient, TEST_PROCESS_INDEX, 'mockId', [ + start, + end, + ]); expect(body.length).toBe(1); }); diff --git a/x-pack/plugins/session_view/server/routes/io_events_route.ts b/x-pack/plugins/session_view/server/routes/io_events_route.ts index 645617b6435c2..76c7256b99d4f 100644 --- a/x-pack/plugins/session_view/server/routes/io_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/io_events_route.ts @@ -13,8 +13,8 @@ import { EventAction, EventKind } from '../../common/types/process_tree'; import { IO_EVENTS_ROUTE, IO_EVENTS_PER_PAGE, - PROCESS_EVENTS_INDEX, ENTRY_SESSION_ENTITY_ID_PROPERTY, + TIMESTAMP_PROPERTY, PROCESS_ENTITY_ID_PROPERTY, PROCESS_EVENTS_PER_PAGE, } from '../../common/constants'; @@ -25,7 +25,9 @@ export const registerIOEventsRoute = (router: IRouter) => { path: IO_EVENTS_ROUTE, validate: { query: schema.object({ + index: schema.string(), sessionEntityId: schema.string(), + sessionStartTime: schema.string(), cursor: schema.maybe(schema.string()), pageSize: schema.maybe(schema.number()), }), @@ -33,17 +35,31 @@ export const registerIOEventsRoute = (router: IRouter) => { }, async (context, request, response) => { const client = (await context.core).elasticsearch.client.asCurrentUser; - const { sessionEntityId, cursor, pageSize = IO_EVENTS_PER_PAGE } = request.query; + const { + index, + sessionEntityId, + sessionStartTime, + cursor, + pageSize = IO_EVENTS_PER_PAGE, + } = request.query; try { const search = await client.search({ - index: [PROCESS_EVENTS_INDEX], + index: [index], body: { query: { bool: { must: [ { term: { [ENTRY_SESSION_ENTITY_ID_PROPERTY]: sessionEntityId } }, { term: { [EVENT_ACTION]: 'text_output' } }, + { + range: { + // optimization to prevent data before this session from being hit. + [TIMESTAMP_PROPERTY]: { + gte: sessionStartTime, + }, + }, + }, ], }, }, @@ -72,6 +88,7 @@ export const registerIOEventsRoute = (router: IRouter) => { export const searchProcessWithIOEvents = async ( client: ElasticsearchClient, + index: string, sessionEntityId: string, range?: string[] ) => { @@ -90,7 +107,7 @@ export const searchProcessWithIOEvents = async ( try { const search = await client.search({ - index: [PROCESS_EVENTS_INDEX], + index: [index], body: { query: { bool: { diff --git a/x-pack/plugins/session_view/server/routes/process_events_route.test.ts b/x-pack/plugins/session_view/server/routes/process_events_route.test.ts index ac53b6df68b5a..13fa5e647b7ce 100644 --- a/x-pack/plugins/session_view/server/routes/process_events_route.test.ts +++ b/x-pack/plugins/session_view/server/routes/process_events_route.test.ts @@ -6,7 +6,12 @@ */ import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { fetchEventsAndScopedAlerts } from './process_events_route'; -import { mockEvents, mockAlerts } from '../../common/mocks/constants/session_view_process.mock'; +import { + TEST_PROCESS_INDEX, + TEST_SESSION_START_TIME, + mockEvents, + mockAlerts, +} from '../../common/mocks/constants/session_view_process.mock'; import { getAlertsClientMockInstance, resetAlertingAuthMock } from './alerts_client_mock.test'; import { EventAction, EventKind, ProcessEvent } from '../../common/types/process_tree'; @@ -42,7 +47,14 @@ describe('process_events_route.ts', () => { const client = elasticsearchServiceMock.createElasticsearchClient(getEmptyResponse()); const alertsClient = getAlertsClientMockInstance(client); - const body = await fetchEventsAndScopedAlerts(client, alertsClient, 'asdf', undefined); + const body = await fetchEventsAndScopedAlerts( + client, + alertsClient, + TEST_PROCESS_INDEX, + 'asdf', + '', + undefined + ); expect(body.events.length).toBe(0); expect(body.total).toBe(0); @@ -52,7 +64,14 @@ describe('process_events_route.ts', () => { const client = elasticsearchServiceMock.createElasticsearchClient(getResponse()); const alertsClient = getAlertsClientMockInstance(); - const body = await fetchEventsAndScopedAlerts(client, alertsClient, 'mockId', undefined); + const body = await fetchEventsAndScopedAlerts( + client, + alertsClient, + TEST_PROCESS_INDEX, + 'mockId', + TEST_SESSION_START_TIME, + undefined + ); expect(body.events.length).toBe(mockEvents.length + mockAlerts.length); @@ -74,7 +93,9 @@ describe('process_events_route.ts', () => { const body = await fetchEventsAndScopedAlerts( client, alertsClient, + TEST_PROCESS_INDEX, 'mockId', + TEST_SESSION_START_TIME, undefined, false ); diff --git a/x-pack/plugins/session_view/server/routes/process_events_route.ts b/x-pack/plugins/session_view/server/routes/process_events_route.ts index f022839940912..b6844d2117ec5 100644 --- a/x-pack/plugins/session_view/server/routes/process_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/process_events_route.ts @@ -17,8 +17,8 @@ import { ALERTS_PER_PROCESS_EVENTS_PAGE, PROCESS_EVENTS_ROUTE, PROCESS_EVENTS_PER_PAGE, - PROCESS_EVENTS_INDEX, ENTRY_SESSION_ENTITY_ID_PROPERTY, + TIMESTAMP_PROPERTY, } from '../../common/constants'; import { ProcessEvent } from '../../common/types/process_tree'; import { searchAlerts } from './alerts_route'; @@ -33,7 +33,9 @@ export const registerProcessEventsRoute = ( path: PROCESS_EVENTS_ROUTE, validate: { query: schema.object({ + index: schema.string(), sessionEntityId: schema.string(), + sessionStartTime: schema.string(), cursor: schema.maybe(schema.string()), forward: schema.maybe(schema.boolean()), pageSize: schema.maybe(schema.number()), @@ -43,13 +45,15 @@ export const registerProcessEventsRoute = ( async (context, request, response) => { const client = (await context.core).elasticsearch.client.asCurrentUser; const alertsClient = await ruleRegistry.getRacClientWithRequest(request); - const { sessionEntityId, cursor, forward, pageSize } = request.query; + const { index, sessionEntityId, sessionStartTime, cursor, forward, pageSize } = request.query; try { const body = await fetchEventsAndScopedAlerts( client, alertsClient, + index, sessionEntityId, + sessionStartTime, cursor, forward, pageSize @@ -71,7 +75,9 @@ export const registerProcessEventsRoute = ( export const fetchEventsAndScopedAlerts = async ( client: ElasticsearchClient, alertsClient: AlertsClient, + index: string, sessionEntityId: string, + sessionStartTime: string, cursor?: string, forward = true, pageSize = PROCESS_EVENTS_PER_PAGE @@ -79,7 +85,7 @@ export const fetchEventsAndScopedAlerts = async ( const cursorMillis = cursor && new Date(cursor).getTime() + (forward ? -1 : 1); const search = await client.search({ - index: [PROCESS_EVENTS_INDEX], + index: [index], body: { query: { bool: { @@ -94,6 +100,14 @@ export const fetchEventsAndScopedAlerts = async ( ], }, }, + { + range: { + // optimization to prevent data before this session from being hit. + [TIMESTAMP_PROPERTY]: { + gte: sessionStartTime, + }, + }, + }, ], }, }, @@ -131,7 +145,12 @@ export const fetchEventsAndScopedAlerts = async ( range ); - const processesWithIOEvents = await searchProcessWithIOEvents(client, sessionEntityId, range); + const processesWithIOEvents = await searchProcessWithIOEvents( + client, + index, + sessionEntityId, + range + ); events = [...events, ...alertsBody.events, ...processesWithIOEvents]; } diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts index 04ddabf9c1bfd..0807f8985af90 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_edit.helpers.ts @@ -88,4 +88,5 @@ type TestSubjects = | 'stepTwo.submitButton' | 'stepTwo.title' | 'submitButton' + | 'codeEditor' | 'title'; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx index 1124a179d245d..7eef715118c95 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/setup_environment.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { merge } from 'lodash'; import { LocationDescriptorObject } from 'history'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { HttpSetup } from '@kbn/core/public'; import { coreMock, scopedHistoryMock } from '@kbn/core/public/mocks'; import { setUiMetricService, httpService } from '../../../public/application/services/http'; @@ -50,8 +51,10 @@ export const services = { setUiMetricService(services.uiMetricService); +const core = coreMock.createStart(); + const appDependencies = { - core: coreMock.createStart(), + core, services, config: { slm_ui: { enabled: true }, @@ -59,6 +62,10 @@ const appDependencies = { plugins: {}, }; +const kibanaContextDependencies = { + uiSettings: core.uiSettings, +}; + export const setupEnvironment = () => { breadcrumbService.setup(() => undefined); textService.setup(i18n); @@ -88,11 +95,13 @@ export const WithAppDependencies = - - - - - + + + + + + + ); }; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.tsx similarity index 93% rename from x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts rename to x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.tsx index 102c0d13a012b..dc05d9c09e627 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { act } from 'react-dom/test-utils'; import { setupEnvironment, pageHelpers, nextTick, TestBed, getRandomString } from './helpers'; @@ -16,6 +17,23 @@ import { REPOSITORY_EDIT, REPOSITORY_NAME } from './helpers/constant'; const { setup } = pageHelpers.repositoryEdit; const { setup: setupRepositoryAdd } = pageHelpers.repositoryAdd; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + { + props.onChange(e.jsonContent); + }} + /> + ), + }; +}); + describe('', () => { let testBed: TestBed; let testBedRepositoryAdd: TestBed; @@ -211,8 +229,8 @@ describe('', () => { ); expect(find('readOnlyToggle').props()['aria-checked']).toBe(settings.readonly); - const codeEditor = testBed.component.find('EuiCodeEditor').at(1); - expect(JSON.parse(codeEditor.props().value as string)).toEqual({ + const codeEditorValue = testBed.find('codeEditor').props()['data-currentvalue']; + expect(JSON.parse(codeEditorValue)).toEqual({ loadDefault: true, conf1: 'foo', conf2: 'bar', diff --git a/x-pack/plugins/snapshot_restore/public/application/app_context.tsx b/x-pack/plugins/snapshot_restore/public/application/app_context.tsx index 4f36da98cfed6..85e4b3b079541 100644 --- a/x-pack/plugins/snapshot_restore/public/application/app_context.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/app_context.tsx @@ -9,7 +9,7 @@ import React, { createContext, useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { Observable } from 'rxjs'; -import { CoreStart, ScopedHistory, CoreTheme } from '@kbn/core/public'; +import { CoreStart, ScopedHistory, CoreTheme, IUiSettingsClient } from '@kbn/core/public'; import { ClientConfigType } from '../types'; import { HttpService, UiMetricService } from './services'; @@ -18,6 +18,7 @@ const AppContext = createContext(undefined); export interface AppDependencies { core: CoreStart; services: { + uiSettings: IUiSettingsClient; httpService: HttpService; uiMetricService: UiMetricService; i18n: typeof i18n; diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx index 724f3a6a17a5f..6abb387e87a1a 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/hdfs_settings.tsx @@ -18,8 +18,8 @@ import { EuiTitle, } from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { HDFSRepository, Repository, SourceRepository } from '../../../../../common/types'; -import { EuiCodeEditor } from '../../../../shared_imports'; import { RepositorySettingsValidation } from '../../../services/validation'; import { ChunkSizeField, MaxSnapshotsField, MaxRestoreField } from './common'; @@ -347,21 +347,16 @@ export const HDFSSettings: React.FunctionComponent = ({ /> } > - = ({ setIsConfInvalid(true); } }} - data-test-subj="codeEditor" /> diff --git a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx index 2f650ee754259..09feaf61ca9e7 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_review.tsx @@ -21,9 +21,9 @@ import { EuiLink, EuiIcon, EuiToolTip, + EuiCodeBlock, } from '@elastic/eui'; import { serializeRestoreSettings } from '../../../../../common/lib'; -import { EuiCodeEditor } from '../../../../shared_imports'; import { useServices } from '../../../app_context'; import { StepProps } from '.'; import { CollapsibleIndicesList } from '../../collapsible_lists'; @@ -285,18 +285,17 @@ export const RestoreSnapshotStepReview: React.FunctionComponent = ({ const renderJsonTab = () => ( - + > + {JSON.stringify(serializedRestoreSettings, null, 2)} + ); diff --git a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx index 46ad27e9a997b..70da1ebb1e449 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_settings.tsx @@ -21,8 +21,8 @@ import { EuiLink, EuiCallOut, } from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { RestoreSettings } from '../../../../../common/types'; -import { EuiCodeEditor } from '../../../../shared_imports'; import { REMOVE_INDEX_SETTINGS_SUGGESTIONS } from '../../../constants'; import { useCore, useServices } from '../../../app_context'; import { StepProps } from '.'; @@ -190,22 +190,15 @@ export const RestoreSnapshotStepSettings: React.FunctionComponent = ( /> } > - ( export const renderApp = (elem: Element, dependencies: AppDependencies) => { render( - - - , + + + + + , elem ); diff --git a/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts b/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts index 352e7c37f7ee2..b86cfe6318c80 100644 --- a/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts +++ b/x-pack/plugins/snapshot_restore/public/application/mount_management_section.ts @@ -37,6 +37,7 @@ export async function mountManagementSection( core, config, services: { + uiSettings: coreSetup.uiSettings, httpService, uiMetricService: services.uiMetricService, i18n, diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx index aee603d5d7f6d..b3256f2a0c6ff 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/tabs/tab_history.tsx @@ -19,11 +19,11 @@ import { EuiText, EuiHorizontalRule, EuiSpacer, + EuiCodeBlock, } from '@elastic/eui'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; import { SlmPolicy } from '../../../../../../../common/types'; -import { EuiCodeEditor } from '../../../../../../shared_imports'; import { FormattedDateTime } from '../../../../../components'; import { linkToSnapshot } from '../../../../../services/navigation'; import { useServices } from '../../../../../app_context'; @@ -148,23 +148,10 @@ export const TabHistory: React.FunctionComponent = ({ policy }) => { - = ({ policy }) => { values: { name }, } )} - /> + > + {JSON.stringify(details, null, 2)} + diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx index c1a379785ffd1..fb03299102db9 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/type_details/default_details.tsx @@ -5,15 +5,12 @@ * 2.0. */ -import 'react-ace'; -import 'brace/theme/textmate'; import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiSpacer, EuiTitle } from '@elastic/eui'; +import { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; import { Repository } from '../../../../../../../common/types'; -import { EuiCodeEditor } from '../../../../../../shared_imports'; interface Props { repository: Repository; @@ -35,22 +32,10 @@ export const DefaultDetails: React.FunctionComponent = ({ - = ({ }, } )} - /> + > + {JSON.stringify(settings, null, 2)} + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/shared_imports.ts b/x-pack/plugins/snapshot_restore/public/shared_imports.ts index 749d5ed51b53b..4a3ec13727dd1 100644 --- a/x-pack/plugins/snapshot_restore/public/shared_imports.ts +++ b/x-pack/plugins/snapshot_restore/public/shared_imports.ts @@ -28,7 +28,6 @@ export { useAuthorizationContext, useRequest, WithPrivileges, - EuiCodeEditor, AuthorizationContext, GlobalFlyout, } from '@kbn/es-ui-shared-plugin/public'; diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index ad73be5e5c19b..19c79c244e6c1 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -173,8 +173,10 @@ export class SpacesPlugin setupCapabilities(core, getSpacesService, this.log); if (plugins.usageCollection) { + const getIndexForType = (type: string) => + core.getStartServices().then(([coreStart]) => coreStart.savedObjects.getIndexForType(type)); registerSpacesUsageCollector(plugins.usageCollection, { - kibanaIndex: core.savedObjects.getKibanaIndex(), + getIndexForType, features: plugins.features, licensing: plugins.licensing, usageStatsServicePromise, diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts index 1d93fb7787050..36d2c99503c11 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts @@ -45,6 +45,7 @@ const MOCK_USAGE_STATS: UsageStats = { }; const kibanaIndex = '.kibana-tests'; +const getIndexForType = () => Promise.resolve(kibanaIndex); function setup({ license = { isAvailable: true }, @@ -122,7 +123,7 @@ describe('error handling', () => { license: { isAvailable: true, type: 'basic' }, }); const collector = getSpacesUsageCollector(usageCollection as any, { - kibanaIndex, + getIndexForType, features, licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), @@ -146,7 +147,7 @@ describe('with a basic license', () => { beforeAll(async () => { const collector = getSpacesUsageCollector(usageCollection as any, { - kibanaIndex, + getIndexForType, features, licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), @@ -205,7 +206,7 @@ describe('with no license', () => { beforeAll(async () => { const collector = getSpacesUsageCollector(usageCollection as any, { - kibanaIndex, + getIndexForType, features, licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), @@ -246,7 +247,7 @@ describe('with platinum license', () => { beforeAll(async () => { const collector = getSpacesUsageCollector(usageCollection as any, { - kibanaIndex, + getIndexForType, features, licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index d994d42f85a8a..bb41531ea9100 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -149,7 +149,7 @@ export interface UsageData extends UsageStats { } interface CollectorDeps { - kibanaIndex: string; + getIndexForType: (type: string) => Promise; features: PluginsSetup['features']; licensing: PluginsSetup['licensing']; usageStatsServicePromise: Promise; @@ -453,11 +453,12 @@ export function getSpacesUsageCollector( }, }, fetch: async ({ esClient }: CollectorFetchContext) => { - const { licensing, kibanaIndex, features, usageStatsServicePromise } = deps; + const { licensing, getIndexForType, features, usageStatsServicePromise } = deps; const license = await firstValueFrom(licensing.license$); const available = license.isAvailable; // some form of spaces is available for all valid licenses - const usageData = await getSpacesUsage(esClient, kibanaIndex, features, available); + const spaceIndex = await getIndexForType('space'); + const usageData = await getSpacesUsage(esClient, spaceIndex, features, available); const usageStats = await getUsageStats(usageStatsServicePromise, available); return { diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts index 66d96c36d2eb9..fded7e8ee37b9 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts @@ -116,11 +116,11 @@ describe('ruleType', () => { groupBy: 'all', }; - expect(ruleType.validate?.params?.validate(params)).toBeTruthy(); + expect(ruleType.validate.params.validate(params)).toBeTruthy(); }); it('validator fails with invalid es query params - threshold', async () => { - const paramsSchema = ruleType.validate?.params; + const paramsSchema = ruleType.validate.params; if (!paramsSchema) throw new Error('params validator not set'); const params: Partial> = { @@ -556,11 +556,11 @@ describe('ruleType', () => { }); it('validator succeeds with valid search source params', async () => { - expect(ruleType.validate?.params?.validate(defaultParams)).toBeTruthy(); + expect(ruleType.validate.params.validate(defaultParams)).toBeTruthy(); }); it('validator fails with invalid search source params - esQuery provided', async () => { - const paramsSchema = ruleType.validate?.params!; + const paramsSchema = ruleType.validate.params; const params: Partial> = { size: 100, timeWindowSize: 5, @@ -713,10 +713,10 @@ async function invokeExecutor({ tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts index 11a02beddc96d..b424fb742b5ab 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts @@ -138,11 +138,11 @@ describe('ruleType', () => { threshold: [0], }; - expect(ruleType.validate?.params?.validate(params)).toBeTruthy(); + expect(ruleType.validate.params.validate(params)).toBeTruthy(); }); it('validator fails with invalid params', async () => { - const paramsSchema = ruleType.validate?.params; + const paramsSchema = ruleType.validate.params; if (!paramsSchema) throw new Error('params validator not set'); const params: Partial> = { @@ -203,10 +203,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -270,10 +270,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -337,10 +337,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -403,10 +403,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, diff --git a/x-pack/plugins/stack_connectors/common/slack_api/constants.ts b/x-pack/plugins/stack_connectors/common/slack_api/constants.ts new file mode 100644 index 0000000000000..3c107e1c05342 --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/slack_api/constants.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 SLACK_API_CONNECTOR_ID = '.slack_api'; +export const SLACK_URL = 'https://slack.com/api/'; diff --git a/x-pack/plugins/stack_connectors/common/slack_api/lib.ts b/x-pack/plugins/stack_connectors/common/slack_api/lib.ts new file mode 100644 index 0000000000000..449b1aef56b14 --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/slack_api/lib.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActionTypeExecutorResult as ConnectorTypeExecutorResult } from '@kbn/actions-plugin/server/types'; +import { i18n } from '@kbn/i18n'; + +export function successResult( + actionId: string, + data: unknown +): ConnectorTypeExecutorResult { + return { status: 'ok', data, actionId }; +} + +export function errorResult(actionId: string, message: string): ConnectorTypeExecutorResult { + return { + status: 'error', + message, + actionId, + }; +} +export function serviceErrorResult( + actionId: string, + serviceMessage?: string +): ConnectorTypeExecutorResult { + const errMessage = i18n.translate('xpack.stackConnectors.slack.errorPostingErrorMessage', { + defaultMessage: 'error posting slack message', + }); + return { + status: 'error', + message: errMessage, + actionId, + serviceMessage, + }; +} + +export function retryResult(actionId: string, message: string): ConnectorTypeExecutorResult { + const errMessage = i18n.translate( + 'xpack.stackConnectors.slack.errorPostingRetryLaterErrorMessage', + { + defaultMessage: 'error posting a slack message, retry later', + } + ); + return { + status: 'error', + message: errMessage, + retry: true, + actionId, + }; +} + +export function retryResultSeconds( + actionId: string, + message: string, + retryAfter: number +): ConnectorTypeExecutorResult { + const retryEpoch = Date.now() + retryAfter * 1000; + const retry = new Date(retryEpoch); + const retryString = retry.toISOString(); + const errMessage = i18n.translate( + 'xpack.stackConnectors.slack.errorPostingRetryDateErrorMessage', + { + defaultMessage: 'error posting a slack message, retry at {retryString}', + values: { + retryString, + }, + } + ); + return { + status: 'error', + message: errMessage, + retry, + actionId, + serviceMessage: message, + }; +} diff --git a/x-pack/plugins/stack_connectors/common/slack_api/schema.ts b/x-pack/plugins/stack_connectors/common/slack_api/schema.ts new file mode 100644 index 0000000000000..a1060f3290b28 --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/slack_api/schema.ts @@ -0,0 +1,30 @@ +/* + * 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 } from '@kbn/config-schema'; + +export const SlackApiSecretsSchema = schema.object({ + token: schema.string({ minLength: 1 }), +}); + +export const GetChannelsParamsSchema = schema.object({ + subAction: schema.literal('getChannels'), +}); + +export const PostMessageSubActionParamsSchema = schema.object({ + channels: schema.arrayOf(schema.string()), + text: schema.string(), +}); +export const PostMessageParamsSchema = schema.object({ + subAction: schema.literal('postMessage'), + subActionParams: PostMessageSubActionParamsSchema, +}); + +export const SlackApiParamsSchema = schema.oneOf([ + GetChannelsParamsSchema, + PostMessageParamsSchema, +]); diff --git a/x-pack/plugins/stack_connectors/common/slack_api/types.ts b/x-pack/plugins/stack_connectors/common/slack_api/types.ts new file mode 100644 index 0000000000000..1098d40eded19 --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/slack_api/types.ts @@ -0,0 +1,67 @@ +/* + * 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 { ActionType as ConnectorType } from '@kbn/actions-plugin/server/types'; +import { TypeOf } from '@kbn/config-schema'; +import type { ActionTypeExecutorOptions as ConnectorTypeExecutorOptions } from '@kbn/actions-plugin/server/types'; +import type { ActionTypeExecutorResult as ConnectorTypeExecutorResult } from '@kbn/actions-plugin/server/types'; +import { + PostMessageParamsSchema, + PostMessageSubActionParamsSchema, + SlackApiSecretsSchema, + SlackApiParamsSchema, +} from './schema'; + +export type SlackApiSecrets = TypeOf; + +export type PostMessageParams = TypeOf; +export type PostMessageSubActionParams = TypeOf; +export type SlackApiParams = TypeOf; +export type SlackApiConnectorType = ConnectorType<{}, SlackApiSecrets, SlackApiParams, unknown>; + +export type SlackApiExecutorOptions = ConnectorTypeExecutorOptions< + {}, + SlackApiSecrets, + SlackApiParams +>; + +export type SlackExecutorOptions = ConnectorTypeExecutorOptions< + {}, + SlackApiSecrets, + SlackApiParams +>; + +export type SlackApiActionParams = TypeOf; + +export interface GetChannelsResponse { + ok: true; + error?: string; + channels?: Array<{ + id: string; + name: string; + is_channel: boolean; + is_archived: boolean; + is_private: boolean; + }>; +} + +export interface PostMessageResponse { + ok: boolean; + channel?: string; + error?: string; + message?: { + text: string; + }; +} + +export interface SlackApiService { + getChannels: () => Promise>; + postMessage: ({ + channels, + text, + }: PostMessageSubActionParams) => Promise>; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/index.ts index 55b2a31d2ca80..2cedad5996a8b 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/index.ts @@ -18,7 +18,8 @@ import { getServerLogConnectorType } from './server_log'; import { getServiceNowITOMConnectorType } from './servicenow_itom'; import { getServiceNowITSMConnectorType } from './servicenow_itsm'; import { getServiceNowSIRConnectorType } from './servicenow_sir'; -import { getSlackConnectorType } from './slack'; +import { getSlackWebhookConnectorType } from './slack'; +import { getSlackApiConnectorType } from './slack_api'; import { getSwimlaneConnectorType } from './swimlane'; import { getTeamsConnectorType } from './teams'; import { getTinesConnectorType } from './tines'; @@ -41,7 +42,8 @@ export function registerConnectorTypes({ services: RegistrationServices; }) { connectorTypeRegistry.register(getServerLogConnectorType()); - connectorTypeRegistry.register(getSlackConnectorType()); + connectorTypeRegistry.register(getSlackWebhookConnectorType()); + connectorTypeRegistry.register(getSlackApiConnectorType()); connectorTypeRegistry.register(getEmailConnectorType(services)); connectorTypeRegistry.register(getIndexConnectorType()); connectorTypeRegistry.register(getPagerDutyConnectorType()); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/slack/index.ts index 05d27afff76fb..74a96853ab149 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/slack/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getConnectorType as getSlackConnectorType } from './slack'; +export { getConnectorType as getSlackWebhookConnectorType } from './slack'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack/slack.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack/slack.tsx index fabfe46a4db12..c1b4b72182fa9 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/slack/slack.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack/slack.tsx @@ -12,10 +12,28 @@ import type { GenericValidationResult, } from '@kbn/triggers-actions-ui-plugin/public/types'; import { SlackActionParams, SlackSecrets } from '../types'; +import { PostMessageParams } from '../../../common/slack_api/types'; + +export const subtype = [ + { + id: '.slack', + name: i18n.translate('xpack.stackConnectors.components.slack.webhook', { + defaultMessage: 'Webhook', + }), + }, + { + id: '.slack_api', + name: i18n.translate('xpack.stackConnectors.components.slack.webApi', { + defaultMessage: 'Web API', + }), + }, +]; export function getConnectorType(): ConnectorTypeModel { return { id: '.slack', + subtype, + modalWidth: 675, iconClass: 'logoSlack', selectMessage: i18n.translate('xpack.stackConnectors.components.slack.selectMessageText', { defaultMessage: 'Send a message to a Slack channel or user.', @@ -38,5 +56,17 @@ export function getConnectorType(): ConnectorTypeModel import('./slack_connectors')), actionParamsFields: lazy(() => import('./slack_params')), + convertParamsBetweenGroups: ( + params: PostMessageParams | SlackActionParams + ): PostMessageParams | SlackActionParams | {} => { + if ('message' in params) { + return params; + } else if ('subAction' in params) { + return { + message: (params as PostMessageParams).subActionParams.text, + }; + } + return {}; + }, }; } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack/slack_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack/slack_connectors.test.tsx index 0910565276216..e81dec2d662c2 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/slack/slack_connectors.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack/slack_connectors.test.tsx @@ -91,7 +91,7 @@ describe('SlackActionFields renders', () => { }); }); - it('validates teh web hook url field correctly', async () => { + it('validates the web hook url field correctly', async () => { const actionConnector = { secrets: { webhookUrl: 'http://test.com', diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/index.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/index.tsx new file mode 100644 index 0000000000000..258473accdd68 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/index.tsx @@ -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 { getConnectorType as getSlackApiConnectorType } from './slack_api'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_api.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_api.test.tsx new file mode 100644 index 0000000000000..17ed4f9380e09 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_api.test.tsx @@ -0,0 +1,88 @@ +/* + * 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 { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import type { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { registerConnectorTypes } from '..'; +import { registrationServicesMock } from '../../mocks'; +import { SLACK_API_CONNECTOR_ID } from '../../../common/slack_api/constants'; + +let connectorTypeModel: ConnectorTypeModel; + +beforeAll(async () => { + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(SLACK_API_CONNECTOR_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('connectorTypeRegistry.get works', () => { + test('connector type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(SLACK_API_CONNECTOR_ID); + expect(connectorTypeModel.iconClass).toEqual('logoSlack'); + }); +}); + +describe('Slack action params validation', () => { + test('should succeed when action params include valid message and channels list', async () => { + const actionParams = { + subAction: 'postMessage', + subActionParams: { channels: ['general'], text: 'some text' }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + text: [], + channels: [], + }, + }); + }); + + test('should fail when channels field is missing in action params', async () => { + const actionParams = { + subAction: 'postMessage', + subActionParams: { text: 'some text' }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + text: [], + channels: ['Selected channel is required.'], + }, + }); + }); + + test('should fail when field text does not exist', async () => { + const actionParams = { + subAction: 'postMessage', + subActionParams: { channels: ['general'] }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + text: ['Message is required.'], + channels: [], + }, + }); + }); + + test('should fail when text is empty string', async () => { + const actionParams = { + subAction: 'postMessage', + subActionParams: { channels: ['general'], text: '' }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + text: ['Message is required.'], + channels: [], + }, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_api.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_api.tsx new file mode 100644 index 0000000000000..6b985dbb90e34 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_api.tsx @@ -0,0 +1,76 @@ +/* + * 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 { lazy } from 'react'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { + ACTION_TYPE_TITLE, + CHANNEL_REQUIRED, + MESSAGE_REQUIRED, + SELECT_MESSAGE, +} from './translations'; +import type { + SlackApiActionParams, + SlackApiSecrets, + PostMessageParams, +} from '../../../common/slack_api/types'; +import { SLACK_API_CONNECTOR_ID } from '../../../common/slack_api/constants'; +import { SlackActionParams } from '../types'; +import { subtype } from '../slack/slack'; + +export const getConnectorType = (): ConnectorTypeModel< + unknown, + SlackApiSecrets, + PostMessageParams +> => ({ + id: SLACK_API_CONNECTOR_ID, + subtype, + hideInUi: true, + modalWidth: 675, + iconClass: 'logoSlack', + selectMessage: SELECT_MESSAGE, + actionTypeTitle: ACTION_TYPE_TITLE, + validateParams: async ( + actionParams: SlackApiActionParams + ): Promise> => { + const errors = { + text: new Array(), + channels: new Array(), + }; + const validationResult = { errors }; + if (actionParams.subAction === 'postMessage') { + if (!actionParams.subActionParams.text) { + errors.text.push(MESSAGE_REQUIRED); + } + if (!actionParams.subActionParams.channels?.length) { + errors.channels.push(CHANNEL_REQUIRED); + } + } + return validationResult; + }, + actionConnectorFields: lazy(() => import('./slack_connectors')), + actionParamsFields: lazy(() => import('./slack_params')), + convertParamsBetweenGroups: ( + params: SlackActionParams | PostMessageParams + ): SlackActionParams | PostMessageParams | {} => { + if ('message' in params) { + return { + subAction: 'postMessage', + subActionParams: { + channels: [], + text: params.message, + }, + }; + } else if ('subAction' in params) { + return params; + } + return {}; + }, +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_connectors.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_connectors.test.tsx new file mode 100644 index 0000000000000..ef9877c5a8772 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_connectors.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { act, render, fireEvent, screen } from '@testing-library/react'; +import SlackActionFields from './slack_connectors'; +import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../lib/test_utils'; + +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); + +describe('SlackActionFields renders', () => { + const onSubmit = jest.fn(); + beforeEach(() => { + jest.clearAllMocks(); + }); + it('all connector fields is rendered for web_api type', async () => { + const actionConnector = { + secrets: { + token: 'some token', + }, + id: 'test', + actionTypeId: '.slack', + name: 'slack', + config: {}, + isDeprecated: false, + }; + + render( + + {}} /> + + ); + + expect(screen.getByTestId('secrets.token-input')).toBeInTheDocument(); + expect(screen.getByTestId('secrets.token-input')).toHaveValue('some token'); + }); + + it('connector validation succeeds when connector config is valid for Web API type', async () => { + const actionConnector = { + secrets: { + token: 'some token', + }, + id: 'test', + actionTypeId: '.slack', + name: 'slack', + config: {}, + isDeprecated: false, + }; + + render( + + {}} /> + + ); + await waitForComponentToUpdate(); + await act(async () => { + fireEvent.click(screen.getByTestId('form-test-provide-submit')); + }); + expect(onSubmit).toBeCalledTimes(1); + expect(onSubmit).toBeCalledWith({ + data: { + secrets: { + token: 'some token', + }, + id: 'test', + actionTypeId: '.slack', + name: 'slack', + isDeprecated: false, + }, + isValid: true, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_connectors.tsx new file mode 100644 index 0000000000000..4d36cc851ce69 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_connectors.tsx @@ -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 React from 'react'; +import { + ActionConnectorFieldsProps, + SecretsFieldSchema, + SimpleConnectorForm, +} from '@kbn/triggers-actions-ui-plugin/public'; +import * as i18n from './translations'; + +const secretsFormSchema: SecretsFieldSchema[] = [ + { + id: 'token', + label: i18n.TOKEN_LABEL, + isPasswordField: true, + }, +]; + +const SlackActionFields: React.FC = ({ readOnly, isEdit }) => { + return ( + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { SlackActionFields as default }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_params.test.tsx new file mode 100644 index 0000000000000..e8353a3bbabf3 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_params.test.tsx @@ -0,0 +1,201 @@ +/* + * 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, screen, fireEvent } from '@testing-library/react'; +import SlackParamsFields from './slack_params'; +import type { UseSubActionParams } from '@kbn/triggers-actions-ui-plugin/public/application/hooks/use_sub_action'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +interface Result { + isLoading: boolean; + response: Record; + error: null | Error; +} + +const triggersActionsPath = '@kbn/triggers-actions-ui-plugin/public'; + +const mockUseSubAction = jest.fn]>( + jest.fn]>(() => ({ + isLoading: false, + response: { + channels: [ + { + id: 'id', + name: 'general', + is_channel: true, + is_archived: false, + is_private: true, + }, + ], + }, + error: null, + })) +); + +const mockToasts = { danger: jest.fn(), warning: jest.fn() }; +jest.mock(triggersActionsPath, () => { + const original = jest.requireActual(triggersActionsPath); + return { + ...original, + useSubAction: (params: UseSubActionParams) => mockUseSubAction(params), + useKibana: () => ({ + ...original.useKibana(), + notifications: { toasts: mockToasts }, + }), + }; +}); + +describe('SlackParamsFields renders', () => { + test('when useDefaultMessage is set to true and the default message changes, the underlying message is replaced with the default message', () => { + const editAction = jest.fn(); + const { rerender } = render( + + + + ); + expect(screen.getByTestId('webApiTextArea')).toBeInTheDocument(); + expect(screen.getByTestId('webApiTextArea')).toHaveValue('some text'); + rerender( + + + + ); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { channels: ['general'], text: 'some different default message' }, + 0 + ); + }); + + test('when useDefaultMessage is set to false and the default message changes, the underlying message is not changed, Web API', () => { + const editAction = jest.fn(); + const { rerender } = render( + + + + ); + expect(screen.getByTestId('webApiTextArea')).toBeInTheDocument(); + expect(screen.getByTestId('webApiTextArea')).toHaveValue('some text'); + + rerender( + + + + ); + expect(editAction).not.toHaveBeenCalled(); + }); + + test('all params fields is rendered for postMessage call', async () => { + render( + + {}} + index={0} + defaultMessage="default message" + messageVariables={[]} + /> + + ); + + expect(screen.getByTestId('webApiTextArea')).toBeInTheDocument(); + expect(screen.getByTestId('webApiTextArea')).toHaveValue('some text'); + }); + + test('all params fields is rendered for getChannels call', async () => { + render( + + {}} + index={0} + defaultMessage="default message" + messageVariables={[]} + /> + + ); + + expect(screen.getByTestId('slackChannelsButton')).toHaveTextContent('Channels'); + fireEvent.click(screen.getByTestId('slackChannelsButton')); + expect(screen.getByTestId('slackChannelsSelectableList')).toBeInTheDocument(); + expect(screen.getByTestId('slackChannelsSelectableList')).toHaveTextContent('general'); + fireEvent.click(screen.getByText('general')); + expect(screen.getByTitle('general').getAttribute('aria-checked')).toEqual('true'); + }); + + test('show error message when no channel is selected', async () => { + render( + + {}} + index={0} + defaultMessage="default message" + messageVariables={[]} + /> + + ); + expect(screen.getByText('my error message')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_params.tsx new file mode 100644 index 0000000000000..6d5f284e764b5 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/slack_params.tsx @@ -0,0 +1,209 @@ +/* + * 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, useMemo, useCallback } from 'react'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { TextAreaWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { + EuiSpacer, + EuiFilterGroup, + EuiPopover, + EuiFilterButton, + EuiSelectable, + EuiSelectableOption, + EuiFormRow, +} from '@elastic/eui'; +import { useSubAction, useKibana } from '@kbn/triggers-actions-ui-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { GetChannelsResponse, PostMessageParams } from '../../../common/slack_api/types'; + +interface ChannelsStatus { + label: string; + checked?: 'on'; +} + +const SlackParamsFields: React.FunctionComponent> = ({ + actionConnector, + actionParams, + editAction, + index, + errors, + messageVariables, + defaultMessage, + useDefaultMessage, +}) => { + const { subAction, subActionParams } = actionParams; + const { channels = [], text } = subActionParams ?? {}; + const { toasts } = useKibana().notifications; + + useEffect(() => { + if (useDefaultMessage || !text) { + editAction('subActionParams', { channels, text: defaultMessage }, index); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultMessage, useDefaultMessage]); + + if (!subAction) { + editAction('subAction', 'postMessage', index); + } + if (!subActionParams) { + editAction( + 'subActionParams', + { + channels, + text, + }, + index + ); + } + + const { + response: { channels: channelsInfo } = {}, + isLoading: isLoadingChannels, + error: channelsError, + } = useSubAction({ + connectorId: actionConnector?.id, + subAction: 'getChannels', + }); + + useEffect(() => { + if (channelsError) { + toasts.danger({ + title: i18n.translate( + 'xpack.stackConnectors.slack.params.componentError.getChannelsRequestFailed', + { + defaultMessage: 'Failed to retrieve Slack channels list', + } + ), + body: channelsError.message, + }); + } + }, [toasts, channelsError]); + + const slackChannels = useMemo( + () => + channelsInfo + ?.filter((slackChannel) => slackChannel.is_channel) + .map((slackChannel) => ({ label: slackChannel.name })) ?? [], + [channelsInfo] + ); + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [selectedChannels, setSelectedChannels] = useState(channels ?? []); + + const button = ( + setIsPopoverOpen(!isPopoverOpen)} + numFilters={selectedChannels.length} + hasActiveFilters={selectedChannels.length > 0} + numActiveFilters={selectedChannels.length} + data-test-subj="slackChannelsButton" + > + + + ); + + const options: ChannelsStatus[] = useMemo( + () => + slackChannels.map((slackChannel) => ({ + label: slackChannel.label, + ...(selectedChannels.includes(slackChannel.label) ? { checked: 'on' } : {}), + })), + [slackChannels, selectedChannels] + ); + + const onChange = useCallback( + (newOptions: EuiSelectableOption[]) => { + const newSelectedChannels = newOptions.reduce((result, option) => { + if (option.checked === 'on') { + result = [...result, option.label]; + } + return result; + }, []); + + setSelectedChannels(newSelectedChannels); + editAction('subActionParams', { channels: newSelectedChannels, text }, index); + }, + [editAction, index, text] + ); + + return ( + <> + 0 && channels.length === 0} + > + + setIsPopoverOpen(false)} + > + + {(list, search) => ( + <> + {search} + + {list} + + )} + + + + + + + editAction('subActionParams', { channels, text: value }, index) + } + messageVariables={messageVariables} + paramsProperty="webApi" + inputTargetValue={text} + label={i18n.translate('xpack.stackConnectors.components.slack.messageTextAreaFieldLabel', { + defaultMessage: 'Message', + })} + errors={(errors.text ?? []) as string[]} + /> + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { SlackParamsFields as default }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack_api/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/translations.ts new file mode 100644 index 0000000000000..2c3ea5276ab92 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack_api/translations.ts @@ -0,0 +1,42 @@ +/* + * 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 MESSAGE_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.slack.error.requiredSlackMessageText', + { + defaultMessage: 'Message is required.', + } +); +export const CHANNEL_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.slack.error.requiredSlackChannel', + { + defaultMessage: 'Selected channel is required.', + } +); +export const TOKEN_LABEL = i18n.translate( + 'xpack.stackConnectors.components.slack.tokenTextFieldLabel', + { + defaultMessage: 'API Token', + } +); +export const WEB_API = i18n.translate('xpack.stackConnectors.components.slack.webApi', { + defaultMessage: 'Web API', +}); +export const SELECT_MESSAGE = i18n.translate( + 'xpack.stackConnectors.components.slack.selectMessageText', + { + defaultMessage: 'Send a message to a Slack channel or user.', + } +); +export const ACTION_TYPE_TITLE = i18n.translate( + 'xpack.stackConnectors.components.slack.connectorTypeTitle', + { + defaultMessage: 'Send to Slack', + } +); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/index.ts index 648695bc7cbbd..0cd9a3b5a7194 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/index.ts @@ -20,7 +20,8 @@ import { getConnectorType as getIndexConnectorType } from './es_index'; import { getConnectorType as getPagerDutyConnectorType } from './pagerduty'; import { getConnectorType as getSwimlaneConnectorType } from './swimlane'; import { getConnectorType as getServerLogConnectorType } from './server_log'; -import { getConnectorType as getSlackConnectorType } from './slack'; +import { getConnectorType as getSlackWebhookConnectorType } from './slack'; +import { getConnectorType as getSlackApiConnectorType } from './slack_api'; import { getConnectorType as getWebhookConnectorType } from './webhook'; import { getConnectorType as getXmattersConnectorType } from './xmatters'; import { getConnectorType as getTeamsConnectorType } from './teams'; @@ -45,8 +46,10 @@ export type { ActionParamsType as PagerDutyActionParams } from './pagerduty'; export { ConnectorTypeId as ServerLogConnectorTypeId } from './server_log'; export type { ActionParamsType as ServerLogActionParams } from './server_log'; export { ServiceNowITOMConnectorTypeId } from './servicenow_itom'; -export { ConnectorTypeId as SlackConnectorTypeId } from './slack'; -export type { ActionParamsType as SlackActionParams } from './slack'; +export { ConnectorTypeId as SlackWebhookConnectorTypeId } from './slack'; +export type { ActionParamsType as SlackWebhookActionParams } from './slack'; +export { SLACK_API_CONNECTOR_ID as SlackApiConnectorTypeId } from '../../common/slack_api/constants'; +export type { SlackApiActionParams as SlackApiActionParams } from '../../common/slack_api/types'; export { ConnectorTypeId as TeamsConnectorTypeId } from './teams'; export type { ActionParamsType as TeamsActionParams } from './teams'; export { ConnectorTypeId as WebhookConnectorTypeId } from './webhook'; @@ -80,7 +83,8 @@ export function registerConnectorTypes({ actions.registerType(getPagerDutyConnectorType()); actions.registerType(getSwimlaneConnectorType()); actions.registerType(getServerLogConnectorType()); - actions.registerType(getSlackConnectorType({})); + actions.registerType(getSlackWebhookConnectorType({})); + actions.registerType(getSlackApiConnectorType()); actions.registerType(getWebhookConnectorType()); actions.registerType(getCasesWebhookConnectorType()); actions.registerType(getXmattersConnectorType()); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/slack_api/api.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/api.test.ts new file mode 100644 index 0000000000000..2ae4a998b261a --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/api.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { SlackApiService } from '../../../common/slack_api/types'; +import { api } from './api'; + +const createMock = (): jest.Mocked => { + const service = { + postMessage: jest.fn().mockImplementation(() => ({ + ok: true, + channel: 'general', + message: { + text: 'a message', + type: 'message', + }, + })), + getChannels: jest.fn().mockImplementation(() => [ + { + ok: true, + channels: [ + { + id: 'channel_id_1', + name: 'general', + is_channel: true, + is_archived: false, + is_private: true, + }, + { + id: 'channel_id_2', + name: 'privat', + is_channel: true, + is_archived: false, + is_private: false, + }, + ], + }, + ]), + }; + + return service; +}; + +const slackServiceMock = { + create: createMock, +}; + +describe('api', () => { + let externalService: jest.Mocked; + + beforeEach(() => { + externalService = slackServiceMock.create(); + }); + + test('getChannels', async () => { + const res = await api.getChannels({ + externalService, + }); + + expect(res).toEqual([ + { + channels: [ + { + id: 'channel_id_1', + is_archived: false, + is_channel: true, + is_private: true, + name: 'general', + }, + { + id: 'channel_id_2', + is_archived: false, + is_channel: true, + is_private: false, + name: 'privat', + }, + ], + ok: true, + }, + ]); + }); + + test('postMessage', async () => { + const res = await api.postMessage({ + externalService, + params: { channels: ['general'], text: 'a message' }, + }); + + expect(res).toEqual({ + channel: 'general', + message: { + text: 'a message', + type: 'message', + }, + ok: true, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/slack_api/api.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/api.ts new file mode 100644 index 0000000000000..b0445b7c26e41 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/api.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PostMessageSubActionParams, SlackApiService } from '../../../common/slack_api/types'; + +const getChannelsHandler = async ({ externalService }: { externalService: SlackApiService }) => + await externalService.getChannels(); + +const postMessageHandler = async ({ + externalService, + params: { channels, text }, +}: { + externalService: SlackApiService; + params: PostMessageSubActionParams; +}) => await externalService.postMessage({ channels, text }); + +export const api = { + getChannels: getChannelsHandler, + postMessage: postMessageHandler, +}; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/slack_api/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/index.test.ts new file mode 100644 index 0000000000000..66bc3fba1219c --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/index.test.ts @@ -0,0 +1,303 @@ +/* + * 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 axios from 'axios'; +import { Logger } from '@kbn/core/server'; +import { Services } from '@kbn/actions-plugin/server/types'; +import { validateParams, validateSecrets } from '@kbn/actions-plugin/server/lib'; +import { getConnectorType } from '.'; +import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; +import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { loggerMock } from '@kbn/logging-mocks'; +import * as utils from '@kbn/actions-plugin/server/lib/axios_utils'; +import type { PostMessageParams, SlackApiConnectorType } from '../../../common/slack_api/types'; +import { SLACK_API_CONNECTOR_ID } from '../../../common/slack_api/constants'; +import { SLACK_CONNECTOR_NAME } from './translations'; + +jest.mock('axios'); +jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { + const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + }; +}); + +const requestMock = utils.request as jest.Mock; + +const services: Services = actionsMock.createServices(); +const mockedLogger: jest.Mocked = loggerMock.create(); + +let connectorType: SlackApiConnectorType; +let configurationUtilities: jest.Mocked; + +beforeEach(() => { + configurationUtilities = actionsConfigMock.create(); + connectorType = getConnectorType(); +}); + +describe('connector registration', () => { + test('returns connector type', () => { + expect(connectorType.id).toEqual(SLACK_API_CONNECTOR_ID); + expect(connectorType.name).toEqual(SLACK_CONNECTOR_NAME); + }); +}); + +describe('validate params', () => { + test('should validate and throw error when params are invalid', () => { + expect(() => { + validateParams(connectorType, {}, { configurationUtilities }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action params: Cannot destructure property 'Symbol(Symbol.iterator)' of 'undefined' as it is undefined."` + ); + + expect(() => { + validateParams(connectorType, { message: 1 }, { configurationUtilities }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action params: Cannot destructure property 'Symbol(Symbol.iterator)' of 'undefined' as it is undefined."` + ); + }); + + test('should validate and pass when params are valid for post message', () => { + expect( + validateParams( + connectorType, + { subAction: 'postMessage', subActionParams: { channels: ['general'], text: 'a text' } }, + { configurationUtilities } + ) + ).toEqual({ + subAction: 'postMessage', + subActionParams: { channels: ['general'], text: 'a text' }, + }); + }); + + test('should validate and pass when params are valid for get channels', () => { + expect( + validateParams(connectorType, { subAction: 'getChannels' }, { configurationUtilities }) + ).toEqual({ + subAction: 'getChannels', + }); + }); +}); + +describe('validate secrets', () => { + test('should validate and throw error when secrets is empty', () => { + expect(() => { + validateSecrets(connectorType, {}, { configurationUtilities }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type secrets: [token]: expected value of type [string] but got [undefined]"` + ); + }); + + test('should validate and pass when secrets is valid', () => { + validateSecrets( + connectorType, + { + token: 'token', + }, + { configurationUtilities } + ); + }); + + test('should validate and throw error when secrets is invalid', () => { + expect(() => { + validateSecrets(connectorType, { token: 1 }, { configurationUtilities }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type secrets: [token]: expected value of type [string] but got [number]"` + ); + }); + + test('config validation returns an error if the specified URL isnt added to allowedHosts', () => { + const configUtils = { + ...actionsConfigMock.create(), + ensureUriAllowed: () => { + throw new Error(`target hostname is not added to allowedHosts`); + }, + }; + + expect(() => { + validateSecrets( + connectorType, + { token: 'fake token' }, + { configurationUtilities: configUtils } + ); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type secrets: error configuring slack action: target hostname is not added to allowedHosts"` + ); + }); +}); + +describe('execute', () => { + beforeEach(() => { + jest.resetAllMocks(); + axios.create = jest.fn().mockImplementation(() => axios); + connectorType = getConnectorType(); + }); + + test('should fail if params does not include subAction', async () => { + requestMock.mockImplementation(() => ({ + data: { + ok: true, + message: { text: 'some text' }, + channel: 'general', + }, + })); + + await expect( + connectorType.executor({ + actionId: SLACK_API_CONNECTOR_ID, + config: {}, + services, + secrets: { token: 'some token' }, + params: {} as PostMessageParams, + configurationUtilities, + logger: mockedLogger, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"[Action][ExternalService] -> [Slack API] Unsupported subAction type undefined."` + ); + }); + + test('should fail if subAction is not postMessage/getChannels', async () => { + requestMock.mockImplementation(() => ({ + data: { + ok: true, + message: { text: 'some text' }, + channel: 'general', + }, + })); + + await expect( + connectorType.executor({ + actionId: SLACK_API_CONNECTOR_ID, + services, + config: {}, + secrets: { token: 'some token' }, + params: { + subAction: 'getMessage' as 'getChannels', + }, + configurationUtilities, + logger: mockedLogger, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"[Action][ExternalService] -> [Slack API] Unsupported subAction type getMessage."` + ); + }); + + test('renders parameter templates as expected', async () => { + expect(connectorType.renderParameterTemplates).toBeTruthy(); + const paramsWithTemplates = { + subAction: 'postMessage' as const, + subActionParams: { text: 'some text', channels: ['general'] }, + }; + const variables = { rogue: '*bold*' }; + const params = connectorType.renderParameterTemplates!( + paramsWithTemplates, + variables + ) as PostMessageParams; + expect(params.subActionParams.text).toBe('some text'); + }); + + test('should execute with success for post message', async () => { + requestMock.mockImplementation(() => ({ + data: { + ok: true, + message: { text: 'some text' }, + channel: 'general', + }, + })); + + const response = await connectorType.executor({ + actionId: SLACK_API_CONNECTOR_ID, + services, + config: {}, + secrets: { token: 'some token' }, + params: { + subAction: 'postMessage', + subActionParams: { channels: ['general'], text: 'some text' }, + }, + configurationUtilities, + logger: mockedLogger, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + configurationUtilities, + logger: mockedLogger, + method: 'post', + url: 'chat.postMessage', + data: { channel: 'general', text: 'some text' }, + }); + + expect(response).toEqual({ + actionId: SLACK_API_CONNECTOR_ID, + data: { + channel: 'general', + message: { + text: 'some text', + }, + ok: true, + }, + + status: 'ok', + }); + }); + + test('should execute with success for get channels', async () => { + requestMock.mockImplementation(() => ({ + data: { + ok: true, + channels: [ + { + id: 'id', + name: 'general', + is_channel: true, + is_archived: false, + is_private: true, + }, + ], + }, + })); + const response = await connectorType.executor({ + actionId: SLACK_API_CONNECTOR_ID, + services, + config: {}, + secrets: { token: 'some token' }, + params: { + subAction: 'getChannels', + }, + configurationUtilities, + logger: mockedLogger, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + configurationUtilities, + logger: mockedLogger, + method: 'get', + url: 'conversations.list?types=public_channel,private_channel', + }); + + expect(response).toEqual({ + actionId: SLACK_API_CONNECTOR_ID, + data: { + channels: [ + { + id: 'id', + is_archived: false, + is_channel: true, + is_private: true, + name: 'general', + }, + ], + ok: true, + }, + status: 'ok', + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/slack_api/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/index.ts new file mode 100644 index 0000000000000..ee467dad3d8a2 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/index.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 { ActionTypeExecutorResult } from '@kbn/actions-plugin/server/types'; +import { + AlertingConnectorFeatureId, + SecurityConnectorFeatureId, +} from '@kbn/actions-plugin/common/types'; +import { renderMustacheString } from '@kbn/actions-plugin/server/lib/mustache_renderer'; +import type { ValidatorServices } from '@kbn/actions-plugin/server/types'; +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; +import type { + SlackApiExecutorOptions, + SlackApiConnectorType, + SlackApiParams, + SlackApiSecrets, +} from '../../../common/slack_api/types'; +import { SlackApiSecretsSchema, SlackApiParamsSchema } from '../../../common/slack_api/schema'; +import { SLACK_API_CONNECTOR_ID, SLACK_URL } from '../../../common/slack_api/constants'; +import { SLACK_CONNECTOR_NAME } from './translations'; +import { api } from './api'; +import { createExternalService } from './service'; + +const supportedSubActions = ['getChannels', 'postMessage']; + +export const getConnectorType = (): SlackApiConnectorType => { + return { + id: SLACK_API_CONNECTOR_ID, + minimumLicenseRequired: 'gold', + name: SLACK_CONNECTOR_NAME, + supportedFeatureIds: [AlertingConnectorFeatureId, SecurityConnectorFeatureId], + validate: { + config: { schema: schema.object({}, { defaultValue: {} }) }, + secrets: { + schema: SlackApiSecretsSchema, + customValidator: validateSlackUrl, + }, + params: { + schema: SlackApiParamsSchema, + }, + }, + renderParameterTemplates, + executor: async (execOptions: SlackApiExecutorOptions) => await slackApiExecutor(execOptions), + }; +}; + +const validateSlackUrl = (secretsObject: SlackApiSecrets, validatorServices: ValidatorServices) => { + const { configurationUtilities } = validatorServices; + + try { + configurationUtilities.ensureUriAllowed(SLACK_URL); + } catch (allowedListError) { + throw new Error( + i18n.translate('xpack.stackConnectors.slack_api.configurationError', { + defaultMessage: 'error configuring slack action: {message}', + values: { + message: allowedListError.message, + }, + }) + ); + } +}; + +const renderParameterTemplates = (params: SlackApiParams, variables: Record) => { + if (params.subAction === 'postMessage') + return { + subAction: params.subAction, + subActionParams: { + ...params.subActionParams, + text: renderMustacheString(params.subActionParams.text, variables, 'slack'), + }, + }; + return params; +}; + +const slackApiExecutor = async ({ + actionId, + params, + secrets, + configurationUtilities, + logger, +}: SlackApiExecutorOptions): Promise> => { + const subAction = params.subAction; + + if (!api[subAction]) { + const errorMessage = `[Action][ExternalService] -> [Slack API] Unsupported subAction type ${subAction}.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + if (!supportedSubActions.includes(subAction)) { + const errorMessage = `[Action][ExternalService] -> [Slack API] subAction ${subAction} not implemented.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + const externalService = createExternalService( + { + secrets, + }, + logger, + configurationUtilities + ); + + if (subAction === 'getChannels') { + return await api.getChannels({ + externalService, + }); + } + + if (subAction === 'postMessage') { + return await api.postMessage({ + externalService, + params: params.subActionParams, + }); + } + + return { status: 'ok', data: {}, actionId }; +}; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/slack_api/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/service.test.ts new file mode 100644 index 0000000000000..350c6fc103fb4 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/service.test.ts @@ -0,0 +1,181 @@ +/* + * 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 axios from 'axios'; +import { request, createAxiosResponse } from '@kbn/actions-plugin/server/lib/axios_utils'; +import { Logger } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { createExternalService } from './service'; +import { SlackApiService } from '../../../common/slack_api/types'; +import { SLACK_API_CONNECTOR_ID } from '../../../common/slack_api/constants'; + +const logger = loggingSystemMock.create().get() as jest.Mocked; + +jest.mock('axios'); +jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { + const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = request as jest.Mock; +const configurationUtilities = actionsConfigMock.create(); + +const channels = [ + { + id: 'channel_id_1', + name: 'general', + is_channel: true, + is_archived: false, + is_private: true, + }, + { + id: 'channel_id_2', + name: 'privat', + is_channel: true, + is_archived: false, + is_private: false, + }, +]; + +const getChannelsResponse = createAxiosResponse({ + data: { + ok: true, + channels, + }, +}); + +const postMessageResponse = createAxiosResponse({ + data: [ + { + ok: true, + channel: 'general', + message: { + text: 'a message', + type: 'message', + }, + }, + { + ok: true, + channel: 'privat', + message: { + text: 'a message', + type: 'message', + }, + }, + ], +}); + +describe('Slack API service', () => { + let service: SlackApiService; + + beforeAll(() => { + service = createExternalService( + { + secrets: { token: 'token' }, + }, + logger, + configurationUtilities + ); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('Secrets validation', () => { + test('throws without token', () => { + expect(() => + createExternalService( + { + secrets: { token: '' }, + }, + logger, + configurationUtilities + ) + ).toThrowErrorMatchingInlineSnapshot(`"[Action][Slack API]: Wrong configuration."`); + }); + }); + + describe('getChannels', () => { + test('should get slack channels', async () => { + requestMock.mockImplementation(() => getChannelsResponse); + const res = await service.getChannels(); + expect(res).toEqual({ + actionId: SLACK_API_CONNECTOR_ID, + data: { + ok: true, + channels, + }, + status: 'ok', + }); + }); + + test('should call request with correct arguments', async () => { + requestMock.mockImplementation(() => getChannelsResponse); + + await service.getChannels(); + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + method: 'get', + url: 'conversations.list?types=public_channel,private_channel', + }); + }); + + test('should throw an error if request to slack fail', async () => { + requestMock.mockImplementation(() => { + throw new Error('request fail'); + }); + + expect(await service.getChannels()).toEqual({ + actionId: SLACK_API_CONNECTOR_ID, + message: 'error posting slack message', + serviceMessage: 'request fail', + status: 'error', + }); + }); + }); + + describe('postMessage', () => { + test('should call request with correct arguments', async () => { + requestMock.mockImplementation(() => postMessageResponse); + + await service.postMessage({ channels: ['general', 'privat'], text: 'a message' }); + + expect(requestMock).toHaveBeenCalledTimes(1); + expect(requestMock).toHaveBeenNthCalledWith(1, { + axios, + logger, + configurationUtilities, + method: 'post', + url: 'chat.postMessage', + data: { channel: 'general', text: 'a message' }, + }); + }); + + test('should throw an error if request to slack fail', async () => { + requestMock.mockImplementation(() => { + throw new Error('request fail'); + }); + + expect( + await service.postMessage({ channels: ['general', 'privat'], text: 'a message' }) + ).toEqual({ + actionId: SLACK_API_CONNECTOR_ID, + message: 'error posting slack message', + serviceMessage: 'request fail', + status: 'error', + }); + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/slack_api/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/service.ts new file mode 100644 index 0000000000000..723d629a74418 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/service.ts @@ -0,0 +1,162 @@ +/* + * 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 axios, { AxiosResponse } from 'axios'; +import { Logger } from '@kbn/core/server'; +import { i18n } from '@kbn/i18n'; +import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { map, getOrElse } from 'fp-ts/lib/Option'; +import type { ActionTypeExecutorResult as ConnectorTypeExecutorResult } from '@kbn/actions-plugin/server/types'; +import { SLACK_CONNECTOR_NAME } from './translations'; +import type { + PostMessageSubActionParams, + SlackApiService, + PostMessageResponse, +} from '../../../common/slack_api/types'; +import { + retryResultSeconds, + retryResult, + serviceErrorResult, + errorResult, + successResult, +} from '../../../common/slack_api/lib'; +import { SLACK_API_CONNECTOR_ID, SLACK_URL } from '../../../common/slack_api/constants'; +import { getRetryAfterIntervalFromHeaders } from '../lib/http_response_retry_header'; + +const buildSlackExecutorErrorResponse = ({ + slackApiError, + logger, +}: { + slackApiError: { + message: string; + response: { + status: number; + statusText: string; + headers: Record; + }; + }; + logger: Logger; +}) => { + if (!slackApiError.response) { + return serviceErrorResult(SLACK_API_CONNECTOR_ID, slackApiError.message); + } + + const { status, statusText, headers } = slackApiError.response; + + // special handling for 5xx + if (status >= 500) { + return retryResult(SLACK_API_CONNECTOR_ID, slackApiError.message); + } + + // special handling for rate limiting + if (status === 429) { + return pipe( + getRetryAfterIntervalFromHeaders(headers), + map((retry) => retryResultSeconds(SLACK_API_CONNECTOR_ID, slackApiError.message, retry)), + getOrElse(() => retryResult(SLACK_API_CONNECTOR_ID, slackApiError.message)) + ); + } + + const errorMessage = i18n.translate( + 'xpack.stackConnectors.slack.unexpectedHttpResponseErrorMessage', + { + defaultMessage: 'unexpected http response from slack: {httpStatus} {httpStatusText}', + values: { + httpStatus: status, + httpStatusText: statusText, + }, + } + ); + logger.error(`error on ${SLACK_API_CONNECTOR_ID} slack action: ${errorMessage}`); + + return errorResult(SLACK_API_CONNECTOR_ID, errorMessage); +}; + +const buildSlackExecutorSuccessResponse = ({ + slackApiResponseData, +}: { + slackApiResponseData: PostMessageResponse; +}) => { + if (!slackApiResponseData) { + const errMessage = i18n.translate( + 'xpack.stackConnectors.slack.unexpectedNullResponseErrorMessage', + { + defaultMessage: 'unexpected null response from slack', + } + ); + return errorResult(SLACK_API_CONNECTOR_ID, errMessage); + } + + if (!slackApiResponseData.ok) { + return serviceErrorResult(SLACK_API_CONNECTOR_ID, slackApiResponseData.error); + } + + return successResult(SLACK_API_CONNECTOR_ID, slackApiResponseData); +}; + +export const createExternalService = ( + { secrets }: { secrets: { token: string } }, + logger: Logger, + configurationUtilities: ActionsConfigurationUtilities +): SlackApiService => { + const { token } = secrets; + + if (!token) { + throw Error(`[Action][${SLACK_CONNECTOR_NAME}]: Wrong configuration.`); + } + + const axiosInstance = axios.create({ + baseURL: SLACK_URL, + headers: { + Authorization: `Bearer ${token}`, + 'Content-type': 'application/json; charset=UTF-8', + }, + }); + + const getChannels = async (): Promise> => { + try { + const result = await request({ + axios: axiosInstance, + configurationUtilities, + logger, + method: 'get', + url: 'conversations.list?types=public_channel,private_channel', + }); + + return buildSlackExecutorSuccessResponse({ slackApiResponseData: result.data }); + } catch (error) { + return buildSlackExecutorErrorResponse({ slackApiError: error, logger }); + } + }; + + const postMessage = async ({ + channels, + text, + }: PostMessageSubActionParams): Promise> => { + try { + const result: AxiosResponse = await request({ + axios: axiosInstance, + method: 'post', + url: 'chat.postMessage', + logger, + data: { channel: channels[0], text }, + configurationUtilities, + }); + + return buildSlackExecutorSuccessResponse({ slackApiResponseData: result.data }); + } catch (error) { + return buildSlackExecutorErrorResponse({ slackApiError: error, logger }); + } + }; + + return { + getChannels, + postMessage, + }; +}; diff --git a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/take_action_menu/translations.ts b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/translations.ts similarity index 65% rename from x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/take_action_menu/translations.ts rename to x-pack/plugins/stack_connectors/server/connector_types/slack_api/translations.ts index b5f77c455c1a2..03157b6c6d53f 100644 --- a/x-pack/packages/kbn-ecs-data-quality-dashboard/impl/data_quality/data_quality_panel/data_quality_summary/summary_actions/take_action_menu/translations.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/slack_api/translations.ts @@ -7,9 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const TAKE_ACTION = i18n.translate( - 'ecsDataQualityDashboard.takeActionMenu.takeActionButton', - { - defaultMessage: 'Take action', - } -); +export const SLACK_CONNECTOR_NAME = i18n.translate('xpack.stackConnectors.slackApi.title', { + defaultMessage: 'Slack API', +}); diff --git a/x-pack/plugins/stack_connectors/server/plugin.test.ts b/x-pack/plugins/stack_connectors/server/plugin.test.ts index bfc2bb9fd4197..a572970e0be15 100644 --- a/x-pack/plugins/stack_connectors/server/plugin.test.ts +++ b/x-pack/plugins/stack_connectors/server/plugin.test.ts @@ -25,7 +25,7 @@ describe('Stack Connectors Plugin', () => { it('should register built in connector types', () => { const actionsSetup = actionsMock.createSetup(); plugin.setup(coreSetup, { actions: actionsSetup }); - expect(actionsSetup.registerType).toHaveBeenCalledTimes(16); + expect(actionsSetup.registerType).toHaveBeenCalledTimes(17); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( 1, expect.objectContaining({ @@ -69,63 +69,63 @@ describe('Stack Connectors Plugin', () => { }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 7, + 8, expect.objectContaining({ id: '.webhook', name: 'Webhook', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 8, + 9, expect.objectContaining({ id: '.cases-webhook', name: 'Webhook - Case Management', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 9, + 10, expect.objectContaining({ id: '.xmatters', name: 'xMatters', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 10, + 11, expect.objectContaining({ id: '.servicenow', name: 'ServiceNow ITSM', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 11, + 12, expect.objectContaining({ id: '.servicenow-sir', name: 'ServiceNow SecOps', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 12, + 13, expect.objectContaining({ id: '.servicenow-itom', name: 'ServiceNow ITOM', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 13, + 14, expect.objectContaining({ id: '.jira', name: 'Jira', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 14, + 15, expect.objectContaining({ id: '.resilient', name: 'IBM Resilient', }) ); expect(actionsSetup.registerType).toHaveBeenNthCalledWith( - 15, + 16, expect.objectContaining({ id: '.teams', name: 'Microsoft Teams', diff --git a/x-pack/plugins/stack_connectors/server/types.ts b/x-pack/plugins/stack_connectors/server/types.ts index 697c6a358fbe0..d9cd9f9b99cad 100644 --- a/x-pack/plugins/stack_connectors/server/types.ts +++ b/x-pack/plugins/stack_connectors/server/types.ts @@ -21,8 +21,10 @@ export type { PagerDutyActionParams, ServerLogConnectorTypeId, ServerLogActionParams, - SlackConnectorTypeId, - SlackActionParams, + SlackApiConnectorTypeId, + SlackApiActionParams, + SlackWebhookConnectorTypeId, + SlackWebhookActionParams, WebhookConnectorTypeId, WebhookActionParams, ServiceNowITSMConnectorTypeId, diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index 956dfa2af23b5..23ceb20ad75d6 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -4,7 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { i18n } from '@kbn/i18n'; import { + CodeEditorMode, BrowserAdvancedFields, BrowserSimpleFields, CommonFields, @@ -22,6 +24,7 @@ import { SourceType, TCPAdvancedFields, TCPSimpleFields, + ThrottlingConfig, TLSFields, TLSVersion, VerificationMode, @@ -30,6 +33,88 @@ import { ConfigKey } from './monitor_management'; export const DEFAULT_NAMESPACE_STRING = 'default'; +export enum PROFILE_VALUES_ENUM { + DEFAULT = 'default', + CABLE = 'cable', + DSL = 'dsl', + THREE_G = '3g', + FOUR_G = '4g', + LTE = 'lte', + FIBRE = 'fibre', + NO_THROTTLING = 'no-throttling', + CUSTOM = 'custom', +} + +export const CUSTOM_LABEL = i18n.translate('xpack.synthetics.connectionProfile.custom', { + defaultMessage: 'Custom', +}); + +export const DEFAULT_THROTTLING_VALUE = { download: '5', upload: '3', latency: '20' }; + +export const PROFILE_VALUES: ThrottlingConfig[] = [ + { + value: DEFAULT_THROTTLING_VALUE, + id: PROFILE_VALUES_ENUM.DEFAULT, + label: i18n.translate('xpack.synthetics.connectionProfile.default', { + defaultMessage: 'Default', + }), + }, + { + value: { download: '5', upload: '1', latency: '28' }, + id: PROFILE_VALUES_ENUM.CABLE, + label: i18n.translate('xpack.synthetics.connectionProfile.cable', { + defaultMessage: 'Cable', + }), + }, + { + value: { download: '1.5', upload: '0.384', latency: '50' }, + id: PROFILE_VALUES_ENUM.DSL, + label: i18n.translate('xpack.synthetics.connectionProfile.dsl', { + defaultMessage: 'DSL', + }), + }, + { + value: { download: '1.6', upload: '0.768', latency: '300' }, + id: PROFILE_VALUES_ENUM.THREE_G, + label: i18n.translate('xpack.synthetics.connectionProfile.threeG', { + defaultMessage: '3G', + }), + }, + { + value: { download: '9', upload: '0.75', latency: '170' }, + id: PROFILE_VALUES_ENUM.FOUR_G, + label: i18n.translate('xpack.synthetics.connectionProfile.fourG', { + defaultMessage: '4G', + }), + }, + { + value: { download: '12', upload: '0.75', latency: '70' }, + id: PROFILE_VALUES_ENUM.LTE, + label: i18n.translate('xpack.synthetics.connectionProfile.lte', { + defaultMessage: 'LTE', + }), + }, + { + value: { download: '20', upload: '5', latency: '4' }, + id: PROFILE_VALUES_ENUM.FIBRE, + label: i18n.translate('xpack.synthetics.connectionProfile.fibre', { + defaultMessage: 'Fibre', + }), + }, + { + value: null, + id: PROFILE_VALUES_ENUM.NO_THROTTLING, + label: i18n.translate('xpack.synthetics.connectionProfile.noThrottling', { + defaultMessage: 'No throttling', + }), + }, +]; + +export const PROFILES_MAP = PROFILE_VALUES.reduce((acc, profile) => { + acc[profile.id] = profile; + return acc; +}, {} as { [key: string]: ThrottlingConfig }); + export const ALLOWED_SCHEDULES_IN_MINUTES = [ '1', '3', @@ -71,11 +156,7 @@ export const DEFAULT_BROWSER_ADVANCED_FIELDS: BrowserAdvancedFields = { [ConfigKey.JOURNEY_FILTERS_MATCH]: '', [ConfigKey.JOURNEY_FILTERS_TAGS]: [], [ConfigKey.IGNORE_HTTPS_ERRORS]: false, - [ConfigKey.IS_THROTTLING_ENABLED]: true, - [ConfigKey.DOWNLOAD_SPEED]: '5', - [ConfigKey.UPLOAD_SPEED]: '3', - [ConfigKey.LATENCY]: '20', - [ConfigKey.THROTTLING_CONFIG]: '5d/3u/20l', + [ConfigKey.THROTTLING_CONFIG]: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], }; export const DEFAULT_BROWSER_SIMPLE_FIELDS: BrowserSimpleFields = { @@ -118,19 +199,25 @@ export const DEFAULT_HTTP_SIMPLE_FIELDS: HTTPSimpleFields = { export const DEFAULT_HTTP_ADVANCED_FIELDS: HTTPAdvancedFields = { [ConfigKey.PASSWORD]: '', [ConfigKey.PROXY_URL]: '', + [ConfigKey.PROXY_HEADERS]: {}, [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: [], [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: [], + [ConfigKey.RESPONSE_JSON_CHECK]: [], [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicy.ON_ERROR, [ConfigKey.RESPONSE_HEADERS_CHECK]: {}, [ConfigKey.RESPONSE_HEADERS_INDEX]: true, [ConfigKey.RESPONSE_STATUS_CHECK]: [], [ConfigKey.REQUEST_BODY_CHECK]: { value: '', - type: Mode.PLAINTEXT, + type: CodeEditorMode.PLAINTEXT, }, [ConfigKey.REQUEST_HEADERS_CHECK]: {}, [ConfigKey.REQUEST_METHOD_CHECK]: HTTPMethod.GET, [ConfigKey.USERNAME]: '', + [ConfigKey.MODE]: Mode.ANY, + [ConfigKey.RESPONSE_BODY_MAX_BYTES]: '1024', + [ConfigKey.IPV4]: true, + [ConfigKey.IPV6]: true, }; export const DEFAULT_ICMP_SIMPLE_FIELDS: ICMPSimpleFields = { @@ -158,6 +245,15 @@ export const DEFAULT_TCP_ADVANCED_FIELDS: TCPAdvancedFields = { [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: false, [ConfigKey.RESPONSE_RECEIVE_CHECK]: '', [ConfigKey.REQUEST_SEND_CHECK]: '', + [ConfigKey.MODE]: Mode.ANY, + [ConfigKey.IPV4]: true, + [ConfigKey.IPV6]: true, +}; + +export const DEFAULT_ICMP_ADVANCED_FIELDS = { + [ConfigKey.MODE]: Mode.ANY, + [ConfigKey.IPV4]: true, + [ConfigKey.IPV6]: true, }; export const DEFAULT_TLS_FIELDS: TLSFields = { @@ -182,6 +278,7 @@ export const DEFAULT_FIELDS: MonitorDefaults = { }, [DataStream.ICMP]: { ...DEFAULT_ICMP_SIMPLE_FIELDS, + ...DEFAULT_ICMP_ADVANCED_FIELDS, }, [DataStream.BROWSER]: { ...DEFAULT_BROWSER_SIMPLE_FIELDS, diff --git a/x-pack/plugins/synthetics/common/constants/monitor_management.ts b/x-pack/plugins/synthetics/common/constants/monitor_management.ts index 231acff2c6230..be3802f132a58 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_management.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_management.ts @@ -27,6 +27,7 @@ export enum ConfigKey { JOURNEY_ID = 'journey_id', MAX_REDIRECTS = 'max_redirects', METADATA = '__ui', + MODE = 'mode', MONITOR_TYPE = 'type', NAME = 'name', NAMESPACE = 'namespace', @@ -37,12 +38,15 @@ export enum ConfigKey { ORIGINAL_SPACE = 'original_space', // the original space the montior was saved in. Used by push monitors to ensure uniqueness of monitor id sent to heartbeat and prevent data collisions PORT = 'url.port', PROXY_URL = 'proxy_url', + PROXY_HEADERS = 'proxy_headers', PROXY_USE_LOCAL_RESOLVER = 'proxy_use_local_resolver', RESPONSE_BODY_CHECK_NEGATIVE = 'check.response.body.negative', RESPONSE_BODY_CHECK_POSITIVE = 'check.response.body.positive', + RESPONSE_JSON_CHECK = 'check.response.json', RESPONSE_BODY_INDEX = 'response.include_body', RESPONSE_HEADERS_CHECK = 'check.response.headers', RESPONSE_HEADERS_INDEX = 'response.include_headers', + RESPONSE_BODY_MAX_BYTES = 'response.include_body_max_bytes', RESPONSE_RECEIVE_CHECK = 'check.receive', RESPONSE_STATUS_CHECK = 'check.response.status', REQUEST_BODY_CHECK = 'check.request.body', @@ -54,6 +58,8 @@ export enum ConfigKey { SCREENSHOTS = 'screenshots', SOURCE_PROJECT_CONTENT = 'source.project.content', SOURCE_INLINE = 'source.inline.script', + IPV4 = 'ipv4', + IPV6 = 'ipv6', PROJECT_ID = 'project_id', SYNTHETICS_ARGS = 'synthetics_args', TEXT_ASSERTION = 'playwright_text_assertion', @@ -65,11 +71,7 @@ export enum ConfigKey { TLS_VERSION = 'ssl.supported_protocols', TAGS = 'tags', TIMEOUT = 'timeout', - THROTTLING_CONFIG = 'throttling.config', - IS_THROTTLING_ENABLED = 'throttling.is_enabled', - DOWNLOAD_SPEED = 'throttling.download_speed', - UPLOAD_SPEED = 'throttling.upload_speed', - LATENCY = 'throttling.latency', + THROTTLING_CONFIG = 'throttling', URLS = 'urls', USERNAME = 'username', WAIT = 'wait', @@ -77,6 +79,7 @@ export enum ConfigKey { } export const secretKeys = [ + ConfigKey.PROXY_HEADERS, ConfigKey.PARAMS, ConfigKey.PASSWORD, ConfigKey.REQUEST_BODY_CHECK, @@ -84,6 +87,7 @@ export const secretKeys = [ ConfigKey.REQUEST_SEND_CHECK, ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE, ConfigKey.RESPONSE_BODY_CHECK_POSITIVE, + ConfigKey.RESPONSE_JSON_CHECK, ConfigKey.RESPONSE_HEADERS_CHECK, ConfigKey.RESPONSE_RECEIVE_CHECK, ConfigKey.SOURCE_INLINE, @@ -106,4 +110,10 @@ export enum LegacyConfigKey { ZIP_URL_TLS_KEY_PASSPHRASE = 'source.zip_url.ssl.key_passphrase', ZIP_URL_TLS_VERIFICATION_MODE = 'source.zip_url.ssl.verification_mode', ZIP_URL_TLS_VERSION = 'source.zip_url.ssl.supported_protocols', + + THROTTLING_CONFIG = 'throttling.config', + IS_THROTTLING_ENABLED = 'throttling.is_enabled', + DOWNLOAD_SPEED = 'throttling.download_speed', + UPLOAD_SPEED = 'throttling.upload_speed', + LATENCY = 'throttling.latency', } diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts new file mode 100644 index 0000000000000..7883e0aa09bca --- /dev/null +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.test.ts @@ -0,0 +1,70 @@ +/* + * 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 { ConfigKey } from '../../runtime_types'; +import { throttlingFormatter } from './formatters'; + +describe('formatters', () => { + describe('throttling formatter', () => { + it('formats for no throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '0', + upload: '0', + latency: '0', + }, + label: 'No throttling', + id: 'no-throttling', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual('false'); + }); + + it('formats for default throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '5', + upload: '3', + latency: '20', + }, + label: 'Default', + id: 'default', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual(JSON.stringify({ download: 5, upload: 3, latency: 20 })); + }); + + it('formats for custom throttling', () => { + expect( + throttlingFormatter!( + { + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '1.25', + upload: '0.75', + latency: '150', + }, + label: 'Custom', + id: 'custom', + }, + }, + ConfigKey.THROTTLING_CONFIG + ) + ).toEqual(JSON.stringify({ download: 1.25, upload: 0.75, latency: 150 })); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts index 1ad7c055aecc3..9dfa027767851 100644 --- a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts @@ -12,34 +12,30 @@ import { objectToJsonFormatter, stringToJsonFormatter, } from '../formatting_utils'; +import { DEFAULT_THROTTLING_VALUE } from '../../constants/monitor_defaults'; import { tlsFormatters } from '../tls/formatters'; export type BrowserFormatMap = Record; -const throttlingFormatter: Formatter = (fields) => { - if (!fields[ConfigKey.IS_THROTTLING_ENABLED]) return 'false'; +export const throttlingFormatter: Formatter = (fields) => { + const throttling = fields[ConfigKey.THROTTLING_CONFIG]; - const getThrottlingValue = (v: string | undefined, suffix: 'd' | 'u' | 'l') => - v !== '' && v !== undefined ? `${v}${suffix}` : null; + if (!throttling || throttling?.id === 'no-throttling' || !throttling?.value) { + return 'false'; + } - return [ - getThrottlingValue(fields[ConfigKey.DOWNLOAD_SPEED], 'd'), - getThrottlingValue(fields[ConfigKey.UPLOAD_SPEED], 'u'), - getThrottlingValue(fields[ConfigKey.LATENCY], 'l'), - ] - .filter((v) => v !== null) - .join('/'); + return JSON.stringify({ + download: Number(throttling?.value?.download || DEFAULT_THROTTLING_VALUE.download), + upload: Number(throttling?.value?.upload || DEFAULT_THROTTLING_VALUE.upload), + latency: Number(throttling?.value?.latency || DEFAULT_THROTTLING_VALUE), + }); }; export const browserFormatters: BrowserFormatMap = { [ConfigKey.SOURCE_PROJECT_CONTENT]: null, [ConfigKey.PARAMS]: null, [ConfigKey.SCREENSHOTS]: null, - [ConfigKey.IS_THROTTLING_ENABLED]: null, - [ConfigKey.DOWNLOAD_SPEED]: null, - [ConfigKey.UPLOAD_SPEED]: null, - [ConfigKey.LATENCY]: null, [ConfigKey.IGNORE_HTTPS_ERRORS]: null, [ConfigKey.PLAYWRIGHT_OPTIONS]: null, [ConfigKey.TEXT_ASSERTION]: null, diff --git a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts index a03c54fc4f34e..2706972456acd 100644 --- a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts +++ b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts @@ -6,6 +6,7 @@ */ import { ConfigKey, DataStream } from '../runtime_types'; import { formatSyntheticsPolicy } from './format_synthetics_policy'; +import { PROFILE_VALUES_ENUM, PROFILES_MAP } from '../constants/monitor_defaults'; const gParams = { proxyUrl: 'https://proxy.com' }; describe('formatSyntheticsPolicy', () => { @@ -421,7 +422,7 @@ describe('formatSyntheticsPolicy', () => { }, 'throttling.config': { type: 'text', - value: '5d/3u/20l', + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), }, timeout: { type: 'text', @@ -1145,11 +1146,7 @@ const browserConfig: any = { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts index 392a50d59ed28..1a9eedd6acf2a 100644 --- a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts +++ b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.ts @@ -7,6 +7,8 @@ import { NewPackagePolicy } from '@kbn/fleet-plugin/common'; import { cloneDeep } from 'lodash'; +import { throttlingFormatter } from './browser/formatters'; +import { LegacyConfigKey } from '../constants/monitor_management'; import { replaceStringWithParams } from './formatting_utils'; import { syntheticsPolicyFormatters } from './formatters'; import { ConfigKey, DataStream, MonitorFields } from '../runtime_types'; @@ -69,5 +71,11 @@ export const formatSyntheticsPolicy = ( } }); + // TODO: remove this once we remove legacy support + const throttling = dataStream?.vars?.[LegacyConfigKey.THROTTLING_CONFIG]; + if (throttling) { + throttling.value = throttlingFormatter?.(config, ConfigKey.THROTTLING_CONFIG); + } + return { formattedPolicy, hasDataStream: Boolean(dataStream), hasInput: Boolean(currentInput) }; }; diff --git a/x-pack/plugins/synthetics/common/formatters/http/formatters.ts b/x-pack/plugins/synthetics/common/formatters/http/formatters.ts index 300e4f9fdb9ff..437112939f283 100644 --- a/x-pack/plugins/synthetics/common/formatters/http/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/http/formatters.ts @@ -23,9 +23,11 @@ export const httpFormatters: HTTPFormatMap = { [ConfigKey.USERNAME]: null, [ConfigKey.PASSWORD]: null, [ConfigKey.PROXY_URL]: null, + [ConfigKey.PROXY_HEADERS]: objectToJsonFormatter, [ConfigKey.PORT]: null, [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: arrayToJsonFormatter, [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: arrayToJsonFormatter, + [ConfigKey.RESPONSE_JSON_CHECK]: arrayToJsonFormatter, [ConfigKey.RESPONSE_HEADERS_CHECK]: objectToJsonFormatter, [ConfigKey.RESPONSE_STATUS_CHECK]: arrayToJsonFormatter, [ConfigKey.REQUEST_HEADERS_CHECK]: objectToJsonFormatter, @@ -33,6 +35,10 @@ export const httpFormatters: HTTPFormatMap = { fields[ConfigKey.REQUEST_BODY_CHECK]?.value ? JSON.stringify(fields[ConfigKey.REQUEST_BODY_CHECK]?.value) : null, + [ConfigKey.RESPONSE_BODY_MAX_BYTES]: null, + [ConfigKey.MODE]: null, + [ConfigKey.IPV4]: null, + [ConfigKey.IPV6]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/synthetics/common/formatters/icmp/formatters.ts b/x-pack/plugins/synthetics/common/formatters/icmp/formatters.ts index 0ebf69db5b408..f58e15c86b3ad 100644 --- a/x-pack/plugins/synthetics/common/formatters/icmp/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/icmp/formatters.ts @@ -15,5 +15,8 @@ export type ICMPFormatMap = Record; export const icmpFormatters: ICMPFormatMap = { [ConfigKey.HOSTS]: null, [ConfigKey.WAIT]: secondsToCronFormatter, + [ConfigKey.MODE]: null, + [ConfigKey.IPV4]: null, + [ConfigKey.IPV6]: null, ...commonFormatters, }; diff --git a/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts b/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts index 6acb9abe21877..2d850e95ceaf1 100644 --- a/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/tcp/formatters.ts @@ -23,6 +23,9 @@ export const tcpFormatters: TCPFormatMap = { [ConfigKey.PROXY_URL]: null, [ConfigKey.PORT]: null, [ConfigKey.URLS]: null, + [ConfigKey.MODE]: null, + [ConfigKey.IPV4]: null, + [ConfigKey.IPV6]: null, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts b/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts index 219790bfe991c..d31e0cd74c360 100644 --- a/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts +++ b/x-pack/plugins/synthetics/common/rules/alert_actions.test.ts @@ -9,7 +9,7 @@ import { populateAlertActions } from './alert_actions'; import { ActionConnector } from './types'; import { MONITOR_STATUS } from '../constants/uptime_alerts'; import { MONITOR_STATUS as SYNTHETICS_MONITOR_STATUS } from '../constants/synthetics_alerts'; -import { MonitorStatusTranslations } from '../translations'; +import { MonitorStatusTranslations } from './legacy_uptime/translations'; import { SyntheticsMonitorStatusTranslations } from './synthetics/translations'; describe('Legacy Alert Actions factory', () => { @@ -33,6 +33,7 @@ describe('Legacy Alert Actions factory', () => { defaultActionMessage: MonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: MonitorStatusTranslations.defaultRecoverySubjectMessage, }, isLegacy: true, }); @@ -60,6 +61,70 @@ describe('Legacy Alert Actions factory', () => { ]); }); + it('generate expected action for email', async () => { + const resp = populateAlertActions({ + groupId: MONITOR_STATUS.id, + defaultActions: [ + { + actionTypeId: '.email', + group: 'xpack.uptime.alerts.actionGroups.monitorStatus', + params: { + dedupKey: 'always-downxpack.uptime.alerts.actionGroups.monitorStatus', + eventAction: 'trigger', + severity: 'error', + summary: MonitorStatusTranslations.defaultActionMessage, + }, + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + }, + ] as unknown as ActionConnector[], + translations: { + defaultActionMessage: MonitorStatusTranslations.defaultActionMessage, + defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, + defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: MonitorStatusTranslations.defaultRecoverySubjectMessage, + }, + isLegacy: true, + defaultEmail: { + to: ['test@email.com'], + }, + }); + expect(resp).toEqual([ + { + group: 'recovered', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + bcc: [], + cc: [], + kibanaFooterLink: { + path: '', + text: '', + }, + message: + 'Alert for monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} has recovered', + subject: + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} has recovered', + to: ['test@email.com'], + }, + }, + { + group: 'xpack.uptime.alerts.actionGroups.monitorStatus', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + bcc: [], + cc: [], + kibanaFooterLink: { + path: '', + text: '', + }, + message: + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}, checked at {{context.checkedAt}}', + subject: 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} is down', + to: ['test@email.com'], + }, + }, + ]); + }); + it('generate expected action for index', async () => { const resp = populateAlertActions({ groupId: MONITOR_STATUS.id, @@ -80,6 +145,7 @@ describe('Legacy Alert Actions factory', () => { defaultActionMessage: MonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: MonitorStatusTranslations.defaultRecoverySubjectMessage, }, isLegacy: true, }); @@ -141,6 +207,7 @@ describe('Legacy Alert Actions factory', () => { defaultActionMessage: MonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: MonitorStatusTranslations.defaultRecoverySubjectMessage, }, }); expect(resp).toEqual([ @@ -189,6 +256,8 @@ describe('Alert Actions factory', () => { defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: + SyntheticsMonitorStatusTranslations.defaultRecoverySubjectMessage, }, }); expect(resp).toEqual([ @@ -235,6 +304,8 @@ describe('Alert Actions factory', () => { defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: + SyntheticsMonitorStatusTranslations.defaultRecoverySubjectMessage, }, }); expect(resp).toEqual([ @@ -295,6 +366,8 @@ describe('Alert Actions factory', () => { defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: + SyntheticsMonitorStatusTranslations.defaultRecoverySubjectMessage, }, }); expect(resp).toEqual([ @@ -320,4 +393,69 @@ describe('Alert Actions factory', () => { }, ]); }); + + it('generate expected action for email action connector', async () => { + const resp = populateAlertActions({ + groupId: SYNTHETICS_MONITOR_STATUS.id, + defaultActions: [ + { + actionTypeId: '.email', + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + params: { + dedupKey: 'always-downxpack.uptime.alerts.actionGroups.monitorStatus', + eventAction: 'trigger', + severity: 'error', + summary: + 'Monitor {{context.monitorName}} with url {{{context.monitorUrl}}} from {{context.observerLocation}} {{{context.statusMessage}}} The latest error message is {{{context.latestErrorMessage}}}', + }, + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + }, + ] as unknown as ActionConnector[], + defaultEmail: { + to: ['test@email.com'], + }, + translations: { + defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, + defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, + defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: + SyntheticsMonitorStatusTranslations.defaultRecoverySubjectMessage, + }, + }); + expect(resp).toEqual([ + { + group: 'recovered', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + bcc: [], + cc: [], + kibanaFooterLink: { + path: '', + text: '', + }, + message: + 'The alert for the monitor {{context.monitorName}} checking {{{context.monitorUrl}}} from {{context.locationName}} is no longer active: {{context.recoveryReason}}.', + subject: + 'The monitor {{context.monitorName}} checking {{{context.monitorUrl}}} has recovered.', + to: ['test@email.com'], + }, + }, + { + group: 'xpack.synthetics.alerts.actionGroups.monitorStatus', + id: 'f2a3b195-ed76-499a-805d-82d24d4eeba9', + params: { + bcc: [], + cc: [], + kibanaFooterLink: { + path: '', + text: '', + }, + message: + 'The monitor {{context.monitorName}} checking {{{context.monitorUrl}}} from {{context.locationName}} last ran at {{context.checkedAt}} and is {{{context.status}}}. The last error received is: {{{context.lastErrorMessage}}}.', + subject: 'The monitor {{context.monitorName}} checking {{{context.monitorUrl}}} is down.', + to: ['test@email.com'], + }, + }, + ]); + }); }); diff --git a/x-pack/plugins/synthetics/common/rules/alert_actions.ts b/x-pack/plugins/synthetics/common/rules/alert_actions.ts index 3f8cedf715536..2dc1990e26b45 100644 --- a/x-pack/plugins/synthetics/common/rules/alert_actions.ts +++ b/x-pack/plugins/synthetics/common/rules/alert_actions.ts @@ -20,7 +20,7 @@ import { v4 as uuidv4 } from 'uuid'; import { ActionConnector, ActionTypeId } from './types'; import { DefaultEmail } from '../runtime_types'; -export const SLACK_ACTION_ID: ActionTypeId = '.slack'; +export const SLACK_WEBHOOK_ACTION_ID: ActionTypeId = '.slack'; export const PAGER_DUTY_ACTION_ID: ActionTypeId = '.pagerduty'; export const SERVER_LOG_ACTION_ID: ActionTypeId = '.server-log'; export const INDEX_ACTION_ID: ActionTypeId = '.index'; @@ -36,6 +36,7 @@ interface Translations { defaultActionMessage: string; defaultRecoveryMessage: string; defaultSubjectMessage: string; + defaultRecoverySubjectMessage: string; } export function populateAlertActions({ @@ -97,7 +98,7 @@ export function populateAlertActions({ recoveredAction.params = getWebhookActionParams(translations, true); actions.push(recoveredAction); break; - case SLACK_ACTION_ID: + case SLACK_WEBHOOK_ACTION_ID: case TEAMS_ACTION_ID: action.params = { message: translations.defaultActionMessage, @@ -107,6 +108,8 @@ export function populateAlertActions({ case EMAIL_ACTION_ID: if (defaultEmail) { action.params = getEmailActionParams(translations, defaultEmail); + recoveredAction.params = getEmailActionParams(translations, defaultEmail, true); + actions.push(recoveredAction); } break; default: @@ -270,13 +273,19 @@ function getJiraActionParams({ defaultActionMessage }: Translations): JiraAction } function getEmailActionParams( - { defaultActionMessage, defaultSubjectMessage }: Translations, - defaultEmail: DefaultEmail + { + defaultActionMessage, + defaultSubjectMessage, + defaultRecoverySubjectMessage, + defaultRecoveryMessage, + }: Translations, + defaultEmail: DefaultEmail, + isRecovery?: boolean ): EmailActionParams { return { to: defaultEmail.to, - subject: defaultSubjectMessage, - message: defaultActionMessage, + subject: isRecovery ? defaultRecoverySubjectMessage : defaultSubjectMessage, + message: isRecovery ? defaultRecoveryMessage : defaultActionMessage, cc: defaultEmail.cc ?? [], bcc: defaultEmail.bcc ?? [], kibanaFooterLink: { diff --git a/x-pack/plugins/synthetics/common/rules/legacy_uptime/translations.ts b/x-pack/plugins/synthetics/common/rules/legacy_uptime/translations.ts new file mode 100644 index 0000000000000..fab705daeb0c0 --- /dev/null +++ b/x-pack/plugins/synthetics/common/rules/legacy_uptime/translations.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. + */ +import { i18n } from '@kbn/i18n'; + +export const MonitorStatusTranslations = { + defaultActionMessage: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.defaultActionMessage', + { + defaultMessage: + 'Monitor {monitorName} with url {monitorUrl} from {observerLocation} {statusMessage} The latest error message is {latestErrorMessage}, checked at {checkedAt}', + values: { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + statusMessage: '{{{context.statusMessage}}}', + latestErrorMessage: '{{{context.latestErrorMessage}}}', + observerLocation: '{{context.observerLocation}}', + checkedAt: '{{context.checkedAt}}', + }, + } + ), + defaultSubjectMessage: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.defaultSubjectMessage', + { + defaultMessage: 'Monitor {monitorName} with url {monitorUrl} is down', + values: { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + }, + } + ), + defaultRecoverySubjectMessage: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.defaultRecoverySubjectMessage', + { + defaultMessage: 'Monitor {monitorName} with url {monitorUrl} has recovered', + values: { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + }, + } + ), + defaultRecoveryMessage: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.defaultRecoveryMessage', + { + defaultMessage: + 'Alert for monitor {monitorName} with url {monitorUrl} from {observerLocation} has recovered', + values: { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.observerLocation}}', + }, + } + ), + name: i18n.translate('xpack.synthetics.alerts.monitorStatus.clientName', { + defaultMessage: 'Uptime monitor status', + }), + description: i18n.translate('xpack.synthetics.alerts.monitorStatus.description', { + defaultMessage: 'Alert when a monitor is down or an availability threshold is breached.', + }), +}; + +export const TlsTranslations = { + defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultActionMessage', { + defaultMessage: `Detected TLS certificate {commonName} from issuer {issuer} is {status}. Certificate {summary}`, + values: { + commonName: '{{context.commonName}}', + issuer: '{{context.issuer}}', + summary: '{{context.summary}}', + status: '{{context.status}}', + }, + }), + defaultRecoveryMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultRecoveryMessage', { + defaultMessage: `Alert for TLS certificate {commonName} from issuer {issuer} has recovered`, + values: { + commonName: '{{context.commonName}}', + issuer: '{{context.issuer}}', + }, + }), + name: i18n.translate('xpack.synthetics.alerts.tls.clientName', { + defaultMessage: 'Uptime TLS', + }), + description: i18n.translate('xpack.synthetics.alerts.tls.description', { + defaultMessage: 'Alert when the TLS certificate of an Uptime monitor is about to expire.', + }), +}; + +export const TlsTranslationsLegacy = { + defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.legacy.defaultActionMessage', { + defaultMessage: `Detected {count} TLS certificates expiring or becoming too old. +{expiringConditionalOpen} +Expiring cert count: {expiringCount} +Expiring Certificates: {expiringCommonNameAndDate} +{expiringConditionalClose} +{agingConditionalOpen} +Aging cert count: {agingCount} +Aging Certificates: {agingCommonNameAndDate} +{agingConditionalClose} +`, + values: { + count: '{{state.count}}', + expiringCount: '{{state.expiringCount}}', + expiringCommonNameAndDate: '{{state.expiringCommonNameAndDate}}', + expiringConditionalOpen: '{{#state.hasExpired}}', + expiringConditionalClose: '{{/state.hasExpired}}', + agingCount: '{{state.agingCount}}', + agingCommonNameAndDate: '{{state.agingCommonNameAndDate}}', + agingConditionalOpen: '{{#state.hasAging}}', + agingConditionalClose: '{{/state.hasAging}}', + }, + }), + name: i18n.translate('xpack.synthetics.alerts.tls.legacy.clientName', { + defaultMessage: 'Uptime TLS (Legacy)', + }), + description: i18n.translate('xpack.synthetics.alerts.tls.legacy.description', { + defaultMessage: + 'Alert when the TLS certificate of an Uptime monitor is about to expire. This alert will be deprecated in a future version.', + }), +}; + +export const DurationAnomalyTranslations = { + defaultActionMessage: i18n.translate( + 'xpack.synthetics.alerts.durationAnomaly.defaultActionMessage', + { + defaultMessage: `Abnormal ({severity} level) response time detected on {monitor} with url {monitorUrl} at {anomalyStartTimestamp}. Anomaly severity score is {severityScore}. +Response times as high as {slowestAnomalyResponse} have been detected from location {observerLocation}. Expected response time is {expectedResponseTime}.`, + values: { + severity: '{{context.severity}}', + anomalyStartTimestamp: '{{context.anomalyStartTimestamp}}', + monitor: '{{context.monitor}}', + monitorUrl: '{{{context.monitorUrl}}}', + slowestAnomalyResponse: '{{context.slowestAnomalyResponse}}', + expectedResponseTime: '{{context.expectedResponseTime}}', + severityScore: '{{context.severityScore}}', + observerLocation: '{{context.observerLocation}}', + }, + } + ), + defaultRecoveryMessage: i18n.translate( + 'xpack.synthetics.alerts.durationAnomaly.defaultRecoveryMessage', + { + defaultMessage: `Alert for abnormal ({severity} level) response time detected on monitor {monitor} with url {monitorUrl} from location {observerLocation} at {anomalyStartTimestamp} has recovered`, + values: { + severity: '{{context.severity}}', + anomalyStartTimestamp: '{{context.anomalyStartTimestamp}}', + monitor: '{{context.monitor}}', + monitorUrl: '{{{context.monitorUrl}}}', + observerLocation: '{{context.observerLocation}}', + }, + } + ), + name: i18n.translate('xpack.synthetics.alerts.durationAnomaly.clientName', { + defaultMessage: 'Uptime Duration Anomaly', + }), + description: i18n.translate('xpack.synthetics.alerts.durationAnomaly.description', { + defaultMessage: 'Alert when the Uptime monitor duration is anomalous.', + }), +}; diff --git a/x-pack/plugins/synthetics/common/rules/synthetics/translations.ts b/x-pack/plugins/synthetics/common/rules/synthetics/translations.ts index a6df24bfb5f8d..167c436dbed98 100644 --- a/x-pack/plugins/synthetics/common/rules/synthetics/translations.ts +++ b/x-pack/plugins/synthetics/common/rules/synthetics/translations.ts @@ -33,6 +33,16 @@ export const SyntheticsMonitorStatusTranslations = { }, } ), + defaultRecoverySubjectMessage: i18n.translate( + 'xpack.synthetics.alerts.syntheticsMonitorStatus.defaultRecoverySubjectMessage', + { + defaultMessage: 'The monitor {monitorName} checking {monitorUrl} has recovered.', + values: { + monitorName: '{{context.monitorName}}', + monitorUrl: '{{{context.monitorUrl}}}', + }, + } + ), defaultRecoveryMessage: i18n.translate( 'xpack.synthetics.alerts.syntheticsMonitorStatus.defaultRecoveryMessage', { diff --git a/x-pack/plugins/synthetics/common/rules/types.ts b/x-pack/plugins/synthetics/common/rules/types.ts index 101ce9c1418c6..c398d66e376a2 100644 --- a/x-pack/plugins/synthetics/common/rules/types.ts +++ b/x-pack/plugins/synthetics/common/rules/types.ts @@ -11,7 +11,7 @@ import type { PagerDutyConnectorTypeId, ServerLogConnectorTypeId, ServiceNowITSMConnectorTypeId as ServiceNowConnectorTypeId, - SlackConnectorTypeId, + SlackWebhookConnectorTypeId, TeamsConnectorTypeId, WebhookConnectorTypeId, EmailConnectorTypeId, @@ -20,7 +20,7 @@ import type { import type { ActionConnector as RawActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; export type ActionTypeId = - | typeof SlackConnectorTypeId + | typeof SlackWebhookConnectorTypeId | typeof PagerDutyConnectorTypeId | typeof ServerLogConnectorTypeId | typeof IndexConnectorTypeId diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts index 934c534795301..dcd6b18974da4 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts @@ -54,15 +54,15 @@ export const MonacoEditorLangIdCodec = tEnum( ); export type MonacoEditorLangIdType = t.TypeOf; -export enum Mode { +export enum CodeEditorMode { FORM = 'form', JSON = 'json', PLAINTEXT = 'text', XML = 'xml', } -export const ModeCodec = tEnum('Mode', Mode); -export type ModeType = t.TypeOf; +export const CodeEditorModeCodec = tEnum('CodeEditorMode', CodeEditorMode); +export type CodeEditorModeType = t.TypeOf; export enum ContentType { JSON = 'application/json', @@ -111,15 +111,6 @@ export enum ScreenshotOption { export const ScreenshotOptionCodec = tEnum('ScreenshotOption', ScreenshotOption); export type ScreenshotOptionType = t.TypeOf; -export enum ThrottlingSuffix { - DOWNLOAD = 'd', - UPLOAD = 'u', - LATENCY = 'l', -} - -export const ThrottlingSuffixCodec = tEnum('ThrottlingSuffix', ThrottlingSuffix); -export type ThrottlingSuffixType = t.TypeOf; - export enum SourceType { UI = 'ui', PROJECT = 'project', @@ -136,3 +127,16 @@ export enum FormMonitorType { } export const FormMonitorTypeCodec = tEnum('FormMonitorType', FormMonitorType); + +export enum Mode { + ANY = 'any', + ALL = 'all', +} +export const ModeCodec = tEnum('Mode', Mode); +export type ModeType = t.TypeOf; + +export const ResponseCheckJSONCodec = t.interface({ + description: t.string, + expression: t.string, +}); +export type ResponseCheckJSON = t.TypeOf; diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index 2e639e360fd1a..d5a8d26568633 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -11,11 +11,13 @@ import { secretKeys } from '../../constants/monitor_management'; import { ConfigKey } from './config_key'; import { MonitorServiceLocationCodec, ServiceLocationErrors } from './locations'; import { + CodeEditorModeCodec, DataStream, DataStreamCodec, FormMonitorTypeCodec, ModeCodec, ResponseBodyIndexPolicyCodec, + ResponseCheckJSONCodec, ScheduleUnitCodec, SourceTypeCodec, TLSVersionCodec, @@ -94,10 +96,17 @@ export const TCPSimpleFieldsCodec = t.intersection([ export type TCPSimpleFields = t.TypeOf; // TCPAdvancedFields -export const TCPAdvancedFieldsCodec = t.interface({ - [ConfigKey.PROXY_URL]: t.string, - [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: t.boolean, -}); +export const TCPAdvancedFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.PROXY_URL]: t.string, + [ConfigKey.PROXY_USE_LOCAL_RESOLVER]: t.boolean, + }), + t.partial({ + [ConfigKey.MODE]: ModeCodec, + [ConfigKey.IPV4]: t.boolean, + [ConfigKey.IPV6]: t.boolean, + }), +]); export const TCPSensitiveAdvancedFieldsCodec = t.interface({ [ConfigKey.RESPONSE_RECEIVE_CHECK]: t.string, @@ -136,7 +145,18 @@ export const ICMPSimpleFieldsCodec = t.intersection([ ]); export type ICMPSimpleFields = t.TypeOf; -export type ICMPFields = t.TypeOf; + +// ICMPAdvancedFields +export const ICMPAdvancedFieldsCodec = t.partial({ + [ConfigKey.MODE]: ModeCodec, + [ConfigKey.IPV4]: t.boolean, + [ConfigKey.IPV6]: t.boolean, +}); + +// ICMPFields +export const ICMPFieldsCodec = t.intersection([ICMPSimpleFieldsCodec, ICMPAdvancedFieldsCodec]); + +export type ICMPFields = t.TypeOf; // HTTPSimpleFields export const HTTPSimpleFieldsCodec = t.intersection([ @@ -152,23 +172,37 @@ export const HTTPSimpleFieldsCodec = t.intersection([ export type HTTPSimpleFields = t.TypeOf; // HTTPAdvancedFields -export const HTTPAdvancedFieldsCodec = t.interface({ - [ConfigKey.PROXY_URL]: t.string, - [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicyCodec, - [ConfigKey.RESPONSE_HEADERS_INDEX]: t.boolean, - [ConfigKey.RESPONSE_STATUS_CHECK]: t.array(t.string), - [ConfigKey.REQUEST_METHOD_CHECK]: t.string, -}); +export const HTTPAdvancedFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.PROXY_URL]: t.string, + [ConfigKey.RESPONSE_BODY_INDEX]: ResponseBodyIndexPolicyCodec, + [ConfigKey.RESPONSE_HEADERS_INDEX]: t.boolean, + [ConfigKey.RESPONSE_STATUS_CHECK]: t.array(t.string), + [ConfigKey.REQUEST_METHOD_CHECK]: t.string, + }), + t.partial({ + [ConfigKey.MODE]: ModeCodec, + [ConfigKey.RESPONSE_BODY_MAX_BYTES]: t.string, + [ConfigKey.IPV4]: t.boolean, + [ConfigKey.IPV6]: t.boolean, + }), +]); -export const HTTPSensitiveAdvancedFieldsCodec = t.interface({ - [ConfigKey.PASSWORD]: t.string, - [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: t.array(t.string), - [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: t.array(t.string), - [ConfigKey.RESPONSE_HEADERS_CHECK]: t.record(t.string, t.string), - [ConfigKey.REQUEST_BODY_CHECK]: t.interface({ value: t.string, type: ModeCodec }), - [ConfigKey.REQUEST_HEADERS_CHECK]: t.record(t.string, t.string), - [ConfigKey.USERNAME]: t.string, -}); +export const HTTPSensitiveAdvancedFieldsCodec = t.intersection([ + t.interface({ + [ConfigKey.PASSWORD]: t.string, + [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: t.array(t.string), + [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: t.array(t.string), + [ConfigKey.RESPONSE_HEADERS_CHECK]: t.record(t.string, t.string), + [ConfigKey.REQUEST_BODY_CHECK]: t.interface({ value: t.string, type: CodeEditorModeCodec }), + [ConfigKey.REQUEST_HEADERS_CHECK]: t.record(t.string, t.string), + [ConfigKey.USERNAME]: t.string, + }), + t.partial({ + [ConfigKey.PROXY_HEADERS]: t.record(t.string, t.string), + [ConfigKey.RESPONSE_JSON_CHECK]: t.array(ResponseCheckJSONCodec), + }), +]); export const HTTPAdvancedCodec = t.intersection([ HTTPAdvancedFieldsCodec, @@ -192,15 +226,6 @@ export const HTTPFieldsCodec = t.intersection([ export type HTTPFields = t.TypeOf; -// Browser Fields -export const ThrottlingConfigKeyCodec = t.union([ - t.literal(ConfigKey.DOWNLOAD_SPEED), - t.literal(ConfigKey.UPLOAD_SPEED), - t.literal(ConfigKey.LATENCY), -]); - -export type ThrottlingConfigKey = t.TypeOf; - export const EncryptedBrowserSimpleFieldsCodec = t.intersection([ t.intersection([ t.interface({ @@ -225,16 +250,28 @@ export const BrowserSensitiveSimpleFieldsCodec = t.intersection([ CommonFieldsCodec, ]); +export const ThrottlingConfigValueCodec = t.interface({ + download: t.string, + upload: t.string, + latency: t.string, +}); + +export type ThrottlingConfigValue = t.TypeOf; + +export const ThrottlingConfigCodec = t.interface({ + value: t.union([ThrottlingConfigValueCodec, t.null]), + label: t.string, + id: t.string, +}); + +export type ThrottlingConfig = t.TypeOf; + export const EncryptedBrowserAdvancedFieldsCodec = t.interface({ [ConfigKey.SCREENSHOTS]: t.string, [ConfigKey.JOURNEY_FILTERS_MATCH]: t.string, [ConfigKey.JOURNEY_FILTERS_TAGS]: t.array(t.string), [ConfigKey.IGNORE_HTTPS_ERRORS]: t.boolean, - [ConfigKey.IS_THROTTLING_ENABLED]: t.boolean, - [ConfigKey.DOWNLOAD_SPEED]: t.string, - [ConfigKey.UPLOAD_SPEED]: t.string, - [ConfigKey.LATENCY]: t.string, - [ConfigKey.THROTTLING_CONFIG]: t.string, + [ConfigKey.THROTTLING_CONFIG]: ThrottlingConfigCodec, }); export const BrowserSimpleFieldsCodec = t.intersection([ diff --git a/x-pack/plugins/synthetics/common/translations.ts b/x-pack/plugins/synthetics/common/translations.ts index bd761456aa7b7..ac2bec7a5506b 100644 --- a/x-pack/plugins/synthetics/common/translations.ts +++ b/x-pack/plugins/synthetics/common/translations.ts @@ -20,146 +20,3 @@ export const VALUE_MUST_BE_AN_INTEGER = i18n.translate( defaultMessage: 'Value must be an integer.', } ); - -export const MonitorStatusTranslations = { - defaultActionMessage: i18n.translate( - 'xpack.synthetics.alerts.monitorStatus.defaultActionMessage', - { - defaultMessage: - 'Monitor {monitorName} with url {monitorUrl} from {observerLocation} {statusMessage} The latest error message is {latestErrorMessage}, checked at {checkedAt}', - values: { - monitorName: '{{context.monitorName}}', - monitorUrl: '{{{context.monitorUrl}}}', - statusMessage: '{{{context.statusMessage}}}', - latestErrorMessage: '{{{context.latestErrorMessage}}}', - observerLocation: '{{context.observerLocation}}', - checkedAt: '{{context.checkedAt}}', - }, - } - ), - defaultSubjectMessage: i18n.translate( - 'xpack.synthetics.alerts.monitorStatus.defaultSubjectMessage', - { - defaultMessage: 'Monitor {monitorName} with url {monitorUrl} is down', - values: { - monitorName: '{{context.monitorName}}', - monitorUrl: '{{{context.monitorUrl}}}', - }, - } - ), - defaultRecoveryMessage: i18n.translate( - 'xpack.synthetics.alerts.monitorStatus.defaultRecoveryMessage', - { - defaultMessage: - 'Alert for monitor {monitorName} with url {monitorUrl} from {observerLocation} has recovered', - values: { - monitorName: '{{context.monitorName}}', - monitorUrl: '{{{context.monitorUrl}}}', - observerLocation: '{{context.observerLocation}}', - }, - } - ), - name: i18n.translate('xpack.synthetics.alerts.monitorStatus.clientName', { - defaultMessage: 'Uptime monitor status', - }), - description: i18n.translate('xpack.synthetics.alerts.monitorStatus.description', { - defaultMessage: 'Alert when a monitor is down or an availability threshold is breached.', - }), -}; - -export const TlsTranslations = { - defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultActionMessage', { - defaultMessage: `Detected TLS certificate {commonName} from issuer {issuer} is {status}. Certificate {summary}`, - values: { - commonName: '{{context.commonName}}', - issuer: '{{context.issuer}}', - summary: '{{context.summary}}', - status: '{{context.status}}', - }, - }), - defaultRecoveryMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultRecoveryMessage', { - defaultMessage: `Alert for TLS certificate {commonName} from issuer {issuer} has recovered`, - values: { - commonName: '{{context.commonName}}', - issuer: '{{context.issuer}}', - }, - }), - name: i18n.translate('xpack.synthetics.alerts.tls.clientName', { - defaultMessage: 'Uptime TLS', - }), - description: i18n.translate('xpack.synthetics.alerts.tls.description', { - defaultMessage: 'Alert when the TLS certificate of an Uptime monitor is about to expire.', - }), -}; - -export const TlsTranslationsLegacy = { - defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.legacy.defaultActionMessage', { - defaultMessage: `Detected {count} TLS certificates expiring or becoming too old. -{expiringConditionalOpen} -Expiring cert count: {expiringCount} -Expiring Certificates: {expiringCommonNameAndDate} -{expiringConditionalClose} -{agingConditionalOpen} -Aging cert count: {agingCount} -Aging Certificates: {agingCommonNameAndDate} -{agingConditionalClose} -`, - values: { - count: '{{state.count}}', - expiringCount: '{{state.expiringCount}}', - expiringCommonNameAndDate: '{{state.expiringCommonNameAndDate}}', - expiringConditionalOpen: '{{#state.hasExpired}}', - expiringConditionalClose: '{{/state.hasExpired}}', - agingCount: '{{state.agingCount}}', - agingCommonNameAndDate: '{{state.agingCommonNameAndDate}}', - agingConditionalOpen: '{{#state.hasAging}}', - agingConditionalClose: '{{/state.hasAging}}', - }, - }), - name: i18n.translate('xpack.synthetics.alerts.tls.legacy.clientName', { - defaultMessage: 'Uptime TLS (Legacy)', - }), - description: i18n.translate('xpack.synthetics.alerts.tls.legacy.description', { - defaultMessage: - 'Alert when the TLS certificate of an Uptime monitor is about to expire. This alert will be deprecated in a future version.', - }), -}; - -export const DurationAnomalyTranslations = { - defaultActionMessage: i18n.translate( - 'xpack.synthetics.alerts.durationAnomaly.defaultActionMessage', - { - defaultMessage: `Abnormal ({severity} level) response time detected on {monitor} with url {monitorUrl} at {anomalyStartTimestamp}. Anomaly severity score is {severityScore}. -Response times as high as {slowestAnomalyResponse} have been detected from location {observerLocation}. Expected response time is {expectedResponseTime}.`, - values: { - severity: '{{context.severity}}', - anomalyStartTimestamp: '{{context.anomalyStartTimestamp}}', - monitor: '{{context.monitor}}', - monitorUrl: '{{{context.monitorUrl}}}', - slowestAnomalyResponse: '{{context.slowestAnomalyResponse}}', - expectedResponseTime: '{{context.expectedResponseTime}}', - severityScore: '{{context.severityScore}}', - observerLocation: '{{context.observerLocation}}', - }, - } - ), - defaultRecoveryMessage: i18n.translate( - 'xpack.synthetics.alerts.durationAnomaly.defaultRecoveryMessage', - { - defaultMessage: `Alert for abnormal ({severity} level) response time detected on monitor {monitor} with url {monitorUrl} from location {observerLocation} at {anomalyStartTimestamp} has recovered`, - values: { - severity: '{{context.severity}}', - anomalyStartTimestamp: '{{context.anomalyStartTimestamp}}', - monitor: '{{context.monitor}}', - monitorUrl: '{{{context.monitorUrl}}}', - observerLocation: '{{context.observerLocation}}', - }, - } - ), - name: i18n.translate('xpack.synthetics.alerts.durationAnomaly.clientName', { - defaultMessage: 'Uptime Duration Anomaly', - }), - description: i18n.translate('xpack.synthetics.alerts.durationAnomaly.description', { - defaultMessage: 'Alert when the Uptime monitor duration is anomalous.', - }), -}; diff --git a/x-pack/plugins/synthetics/common/types/monitor_validation.ts b/x-pack/plugins/synthetics/common/types/monitor_validation.ts index 9d37a2d9b6104..09cf19cff4e48 100644 --- a/x-pack/plugins/synthetics/common/types/monitor_validation.ts +++ b/x-pack/plugins/synthetics/common/types/monitor_validation.ts @@ -5,10 +5,12 @@ * 2.0. */ -import { ConfigKey, MonitorFields } from '../runtime_types'; +import { ConfigKey, MonitorFields, ThrottlingConfig } from '../runtime_types'; -export type Validator = (config: Partial) => boolean; -export type NamespaceValidator = (config: Partial) => false | string; +export type Validator = (config: Partial) => boolean; +export type NamespaceValidator = ( + config: Partial +) => false | string; export type ConfigValidation = Omit, ConfigKey.NAMESPACE> & Record; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts index 44053aa29ed26..fb79e0ec94ff2 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts @@ -130,11 +130,15 @@ export const testDataMonitor = { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts index f65d82a9933f4..f7143ee5b89e8 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts +++ b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts @@ -48,11 +48,15 @@ export const importMonitors = async ({ 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, }; const id = '1c215bd0-f580-11ec-89e5-694db461b7a5'; diff --git a/x-pack/plugins/synthetics/public/apps/locators/overview.test.ts b/x-pack/plugins/synthetics/public/apps/locators/overview.test.ts index 14c05192482a2..a8ca60077bdf6 100644 --- a/x-pack/plugins/synthetics/public/apps/locators/overview.test.ts +++ b/x-pack/plugins/synthetics/public/apps/locators/overview.test.ts @@ -21,7 +21,9 @@ describe('uptimeOverviewNavigatorParams', () => { it('creates a path with expected search when ip is specified', async () => { const location = await uptimeOverviewNavigatorParams.getLocation({ ip: '127.0.0.1' }); - expect(location.path).toEqual(`${OVERVIEW_ROUTE}?search=monitor.ip: "127.0.0.1"`); + expect(location.path).toEqual( + `${OVERVIEW_ROUTE}?search=host.ip: "127.0.0.1" OR monitor.ip: "127.0.0.1"` + ); }); it('creates a path with expected search when hostname is specified', async () => { @@ -35,7 +37,7 @@ describe('uptimeOverviewNavigatorParams', () => { ip: '127.0.0.1', }); expect(location.path).toEqual( - `${OVERVIEW_ROUTE}?search=host.name: "elastic.co" OR host.ip: "127.0.0.1"` + `${OVERVIEW_ROUTE}?search=host.name: "elastic.co" OR host.ip: "127.0.0.1" OR monitor.ip: "127.0.0.1"` ); }); @@ -45,7 +47,7 @@ describe('uptimeOverviewNavigatorParams', () => { ip: '10.0.0.1', }); expect(location.path).toEqual( - `${OVERVIEW_ROUTE}?search=kubernetes.pod.uid: "foo" OR monitor.ip: "10.0.0.1"` + `${OVERVIEW_ROUTE}?search=kubernetes.pod.uid: "foo" OR host.ip: "10.0.0.1" OR monitor.ip: "10.0.0.1"` ); }); diff --git a/x-pack/plugins/synthetics/public/apps/locators/overview.ts b/x-pack/plugins/synthetics/public/apps/locators/overview.ts index e2267d4d0fe6d..785668d8e8c74 100644 --- a/x-pack/plugins/synthetics/public/apps/locators/overview.ts +++ b/x-pack/plugins/synthetics/public/apps/locators/overview.ts @@ -28,8 +28,8 @@ async function navigate({ if (pod) searchParams.push(formatSearchKey('kubernetes.pod.uid', pod)); if (ip) { - const root = host ? 'host' : 'monitor'; - searchParams.push(formatSearchKey(`${root}.ip`, ip)); + searchParams.push(formatSearchKey(`host.ip`, ip)); + searchParams.push(formatSearchKey(`monitor.ip`, ip)); } const searchString = searchParams.join(' OR '); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx index 6c5e80c9f28e4..cbe375774ef50 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx @@ -53,6 +53,11 @@ export const StdErrorLogs = ({ sortable: true, render: (date: string) => formatDate(date, 'dateTime'), }, + { + field: 'synthetics.type', + name: TYPE_LABEL, + sortable: true, + }, { field: 'synthetics.payload.message', name: 'Message', @@ -146,6 +151,10 @@ export const TIMESTAMP_LABEL = i18n.translate('xpack.synthetics.monitorList.time defaultMessage: 'Timestamp', }); +export const TYPE_LABEL = i18n.translate('xpack.synthetics.monitorList.type', { + defaultMessage: 'Type', +}); + export const ERROR_SUMMARY_LABEL = i18n.translate('xpack.synthetics.monitorList.errorSummary', { defaultMessage: 'Error summary', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/use_std_error_logs.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/use_std_error_logs.ts index a1317da94ccd2..ac26d594af5e1 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/use_std_error_logs.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/use_std_error_logs.ts @@ -25,8 +25,8 @@ export const useStdErrorLogs = ({ bool: { filter: [ { - term: { - 'synthetics.type': 'stderr', + terms: { + 'synthetics.type': ['stderr', 'stdout'], }, }, ...(monitorId diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx index 6fae43af920e5..a78710dd9994e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.test.tsx @@ -25,26 +25,6 @@ describe('SyntheticsDatePicker component', () => { expect(await findByText('Refresh')).toBeInTheDocument(); }); - it('uses shared date range state when there is no url date range state', async () => { - const customHistory = createMemoryHistory({ - initialEntries: ['/?dateRangeStart=now-24h&dateRangeEnd=now'], - }); - - jest.spyOn(customHistory, 'push'); - - const { findByText } = render(, { - history: customHistory, - core: startPlugins, - }); - - expect(await findByText('~ 30 minutes ago')).toBeInTheDocument(); - - expect(customHistory.push).toHaveBeenCalledWith({ - pathname: '/', - search: 'dateRangeEnd=now-15m&dateRangeStart=now-30m', - }); - }); - it('should use url date range even if shared date range is present', async () => { const customHistory = createMemoryHistory({ initialEntries: ['/?g=%22%22&dateRangeStart=now-10m&dateRangeEnd=now'], diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx index 82f2874f9ffa2..a5eaeacf6c7ed 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx @@ -7,7 +7,6 @@ import React, { useContext, useEffect } from 'react'; import { EuiSuperDatePicker } from '@elastic/eui'; -import { CLIENT_DEFAULTS_SYNTHETICS } from '../../../../../../common/constants/synthetics/client_defaults'; import { useUrlParams } from '../../../hooks'; import { CLIENT_DEFAULTS } from '../../../../../../common/constants'; import { @@ -16,12 +15,6 @@ import { SyntheticsRefreshContext, } from '../../../contexts'; -const isSyntheticsDefaultDateRange = (dateRangeStart: string, dateRangeEnd: string) => { - const { DATE_RANGE_START, DATE_RANGE_END } = CLIENT_DEFAULTS_SYNTHETICS; - - return dateRangeStart === DATE_RANGE_START && dateRangeEnd === DATE_RANGE_END; -}; - export const SyntheticsDatePicker = ({ fullWidth }: { fullWidth?: boolean }) => { const [getUrlParams, updateUrl] = useUrlParams(); const { commonlyUsedRanges } = useContext(SyntheticsSettingsContext); @@ -36,10 +29,7 @@ export const SyntheticsDatePicker = ({ fullWidth }: { fullWidth?: boolean }) => useEffect(() => { const { from, to } = sharedTimeState ?? {}; - // if it's synthetics default range, and we have shared state from kibana, let's use that - if (isSyntheticsDefaultDateRange(start, end) && (from !== start || to !== end)) { - updateUrl({ dateRangeStart: from, dateRangeEnd: to }); - } else if (from !== start || to !== end) { + if (from !== start || to !== end) { // if it's coming url. let's update shared state data?.query.timefilter.timefilter.setTime({ from: start, to: end }); } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx index 4a6ea2d3f34e1..a759273546005 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/error_details/error_details_page.tsx @@ -67,7 +67,11 @@ export function ErrorDetailsPage() { /> - + diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx index 049d213249cb9..a4d3460225462 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/simple_monitor_form.tsx @@ -142,6 +142,13 @@ export const MONITOR_SUCCESS_LABEL = i18n.translate( } ); +export const MONITOR_FAILURE_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.monitorFailureMessage', + { + defaultMessage: 'Monitor was unable to be saved. Please try again later.', + } +); + export const URL_REQUIRED_LABEL = i18n.translate( 'xpack.synthetics.monitorManagement.urlRequiredLabel', { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts index e8a9e51dad844..9e344af3fcfbc 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useFetcher } from '@kbn/observability-plugin/public'; +import { FETCH_STATUS, useFetcher } from '@kbn/observability-plugin/public'; import { useEffect } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useDispatch, useSelector } from 'react-redux'; @@ -20,7 +20,11 @@ import { ServiceLocationErrors, SyntheticsMonitorWithId, } from '../../../../../common/runtime_types'; -import { MONITOR_SUCCESS_LABEL, SimpleFormData } from './simple_monitor_form'; +import { + MONITOR_SUCCESS_LABEL, + MONITOR_FAILURE_LABEL, + SimpleFormData, +} from './simple_monitor_form'; import { kibanaService } from '../../../../utils/kibana_service'; export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData }) => { @@ -31,7 +35,7 @@ export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData const { refreshApp } = useSyntheticsRefreshContext(); - const { data, loading } = useFetcher(() => { + const { data, loading, status } = useFetcher(() => { if (!monitorData) { return new Promise((resolve) => resolve(undefined)); } @@ -62,7 +66,12 @@ export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData ); } - if (!loading && newMonitor?.id) { + if (!loading && status === FETCH_STATUS.FAILURE) { + kibanaService.toasts.addDanger({ + title: MONITOR_FAILURE_LABEL, + toastLifeTimeMs: 3000, + }); + } else if (!loading && newMonitor?.id) { kibanaService.toasts.addSuccess({ title: MONITOR_SUCCESS_LABEL, toastLifeTimeMs: 3000, @@ -71,7 +80,7 @@ export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData dispatch(cleanMonitorListState()); application?.navigateToApp('synthetics', { path: 'monitors' }); } - }, [application, data, dispatch, loading, refreshApp, serviceLocations]); + }, [application, data, status, dispatch, loading, refreshApp, serviceLocations]); return { data: data as SyntheticsMonitorWithId, loading }; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.test.tsx index 6f920bf10d84a..c08434460aab5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { fireEvent, waitFor } from '@testing-library/react'; import { render } from '../../../utils/testing/rtl_helpers'; import { HeaderField, contentTypes } from './header_field'; -import { Mode } from '../types'; +import { CodeEditorMode } from '../types'; describe('', () => { const onChange = jest.fn(); @@ -95,14 +95,14 @@ describe('', () => { }); it('handles content mode', async () => { - const contentMode: Mode = Mode.PLAINTEXT; + const contentMode: CodeEditorMode = CodeEditorMode.PLAINTEXT; render( ); await waitFor(() => { expect(onChange).toBeCalledWith({ - 'Content-Type': contentTypes[Mode.PLAINTEXT], + 'Content-Type': contentTypes[CodeEditorMode.PLAINTEXT], }); }); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.tsx index a26fe8616d90b..c3159043b9958 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/header_field.tsx @@ -7,12 +7,12 @@ import React, { useEffect, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ContentType, Mode } from '../types'; +import { ContentType, CodeEditorMode } from '../types'; import { KeyValuePairsField, Pair } from './key_value_field'; export interface HeaderFieldProps { - contentMode?: Mode; + contentMode?: CodeEditorMode; defaultValue: Record; onChange: (value: Record) => void; onBlur?: () => void; @@ -72,9 +72,9 @@ export const HeaderField = ({ ); }; -export const contentTypes: Record = { - [Mode.JSON]: ContentType.JSON, - [Mode.PLAINTEXT]: ContentType.TEXT, - [Mode.XML]: ContentType.XML, - [Mode.FORM]: ContentType.FORM, +export const contentTypes: Record = { + [CodeEditorMode.JSON]: ContentType.JSON, + [CodeEditorMode.PLAINTEXT]: ContentType.TEXT, + [CodeEditorMode.XML]: ContentType.XML, + [CodeEditorMode.FORM]: ContentType.FORM, }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/key_value_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/key_value_field.tsx index 95b2348fd0219..7d062fbde7548 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/key_value_field.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/key_value_field.tsx @@ -46,13 +46,15 @@ export type Pair = [ string // value ]; -interface Props { +export interface KeyValuePairsFieldProps { addPairControlLabel: string | React.ReactElement; defaultPairs: Pair[]; onChange: (pairs: Pair[]) => void; onBlur?: () => void; 'data-test-subj'?: string; readOnly?: boolean; + keyLabel?: string | React.ReactElement; + valueLabel?: string | React.ReactElement; } export const KeyValuePairsField = ({ @@ -62,7 +64,9 @@ export const KeyValuePairsField = ({ onBlur, 'data-test-subj': dataTestSubj, readOnly, -}: Props) => { + keyLabel, + valueLabel, +}: KeyValuePairsFieldProps) => { const [pairs, setPairs] = useState(defaultPairs); const handleOnChange = useCallback( @@ -121,20 +125,20 @@ export const KeyValuePairsField = ({ children: ( - { + {keyLabel || ( - } + )} - { + {valueLabel || ( - } + )} ), diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.test.tsx index a472c3231053b..d3a8bd8076fe1 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.test.tsx @@ -13,7 +13,7 @@ import { fireEvent, waitFor } from '@testing-library/react'; import { mockGlobals } from '../../../utils/testing'; import { render } from '../../../utils/testing/rtl_helpers'; import { RequestBodyField } from './request_body_field'; -import { Mode } from '../types'; +import { CodeEditorMode } from '../types'; mockGlobals(); @@ -40,7 +40,7 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { }); describe('', () => { - const defaultMode = Mode.PLAINTEXT; + const defaultMode = CodeEditorMode.PLAINTEXT; const defaultValue = 'sample value'; const WrappedComponent = ({ readOnly }: { readOnly?: boolean }) => { const [config, setConfig] = useState({ @@ -55,7 +55,7 @@ describe('', () => { type: config.type, }} onChange={useCallback( - (code) => setConfig({ type: code.type as Mode, value: code.value }), + (code) => setConfig({ type: code.type as CodeEditorMode, value: code.value }), [setConfig] )} readOnly={readOnly} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.tsx index fe117a4703ffb..e6877942f4f14 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/request_body_field.tsx @@ -9,15 +9,15 @@ import { stringify, parse } from 'query-string'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { EuiTabbedContent } from '@elastic/eui'; -import { Mode, MonacoEditorLangId } from '../types'; +import { CodeEditorMode, MonacoEditorLangId } from '../types'; import { KeyValuePairsField, Pair } from './key_value_field'; import { CodeEditor } from './code_editor'; export interface RequestBodyFieldProps { - onChange: (requestBody: { type: Mode; value: string }) => void; + onChange: (requestBody: { type: CodeEditorMode; value: string }) => void; onBlur?: () => void; value: { - type: Mode; + type: CodeEditorMode; value: string; }; readOnly?: boolean; @@ -36,22 +36,27 @@ export const RequestBodyField = ({ readOnly, }: RequestBodyFieldProps) => { const [values, setValues] = useState>({ - [ResponseBodyType.FORM]: type === Mode.FORM ? value : '', - [ResponseBodyType.CODE]: type !== Mode.FORM ? value : '', + [ResponseBodyType.FORM]: type === CodeEditorMode.FORM ? value : '', + [ResponseBodyType.CODE]: type !== CodeEditorMode.FORM ? value : '', }); useEffect(() => { onChange({ type, - value: type === Mode.FORM ? values[ResponseBodyType.FORM] : values[ResponseBodyType.CODE], + value: + type === CodeEditorMode.FORM + ? values[ResponseBodyType.FORM] + : values[ResponseBodyType.CODE], }); }, [onChange, type, values]); const handleSetMode = useCallback( - (currentMode: Mode) => { + (currentMode: CodeEditorMode) => { onChange({ type: currentMode, value: - currentMode === Mode.FORM ? values[ResponseBodyType.FORM] : values[ResponseBodyType.CODE], + currentMode === CodeEditorMode.FORM + ? values[ResponseBodyType.FORM] + : values[ResponseBodyType.CODE], }); }, [onChange, values] @@ -71,14 +76,14 @@ export const RequestBodyField = ({ }, {}); return setValues((prevValues) => ({ ...prevValues, - [Mode.FORM]: stringify(formattedPairs), + [CodeEditorMode.FORM]: stringify(formattedPairs), })); }, [setValues] ); const defaultFormPairs: Pair[] = useMemo(() => { - const pairs = parse(values[Mode.FORM]); + const pairs = parse(values[CodeEditorMode.FORM]); const keys = Object.keys(pairs); const formattedPairs: Pair[] = keys.map((key: string) => { // key, value, checked; @@ -89,9 +94,9 @@ export const RequestBodyField = ({ const tabs = [ { - id: Mode.PLAINTEXT, - name: modeLabels[Mode.PLAINTEXT], - 'data-test-subj': `syntheticsRequestBodyTab__${Mode.PLAINTEXT}`, + id: CodeEditorMode.PLAINTEXT, + name: modeLabels[CodeEditorMode.PLAINTEXT], + 'data-test-subj': `syntheticsRequestBodyTab__${CodeEditorMode.PLAINTEXT}`, content: ( { setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })); @@ -112,9 +117,9 @@ export const RequestBodyField = ({ ), }, { - id: Mode.JSON, - name: modeLabels[Mode.JSON], - 'data-test-subj': `syntheticsRequestBodyTab__${Mode.JSON}`, + id: CodeEditorMode.JSON, + name: modeLabels[CodeEditorMode.JSON], + 'data-test-subj': `syntheticsRequestBodyTab__${CodeEditorMode.JSON}`, content: ( { setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })); @@ -135,9 +140,9 @@ export const RequestBodyField = ({ ), }, { - id: Mode.XML, - name: modeLabels[Mode.XML], - 'data-test-subj': `syntheticsRequestBodyTab__${Mode.XML}`, + id: CodeEditorMode.XML, + name: modeLabels[CodeEditorMode.XML], + 'data-test-subj': `syntheticsRequestBodyTab__${CodeEditorMode.XML}`, content: ( { setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })); @@ -158,9 +163,9 @@ export const RequestBodyField = ({ ), }, { - id: Mode.FORM, - name: modeLabels[Mode.FORM], - 'data-test-subj': `syntheticsRequestBodyTab__${Mode.FORM}`, + id: CodeEditorMode.FORM, + name: modeLabels[CodeEditorMode.FORM], + 'data-test-subj': `syntheticsRequestBodyTab__${CodeEditorMode.FORM}`, content: ( tab.id === type)} autoFocus="selected" onTabClick={(tab) => { - handleSetMode(tab.id as Mode); + handleSetMode(tab.id as CodeEditorMode); }} />
    @@ -195,25 +200,25 @@ export const RequestBodyField = ({ }; const modeLabels = { - [Mode.FORM]: i18n.translate( + [CodeEditorMode.FORM]: i18n.translate( 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.form', { defaultMessage: 'Form', } ), - [Mode.PLAINTEXT]: i18n.translate( + [CodeEditorMode.PLAINTEXT]: i18n.translate( 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.text', { defaultMessage: 'Text', } ), - [Mode.JSON]: i18n.translate( + [CodeEditorMode.JSON]: i18n.translate( 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.JSON', { defaultMessage: 'JSON', } ), - [Mode.XML]: i18n.translate( + [CodeEditorMode.XML]: i18n.translate( 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.XML', { defaultMessage: 'XML', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/source_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/source_field.tsx index f5b9602923dc4..bd5c46810d202 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/source_field.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/source_field.tsx @@ -5,16 +5,9 @@ * 2.0. */ import React, { useEffect, useState } from 'react'; -import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { - EuiTabbedContent, - EuiFormRow, - EuiBetaBadge, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; +import { EuiTabbedContent, EuiFormRow } from '@elastic/eui'; import { CodeEditor } from './code_editor'; import { ScriptRecorderFields } from './script_recorder_fields'; import { ConfigKey, MonacoEditorLangId } from '../types'; @@ -51,40 +44,16 @@ export const SourceField = ({ onChange, onBlur, value, isEditFlow = false }: Sou const allTabs = [ { id: 'syntheticsBrowserScriptRecorderConfig', - name: ( - - - {isEditFlow ? ( - - ) : ( - - )} - - - - - + name: isEditFlow ? ( + + ) : ( + ), 'data-test-subj': 'syntheticsSourceTab__scriptRecorder', content: ( @@ -171,9 +140,3 @@ export const SourceField = ({ onChange, onBlur, value, isEditFlow = false }: Sou /> ); }; - -const StyledBetaBadgeWrapper = styled(EuiFlexItem)` - .euiToolTipAnchor { - display: flex; - } -`; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/connection_profile.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/connection_profile.tsx new file mode 100644 index 0000000000000..83beb1e63939f --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/connection_profile.tsx @@ -0,0 +1,81 @@ +/* + * 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, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { PROFILE_VALUES_ENUM } from '../../../../../../../common/constants/monitor_defaults'; +import { ThrottlingConfig } from '../../../../../../../common/runtime_types'; + +export const ConnectionProfile = ({ + throttling, + id, +}: { + throttling?: ThrottlingConfig; + id: string; +}) => { + if (id === PROFILE_VALUES_ENUM.NO_THROTTLING) { + return ( + + + + + + + + ); + } + + if (throttling && throttling.value) { + const { label, value } = throttling; + + return ( + + + {label} + + + + + + + + ); + } else { + return ( + + + + + + + + + + + + + ); + } +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_config_field.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_config_field.test.tsx new file mode 100644 index 0000000000000..f0dcb655749db --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_config_field.test.tsx @@ -0,0 +1,81 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { PROFILE_OPTIONS, ThrottlingConfigField } from './throttling_config_field'; +import { render } from '../../../../utils/testing'; +import { PROFILES_MAP } from '../../../../../../../common/constants/monitor_defaults'; + +describe('ThrottlingConfigField', () => { + it('renders', async () => { + render( + {}} + /> + ); + expect(await screen.findByText('(5 Mbps, 3 Mbps, 20 ms)')).toBeInTheDocument(); + }); + + it('selects custom values', async () => { + const onChange = jest.fn(); + render( + + ); + expect(await screen.findByText('(5 Mbps, 3 Mbps, 20 ms)')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('syntheticsThrottlingSelect')); + fireEvent.click(await screen.findByTestId('syntheticsThrottlingSelectCustom')); + + const customValue = { + id: 'custom', + label: 'Custom', + value: { download: '5', latency: '20', upload: '3' }, + }; + + expect(onChange).toHaveBeenCalledWith(customValue); + }); + + it('changes custom values', async () => { + const onChange = jest.fn(); + const customValue = { + id: 'custom', + label: 'Custom', + value: { download: '5', latency: '20', upload: '3' }, + }; + + render( + + ); + + fireEvent.input(screen.getByTestId('syntheticsBrowserUploadSpeed'), { + target: { value: '10' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + ...customValue, + value: { ...customValue.value, upload: '10' }, + }); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_config_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_config_field.tsx new file mode 100644 index 0000000000000..9635584e13673 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_config_field.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiSuperSelect } from '@elastic/eui'; +import { useConnectionProfiles } from './use_connection_profiles'; +import { ThrottlingDisabledCallout } from './throttling_disabled_callout'; +import { ThrottlingConfig } from '../../../../../../../common/runtime_types'; +import { ThrottlingFields } from './throttling_fields'; +import { PROFILE_VALUES_ENUM, PROFILE_VALUES, PROFILES_MAP, CUSTOM_LABEL } from '../../constants'; +import { ConnectionProfile } from './connection_profile'; + +export interface ThrottlingConfigFieldProps { + ariaLabel: string; + id: string; + onChange: (value: ThrottlingConfig) => void; + value: ThrottlingConfig; + placeholder?: string; + height?: string; + readOnly?: boolean; + disabled?: boolean; + fullWidth?: boolean; + options: typeof PROFILE_OPTIONS; + initialValue?: ThrottlingConfig; +} + +export const ThrottlingConfigField = (props: ThrottlingConfigFieldProps) => { + const { value, initialValue } = props; + + const isCustom = PROFILES_MAP[value?.id] === undefined; + + const isThrottlingDisabled = value?.id === PROFILE_VALUES_ENUM.NO_THROTTLING; + + const options = useConnectionProfiles(initialValue); + + return ( + <> + { + if (newValue === PROFILE_VALUES_ENUM.CUSTOM) { + props.onChange({ + ...PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], + id: PROFILE_VALUES_ENUM.CUSTOM, + label: CUSTOM_LABEL, + }); + } else { + props.onChange({ + ...PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], + ...PROFILES_MAP[newValue], + }); + } + }} + defaultValue={PROFILE_VALUES_ENUM.DEFAULT} + valueOfSelected={value?.id} + fullWidth={props.fullWidth} + readOnly={props.readOnly} + /> + {isThrottlingDisabled && } + {isCustom && ( + + )} + + ); +}; + +export const PROFILE_OPTIONS = PROFILE_VALUES.map(({ id }) => ({ + value: id, + inputDisplay: , + 'data-test-subj': `syntheticsThrottlingSelect-${id}`, +})); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_disabled_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_disabled_callout.tsx new file mode 100644 index 0000000000000..05d61b5b80d4d --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_disabled_callout.tsx @@ -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. + */ + +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; + +export const ThrottlingDisabledCallout = () => { + return ( + <> + + + } + color="warning" + iconType="warning" + > + + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_download_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_download_field.tsx new file mode 100644 index 0000000000000..918e5828cb70f --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_download_field.tsx @@ -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 { EuiFieldNumber, EuiFormRow, EuiText } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { Validation } from '../../../../../../../common/types'; +import { + BandwidthLimitKey, + ConfigKey, + DEFAULT_BANDWIDTH_LIMIT, + ThrottlingConfig, + ThrottlingConfigValue, +} from '../../../../../../../common/runtime_types'; +import { ThrottlingExceededMessage } from './throttling_exceeded_callout'; +import { OptionalLabel } from '../optional_label'; + +export const ThrottlingDownloadField = ({ + handleInputChange, + readOnly, + onFieldBlur, + validate, + throttling, + throttlingValue, +}: { + readOnly?: boolean; + handleInputChange: (value: string) => void; + onFieldBlur?: (field: keyof ThrottlingConfigValue) => void; + validate?: Validation; + throttling: ThrottlingConfig; + throttlingValue: ThrottlingConfigValue; +}) => { + const maxDownload = Number(DEFAULT_BANDWIDTH_LIMIT[BandwidthLimitKey.DOWNLOAD]); + + const exceedsDownloadLimits = Number(throttlingValue.download) > maxDownload; + + return ( + } + isInvalid={ + (validate ? !!validate?.[ConfigKey.THROTTLING_CONFIG]?.(throttling) : false) || + exceedsDownloadLimits + } + error={ + exceedsDownloadLimits ? ( + + ) : ( + DOWNLOAD_SPEED_ERROR + ) + } + > + { + handleInputChange(event.target.value); + }} + onBlur={() => onFieldBlur?.('download')} + data-test-subj="syntheticsBrowserDownloadSpeed" + append={ + + Mbps + + } + readOnly={readOnly} + /> + + ); +}; + +export const DOWNLOAD_LABEL = i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.download.label', + { + defaultMessage: 'Download Speed', + } +); + +export const DOWNLOAD_SPEED_ERROR = i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.download.error', + { + defaultMessage: 'Download speed must be greater than zero.', + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_exceeded_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_exceeded_callout.tsx new file mode 100644 index 0000000000000..fe65ddb38a606 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_exceeded_callout.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; + +export const ThrottlingExceededCallout = () => { + return ( + <> + + + } + color="warning" + iconType="warning" + > + + + + ); +}; + +export const ThrottlingExceededMessage = ({ + throttlingField, + limit, +}: { + throttlingField: string; + limit: number; +}) => { + return ( + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_fields.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_fields.test.tsx new file mode 100644 index 0000000000000..9beffa5537cd1 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_fields.test.tsx @@ -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 React from 'react'; +import { fireEvent, screen } from '@testing-library/react'; +import { render } from '../../../../utils/testing'; +import { PROFILES_MAP } from '../../../../../../../common/constants/monitor_defaults'; +import { ThrottlingFields } from './throttling_fields'; + +describe('ThrottlingFields', () => { + it('renders', async () => { + render( {}} />); + + expect(await screen.findByText('Download Speed')).toBeInTheDocument(); + expect(await screen.findByText('Upload Speed')).toBeInTheDocument(); + expect(await screen.findByText('Latency')).toBeInTheDocument(); + }); + + it('calls setValue on change', async () => { + const setValue = jest.fn(); + render(); + + const throttling = PROFILES_MAP.default; + + const download = await screen.findByTestId('syntheticsBrowserDownloadSpeed'); + fireEvent.change(download, { target: { value: '10' } }); + expect(setValue).toHaveBeenCalledWith({ + ...throttling, + label: 'Custom', + id: 'custom', + value: { download: '10', latency: '20', upload: '3' }, + }); + + const upload = await screen.findByTestId('syntheticsBrowserUploadSpeed'); + fireEvent.change(upload, { target: { value: '10' } }); + expect(setValue).toHaveBeenLastCalledWith({ + ...throttling, + label: 'Custom', + id: 'custom', + value: { download: '5', latency: '20', upload: '10' }, + }); + + const latency = await screen.findByTestId('syntheticsBrowserLatency'); + fireEvent.change(latency, { target: { value: '10' } }); + expect(setValue).toHaveBeenLastCalledWith({ + ...throttling, + label: 'Custom', + id: 'custom', + value: { download: '5', latency: '10', upload: '3' }, + }); + }); + + it('shows maximum bandwidth callout on download and upload change', async () => { + const setValue = jest.fn(); + const throttling = PROFILES_MAP.default; + + render( + + ); + + expect( + await screen.findByText( + 'When using throttling values larger than a Synthetics Node bandwidth limit, your monitor will still have its bandwidth capped.' + ) + ).toBeInTheDocument(); + + expect( + await screen.findByText( + "You have exceeded the download limit for Synthetic Nodes. The download value can't be larger than 100Mbps." + ) + ).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_fields.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_fields.tsx new file mode 100644 index 0000000000000..886f482a86ed8 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_fields.tsx @@ -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 React, { memo, useCallback } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { ThrottlingUploadField } from './throttling_upload_field'; +import { ThrottlingExceededCallout } from './throttling_exceeded_callout'; +import { + BandwidthLimitKey, + DEFAULT_BANDWIDTH_LIMIT, + ThrottlingConfig, + ThrottlingConfigValue, +} from '../../../../../../../common/runtime_types'; +import { Validation } from '../../types'; +import { ThrottlingDisabledCallout } from './throttling_disabled_callout'; +import { ThrottlingDownloadField } from './throttling_download_field'; +import { ThrottlingLatencyField } from './throttling_latency_field'; +import { CUSTOM_LABEL, PROFILE_VALUES_ENUM } from '../../constants'; + +interface Props { + validate?: Validation; + minColumnWidth?: string; + onFieldBlur?: (field: keyof ThrottlingConfigValue) => void; + readOnly?: boolean; + throttling: ThrottlingConfig; + setValue: (value: ThrottlingConfig) => void; +} + +export const ThrottlingFields = memo( + ({ validate, onFieldBlur, setValue, readOnly = false, throttling }) => { + const maxDownload = DEFAULT_BANDWIDTH_LIMIT[BandwidthLimitKey.DOWNLOAD]; + const maxUpload = DEFAULT_BANDWIDTH_LIMIT[BandwidthLimitKey.UPLOAD]; + + const handleInputChange = useCallback( + ({ value, configKey }: { value: string; configKey: string }) => { + setValue({ + ...throttling, + value: { ...throttling.value!, [configKey]: value }, + label: CUSTOM_LABEL, + id: PROFILE_VALUES_ENUM.CUSTOM, + }); + }, + [setValue, throttling] + ); + + const exceedsDownloadLimits = Number(throttling.value?.download) > maxDownload; + const exceedsUploadLimits = Number(throttling.value?.upload) > maxUpload; + const isThrottlingEnabled = throttling.id !== PROFILE_VALUES_ENUM.NO_THROTTLING; + + const hasExceededLimits = isThrottlingEnabled && (exceedsDownloadLimits || exceedsUploadLimits); + + if (!isThrottlingEnabled || !throttling.value) { + return ; + } + + return ( +
    + {hasExceededLimits && } + + { + handleInputChange({ value: val, configKey: 'download' }); + }} + readOnly={readOnly} + /> + { + handleInputChange({ value: val, configKey: 'upload' }); + }} + readOnly={readOnly} + /> + { + handleInputChange({ value: val, configKey: 'latency' }); + }} + readOnly={readOnly} + /> +
    + ); + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_latency_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_latency_field.tsx new file mode 100644 index 0000000000000..59ebe3aa509cf --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_latency_field.tsx @@ -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 { EuiFieldNumber, EuiFormRow, EuiText } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { Validation } from '../../../../../../../common/types'; +import { + ConfigKey, + ThrottlingConfig, + ThrottlingConfigValue, +} from '../../../../../../../common/runtime_types'; +import { OptionalLabel } from '../optional_label'; + +export const ThrottlingLatencyField = ({ + throttling, + readOnly, + onFieldBlur, + validate, + handleInputChange, + throttlingValue, +}: { + readOnly?: boolean; + handleInputChange: (value: string) => void; + onFieldBlur?: (field: keyof ThrottlingConfigValue) => void; + validate?: Validation; + throttling: ThrottlingConfig; + throttlingValue: ThrottlingConfigValue; +}) => { + return ( + } + isInvalid={validate ? !!validate?.[ConfigKey.THROTTLING_CONFIG]?.(throttling) : false} + error={LATENCY_NEGATIVE_ERROR} + > + handleInputChange(event.target.value)} + onBlur={() => onFieldBlur?.('latency')} + data-test-subj="syntheticsBrowserLatency" + append={ + + ms + + } + readOnly={readOnly} + /> + + ); +}; + +export const LATENCY_LABEL = i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.latency.label', + { + defaultMessage: 'Latency', + } +); + +export const LATENCY_NEGATIVE_ERROR = i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.latency.error', + { + defaultMessage: 'Latency must not be negative.', + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_upload_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_upload_field.tsx new file mode 100644 index 0000000000000..8e245ba44c2a6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/throttling_upload_field.tsx @@ -0,0 +1,87 @@ +/* + * 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 { EuiFieldNumber, EuiFormRow, EuiText } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { Validation } from '../../../../../../../common/types'; +import { + BandwidthLimitKey, + ConfigKey, + DEFAULT_BANDWIDTH_LIMIT, + ThrottlingConfig, + ThrottlingConfigValue, +} from '../../../../../../../common/runtime_types'; +import { ThrottlingExceededMessage } from './throttling_exceeded_callout'; +import { OptionalLabel } from '../optional_label'; + +export const ThrottlingUploadField = ({ + readOnly, + onFieldBlur, + throttling, + validate, + handleInputChange, + throttlingValue, +}: { + readOnly?: boolean; + handleInputChange: (value: string) => void; + onFieldBlur?: (field: keyof ThrottlingConfigValue) => void; + validate?: Validation; + throttling: ThrottlingConfig; + throttlingValue: ThrottlingConfigValue; +}) => { + const maxUpload = Number(DEFAULT_BANDWIDTH_LIMIT[BandwidthLimitKey.UPLOAD]); + + const exceedsUploadLimits = Number(throttlingValue.upload) > maxUpload; + + return ( + } + isInvalid={ + (validate ? !!validate?.[ConfigKey.THROTTLING_CONFIG]?.(throttling) : false) || + exceedsUploadLimits + } + error={ + exceedsUploadLimits ? ( + + ) : ( + UPLOAD_SPEED_ERROR + ) + } + > + handleInputChange(event.target.value)} + onBlur={() => onFieldBlur?.('upload')} + data-test-subj="syntheticsBrowserUploadSpeed" + append={ + + Mbps + + } + readOnly={readOnly} + /> + + ); +}; + +export const UPLOAD_LABEL = i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.upload.label', + { defaultMessage: 'Upload Speed' } +); + +export const UPLOAD_SPEED_ERROR = i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.browserAdvancedSettings.throttling.upload.error', + { + defaultMessage: 'Upload speed must be greater than zero.', + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/use_connection_profiles.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/use_connection_profiles.tsx new file mode 100644 index 0000000000000..c65fb046bbe59 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/fields/throttling/use_connection_profiles.tsx @@ -0,0 +1,30 @@ +/* + * 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 { PROFILE_VALUES_ENUM } from '../../../../../../../common/constants/monitor_defaults'; +import { ConnectionProfile } from './connection_profile'; +import { PROFILE_OPTIONS } from './throttling_config_field'; +import { ThrottlingConfig } from '../../../../../../../common/runtime_types'; + +export const useConnectionProfiles = (initialValue?: ThrottlingConfig) => { + return useMemo(() => { + return [ + ...PROFILE_OPTIONS, + { + value: PROFILE_VALUES_ENUM.CUSTOM, + inputDisplay: ( + + ), + 'data-test-subj': 'syntheticsThrottlingSelectCustom', + }, + ]; + }, [initialValue]); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx index 70521c28983f7..c2090eb0f47b0 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { ConfigKey, DataStream, FormMonitorType, SyntheticsMonitor } from '../types'; -import { DEFAULT_FIELDS } from '../constants'; +import { DEFAULT_FIELDS, PROFILE_VALUES_ENUM, PROFILES_MAP } from '../constants'; import { formatDefaultFormValues } from './defaults'; describe('defaults', () => { @@ -52,11 +52,15 @@ describe('defaults', () => { 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', + throttling: { + value: { + download: '5', + latency: '20', + upload: '3', + }, + id: 'default', + label: 'Default', + }, timeout: '16', type: 'browser', 'url.port': null, @@ -114,11 +118,7 @@ describe('defaults', () => { 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], timeout: '16', type: 'browser', 'url.port': null, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field.tsx index 7aed077680d4b..cd39245c1c7ec 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field.tsx @@ -24,7 +24,6 @@ export const Field = memo( props, fieldKey, controlled, - showWhen, shouldUseSetValue, required, validation, @@ -32,6 +31,7 @@ export const Field = memo( fieldError, dependencies, customHook, + hidden, }: Props) => { const { register, watch, control, setValue, reset, getFieldState, formState } = useFormContext(); @@ -41,13 +41,7 @@ export const Field = memo( const [dependenciesFieldMeta, setDependenciesFieldMeta] = useState< Record >({}); - let show = true; let dependenciesValues: unknown[] = []; - if (showWhen) { - const [showKey, expectedValue] = showWhen; - const [actualValue] = watch([showKey]); - show = actualValue === expectedValue; - } if (dependencies) { dependenciesValues = watch(dependencies); } @@ -64,7 +58,7 @@ export const Field = memo( // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(dependenciesValues || []), dependencies, getFieldState]); - if (!show) { + if (hidden && hidden(dependenciesValues)) { return null; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx index 7e1ea329fe3f8..9653c415e171a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { isValidNamespace } from '@kbn/fleet-plugin/common'; @@ -14,10 +15,7 @@ import { EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem, - EuiSuperSelect, - EuiText, EuiLink, - EuiTextArea, EuiSelectProps, EuiFieldTextProps, EuiSwitchProps, @@ -27,10 +25,13 @@ import { EuiCheckboxProps, EuiTextAreaProps, EuiButtonGroupProps, - EuiSuperSelectProps, EuiHighlight, EuiBadge, } from '@elastic/eui'; +import { + PROFILE_OPTIONS, + ThrottlingConfigFieldProps, +} from '../fields/throttling/throttling_config_field'; import { FieldText, FieldNumber, @@ -53,6 +54,9 @@ import { ResponseBodyIndexField, ResponseBodyIndexFieldProps, ControlledFieldProp, + KeyValuePairsField, + TextArea, + ThrottlingWrapper, } from './field_wrappers'; import { getDocLinks } from '../../../../../kibana_services'; import { useMonitorName } from '../hooks/use_monitor_name'; @@ -62,19 +66,20 @@ import { FormMonitorType, HTTPMethod, ScreenshotOption, + Mode, MonitorFields, TLSVersion, VerificationMode, FieldMap, FormLocation, + ResponseBodyIndexPolicy, + ResponseCheckJSON, + ThrottlingConfig, } from '../types'; -import { - AlertConfigKey, - DEFAULT_BROWSER_ADVANCED_FIELDS, - ALLOWED_SCHEDULES_IN_MINUTES, -} from '../constants'; +import { AlertConfigKey, ALLOWED_SCHEDULES_IN_MINUTES } from '../constants'; import { getDefaultFormFields } from './defaults'; import { validate, validateHeaders, WHOLE_NUMBERS_ONLY, FLOATS_ONLY } from './validation'; +import { KeyValuePairsFieldProps } from '../fields/key_value_field'; const getScheduleContent = (value: number) => { if (value > 60) { @@ -119,7 +124,7 @@ export const MONITOR_TYPE_CONFIG = { ), link: '#', icon: 'videoPlayer', - beta: true, + beta: false, }, [FormMonitorType.SINGLE]: { id: 'syntheticsMonitorTypeSingle', @@ -143,7 +148,7 @@ export const MONITOR_TYPE_CONFIG = { ), link: '#', icon: 'videoPlayer', - beta: true, + beta: false, }, [FormMonitorType.HTTP]: { id: 'syntheticsMonitorTypeHTTP', @@ -766,7 +771,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ fieldKey: ConfigKey.RESPONSE_STATUS_CHECK, component: FormattedComboBox, label: i18n.translate('xpack.synthetics.monitorConfig.responseStatusCheck.label', { - defaultMessage: 'Check response status equals', + defaultMessage: 'Response status equals', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.responseStatusCheck.helpText', { defaultMessage: @@ -795,7 +800,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ fieldKey: ConfigKey.RESPONSE_HEADERS_CHECK, component: HeaderField, label: i18n.translate('xpack.synthetics.monitorConfig.responseHeadersCheck.label', { - defaultMessage: 'Check response headers contain', + defaultMessage: 'Response headers contain', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.responseHeadersCheck.helpText', { defaultMessage: 'A list of expected response headers.', @@ -815,7 +820,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ fieldKey: ConfigKey.RESPONSE_BODY_CHECK_POSITIVE, component: FormattedComboBox, label: i18n.translate('xpack.synthetics.monitorConfig.responseBodyCheck.label', { - defaultMessage: 'Check response body contains', + defaultMessage: 'Response body contains', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.responseBodyCheck.helpText', { defaultMessage: @@ -831,7 +836,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ fieldKey: ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE, component: FormattedComboBox, label: i18n.translate('xpack.synthetics.monitorConfig.responseBodyCheckNegative.label', { - defaultMessage: 'Check response body does not contain', + defaultMessage: 'Response body does not contain', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.responseBodyCheckNegative.helpText', { defaultMessage: @@ -847,7 +852,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ fieldKey: ConfigKey.RESPONSE_RECEIVE_CHECK, component: FieldText, label: i18n.translate('xpack.synthetics.monitorConfig.responseReceiveCheck.label', { - defaultMessage: 'Check response contains', + defaultMessage: 'Response contains', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.responseReceiveCheck.helpText', { defaultMessage: 'The expected remote host response.', @@ -895,7 +900,27 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ isEditFlow: isEdit, }), validation: () => ({ - validate: (value) => Boolean(value.script), + validate: (value) => { + // return false if script contains import or require statement + if ( + value.script?.includes('import ') || + value.script?.includes('require(') || + value.script?.includes('journey(') + ) { + return i18n.translate('xpack.synthetics.monitorConfig.monitorScript.invalid', { + defaultMessage: + 'Monitor script is invalid. Inline scripts cannot be full journey scripts, they may only contain step definitions.', + }); + } + // should contain at least one step + if (value.script && !value.script?.includes('step(')) { + return i18n.translate('xpack.synthetics.monitorConfig.monitorScript.invalid.oneStep', { + defaultMessage: + 'Monitor script is invalid. Inline scripts must contain at least one step definition.', + }); + } + return Boolean(value.script); + }, }), error: i18n.translate('xpack.synthetics.monitorConfig.monitorScript.error', { defaultMessage: 'Monitor script is required', @@ -967,7 +992,11 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ defaultMessage: 'Verifies that the provided certificate is signed by a trusted authority (CA) and also verifies that the server’s hostname (or IP address) matches the names identified within the certificate. If the Subject Alternative Name is empty, it returns an error.', }), - showWhen: ['isTLSEnabled', true], + hidden: (dependencies) => { + const [isTLSEnabled] = dependencies; + return !Boolean(isTLSEnabled); + }, + dependencies: ['isTLSEnabled'], props: (): EuiSelectProps => ({ options: Object.values(VerificationMode).map((method) => ({ value: method, @@ -983,7 +1012,11 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ defaultMessage: 'Supported TLS protocols', }), controlled: true, - showWhen: ['isTLSEnabled', true], + hidden: (dependencies) => { + const [isTLSEnabled] = dependencies; + return !Boolean(isTLSEnabled); + }, + dependencies: ['isTLSEnabled'], props: ({ field, setValue }): EuiComboBoxProps => { return { options: Object.values(TLSVersion).map((version) => ({ @@ -1004,42 +1037,54 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ }, [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: { fieldKey: ConfigKey.TLS_CERTIFICATE_AUTHORITIES, - component: EuiTextArea, + component: TextArea, label: i18n.translate('xpack.synthetics.monitorConfig.certificateAuthorities.label', { defaultMessage: 'Certificate authorities', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.certificateAuthorities.helpText', { defaultMessage: 'PEM-formatted custom certificate authorities.', }), - showWhen: ['isTLSEnabled', true], + hidden: (dependencies) => { + const [isTLSEnabled] = dependencies; + return !Boolean(isTLSEnabled); + }, + dependencies: ['isTLSEnabled'], props: (): EuiTextAreaProps => ({ readOnly, }), }, [ConfigKey.TLS_CERTIFICATE]: { fieldKey: ConfigKey.TLS_CERTIFICATE, - component: EuiTextArea, + component: TextArea, label: i18n.translate('xpack.synthetics.monitorConfig.clientCertificate.label', { defaultMessage: 'Client certificate', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.clientCertificate.helpText', { defaultMessage: 'PEM-formatted certificate for TLS client authentication.', }), - showWhen: ['isTLSEnabled', true], + hidden: (dependencies) => { + const [isTLSEnabled] = dependencies; + return !Boolean(isTLSEnabled); + }, + dependencies: ['isTLSEnabled'], props: (): EuiTextAreaProps => ({ readOnly, }), }, [ConfigKey.TLS_KEY]: { fieldKey: ConfigKey.TLS_KEY, - component: EuiTextArea, + component: TextArea, label: i18n.translate('xpack.synthetics.monitorConfig.clientKey.label', { defaultMessage: 'Client key', }), helpText: i18n.translate('xpack.synthetics.monitorConfig.clientKey.helpText', { defaultMessage: 'PEM-formatted certificate key for TLS client authentication.', }), - showWhen: ['isTLSEnabled', true], + hidden: (dependencies) => { + const [isTLSEnabled] = dependencies; + return !Boolean(isTLSEnabled); + }, + dependencies: ['isTLSEnabled'], props: (): EuiTextAreaProps => ({ readOnly, }), @@ -1053,7 +1098,11 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ helpText: i18n.translate('xpack.synthetics.monitorConfig.clientKeyPassphrase.helpText', { defaultMessage: 'Certificate key passphrase for TLS client authentication.', }), - showWhen: ['isTLSEnabled', true], + hidden: (dependencies) => { + const [isTLSEnabled] = dependencies; + return !Boolean(isTLSEnabled); + }, + dependencies: ['isTLSEnabled'], props: (): EuiFieldPasswordProps => ({ readOnly, }), @@ -1103,41 +1152,23 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ }, [ConfigKey.THROTTLING_CONFIG]: { fieldKey: ConfigKey.THROTTLING_CONFIG, - component: EuiSuperSelect, + component: ThrottlingWrapper, label: i18n.translate('xpack.synthetics.monitorConfig.throttling.label', { defaultMessage: 'Connection profile', }), required: true, controlled: true, helpText: i18n.translate('xpack.synthetics.monitorConfig.throttling.helpText', { - defaultMessage: - 'Simulate network throttling (download, upload, latency). More options will be added in a future version.', - }), - props: (): EuiSuperSelectProps => ({ - options: [ - { - value: DEFAULT_BROWSER_ADVANCED_FIELDS[ConfigKey.THROTTLING_CONFIG], - inputDisplay: ( - - - - {i18n.translate('xpack.synthetics.monitorConfig.throttling.options.default', { - defaultMessage: 'Default', - })} - - - - - {'(5 Mbps, 3 Mbps, 20 ms)'} - - - - ), - }, - ], - readOnly, - disabled: true, // currently disabled through 1.0 until we define connection profiles + defaultMessage: 'Simulate network throttling (download, upload, latency).', }), + props: ({ formState }): Partial => { + return { + options: PROFILE_OPTIONS, + readOnly, + disabled: false, + initialValue: formState.defaultValues?.[ConfigKey.THROTTLING_CONFIG] as ThrottlingConfig, + }; + }, validation: () => ({ required: true, }), @@ -1251,4 +1282,165 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ isDisabled: readOnly, }), }, + [ConfigKey.MODE]: { + fieldKey: ConfigKey.MODE, + component: Select, + label: i18n.translate('xpack.synthetics.monitorConfig.mode.label', { + defaultMessage: 'Mode', + }), + helpText: ( + all, + any: any, + }} + /> + ), + props: (): EuiSelectProps => ({ + options: Object.values(Mode).map((value) => ({ + value, + text: value, + })), + disabled: readOnly, + }), + }, + [ConfigKey.RESPONSE_BODY_MAX_BYTES]: { + fieldKey: ConfigKey.RESPONSE_BODY_MAX_BYTES, + component: FieldNumber, + label: i18n.translate('xpack.synthetics.monitorConfig.responseBodyMaxBytes.label', { + defaultMessage: 'Response body max bytes', + }), + helpText: i18n.translate('xpack.synthetics.monitorConfig.responseBodyMaxBytes.helpText', { + defaultMessage: 'Controls the maximum size of the stored body contents.', + }), + hidden: (dependencies) => { + const [responseBodyIndex] = dependencies || []; + return responseBodyIndex === ResponseBodyIndexPolicy.NEVER; + }, + props: (): EuiFieldNumberProps => ({ min: 1, step: 'any', readOnly }), + dependencies: [ConfigKey.RESPONSE_BODY_INDEX], + }, + [ConfigKey.IPV4]: { + fieldKey: ConfigKey.IPV4, // also controls ipv6 + component: ComboBox, + label: i18n.translate('xpack.synthetics.monitorConfig.ipv4.label', { + defaultMessage: 'IP protocols', + }), + helpText: i18n.translate('xpack.synthetics.monitorConfig.ipv4.helpText', { + defaultMessage: 'IP protocols to use when pinging the remote host.', + }), + controlled: true, + dependencies: [ConfigKey.IPV6], + props: ({ field, setValue, dependencies }): EuiComboBoxProps => { + const [ipv6] = dependencies; + const ipv4 = field?.value; + const values: string[] = []; + if (ipv4) { + values.push('IPv4'); + } + if (ipv6) { + values.push('IPv6'); + } + return { + options: [ + { + label: 'IPv4', + }, + { + label: 'IPv6', + }, + ], + selectedOptions: values.map((version) => ({ + label: version, + })), + onChange: (updatedValues: Array>) => { + setValue( + ConfigKey.IPV4, + updatedValues.some((value) => value.label === 'IPv4') + ); + setValue( + ConfigKey.IPV6, + updatedValues.some((value) => value.label === 'IPv6') + ); + }, + isDisabled: readOnly, + }; + }, + }, + [ConfigKey.PROXY_HEADERS]: { + fieldKey: ConfigKey.PROXY_HEADERS, + component: HeaderField, + label: i18n.translate('xpack.synthetics.monitorConfig.proxyHeaders.label', { + defaultMessage: 'Proxy headers', + }), + helpText: i18n.translate('xpack.synthetics.monitorConfig.proxyHeaders.helpText', { + defaultMessage: 'Additional headers to send to proxies for CONNECT requests.', + }), + controlled: true, + validation: () => ({ + validate: (headers) => !validateHeaders(headers), + }), + error: i18n.translate('xpack.synthetics.monitorConfig.proxyHeaders.error', { + defaultMessage: 'The header key must be a valid HTTP token.', + }), + props: (): HeaderFieldProps => ({ + readOnly, + }), + }, + ['check.response.json']: { + fieldKey: ConfigKey.RESPONSE_JSON_CHECK, + component: KeyValuePairsField, + label: i18n.translate('xpack.synthetics.monitorConfig.responseJSON.label', { + defaultMessage: 'Response body contains JSON', + }), + helpText: i18n.translate('xpack.synthetics.monitorConfig.responseJSON.helpText', { + defaultMessage: + 'A list of expressions executed against the body when parsed as JSON. The body size must be less than or equal to 100 MiB.', + }), + controlled: true, + props: ({ field, setValue }): KeyValuePairsFieldProps => ({ + readOnly, + keyLabel: i18n.translate('xpack.synthetics.monitorConfig.responseJSON.key.label', { + defaultMessage: 'Description', + }), + valueLabel: i18n.translate('xpack.synthetics.monitorConfig.responseJSON.value.label', { + defaultMessage: 'Expression', + }), + addPairControlLabel: i18n.translate( + 'xpack.synthetics.monitorConfig.responseJSON.addPair.label', + { + defaultMessage: 'Add expression', + } + ), + onChange: (pairs) => { + const value: ResponseCheckJSON[] = pairs + .map((pair) => { + const [description, expression] = pair; + return { + description, + expression, + }; + }) + .filter((pair) => pair.description || pair.expression); + if (!isEqual(value, field?.value)) { + setValue(ConfigKey.RESPONSE_JSON_CHECK, value); + } + }, + defaultPairs: field?.value.map((check) => [check.description, check.expression]) || [], + }), + validation: () => { + return { + validate: (value: ResponseCheckJSON[]) => { + if (value.some((check) => !check.expression || !check.description)) { + return i18n.translate('xpack.synthetics.monitorConfig.responseJSON.error', { + defaultMessage: + "This JSON expression isn't valid. Make sure that both the label and expression are defined.", + }); + } + }, + }; + }, + }, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_wrappers.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_wrappers.tsx index 80455d8dd4e4d..b2ae5b290aecc 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_wrappers.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_wrappers.tsx @@ -25,7 +25,13 @@ import { EuiButtonGroupProps, EuiComboBox, EuiComboBoxProps, + EuiTextArea, + EuiTextAreaProps, } from '@elastic/eui'; +import { + ThrottlingConfigField, + ThrottlingConfigFieldProps, +} from '../fields/throttling/throttling_config_field'; import { SourceField, SourceFieldProps } from '../fields/source_field'; import { FormattedComboBox as DefaultFormattedComboBox, @@ -43,6 +49,10 @@ import { HeaderField as DefaultHeaderField, HeaderFieldProps as DefaultHeaderFieldProps, } from '../fields/header_field'; +import { + KeyValuePairsField as DefaultKeyValuePairsField, + KeyValuePairsFieldProps as DefaultKeyValuePairsFieldProps, +} from '../fields/key_value_field'; import { RequestBodyField as DefaultRequestBodyField, RequestBodyFieldProps as DefaultRequestBodyFieldProps, @@ -77,6 +87,10 @@ export const FieldText = React.forwardRef( ) ); +export const TextArea = React.forwardRef((props, ref) => ( + +)); + export const FieldNumber = React.forwardRef((props, ref) => ( )); @@ -125,6 +139,10 @@ export const HeaderField = React.forwardRef((p )); +export const KeyValuePairsField = React.forwardRef( + (props, _ref) => +); + export const RequestBodyField = React.forwardRef( (props, _ref) => ); @@ -132,3 +150,7 @@ export const RequestBodyField = React.forwardRef( (props, _ref) => ); + +export const ThrottlingWrapper = React.forwardRef( + (props, _ref) => +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx index a33f0e14b7777..8e74162ecef77 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/form_config.tsx @@ -39,10 +39,13 @@ const HTTP_ADVANCED = (readOnly: boolean) => ({ components: [ FIELD(readOnly)[ConfigKey.USERNAME], FIELD(readOnly)[ConfigKey.PASSWORD], - FIELD(readOnly)[ConfigKey.PROXY_URL], FIELD(readOnly)[ConfigKey.REQUEST_METHOD_CHECK], FIELD(readOnly)[ConfigKey.REQUEST_HEADERS_CHECK], FIELD(readOnly)[ConfigKey.REQUEST_BODY_CHECK], + FIELD(readOnly)[ConfigKey.PROXY_URL], + FIELD(readOnly)[ConfigKey.PROXY_HEADERS], + FIELD(readOnly)[ConfigKey.MODE], + FIELD(readOnly)[ConfigKey.IPV4], ], }, responseConfig: { @@ -58,6 +61,7 @@ const HTTP_ADVANCED = (readOnly: boolean) => ({ components: [ FIELD(readOnly)[ConfigKey.RESPONSE_HEADERS_INDEX], FIELD(readOnly)[ConfigKey.RESPONSE_BODY_INDEX], + FIELD(readOnly)[ConfigKey.RESPONSE_BODY_MAX_BYTES], ], }, responseChecks: { @@ -75,6 +79,7 @@ const HTTP_ADVANCED = (readOnly: boolean) => ({ FIELD(readOnly)[ConfigKey.RESPONSE_HEADERS_CHECK], FIELD(readOnly)[ConfigKey.RESPONSE_BODY_CHECK_POSITIVE], FIELD(readOnly)[ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE], + FIELD(readOnly)[ConfigKey.RESPONSE_JSON_CHECK], ], }, }); @@ -93,6 +98,8 @@ export const TCP_ADVANCED = (readOnly: boolean) => ({ components: [ FIELD(readOnly)[`${ConfigKey.PROXY_URL}__tcp`], FIELD(readOnly)[ConfigKey.REQUEST_SEND_CHECK], + FIELD(readOnly)[ConfigKey.MODE], + FIELD(readOnly)[ConfigKey.IPV4], ], }, responseChecks: { @@ -109,6 +116,21 @@ export const TCP_ADVANCED = (readOnly: boolean) => ({ }, }); +export const ICMP_ADVANCED = (readOnly: boolean) => ({ + requestConfig: { + title: i18n.translate('xpack.synthetics.monitorConfig.section.requestConfigICMP.title', { + defaultMessage: 'Request configuration', + }), + description: i18n.translate( + 'xpack.synthetics.monitorConfig.section.requestConfigICMP.description', + { + defaultMessage: 'Configure the payload sent to the remote host.', + } + ), + components: [FIELD(readOnly)[ConfigKey.MODE], FIELD(readOnly)[ConfigKey.IPV4]], + }, +}); + export const BROWSER_ADVANCED = (readOnly: boolean) => [ { title: i18n.translate('xpack.synthetics.monitorConfig.section.syntAgentOptions.title', { @@ -121,6 +143,7 @@ export const BROWSER_ADVANCED = (readOnly: boolean) => [ } ), components: [ + FIELD(readOnly)[ConfigKey.THROTTLING_CONFIG], FIELD(readOnly)[ConfigKey.IGNORE_HTTPS_ERRORS], FIELD(readOnly)[ConfigKey.SYNTHETICS_ARGS], FIELD(readOnly)[ConfigKey.PLAYWRIGHT_OPTIONS], @@ -209,7 +232,6 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.NAME], FIELD(readOnly)[ConfigKey.LOCATIONS], FIELD(readOnly)[`${ConfigKey.SCHEDULE}.number`], - FIELD(readOnly)[ConfigKey.THROTTLING_CONFIG], FIELD(readOnly)[ConfigKey.ENABLED], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], ], @@ -236,7 +258,6 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.TEXT_ASSERTION], FIELD(readOnly)[ConfigKey.LOCATIONS], FIELD(readOnly)[`${ConfigKey.SCHEDULE}.number`], - FIELD(readOnly)[ConfigKey.THROTTLING_CONFIG], FIELD(readOnly)[ConfigKey.ENABLED], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], ], @@ -265,6 +286,6 @@ export const FORM_CONFIG = (readOnly: boolean): FieldConfig => ({ FIELD(readOnly)[ConfigKey.ENABLED], FIELD(readOnly)[AlertConfigKey.STATUS_ENABLED], ], - advanced: [DEFAULT_DATA_OPTIONS(readOnly)], + advanced: [DEFAULT_DATA_OPTIONS(readOnly), ICMP_ADVANCED(readOnly).requestConfig], }, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx index 6000b02cb7f87..0464c36ba2429 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx @@ -7,7 +7,11 @@ import { format, ALLOWED_FIELDS } from './formatter'; import { DataStream } from '../../../../../../common/runtime_types'; -import { DEFAULT_FIELDS } from '../../../../../../common/constants/monitor_defaults'; +import { + DEFAULT_FIELDS, + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '../../../../../../common/constants/monitor_defaults'; describe('format', () => { let formValues: Record; @@ -199,11 +203,6 @@ describe('format', () => { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.key': '', @@ -215,9 +214,7 @@ describe('format', () => { script: '', fileName: '', }, - throttling: { - config: '5d/3u/20l', - }, + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], source: { inline: { type: scriptType, @@ -278,11 +275,6 @@ describe('format', () => { 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', timeout: '16', type: 'browser', 'url.port': null, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/run_test_btn.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/run_test_btn.tsx index 5bfc724e39f49..07497c0e6cd50 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/run_test_btn.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/run_test_btn.tsx @@ -20,14 +20,14 @@ import { import { runOnceMonitor } from '../../../state/manual_test_runs/api'; export const RunTestButton = () => { - const { watch, formState, getValues } = useFormContext(); + const { watch, formState, getValues, handleSubmit } = useFormContext(); const [inProgress, setInProgress] = useState(false); const [testRun, setTestRun] = useState(); const handleTestNow = () => { const config = getValues() as MonitorFieldsType; - if (config) { + if (config && !Object.keys(formState.errors).length) { setInProgress(true); setTestRun({ id: uuidv4(), @@ -68,9 +68,7 @@ export const RunTestButton = () => { disabled={isDisabled} aria-label={TEST_NOW_ARIA_LABEL} iconType="play" - onClick={() => { - handleTestNow(); - }} + onClick={handleSubmit(handleTestNow)} > {RUN_TEST} @@ -111,7 +109,7 @@ const useTooltipContent = ( tooltipContent = isTestRunInProgress ? TEST_SCHEDULED_LABEL : tooltipContent; - const isDisabled = !isValid || isTestRunInProgress || !isAnyPublicLocationSelected; + const isDisabled = isTestRunInProgress || !isAnyPublicLocationSelected; return { tooltipContent, isDisabled }; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx index cc03228d755be..332ee8e6fbc55 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx @@ -148,11 +148,15 @@ const validateThrottleValue = (speed: string | undefined, allowZero?: boolean) = const validateBrowser: ValidationLibrary = { ...validateCommon, [ConfigKey.SOURCE_INLINE]: ({ [ConfigKey.SOURCE_INLINE]: inlineScript }) => !inlineScript, - [ConfigKey.DOWNLOAD_SPEED]: ({ [ConfigKey.DOWNLOAD_SPEED]: downloadSpeed }) => - validateThrottleValue(downloadSpeed), - [ConfigKey.UPLOAD_SPEED]: ({ [ConfigKey.UPLOAD_SPEED]: uploadSpeed }) => - validateThrottleValue(uploadSpeed), - [ConfigKey.LATENCY]: ({ [ConfigKey.LATENCY]: latency }) => validateThrottleValue(latency, true), + [ConfigKey.THROTTLING_CONFIG]: ({ throttling }) => { + if (!throttling || throttling.value === null) return true; + const { download, upload, latency } = throttling.value; + return ( + validateThrottleValue(String(download)) || + validateThrottleValue(String(upload)) || + validateThrottleValue(String(latency), true) + ); + }, [ConfigKey.PLAYWRIGHT_OPTIONS]: ({ [ConfigKey.PLAYWRIGHT_OPTIONS]: playwrightOptions }) => playwrightOptions ? !validJSONFormat(playwrightOptions) : false, [ConfigKey.PARAMS]: ({ [ConfigKey.PARAMS]: params }) => diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx similarity index 74% rename from x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.ts rename to x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx index 9fd18e5dfa04d..d8638c4b9ed92 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx @@ -6,8 +6,9 @@ */ import { FETCH_STATUS, useFetcher } from '@kbn/observability-plugin/public'; +import { toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; import { useParams, useRouteMatch } from 'react-router-dom'; -import { useEffect } from 'react'; +import React, { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { i18n } from '@kbn/i18n'; import { MONITOR_EDIT_ROUTE } from '../../../../../../common/constants'; @@ -18,6 +19,8 @@ import { cleanMonitorListState } from '../../../state'; import { useSyntheticsRefreshContext } from '../../../contexts'; export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonitor }) => { + const core = useKibana(); + const theme$ = core.services.theme?.theme$; const dispatch = useDispatch(); const { refreshApp } = useSyntheticsRefreshContext(); const { monitorId } = useParams<{ monitorId: string }>(); @@ -51,10 +54,16 @@ export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonito dispatch(cleanMonitorListState()); kibanaService.toasts.addSuccess({ title: monitorId ? MONITOR_UPDATED_SUCCESS_LABEL : MONITOR_SUCCESS_LABEL, + text: toMountPoint( +

    + {monitorId ? MONITOR_UPDATED_SUCCESS_LABEL_SUBTEXT : MONITOR_SUCCESS_LABEL_SUBTEXT} +

    , + { theme$ } + ), toastLifeTimeMs: 3000, }); } - }, [data, status, monitorId, loading, refreshApp, dispatch]); + }, [data, status, monitorId, loading, refreshApp, dispatch, theme$]); return { status, loading, isEdit }; }; @@ -66,6 +75,13 @@ const MONITOR_SUCCESS_LABEL = i18n.translate( } ); +const MONITOR_SUCCESS_LABEL_SUBTEXT = i18n.translate( + 'xpack.synthetics.monitorManagement.monitorAddedSuccessMessage.subtext', + { + defaultMessage: 'It will next run according to its defined schedule.', + } +); + const MONITOR_UPDATED_SUCCESS_LABEL = i18n.translate( 'xpack.synthetics.monitorManagement.monitorEditedSuccessMessage', { @@ -79,3 +95,10 @@ const MONITOR_FAILURE_LABEL = i18n.translate( defaultMessage: 'Monitor was unable to be saved. Please try again later.', } ); + +const MONITOR_UPDATED_SUCCESS_LABEL_SUBTEXT = i18n.translate( + 'xpack.synthetics.monitorManagement.monitorFailureMessage.subtext', + { + defaultMessage: 'It will next run according to its defined schedule.', + } +); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_edit_page.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_edit_page.test.tsx index bd9bc9d57c4aa..6b45a7d3920c2 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_edit_page.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/monitor_edit_page.test.tsx @@ -12,6 +12,10 @@ import { MonitorEditPage } from './monitor_edit_page'; import { ConfigKey } from '../../../../../common/runtime_types'; import * as observabilityPublic from '@kbn/observability-plugin/public'; +import { + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '../../../../../common/constants/monitor_defaults'; mockGlobals(); @@ -45,6 +49,7 @@ describe('MonitorEditPage', () => { [ConfigKey.MONITOR_SOURCE_TYPE]: 'ui', [ConfigKey.FORM_MONITOR_TYPE]: 'multistep', [ConfigKey.LOCATIONS]: [], + [ConfigKey.THROTTLING_CONFIG]: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], }, }, refetch: () => null, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts index b3b86eef542fe..6abe63786563e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts @@ -17,6 +17,7 @@ import { ServiceLocation, FormMonitorType, MonitorFields, + ResponseCheckJSON, } from '../../../../../common/runtime_types/monitor_management'; import { AlertConfigKey } from './constants'; @@ -55,6 +56,11 @@ export type FormConfig = MonitorFields & { ssl: { supported_protocols: MonitorFields[ConfigKey.TLS_VERSION]; }; + check: { + response: { + json: ResponseCheckJSON[]; + }; + }; }; export interface FieldMeta { @@ -63,6 +69,7 @@ export interface FieldMeta { label?: string; ariaLabel?: string; helpText?: string | React.ReactNode; + hidden?: (depenencies: unknown[]) => boolean; props?: (params: { field?: ControllerRenderProps; formState: FormState; @@ -88,7 +95,6 @@ export interface FieldMeta { event: React.ChangeEvent, formOnChange: (event: React.ChangeEvent) => void ) => void; - showWhen?: [keyof FormConfig, any]; // show field when another field equals an arbitrary value validation?: (dependencies: unknown[]) => Parameters[1]; error?: React.ReactNode; dependencies?: Array; // fields that another field may depend for or validation. Values are passed to the validation function @@ -123,16 +129,19 @@ export interface FieldMap { [ConfigKey.USERNAME]: FieldMeta; [ConfigKey.PASSWORD]: FieldMeta; [ConfigKey.PROXY_URL]: FieldMeta; + [ConfigKey.PROXY_HEADERS]: FieldMeta; ['proxy_url__tcp']: FieldMeta; [ConfigKey.REQUEST_METHOD_CHECK]: FieldMeta; [ConfigKey.REQUEST_HEADERS_CHECK]: FieldMeta; [ConfigKey.REQUEST_BODY_CHECK]: FieldMeta; [ConfigKey.RESPONSE_HEADERS_INDEX]: FieldMeta; [ConfigKey.RESPONSE_BODY_INDEX]: FieldMeta; + [ConfigKey.RESPONSE_BODY_MAX_BYTES]: FieldMeta; [ConfigKey.RESPONSE_STATUS_CHECK]: FieldMeta; [ConfigKey.RESPONSE_HEADERS_CHECK]: FieldMeta; [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: FieldMeta; [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: FieldMeta; + [ConfigKey.RESPONSE_JSON_CHECK]: FieldMeta; [ConfigKey.RESPONSE_RECEIVE_CHECK]: FieldMeta; [ConfigKey.REQUEST_SEND_CHECK]: FieldMeta; ['source.inline']: FieldMeta; @@ -142,4 +151,6 @@ export interface FieldMap { [ConfigKey.PLAYWRIGHT_OPTIONS]: FieldMeta; [ConfigKey.SYNTHETICS_ARGS]: FieldMeta; [ConfigKey.IGNORE_HTTPS_ERRORS]: FieldMeta; + [ConfigKey.MODE]: FieldMeta; + [ConfigKey.IPV4]: FieldMeta; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_panel.tsx index 85ab6773033be..7ca1d1e003cc4 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_panel.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_panel.tsx @@ -42,7 +42,7 @@ export const DurationPanel = (props: DurationPanelProps) => { attributes={[ { time: props, - name: AVG_DURATION_LABEL, + name: MEDIAN_DURATION_LABEL, dataType: 'synthetics', selectedMetricField: 'monitor_duration', reportDefinitions: { @@ -55,9 +55,9 @@ export const DurationPanel = (props: DurationPanelProps) => { ); }; -export const AVG_DURATION_LABEL = i18n.translate( - 'xpack.synthetics.monitorDetails.summary.avgDuration', +export const MEDIAN_DURATION_LABEL = i18n.translate( + 'xpack.synthetics.monitorDetails.summary.medianDuration', { - defaultMessage: 'Avg. duration', + defaultMessage: 'Median duration', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_sparklines.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_sparklines.tsx index 1c1370d4da3ab..5851d1c47cdf5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_sparklines.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_sparklines.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ReportTypes } from '@kbn/exploratory-view-plugin/public'; import { useTheme } from '@kbn/observability-plugin/public'; -import { AVG_DURATION_LABEL } from './duration_panel'; +import { MEDIAN_DURATION_LABEL } from './duration_panel'; import { useMonitorQueryId } from '../hooks/use_monitor_query_id'; import { ClientPluginsStart } from '../../../../../plugin'; import { useSelectedLocation } from '../hooks/use_selected_location'; @@ -47,7 +47,7 @@ export const DurationSparklines = (props: DurationSparklinesProps) => { { seriesType: 'area', time: props, - name: AVG_DURATION_LABEL, + name: MEDIAN_DURATION_LABEL, dataType: 'synthetics', selectedMetricField: 'monitor.duration.us', reportDefinitions: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx index 7e19abbf758ce..a59a26613a427 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/filter_group.tsx @@ -61,7 +61,18 @@ export const FilterGroup = ({ label: LOCATION_LABEL, field: 'locations', values: getSyntheticsFilterDisplayValues( - mixUrlValues(data.locations, urlParams.locations), + mixUrlValues( + data.locations.map((locationData) => { + const matchingLocation = locations.find( + (location) => location.id === locationData.label + ); + return { + label: matchingLocation ? matchingLocation.label : locationData.label, + count: locationData.count, + }; + }), + urlParams.locations + ), 'locations', locations ), diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts index 014966f31bbc0..0e3fe38566f34 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/monitor_filters/use_filters.ts @@ -132,7 +132,7 @@ export const useFilters = (): FiltersList => { })) ?? [], schedules: schedules?.buckets?.map(({ key, doc_count: count }) => ({ - label: key, + label: String(key), count, })) ?? [], }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx index 00c6e9b555727..885f44eaa5ae1 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx @@ -6,48 +6,36 @@ */ import React from 'react'; -import { EuiButton, EuiCallOut, EuiLink } from '@elastic/eui'; -import { InvalidApiKeyCalloutCallout } from './invalid_api_key_callout'; +import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import * as labels from './labels'; import { useEnablement } from '../../../hooks'; export const DisabledCallout = ({ total }: { total: number }) => { - const { enablement, enableSynthetics, invalidApiKeyError, loading } = useEnablement(); + const { enablement, invalidApiKeyError, loading } = useEnablement(); const showDisableCallout = !enablement.isEnabled && total > 0; - const showInvalidApiKeyError = invalidApiKeyError && total > 0; + const showInvalidApiKeyCallout = invalidApiKeyError && total > 0; - if (showInvalidApiKeyError) { - return ; - } - - if (!showDisableCallout) { + if (!showDisableCallout && !showInvalidApiKeyCallout) { return null; } - return ( - -

    {labels.CALLOUT_MANAGEMENT_DESCRIPTION}

    - {enablement.canEnable || loading ? ( - { - enableSynthetics(); - }} - isLoading={loading} - > - {labels.SYNTHETICS_ENABLE_LABEL} - - ) : ( + return !enablement.canEnable && !loading ? ( + <> + +

    {labels.CALLOUT_MANAGEMENT_DESCRIPTION}

    {labels.CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} - + {labels.LEARN_MORE_LABEL}

    - )} -
    - ); +
    + + + ) : null; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx deleted file mode 100644 index 8361df0588c04..0000000000000 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx +++ /dev/null @@ -1,80 +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 { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { useEnablement } from '../../../hooks'; - -export const InvalidApiKeyCalloutCallout = () => { - const { enablement, enableSynthetics, loading } = useEnablement(); - - return ( - <> - -

    {CALLOUT_MANAGEMENT_DESCRIPTION}

    - {enablement.canEnable || loading ? ( - { - enableSynthetics(); - }} - isLoading={loading} - > - {SYNTHETICS_ENABLE_LABEL} - - ) : ( -

    - {CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} - - {LEARN_MORE_LABEL} - -

    - )} -
    - - - ); -}; - -const LEARN_MORE_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.invalidKey', - { - defaultMessage: 'Learn more', - } -); - -const API_KEY_MISSING = i18n.translate('xpack.synthetics.monitorManagement.callout.apiKeyMissing', { - defaultMessage: 'Monitor Management is currently disabled because of missing API key', -}); - -const CALLOUT_MANAGEMENT_CONTACT_ADMIN = i18n.translate( - 'xpack.synthetics.monitorManagement.callout.disabledCallout.invalidKey', - { - defaultMessage: 'Contact your administrator to enable Monitor Management.', - } -); - -const CALLOUT_MANAGEMENT_DESCRIPTION = i18n.translate( - 'xpack.synthetics.monitorManagement.callout.description.invalidKey', - { - defaultMessage: `Monitor Management is currently disabled. To run your monitors in one of Elastic's global managed testing locations, you need to re-enable monitor management.`, - } -); - -const SYNTHETICS_ENABLE_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.syntheticsEnableLabel.invalidKey', - { - defaultMessage: 'Enable monitor management', - } -); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts index d7f3f892c0c76..ff297267dcb62 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts @@ -24,14 +24,14 @@ export const LEARN_MORE_LABEL = i18n.translate( export const CALLOUT_MANAGEMENT_DISABLED = i18n.translate( 'xpack.synthetics.monitorManagement.callout.disabled', { - defaultMessage: 'Monitor Management is disabled', + defaultMessage: 'Monitor Management is currently disabled', } ); export const CALLOUT_MANAGEMENT_CONTACT_ADMIN = i18n.translate( 'xpack.synthetics.monitorManagement.callout.disabled.adminContact', { - defaultMessage: 'Please contact your administrator to enable Monitor Management.', + defaultMessage: 'Monitor Management will be enabled when an admin visits the Synthetics app.', } ); @@ -39,7 +39,7 @@ export const CALLOUT_MANAGEMENT_DESCRIPTION = i18n.translate( 'xpack.synthetics.monitorManagement.callout.description.disabled', { defaultMessage: - 'Monitor Management is currently disabled. To run your monitors on Elastic managed Synthetics service, enable Monitor Management. Your existing monitors are paused.', + "Monitor Management requires a valid API key to run your monitors on Elastic's global managed testing locations. If you already had enabled Monitor Management previously, the API key may no longer be valid.", } ); @@ -64,14 +64,6 @@ export const ERROR_HEADING_LABEL = i18n.translate( } ); -export const BETA_TOOLTIP_MESSAGE = i18n.translate( - 'xpack.synthetics.monitors.management.betaLabel', - { - defaultMessage: - 'This functionality is in beta and is subject to change. The design and code is less mature than official generally available features and is being provided as-is with no warranties. Beta features are not subject to the support service level agreement of official generally available features.', - } -); - export const SUMMARY_LABEL = i18n.translate('xpack.synthetics.monitorManagement.summary.heading', { defaultMessage: 'Summary', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/page_header/monitors_page_header.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/page_header/monitors_page_header.tsx index 8fd9f969d8e98..d4f30cb75236c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/page_header/monitors_page_header.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/page_header/monitors_page_header.tsx @@ -7,19 +7,12 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - -import { BETA_TOOLTIP_MESSAGE } from '../labels'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; export const MonitorsPageHeader = () => ( - -
    - -
    -
    ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts index ad7220e328f3b..961e0c3782e81 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts @@ -7,20 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const SYNTHETICS_ENABLE_FAILURE = i18n.translate( - 'xpack.synthetics.monitorManagement.syntheticsEnabledFailure', - { - defaultMessage: 'Monitor Management was not able to be enabled. Please contact support.', - } -); - -export const SYNTHETICS_DISABLE_FAILURE = i18n.translate( - 'xpack.synthetics.monitorManagement.syntheticsDisabledFailure', - { - defaultMessage: 'Monitor Management was not able to be disabled. Please contact support.', - } -); - export const SYNTHETICS_ENABLE_SUCCESS = i18n.translate( 'xpack.synthetics.monitorManagement.syntheticsEnableSuccess', { @@ -42,10 +28,10 @@ export const MONITOR_MANAGEMENT_ENABLEMENT_LABEL = i18n.translate( } ); -export const MONITOR_MANAGEMENT_DISABLED_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.emptyState.enablement.disabled.title', +export const SYNTHETICS_APP_DISABLED_LABEL = i18n.translate( + 'xpack.synthetics.emptyState.enablement.disabled.title', { - defaultMessage: 'Monitor Management is disabled', + defaultMessage: 'Synthetics App is disabled', } ); @@ -58,10 +44,10 @@ export const MONITOR_MANAGEMENT_ENABLEMENT_MESSAGE = i18n.translate( ); export const MONITOR_MANAGEMENT_DISABLED_MESSAGE = i18n.translate( - 'xpack.synthetics.monitorManagement.emptyState.enablement.disabledDescription', + 'xpack.synthetics.emptyState.enablement.disabledDescription', { defaultMessage: - 'Monitor Management is currently disabled. Monitor Management allows you to run lightweight and real-browser monitors from hosted testing locations around the world. To enable Monitor Management, please contact an administrator.', + 'Synthetics App is currently disabled. Synthetics App allows you to run lightweight and real-browser monitors from hosted testing locations around the world. To enable Synthetics App, please contact an administrator.', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx index 09496214c45ac..e4fcee12f65a5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx @@ -6,16 +6,16 @@ */ import React, { useState, useEffect, useRef } from 'react'; -import { EuiEmptyPrompt, EuiButton, EuiTitle, EuiLink } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiTitle, EuiLink } from '@elastic/eui'; import { useEnablement } from '../../../../hooks/use_enablement'; import { kibanaService } from '../../../../../../utils/kibana_service'; import * as labels from './labels'; export const EnablementEmptyState = () => { - const { error, enablement, enableSynthetics, loading } = useEnablement(); + const { error, enablement, loading } = useEnablement(); const [shouldFocusEnablementButton, setShouldFocusEnablementButton] = useState(false); const [isEnabling, setIsEnabling] = useState(false); - const { isEnabled, canEnable } = enablement; + const { isEnabled } = enablement; const isEnabledRef = useRef(isEnabled); const buttonRef = useRef(null); @@ -44,11 +44,6 @@ export const EnablementEmptyState = () => { } }, [isEnabled, isEnabling, error]); - const handleEnableSynthetics = () => { - enableSynthetics(); - setIsEnabling(true); - }; - useEffect(() => { if (shouldFocusEnablementButton) { buttonRef.current?.focus(); @@ -57,33 +52,8 @@ export const EnablementEmptyState = () => { return !isEnabled && !loading ? ( - {canEnable - ? labels.MONITOR_MANAGEMENT_ENABLEMENT_LABEL - : labels.MONITOR_MANAGEMENT_DISABLED_LABEL} - - } - body={ -

    - {canEnable - ? labels.MONITOR_MANAGEMENT_ENABLEMENT_MESSAGE - : labels.MONITOR_MANAGEMENT_DISABLED_MESSAGE} -

    - } - actions={ - canEnable ? ( - - {labels.MONITOR_MANAGEMENT_ENABLEMENT_BTN_LABEL} - - ) : null - } + title={

    {labels.SYNTHETICS_APP_DISABLED_LABEL}

    } + body={

    {labels.MONITOR_MANAGEMENT_DISABLED_MESSAGE}

    } footer={ <> @@ -91,7 +61,7 @@ export const EnablementEmptyState = () => { {labels.DOCS_LABEL} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx index 88cfaf75e061c..369010408917c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { Chart, Settings, Metric, MetricTrendShape } from '@elastic/charts'; -import { EuiPanel } from '@elastic/eui'; +import { EuiPanel, EuiIconTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { DARK_THEME } from '@elastic/charts'; import { useTheme } from '@kbn/observability-plugin/public'; import { useDispatch, useSelector } from 'react-redux'; @@ -46,13 +46,19 @@ export const getColor = ( export const MetricItem = ({ monitor, - averageDuration, + medianDuration, + maxDuration, + minDuration, + avgDuration, data, onClick, }: { monitor: MonitorOverviewItem; data: Array<{ x: number; y: number }>; - averageDuration: number; + medianDuration: number; + avgDuration: number; + minDuration: number; + maxDuration: number; onClick: (params: { id: string; configId: string; location: string; locationId: string }) => void; }) => { const [isMouseOver, setIsMouseOver] = useState(false); @@ -119,15 +125,42 @@ export const MetricItem = ({ { title: monitor.name, subtitle: locationName, - value: averageDuration, + value: medianDuration, trendShape: MetricTrendShape.Area, trend: data, extra: ( - - {i18n.translate('xpack.synthetics.overview.duration.label', { - defaultMessage: 'Duration Avg.', - })} - + + + {i18n.translate('xpack.synthetics.overview.duration.label', { + defaultMessage: 'Duration', + })} + + + + + ), valueFormatter: (d: number) => formatDuration(d), color: getColor(theme, monitor.isEnabled, status), diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.test.tsx index 8238d7b26b62a..ed470f6f24bce 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.test.tsx @@ -64,9 +64,14 @@ describe('Overview Grid', () => { const perPage = 20; it('renders correctly', async () => { - jest - .spyOn(hooks, 'useLast50DurationChart') - .mockReturnValue({ data: getMockChart(), averageDuration: 30000, loading: false }); + jest.spyOn(hooks, 'useLast50DurationChart').mockReturnValue({ + data: getMockChart(), + avgDuration: 30000, + minDuration: 0, + maxDuration: 50000, + medianDuration: 15000, + loading: false, + }); const { getByText, getAllByTestId, queryByText } = render(, { state: { @@ -124,9 +129,14 @@ describe('Overview Grid', () => { }); it('displays showing all monitors label when reaching the end of the list', async () => { - jest - .spyOn(hooks, 'useLast50DurationChart') - .mockReturnValue({ data: getMockChart(), averageDuration: 30000, loading: false }); + jest.spyOn(hooks, 'useLast50DurationChart').mockReturnValue({ + data: getMockChart(), + avgDuration: 30000, + minDuration: 0, + maxDuration: 50000, + medianDuration: 15000, + loading: false, + }); const { getByText } = render(, { state: { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid_item.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid_item.tsx index de153cf01eca7..952a48d424733 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid_item.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid_item.tsx @@ -32,12 +32,20 @@ export const OverviewGridItem = ({ const { timestamp } = useStatusByLocationOverview(monitor.configId, locationName); - const { data, averageDuration } = useLast50DurationChart({ + const { data, medianDuration, maxDuration, avgDuration, minDuration } = useLast50DurationChart({ locationId: monitor.location?.id, monitorId: monitor.id, timestamp, }); return ( - + ); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview_page.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview_page.tsx index a98f78249adbe..2e4eb6ca03a31 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview_page.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview_page.tsx @@ -101,8 +101,8 @@ export const OverviewPage: React.FC = () => { return ( <> - + diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/step_details_page/step_page_nav.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/step_details_page/step_page_nav.tsx index 528e3c9d6aa1b..9bd25a715dff1 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/step_details_page/step_page_nav.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/step_details_page/step_page_nav.tsx @@ -57,19 +57,23 @@ export const StepPageNavigation = ({ testRunPage }: { testRunPage?: boolean }) = checkGroupId: data?.details?.next?.checkGroup, }); - if (testRunPage && data?.details?.previous?.checkGroup && data?.details?.next?.checkGroup) { - prevHref = getTestRunDetailLink({ - basePath, - monitorId, - locationId: selectedLocation?.id, - checkGroup: data?.details?.previous?.checkGroup, - }); - nextHref = getTestRunDetailLink({ - basePath, - monitorId, - locationId: selectedLocation?.id, - checkGroup: data?.details?.next?.checkGroup, - }); + if (testRunPage) { + if (data?.details?.previous?.checkGroup) { + prevHref = getTestRunDetailLink({ + basePath, + monitorId, + locationId: selectedLocation?.id, + checkGroup: data?.details?.previous?.checkGroup, + }); + } + if (data?.details?.next?.checkGroup) { + nextHref = getTestRunDetailLink({ + basePath, + monitorId, + locationId: selectedLocation?.id, + checkGroup: data?.details?.next?.checkGroup, + }); + } } if (!startedAt) { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_info.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_info.tsx index 0e13c7efa251d..beda3ed42ead3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_info.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_run_details/components/step_info.tsx @@ -69,7 +69,7 @@ export const StepMetaInfo = ({ { const isDownMonitor = journeyDetails?.journey?.monitor?.status === 'down'; @@ -40,14 +42,13 @@ export const TestRunErrorInfo = ({ )} - {(hasNoSteps || isDownMonitor) && - errorMessage?.includes('journey did not finish executing') && ( - - )} + {isDownMonitor && (showErrorLogs || hasNoSteps) && ( + + )} ); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_enablement.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_enablement.ts index 394da8aefc086..fe726d0cbe3d2 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_enablement.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_enablement.ts @@ -5,14 +5,9 @@ * 2.0. */ -import { useEffect, useCallback } from 'react'; +import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { - getSyntheticsEnablement, - enableSynthetics, - disableSynthetics, - selectSyntheticsEnablement, -} from '../state'; +import { getSyntheticsEnablement, selectSyntheticsEnablement } from '../state'; export function useEnablement() { const dispatch = useDispatch(); @@ -35,7 +30,5 @@ export function useEnablement() { invalidApiKeyError: enablement ? !Boolean(enablement?.isValidApiKey) : false, error, loading, - enableSynthetics: useCallback(() => dispatch(enableSynthetics()), [dispatch]), - disableSynthetics: useCallback(() => dispatch(disableSynthetics()), [dispatch]), }; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.test.ts index bb2e74712322f..47cb97793bab1 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.test.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.test.ts @@ -33,7 +33,10 @@ describe('useLast50DurationChart', () => { { wrapper: WrappedHelper } ); expect(result.current).toEqual({ - averageDuration: 4.5, + medianDuration: 5, + maxDuration: 9, + minDuration: 0, + avgDuration: 4.5, data: [ { x: 0, @@ -132,7 +135,10 @@ describe('useLast50DurationChart', () => { ]; expect(result.current).toEqual({ - averageDuration: data.reduce((acc, datum) => (acc += datum.y), 0) / 9, + medianDuration: [...data].sort((a, b) => a.y - b.y)[Math.floor(data.length / 2)].y, + maxDuration: 9, + minDuration: 0, + avgDuration: 4.4, data, loading: false, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.ts b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.ts index 78e4cc6cecbbf..8cb7d524635c7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_last_50_duration_chart.ts @@ -28,20 +28,23 @@ export function useLast50DurationChart({ size: 50, timestamp, }); - const { data, averageDuration } = useMemo(() => { + const { data, median, min, max, avg } = useMemo(() => { if (loading) { return { data: [], - averageDuration: 0, + median: 0, + avg: 0, + min: 0, + max: 0, }; } - let totalDuration = 0; + + // calculate min, max, average duration and median const coords = hits .reverse() // results are returned in desc order by timestamp. Reverse to ensure the data is in asc order by timestamp .map((hit, index) => { const duration = hit?.['monitor.duration.us']?.[0]; - totalDuration += duration || 0; if (duration === undefined) { return null; } @@ -52,18 +55,30 @@ export function useLast50DurationChart({ }) .filter((item) => item !== null); + const sortedByDuration = [...hits].sort( + (a, b) => (a?.['monitor.duration.us']?.[0] || 0) - (b?.['monitor.duration.us']?.[0] || 0) + ); + return { data: coords as Array<{ x: number; y: number }>, - averageDuration: totalDuration / coords.length, + median: sortedByDuration[Math.floor(hits.length / 2)]?.['monitor.duration.us']?.[0] || 0, + avg: + sortedByDuration.reduce((acc, curr) => acc + (curr?.['monitor.duration.us']?.[0] || 0), 0) / + hits.length, + min: sortedByDuration[0]?.['monitor.duration.us']?.[0] || 0, + max: sortedByDuration[sortedByDuration.length - 1]?.['monitor.duration.us']?.[0] || 0, }; }, [hits, loading]); return useMemo( () => ({ data, - averageDuration, + medianDuration: median, + avgDuration: avg, + minDuration: min, + maxDuration: max, loading, }), - [loading, data, averageDuration] + [data, median, avg, min, max, loading] ); } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/actions.ts index 7369ce0917e5a..78c0d9484149e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/actions.ts @@ -16,17 +16,3 @@ export const getSyntheticsEnablementSuccess = createAction( '[SYNTHETICS_ENABLEMENT] GET FAILURE' ); - -export const disableSynthetics = createAction('[SYNTHETICS_ENABLEMENT] DISABLE'); -export const disableSyntheticsSuccess = createAction<{}>('[SYNTHETICS_ENABLEMENT] DISABLE SUCCESS'); -export const disableSyntheticsFailure = createAction( - '[SYNTHETICS_ENABLEMENT] DISABLE FAILURE' -); - -export const enableSynthetics = createAction('[SYNTHETICS_ENABLEMENT] ENABLE'); -export const enableSyntheticsSuccess = createAction( - '[SYNTHETICS_ENABLEMENT] ENABLE SUCCESS' -); -export const enableSyntheticsFailure = createAction( - '[SYNTHETICS_ENABLEMENT] ENABLE FAILURE' -); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/api.ts index 62b48676e3965..2e009cc0b89d2 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/api.ts @@ -14,17 +14,9 @@ import { apiService } from '../../../../utils/api_service'; export const fetchGetSyntheticsEnablement = async (): Promise => { - return await apiService.get( + return await apiService.put( API_URLS.SYNTHETICS_ENABLEMENT, undefined, MonitorManagementEnablementResultCodec ); }; - -export const fetchDisableSynthetics = async (): Promise<{}> => { - return await apiService.delete(API_URLS.SYNTHETICS_ENABLEMENT); -}; - -export const fetchEnableSynthetics = async (): Promise => { - return await apiService.post(API_URLS.SYNTHETICS_ENABLEMENT); -}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts index d3134c60f8fd3..14c912b07ce99 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts @@ -5,20 +5,15 @@ * 2.0. */ -import { takeLatest, takeLeading } from 'redux-saga/effects'; +import { takeLeading } from 'redux-saga/effects'; +import { i18n } from '@kbn/i18n'; import { getSyntheticsEnablement, getSyntheticsEnablementSuccess, getSyntheticsEnablementFailure, - disableSynthetics, - disableSyntheticsSuccess, - disableSyntheticsFailure, - enableSynthetics, - enableSyntheticsSuccess, - enableSyntheticsFailure, } from './actions'; -import { fetchGetSyntheticsEnablement, fetchDisableSynthetics, fetchEnableSynthetics } from './api'; import { fetchEffectFactory } from '../utils/fetch_effect'; +import { fetchGetSyntheticsEnablement } from './api'; export function* fetchSyntheticsEnablementEffect() { yield takeLeading( @@ -26,15 +21,13 @@ export function* fetchSyntheticsEnablementEffect() { fetchEffectFactory( fetchGetSyntheticsEnablement, getSyntheticsEnablementSuccess, - getSyntheticsEnablementFailure + getSyntheticsEnablementFailure, + undefined, + failureMessage ) ); - yield takeLatest( - disableSynthetics, - fetchEffectFactory(fetchDisableSynthetics, disableSyntheticsSuccess, disableSyntheticsFailure) - ); - yield takeLatest( - enableSynthetics, - fetchEffectFactory(fetchEnableSynthetics, enableSyntheticsSuccess, enableSyntheticsFailure) - ); } + +const failureMessage = i18n.translate('xpack.synthetics.settings.enablement.fail', { + defaultMessage: 'Failed to enable Monitor Management', +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/index.ts index 62cbce9bfe05b..26bf2b50b8325 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/index.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/index.ts @@ -9,12 +9,6 @@ import { createReducer } from '@reduxjs/toolkit'; import { getSyntheticsEnablement, getSyntheticsEnablementSuccess, - disableSynthetics, - disableSyntheticsSuccess, - disableSyntheticsFailure, - enableSynthetics, - enableSyntheticsSuccess, - enableSyntheticsFailure, getSyntheticsEnablementFailure, } from './actions'; import { MonitorManagementEnablementResult } from '../../../../../common/runtime_types'; @@ -45,39 +39,6 @@ export const syntheticsEnablementReducer = createReducer(initialState, (builder) .addCase(getSyntheticsEnablementFailure, (state, action) => { state.loading = false; state.error = action.payload; - }) - - .addCase(disableSynthetics, (state) => { - state.loading = true; - }) - .addCase(disableSyntheticsSuccess, (state, action) => { - state.loading = false; - state.error = null; - state.enablement = { - canEnable: state.enablement?.canEnable ?? false, - areApiKeysEnabled: state.enablement?.areApiKeysEnabled ?? false, - canManageApiKeys: state.enablement?.canManageApiKeys ?? false, - isEnabled: false, - isValidApiKey: true, - }; - }) - .addCase(disableSyntheticsFailure, (state, action) => { - state.loading = false; - state.error = action.payload; - }) - - .addCase(enableSynthetics, (state) => { - state.loading = true; - state.enablement = null; - }) - .addCase(enableSyntheticsSuccess, (state, action) => { - state.loading = false; - state.error = null; - state.enablement = action.payload; - }) - .addCase(enableSyntheticsFailure, (state, action) => { - state.loading = false; - state.error = action.payload; }); }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/formatting/format.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/formatting/format.ts index 5dab17f55ad68..433d4a9c8cfec 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/formatting/format.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/formatting/format.ts @@ -25,8 +25,14 @@ export const microsToMillis = (microseconds: number | null): number | null => { return Math.floor(microseconds / NUM_MICROSECONDS_IN_MILLISECOND); }; -export const formatDuration = (durationMicros: number) => { +export const formatDuration = (durationMicros: number, { noSpace }: { noSpace?: true } = {}) => { if (durationMicros < MILLIS_LIMIT) { + if (noSpace) { + return i18n.translate('xpack.synthetics.overview.durationMsFormattingNoSpace', { + values: { millis: microsToMillis(durationMicros) }, + defaultMessage: '{millis}ms', + }); + } return i18n.translate('xpack.synthetics.overview.durationMsFormatting', { values: { millis: microsToMillis(durationMicros) }, defaultMessage: '{millis} ms', @@ -34,6 +40,13 @@ export const formatDuration = (durationMicros: number) => { } const seconds = (durationMicros / ONE_SECOND_AS_MICROS).toFixed(0); + if (noSpace) { + return i18n.translate('xpack.synthetics.overview.durationSecondsFormattingNoSpace', { + values: { seconds }, + defaultMessage: '{seconds}s', + }); + } + return i18n.translate('xpack.synthetics.overview.durationSecondsFormatting', { values: { seconds }, defaultMessage: '{seconds} s', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index d65c68f2843b9..06ed54738fbda 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -430,11 +430,15 @@ function getMonitorDetailsMockSlice() { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + value: { + download: '5', + upload: '3', + latency: '20', + }, + label: 'Regular 3G', + id: 'three_g', + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx index 9c938416ef5f5..900ca0886e759 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ml/manage_ml_job.tsx @@ -184,6 +184,7 @@ export const ManageMLJobComponent = ({ hasMLJob, onEnableJob, onJobDelete }: Pro onConfirm={() => { deleteAnomalyAlert(); setIsConfirmAlertDeleteOpen(false); + setIsPopOverOpen(false); }} onCancel={() => { setIsConfirmAlertDeleteOpen(false); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx index 92406d65948f4..b6d5a63af7712 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/duration_anomaly.tsx @@ -14,7 +14,7 @@ import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; import { AlertTypeInitializer } from '.'; import { getMonitorRouteFromMonitorId } from '../../../../common/utils/get_monitor_url'; import { CLIENT_ALERT_TYPES } from '../../../../common/constants/uptime_alerts'; -import { DurationAnomalyTranslations } from '../../../../common/translations'; +import { DurationAnomalyTranslations } from '../../../../common/rules/legacy_uptime/translations'; const { defaultActionMessage, defaultRecoveryMessage, description } = DurationAnomalyTranslations; const DurationAnomalyAlert = React.lazy(() => import('./lazy_wrapper/duration_anomaly')); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx index 5910d3fb5093a..9af2ede31e42e 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/monitor_status.tsx @@ -20,7 +20,7 @@ import { ObservabilityRuleTypeModel } from '@kbn/observability-plugin/public'; import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; import { AlertTypeInitializer } from '.'; import { getMonitorRouteFromMonitorId } from '../../../../common/utils/get_monitor_url'; -import { MonitorStatusTranslations } from '../../../../common/translations'; +import { MonitorStatusTranslations } from '../../../../common/rules/legacy_uptime/translations'; import { CLIENT_ALERT_TYPES } from '../../../../common/constants/uptime_alerts'; const { defaultActionMessage, defaultRecoveryMessage, description } = MonitorStatusTranslations; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx index f9d510243393b..c44949f930ae6 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls.tsx @@ -12,7 +12,7 @@ import type { RuleTypeParamsExpressionProps } from '@kbn/triggers-actions-ui-plu import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; import { TLSParams } from '../../../../common/runtime_types/alerts/tls'; import { CLIENT_ALERT_TYPES } from '../../../../common/constants/uptime_alerts'; -import { TlsTranslations } from '../../../../common/translations'; +import { TlsTranslations } from '../../../../common/rules/legacy_uptime/translations'; import { AlertTypeInitializer } from '.'; import { CERTIFICATES_ROUTE } from '../../../../common/constants/ui'; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx index ed67a50ee08b7..38106cd6ce2f8 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/lib/alert_types/tls_legacy.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; import { CLIENT_ALERT_TYPES } from '../../../../common/constants/uptime_alerts'; -import { TlsTranslationsLegacy } from '../../../../common/translations'; +import { TlsTranslationsLegacy } from '../../../../common/rules/legacy_uptime/translations'; import { AlertTypeInitializer } from '.'; const { defaultActionMessage, description } = TlsTranslationsLegacy; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts index db335a1ef458b..9355cea954f98 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/alerts.ts @@ -5,9 +5,14 @@ * 2.0. */ -import type { ActionType, AsApiContract, Rule } from '@kbn/triggers-actions-ui-plugin/public'; +import { + ActionType, + AsApiContract, + Rule, + transformRule, +} from '@kbn/triggers-actions-ui-plugin/public'; import { RuleTypeParams } from '@kbn/alerting-plugin/common'; -import { MonitorStatusTranslations } from '../../../../common/translations'; +import { MonitorStatusTranslations } from '../../../../common/rules/legacy_uptime/translations'; import { ActionConnector } from '../../../../common/rules/types'; import { CLIENT_ALERT_TYPES, MONITOR_STATUS } from '../../../../common/constants/uptime_alerts'; import { apiService } from './utils'; @@ -87,6 +92,7 @@ export const createAlert = async ({ defaultActionMessage: MonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: MonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: MonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: MonitorStatusTranslations.defaultRecoverySubjectMessage, }, isLegacy: true, }); @@ -142,17 +148,12 @@ export const fetchAnomalyAlertRecords = async ({ sort_order: 'asc', }; const rawRules = await apiService.get<{ - data: Array & { rule_type_id: string }>; + data: Array>; }>(API_URLS.RULES_FIND, data); - const monitorRule = rawRules.data.find( - (rule) => rule.params.monitorId === monitorId - ) as Rule & { rule_type_id: string }; - if (monitorRule) { - return { - ...monitorRule, - ruleTypeId: monitorRule.rule_type_id, - }; - } + const monitorRule = rawRules.data.find((rule) => rule.params.monitorId === monitorId); + if (!monitorRule) return undefined; + + return transformRule(monitorRule) as Rule; }; export const disableAlertById = async ({ alertId }: { alertId: string }) => { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts index 18ce74d9823bb..ae069537e1b26 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts @@ -11,7 +11,7 @@ import { JobExistResult, MlCapabilitiesResponse, } from '@kbn/ml-plugin/public'; -import { extractErrorMessage } from '@kbn/ml-plugin/common'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { apiService } from './utils'; import { AnomalyRecords, AnomalyRecordsParams } from '../actions'; import { API_URLS, ML_MODULE_ID } from '../../../../common/constants'; diff --git a/x-pack/plugins/synthetics/public/plugin.ts b/x-pack/plugins/synthetics/public/plugin.ts index 370dd2e802bdf..0900b6f1685dc 100644 --- a/x-pack/plugins/synthetics/public/plugin.ts +++ b/x-pack/plugins/synthetics/public/plugin.ts @@ -213,11 +213,7 @@ export class UptimePlugin id: 'synthetics', euiIconType: 'logoObservability', order: 8400, - title: - PLUGIN.SYNTHETICS + - i18n.translate('xpack.synthetics.overview.headingBeta', { - defaultMessage: ' (beta)', - }), + title: PLUGIN.SYNTHETICS, category: DEFAULT_APP_CATEGORIES.observability, keywords: appKeywords, deepLinks: [], @@ -313,7 +309,6 @@ function registerUptimeRoutesWithNavigation( path: OVERVIEW_ROUTE, matchFullPath: false, ignoreTrailingSlash: true, - isBetaFeature: true, }, ], }, diff --git a/x-pack/plugins/synthetics/scripts/e2e.js b/x-pack/plugins/synthetics/scripts/e2e.js index 69a1f3319491e..4edd824b448e3 100644 --- a/x-pack/plugins/synthetics/scripts/e2e.js +++ b/x-pack/plugins/synthetics/scripts/e2e.js @@ -12,4 +12,8 @@ const path = require('path'); const e2eDir = path.join(__dirname, '../e2e'); -executeSyntheticsRunner(e2eDir); +try { + executeSyntheticsRunner(e2eDir); +} catch (e) { + console.log(e); +} diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts index 367c27c4bca08..28d7018448374 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts @@ -169,11 +169,15 @@ const testMonitors = [ 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + upload: '3', + latency: '20', + }, + }, 'ssl.certificate_authorities': '', 'ssl.certificate': '', 'ssl.verification_mode': 'full', diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts index f70e3a96d1fb8..42d81f6e0d0d6 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts @@ -30,7 +30,7 @@ import { UptimeAlertTypeFactory } from './types'; import { Ping } from '../../../../common/runtime_types/ping'; import { getMLJobId } from '../../../../common/lib'; -import { DurationAnomalyTranslations as CommonDurationAnomalyTranslations } from '../../../../common/translations'; +import { DurationAnomalyTranslations as CommonDurationAnomalyTranslations } from '../../../../common/rules/legacy_uptime/translations'; import { getMonitorRouteFromMonitorId } from '../../../../common/utils/get_monitor_url'; import { ALERT_REASON_MSG, ACTION_VARIABLES, VIEW_IN_APP_URL } from './action_variables'; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts index 6b23d90db0a7e..8bf4fc9de413e 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts @@ -22,7 +22,7 @@ import { CLIENT_ALERT_TYPES, TLS } from '../../../../common/constants/uptime_ale import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; import { Cert, CertResult } from '../../../../common/runtime_types'; import { commonStateTranslations, tlsTranslations } from './translations'; -import { TlsTranslations } from '../../../../common/translations'; +import { TlsTranslations } from '../../../../common/rules/legacy_uptime/translations'; import { savedObjectsAdapter } from '../saved_objects/saved_objects'; import { UptimeEsClient } from '../lib'; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts index a682252be3c79..65768459dc96e 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts @@ -8,7 +8,11 @@ import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/s import { migration880 } from './8.8.0'; import { migrationMocks } from '@kbn/core/server/mocks'; import { ConfigKey, ScheduleUnit } from '../../../../../../common/runtime_types'; -import { ALLOWED_SCHEDULES_IN_MINUTES } from '../../../../../../common/constants/monitor_defaults'; +import { + ALLOWED_SCHEDULES_IN_MINUTES, + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '../../../../../../common/constants/monitor_defaults'; import { browserUI, browserProject, @@ -18,6 +22,8 @@ import { httpUptimeUI, } from './test_fixtures/8.7.0'; import { httpUI as httpUI850 } from './test_fixtures/8.5.0'; +import { LegacyConfigKey } from '../../../../../../common/constants/monitor_management'; +import { omit } from 'lodash'; const context = migrationMocks.createContext(); const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); @@ -151,11 +157,7 @@ describe('Monitor migrations v8.7.0 -> v8.8.0', () => { 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], 'ssl.verification_mode': 'full', tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], timeout: null, type: 'browser', 'url.port': null, @@ -195,9 +197,24 @@ describe('Monitor migrations v8.7.0 -> v8.8.0', () => { name: null, }, }; - // @ts-ignore specificially testing monitors with invalid values + // @ts-ignore specifically testing monitors with invalid values const actual = migration880(encryptedSavedObjectsSetup)(invalidTestMonitor, context); - expect(actual).toEqual(invalidTestMonitor); + expect(actual).toEqual({ + ...invalidTestMonitor, + attributes: omit( + { + ...invalidTestMonitor.attributes, + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], + }, + [ + LegacyConfigKey.THROTTLING_CONFIG, + LegacyConfigKey.IS_THROTTLING_ENABLED, + LegacyConfigKey.DOWNLOAD_SPEED, + LegacyConfigKey.UPLOAD_SPEED, + LegacyConfigKey.LATENCY, + ] + ), + }); }); }); @@ -413,4 +430,77 @@ describe('Monitor migrations v8.7.0 -> v8.8.0', () => { expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); }); }); + + describe('throttling migration', () => { + it('handles migrating with enabled throttling', () => { + const actual = migration880(encryptedSavedObjectsSetup)(browserUI, context); + // @ts-ignore + expect(actual.attributes[ConfigKey.THROTTLING_CONFIG]).toEqual( + PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT] + ); + }); + + it('handles migrating with defined throttling value', () => { + const testMonitor = { + ...browserUI, + attributes: { + ...browserUI.attributes, + [LegacyConfigKey.UPLOAD_SPEED]: '0.75', + [LegacyConfigKey.DOWNLOAD_SPEED]: '9', + [LegacyConfigKey.LATENCY]: '170', + }, + }; + const actual = migration880(encryptedSavedObjectsSetup)(testMonitor, context); + // @ts-ignore + expect(actual.attributes[ConfigKey.THROTTLING_CONFIG]).toEqual({ + id: '4g', + label: '4G', + value: { + download: '9', + upload: '0.75', + latency: '170', + }, + }); + }); + + it('handles migrating with custom throttling value', () => { + const testMonitor = { + ...browserUI, + attributes: { + ...browserUI.attributes, + [LegacyConfigKey.UPLOAD_SPEED]: '5', + [LegacyConfigKey.DOWNLOAD_SPEED]: '10', + [LegacyConfigKey.LATENCY]: '30', + }, + }; + const actual = migration880(encryptedSavedObjectsSetup)(testMonitor, context); + // @ts-ignore + expect(actual.attributes[ConfigKey.THROTTLING_CONFIG]).toEqual({ + id: 'custom', + label: 'Custom', + value: { + download: '10', + upload: '5', + latency: '30', + }, + }); + }); + + it('handles migrating with disabled throttling', () => { + const testMonitor = { + ...browserUI, + attributes: { + ...browserUI.attributes, + [LegacyConfigKey.IS_THROTTLING_ENABLED]: false, + }, + }; + const actual = migration880(encryptedSavedObjectsSetup)(testMonitor, context); + // @ts-ignore + expect(actual.attributes[ConfigKey.THROTTLING_CONFIG]).toEqual({ + id: 'no-throttling', + label: 'No throttling', + value: null, + }); + }); + }); }); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts index 7df6eb0c143cf..9aec3104fc2bf 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts @@ -6,23 +6,31 @@ */ import { omit } from 'lodash'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; -import { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; +import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; +import { LegacyConfigKey } from '../../../../../../common/constants/monitor_management'; import { + BrowserFields, ConfigKey, - SyntheticsMonitorWithSecrets, MonitorFields, - BrowserFields, ScheduleUnit, + SyntheticsMonitorWithSecrets, + ThrottlingConfig, } from '../../../../../../common/runtime_types'; -import { ALLOWED_SCHEDULES_IN_MINUTES } from '../../../../../../common/constants/monitor_defaults'; +import { + ALLOWED_SCHEDULES_IN_MINUTES, + CUSTOM_LABEL, + PROFILE_VALUES, + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '../../../../../../common/constants/monitor_defaults'; import { LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE, SYNTHETICS_MONITOR_ENCRYPTED_TYPE, } from '../../synthetics_monitor'; import { validateMonitor } from '../../../../../routes/monitor_cruds/monitor_validation'; import { - normalizeMonitorSecretAttributes, formatSecrets, + normalizeMonitorSecretAttributes, } from '../../../../../synthetics_service/utils/secrets'; export const migration880 = (encryptedSavedObjects: EncryptedSavedObjectsPluginSetup) => { @@ -61,6 +69,8 @@ export const migration880 = (encryptedSavedObjects: EncryptedSavedObjectsPluginS }, }; if (migrated.attributes.type === 'browser') { + migrated = updateThrottlingFields(migrated, logger); + try { const normalizedMonitorAttributes = normalizeMonitorSecretAttributes(migrated.attributes); migrated = { @@ -95,7 +105,7 @@ const getNearestSupportedSchedule = (currentSchedule: string): string => { return closest; } catch { - return ALLOWED_SCHEDULES_IN_MINUTES[0]; + return '10'; } }; @@ -118,3 +128,103 @@ const omitZipUrlFields = (fields: BrowserFields) => { return formatSecrets(validationResult.decodedMonitor); }; + +const updateThrottlingFields = ( + doc: SavedObjectUnsanitizedDoc< + SyntheticsMonitorWithSecrets & + Partial<{ + [LegacyConfigKey.THROTTLING_CONFIG]: string; + [LegacyConfigKey.IS_THROTTLING_ENABLED]: boolean; + [LegacyConfigKey.DOWNLOAD_SPEED]: string; + [LegacyConfigKey.UPLOAD_SPEED]: string; + [LegacyConfigKey.LATENCY]: string; + [ConfigKey.THROTTLING_CONFIG]: ThrottlingConfig; + }> + >, + logger: SavedObjectMigrationContext +) => { + try { + const { attributes } = doc; + + const migrated = { + ...doc, + attributes: { + ...doc.attributes, + [ConfigKey.CONFIG_HASH]: '', + }, + }; + + const isThrottlingEnabled = attributes[LegacyConfigKey.IS_THROTTLING_ENABLED]; + if (isThrottlingEnabled) { + const defaultProfileValue = PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT].value!; + + const download = + String(attributes[LegacyConfigKey.DOWNLOAD_SPEED]) || defaultProfileValue.download; + const upload = String(attributes[LegacyConfigKey.UPLOAD_SPEED]) || defaultProfileValue.upload; + const latency = String(attributes[LegacyConfigKey.LATENCY]) || defaultProfileValue.latency; + + migrated.attributes[ConfigKey.THROTTLING_CONFIG] = getMatchingThrottlingConfig( + download, + upload, + latency + ); + } else { + migrated.attributes[ConfigKey.THROTTLING_CONFIG] = + PROFILES_MAP[PROFILE_VALUES_ENUM.NO_THROTTLING]; + } + + // filter out legacy throttling fields + return { + ...migrated, + attributes: omit(migrated.attributes, [ + LegacyConfigKey.THROTTLING_CONFIG, + LegacyConfigKey.IS_THROTTLING_ENABLED, + LegacyConfigKey.DOWNLOAD_SPEED, + LegacyConfigKey.UPLOAD_SPEED, + LegacyConfigKey.LATENCY, + ]), + }; + } catch (e) { + logger.log.warn( + `Failed to migrate throttling fields from legacy Synthetics monitor: ${e.message}` + ); + const { attributes } = doc; + + attributes[ConfigKey.THROTTLING_CONFIG] = PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT]; + + return { + ...doc, + attributes: omit(attributes, [ + LegacyConfigKey.THROTTLING_CONFIG, + LegacyConfigKey.IS_THROTTLING_ENABLED, + LegacyConfigKey.DOWNLOAD_SPEED, + LegacyConfigKey.UPLOAD_SPEED, + LegacyConfigKey.LATENCY, + ]), + }; + } +}; + +const getMatchingThrottlingConfig = (download: string, upload: string, latency: string) => { + const matchedProfile = PROFILE_VALUES.find(({ value }) => { + return ( + Number(value?.download) === Number(download) && + Number(value?.upload) === Number(upload) && + Number(value?.latency) === Number(latency) + ); + }); + + if (matchedProfile) { + return matchedProfile; + } else { + return { + id: PROFILE_VALUES_ENUM.CUSTOM, + label: CUSTOM_LABEL, + value: { + download: String(download), + upload: String(upload), + latency: String(latency), + }, + }; + } +}; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/service_api_key.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/service_api_key.ts index adab53c9d4268..3c62f99f7e67b 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/service_api_key.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/service_api_key.ts @@ -72,7 +72,7 @@ const getSyntheticsServiceAPIKey = async (server: UptimeServerSetup) => { } }; -const setSyntheticsServiceApiKey = async ( +export const setSyntheticsServiceApiKey = async ( soClient: SavedObjectsClientContract, apiKey: SyntheticsServiceApiKey ) => { diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts index 3d5ddecf188a5..c8aa68f478703 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts @@ -170,6 +170,13 @@ export const getSyntheticsMonitorSavedObjectType = ( }, }, }, + throttling: { + properties: { + label: { + type: 'keyword', + }, + }, + }, }, }, management: { diff --git a/x-pack/plugins/synthetics/server/queries/get_journey_details.ts b/x-pack/plugins/synthetics/server/queries/get_journey_details.ts index 025688af45242..551696614d749 100644 --- a/x-pack/plugins/synthetics/server/queries/get_journey_details.ts +++ b/x-pack/plugins/synthetics/server/queries/get_journey_details.ts @@ -64,6 +64,15 @@ export const getJourneyDetails: UMElasticsearchQueryFn< body: { query: { bool: { + must_not: [ + { + term: { + 'monitor.check_group': { + value: journeySource.monitor.check_group, + }, + }, + }, + ], filter: [ { term: { @@ -93,6 +102,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< ...baseSiblingParams.body, query: { bool: { + must_not: baseSiblingParams.body.query.bool.must_not, filter: [ ...baseSiblingParams.body.query.bool.filter, { @@ -114,6 +124,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< ...baseSiblingParams.body, query: { bool: { + must_not: baseSiblingParams.body.query.bool.must_not, filter: [ ...baseSiblingParams.body.query.bool.filter, { @@ -151,20 +162,6 @@ export const getJourneyDetails: UMElasticsearchQueryFn< ({ _source: summarySource }) => summarySource.synthetics?.type === 'heartbeat/summary' )?._source; - const previousInfo = previousJourney - ? { - checkGroup: previousJourney._source.monitor.check_group, - timestamp: previousJourney._source['@timestamp'], - } - : undefined; - - const nextInfo = nextJourney - ? { - checkGroup: nextJourney._source.monitor.check_group, - timestamp: nextJourney._source['@timestamp'], - } - : undefined; - return { timestamp: journeySource['@timestamp'], journey: { ...journeySource, _id: foundJourney._id }, @@ -175,10 +172,19 @@ export const getJourneyDetails: UMElasticsearchQueryFn< }, } : {}), - previous: previousInfo, - next: nextInfo, + previous: filterNextPrevJourney(journeySource.monitor.check_group, previousJourney?._source), + next: filterNextPrevJourney(journeySource.monitor.check_group, nextJourney?._source), }; } else { return null; } }; + +const filterNextPrevJourney = (checkGroup: string, pingSource: DocumentSource) => { + return pingSource && pingSource.monitor.check_group !== checkGroup + ? { + checkGroup: pingSource.monitor.check_group, + timestamp: pingSource['@timestamp'], + } + : undefined; +}; diff --git a/x-pack/plugins/synthetics/server/routes/default_alerts/status_alert_service.ts b/x-pack/plugins/synthetics/server/routes/default_alerts/status_alert_service.ts index 1de80b1a27c08..e175325d51f4c 100644 --- a/x-pack/plugins/synthetics/server/routes/default_alerts/status_alert_service.ts +++ b/x-pack/plugins/synthetics/server/routes/default_alerts/status_alert_service.ts @@ -114,6 +114,8 @@ export class StatusAlertService { defaultActionMessage: SyntheticsMonitorStatusTranslations.defaultActionMessage, defaultRecoveryMessage: SyntheticsMonitorStatusTranslations.defaultRecoveryMessage, defaultSubjectMessage: SyntheticsMonitorStatusTranslations.defaultSubjectMessage, + defaultRecoverySubjectMessage: + SyntheticsMonitorStatusTranslations.defaultRecoverySubjectMessage, }, }); } diff --git a/x-pack/plugins/synthetics/server/routes/index.ts b/x-pack/plugins/synthetics/server/routes/index.ts index 9e2038d05962a..836143d55f014 100644 --- a/x-pack/plugins/synthetics/server/routes/index.ts +++ b/x-pack/plugins/synthetics/server/routes/index.ts @@ -20,7 +20,6 @@ import { getServiceLocationsRoute } from './synthetics_service/get_service_locat import { deleteSyntheticsMonitorRoute } from './monitor_cruds/delete_monitor'; import { disableSyntheticsRoute, - enableSyntheticsRoute, getSyntheticsEnablementRoute, } from './synthetics_service/enablement'; import { @@ -61,7 +60,6 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [ deleteSyntheticsMonitorProjectRoute, disableSyntheticsRoute, editSyntheticsMonitorRoute, - enableSyntheticsRoute, getServiceLocationsRoute, getSyntheticsMonitorRoute, getSyntheticsProjectMonitorsRoute, diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts index 59f798b5bc623..b2e4d050e4d7c 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor_project.ts @@ -78,7 +78,7 @@ export const addSyntheticsProjectMonitorRoute: SyntheticsRestApiRouteFactory = ( }; } catch (error) { server.logger.error(`Error adding monitors to project ${decodedProjectName}`); - if (error.output.statusCode === 404) { + if (error.output?.statusCode === 404) { const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } }); } diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts index 7678cfc54615e..7c2498242b35c 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts @@ -9,6 +9,7 @@ import { BrowserAdvancedFields, BrowserFields, BrowserSimpleFields, + CodeEditorMode, CommonFields, ConfigKey, DataStream, @@ -18,7 +19,6 @@ import { HTTPSimpleFields, ICMPSimpleFields, Metadata, - Mode, MonitorFields, ResponseBodyIndexPolicy, ScheduleUnit, @@ -142,7 +142,7 @@ describe('validateMonitor', () => { [ConfigKey.RESPONSE_HEADERS_CHECK]: {}, [ConfigKey.RESPONSE_HEADERS_INDEX]: true, [ConfigKey.RESPONSE_STATUS_CHECK]: ['200', '201'], - [ConfigKey.REQUEST_BODY_CHECK]: { value: 'testValue', type: Mode.JSON }, + [ConfigKey.REQUEST_BODY_CHECK]: { value: 'testValue', type: CodeEditorMode.JSON }, [ConfigKey.REQUEST_HEADERS_CHECK]: {}, [ConfigKey.REQUEST_METHOD_CHECK]: '', [ConfigKey.USERNAME]: 'test-username', @@ -175,11 +175,15 @@ describe('validateMonitor', () => { [ConfigKey.JOURNEY_FILTERS_MATCH]: 'false', [ConfigKey.JOURNEY_FILTERS_TAGS]: testTags, [ConfigKey.IGNORE_HTTPS_ERRORS]: false, - [ConfigKey.IS_THROTTLING_ENABLED]: true, - [ConfigKey.DOWNLOAD_SPEED]: '5', - [ConfigKey.UPLOAD_SPEED]: '3', - [ConfigKey.LATENCY]: '20', - [ConfigKey.THROTTLING_CONFIG]: '5d/3u/20l', + [ConfigKey.THROTTLING_CONFIG]: { + value: { + download: '5', + upload: '3', + latency: '20', + }, + id: 'test', + label: 'test', + }, }; testBrowserFields = { diff --git a/x-pack/plugins/synthetics/server/routes/synthetics_service/enablement.ts b/x-pack/plugins/synthetics/server/routes/synthetics_service/enablement.ts index defd82c415726..87a10dbee9a8e 100644 --- a/x-pack/plugins/synthetics/server/routes/synthetics_service/enablement.ts +++ b/x-pack/plugins/synthetics/server/routes/synthetics_service/enablement.ts @@ -5,26 +5,43 @@ * 2.0. */ import { syntheticsServiceAPIKeySavedObject } from '../../legacy_uptime/lib/saved_objects/service_api_key'; -import { - SyntheticsRestApiRouteFactory, - UMRestApiRouteFactory, -} from '../../legacy_uptime/routes/types'; +import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types'; import { API_URLS } from '../../../common/constants'; -import { - generateAndSaveServiceAPIKey, - SyntheticsForbiddenError, -} from '../../synthetics_service/get_api_key'; +import { generateAndSaveServiceAPIKey } from '../../synthetics_service/get_api_key'; -export const getSyntheticsEnablementRoute: UMRestApiRouteFactory = (libs) => ({ - method: 'GET', +export const getSyntheticsEnablementRoute: SyntheticsRestApiRouteFactory = (libs) => ({ + method: 'PUT', path: API_URLS.SYNTHETICS_ENABLEMENT, validate: {}, - handler: async ({ response, server }): Promise => { + handler: async ({ savedObjectsClient, request, server }): Promise => { try { - return response.ok({ - body: await libs.requests.getSyntheticsEnablement({ + const result = await libs.requests.getSyntheticsEnablement({ + server, + }); + const { canEnable, isEnabled } = result; + const { security } = server; + const { apiKey, isValid } = await libs.requests.getAPIKeyForSyntheticsService({ + server, + }); + if (apiKey && !isValid) { + await syntheticsServiceAPIKeySavedObject.delete(savedObjectsClient); + await security.authc.apiKeys?.invalidateAsInternalUser({ + ids: [apiKey?.id || ''], + }); + } + const regenerationRequired = !isEnabled || !isValid; + if (canEnable && regenerationRequired && server.config.service?.manifestUrl) { + await generateAndSaveServiceAPIKey({ + request, + authSavedObjectsClient: savedObjectsClient, server, - }), + }); + } else { + return result; + } + + return libs.requests.getSyntheticsEnablement({ + server, }); } catch (e) { server.logger.error(e); @@ -56,7 +73,7 @@ export const disableSyntheticsRoute: SyntheticsRestApiRouteFactory = (libs) => ( server, }); await syntheticsServiceAPIKeySavedObject.delete(savedObjectsClient); - await security.authc.apiKeys?.invalidate(request, { ids: [apiKey?.id || ''] }); + await security.authc.apiKeys?.invalidateAsInternalUser({ ids: [apiKey?.id || ''] }); return response.ok({}); } catch (e) { server.logger.error(e); @@ -64,30 +81,3 @@ export const disableSyntheticsRoute: SyntheticsRestApiRouteFactory = (libs) => ( } }, }); - -export const enableSyntheticsRoute: UMRestApiRouteFactory = (libs) => ({ - method: 'POST', - path: API_URLS.SYNTHETICS_ENABLEMENT, - validate: {}, - handler: async ({ request, response, server }): Promise => { - const { authSavedObjectsClient, logger } = server; - try { - await generateAndSaveServiceAPIKey({ - request, - authSavedObjectsClient, - server, - }); - return response.ok({ - body: await libs.requests.getSyntheticsEnablement({ - server, - }), - }); - } catch (e) { - logger.error(e); - if (e instanceof SyntheticsForbiddenError) { - return response.forbidden(); - } - throw e; - } - }, -}); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts index 391556bb99649..75fa3da2dcd3d 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts @@ -15,21 +15,21 @@ import { arrayFormatter, objectFormatter, stringToObjectFormatter } from './form export type BrowserFormatMap = Record; const throttlingFormatter: Formatter = (fields) => { - if (!fields[ConfigKey.IS_THROTTLING_ENABLED]) return false; + const value = fields[ConfigKey.THROTTLING_CONFIG]; + const defaultThrottling = DEFAULT_BROWSER_ADVANCED_FIELDS[ConfigKey.THROTTLING_CONFIG].value; + + const thValue = value?.value; + + if (!thValue || !defaultThrottling) return false; + + if (thValue?.download === '0' && thValue?.upload === '0' && thValue?.latency === '0') + return false; + if (value?.label === 'no-throttling') return false; return { - download: parseInt( - fields[ConfigKey.DOWNLOAD_SPEED] || DEFAULT_BROWSER_ADVANCED_FIELDS[ConfigKey.DOWNLOAD_SPEED], - 10 - ), - upload: parseInt( - fields[ConfigKey.UPLOAD_SPEED] || DEFAULT_BROWSER_ADVANCED_FIELDS[ConfigKey.UPLOAD_SPEED], - 10 - ), - latency: parseInt( - fields[ConfigKey.LATENCY] || DEFAULT_BROWSER_ADVANCED_FIELDS[ConfigKey.LATENCY], - 10 - ), + download: Number(thValue?.download ?? defaultThrottling.download), + upload: Number(thValue?.upload ?? defaultThrottling.upload), + latency: Number(thValue?.latency ?? defaultThrottling.latency), }; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts index eb2b40e324249..921fd737e5d3e 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts @@ -14,7 +14,7 @@ import { import { ConfigKey, DataStream, - Mode, + CodeEditorMode, MonitorFields, ResponseBodyIndexPolicy, ScheduleUnit, @@ -39,11 +39,19 @@ const testHTTPConfig: Partial = { proxy_url: '${proxyUrl}', 'check.response.body.negative': [], 'check.response.body.positive': [], + 'check.response.json': [ + { + description: 'test description', + expression: 'foo.bar == "myValue"', + }, + ], 'response.include_body': 'on_error' as ResponseBodyIndexPolicy, - 'check.response.headers': {}, + 'check.response.headers': { + 'test-header': 'test-value', + }, 'response.include_headers': true, 'check.response.status': [], - 'check.request.body': { type: 'text' as Mode, value: '' }, + 'check.request.body': { type: 'text' as CodeEditorMode, value: '' }, 'check.request.headers': {}, 'check.request.method': 'GET', 'ssl.verification_mode': VerificationMode.NONE, @@ -73,11 +81,15 @@ const testBrowserConfig: Partial = { 'filter_journeys.match': '', 'filter_journeys.tags': ['dev'], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { + value: { + download: '5', + latency: '20', + upload: '3', + }, + id: 'default', + label: 'default', + }, project_id: 'test-project', }; @@ -95,6 +107,15 @@ describe('formatMonitorConfig', () => { expect(yamlConfig).toEqual({ 'check.request.method': 'GET', + 'check.response.headers': { + 'test-header': 'test-value', + }, + 'check.response.json': [ + { + description: 'test description', + expression: 'foo.bar == "myValue"', + }, + ], enabled: true, locations: [], max_redirects: '0', @@ -125,6 +146,15 @@ describe('formatMonitorConfig', () => { expect(yamlConfig).toEqual({ 'check.request.method': 'GET', + 'check.response.headers': { + 'test-header': 'test-value', + }, + 'check.response.json': [ + { + description: 'test description', + expression: 'foo.bar == "myValue"', + }, + ], enabled: true, locations: [], max_redirects: '0', @@ -203,12 +233,16 @@ describe('browser fields', () => { }); it('excludes UI fields', () => { - testBrowserConfig['throttling.is_enabled'] = false; - testBrowserConfig['throttling.upload_speed'] = '3'; - const formattedConfig = formatMonitorConfigFields( Object.keys(testBrowserConfig) as ConfigKey[], - testBrowserConfig, + { + ...testBrowserConfig, + throttling: { + value: null, + label: 'no-throttling', + id: 'no-throttling', + }, + }, logger, { proxyUrl: 'https://www.google.com' } ); @@ -216,8 +250,6 @@ describe('browser fields', () => { const expected = { ...formattedConfig, throttling: false, - 'throttling.is_enabled': undefined, - 'throttling.upload_speed': undefined, }; expect(formattedConfig).toEqual(expected); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.ts index d9b12c550532f..16056d6687a70 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.ts @@ -23,10 +23,6 @@ const UI_KEYS_TO_SKIP = [ ConfigKey.JOURNEY_ID, ConfigKey.PROJECT_ID, ConfigKey.METADATA, - ConfigKey.UPLOAD_SPEED, - ConfigKey.DOWNLOAD_SPEED, - ConfigKey.LATENCY, - ConfigKey.IS_THROTTLING_ENABLED, ConfigKey.REVISION, ConfigKey.CUSTOM_HEARTBEAT_ID, ConfigKey.FORM_MONITOR_TYPE, @@ -36,19 +32,13 @@ const UI_KEYS_TO_SKIP = [ 'secrets', ]; -const uiToHeartbeatKeyMap = { - throttling: ConfigKey.THROTTLING_CONFIG, -}; - -type YamlKeys = keyof typeof uiToHeartbeatKeyMap; - export const formatMonitorConfigFields = ( configKeys: ConfigKey[], config: Partial, logger: Logger, params: Record ) => { - const formattedMonitor = {} as Record; + const formattedMonitor = {} as Record; configKeys.forEach((key) => { if (!UI_KEYS_TO_SKIP.includes(key)) { @@ -85,13 +75,6 @@ export const formatMonitorConfigFields = ( sslKeys.forEach((key) => (formattedMonitor[key] = null)); } - Object.keys(uiToHeartbeatKeyMap).forEach((key) => { - const hbKey = key as YamlKeys; - const configKey = uiToHeartbeatKeyMap[hbKey]; - formattedMonitor[hbKey] = formattedMonitor[configKey]; - delete formattedMonitor[configKey]; - }); - return omitBy(formattedMonitor, isNil) as Partial; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts index 43c1a5e76ea70..4f65e7546d966 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/http.ts @@ -17,10 +17,12 @@ export const httpFormatters: HTTPFormatMap = { [ConfigKey.METADATA]: objectFormatter, [ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE]: arrayFormatter, [ConfigKey.RESPONSE_BODY_CHECK_POSITIVE]: arrayFormatter, + [ConfigKey.RESPONSE_JSON_CHECK]: arrayFormatter, [ConfigKey.RESPONSE_HEADERS_CHECK]: objectFormatter, [ConfigKey.RESPONSE_STATUS_CHECK]: arrayFormatter, [ConfigKey.REQUEST_HEADERS_CHECK]: objectFormatter, [ConfigKey.REQUEST_BODY_CHECK]: (fields) => fields[ConfigKey.REQUEST_BODY_CHECK]?.value || null, + [ConfigKey.PROXY_HEADERS]: objectFormatter, ...tlsFormatters, ...commonFormatters, }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.test.ts index 4b15f4da43515..ca4a18e88d5d9 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.test.ts @@ -25,16 +25,6 @@ describe('getAPIKeyTest', function () { const logger = loggerMock.create(); - jest.spyOn(authUtils, 'checkHasPrivileges').mockResolvedValue({ - index: { - [syntheticsIndex]: { - auto_configure: true, - create_doc: true, - view_index_metadata: true, - }, - }, - } as any); - const server = { logger, security, @@ -52,6 +42,20 @@ describe('getAPIKeyTest', function () { encoded: '@#$%^&', }); + beforeEach(() => { + jest.clearAllMocks(); + jest.spyOn(authUtils, 'checkHasPrivileges').mockResolvedValue({ + index: { + [syntheticsIndex]: { + auto_configure: true, + create_doc: true, + view_index_metadata: true, + read: true, + }, + }, + } as any); + }); + it('should return existing api key', async () => { const getObject = jest .fn() @@ -79,4 +83,43 @@ describe('getAPIKeyTest', function () { 'ba997842-b0cf-4429-aa9d-578d9bf0d391' ); }); + + it('invalidates api keys with missing read permissions', async () => { + jest.spyOn(authUtils, 'checkHasPrivileges').mockResolvedValue({ + index: { + [syntheticsIndex]: { + auto_configure: true, + create_doc: true, + view_index_metadata: true, + read: false, + }, + }, + } as any); + + const getObject = jest + .fn() + .mockReturnValue({ attributes: { apiKey: 'qwerty', id: 'test', name: 'service-api-key' } }); + + encryptedSavedObjects.getClient = jest.fn().mockReturnValue({ + getDecryptedAsInternalUser: getObject, + }); + const apiKey = await getAPIKeyForSyntheticsService({ + server, + }); + + expect(apiKey).toEqual({ + apiKey: { apiKey: 'qwerty', id: 'test', name: 'service-api-key' }, + isValid: false, + }); + + expect(encryptedSavedObjects.getClient).toHaveBeenCalledTimes(1); + expect(getObject).toHaveBeenCalledTimes(1); + expect(encryptedSavedObjects.getClient).toHaveBeenCalledWith({ + includedHiddenTypes: [syntheticsServiceApiKey.name], + }); + expect(getObject).toHaveBeenCalledWith( + 'uptime-synthetics-api-key', + 'ba997842-b0cf-4429-aa9d-578d9bf0d391' + ); + }); }); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts index f3363b4d18a52..79af4d6cfc718 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts @@ -56,7 +56,8 @@ export const getAPIKeyForSyntheticsService = async ({ const hasPermissions = indexPermissions.auto_configure && indexPermissions.create_doc && - indexPermissions.view_index_metadata; + indexPermissions.view_index_metadata && + indexPermissions.read; if (!hasPermissions) { return { isValid: false, apiKey }; @@ -92,6 +93,7 @@ export const generateAPIKey = async ({ } if (uptimePrivileges) { + /* Exposed to the user. Must create directly with the user */ return security.authc.apiKeys?.create(request, { name: 'synthetics-api-key (required for project monitors)', kibana_role_descriptors: { @@ -122,7 +124,8 @@ export const generateAPIKey = async ({ throw new SyntheticsForbiddenError(); } - return security.authc.apiKeys?.create(request, { + /* Not exposed to the user. May grant as internal user */ + return security.authc.apiKeys?.grantAsInternalUser(request, { name: 'synthetics-api-key (required for monitor management)', role_descriptors: { synthetics_writer: serviceApiKeyPrivileges, @@ -158,7 +161,7 @@ export const generateAndSaveServiceAPIKey = async ({ }; export const getSyntheticsEnablement = async ({ server }: { server: UptimeServerSetup }) => { - const { security } = server; + const { security, config } = server; const [apiKey, hasPrivileges, areApiKeysEnabled] = await Promise.all([ getAPIKeyForSyntheticsService({ server }), @@ -167,6 +170,17 @@ export const getSyntheticsEnablement = async ({ server }: { server: UptimeServer ]); const { canEnable, canManageApiKeys } = hasPrivileges; + + if (!config.service?.manifestUrl) { + return { + canEnable: true, + canManageApiKeys, + isEnabled: true, + isValidApiKey: true, + areApiKeysEnabled: true, + }; + } + return { canEnable, canManageApiKeys, @@ -207,7 +221,7 @@ const hasEnablePermissions = async ({ uptimeEsClient }: UptimeServerSetup) => { return { canManageApiKeys, - canEnable: canManageApiKeys && hasClusterPermissions && hasIndexPermissions, + canEnable: hasClusterPermissions && hasIndexPermissions, }; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.ts index 3ee44e9b999dd..5fdf931b5605e 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.ts @@ -33,8 +33,9 @@ export async function getServiceLocations(server: UptimeServerSetup) { if (server.config.service?.devUrl) { locations = [getDevLocation(server.config.service.devUrl)]; } + const manifestUrl = server.config.service?.manifestUrl; - if (!server.config.service?.manifestUrl) { + if (!manifestUrl || manifestUrl === 'mockDevUrl') { return { locations }; } diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts index 5f1c74d5dfb05..f1ebab30182ce 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts @@ -263,7 +263,7 @@ describe('SyntheticsPrivateLocation', () => { }, 'throttling.config': { type: 'text', - value: '5d/3u/20l', + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), }, timeout: { type: 'text', @@ -311,11 +311,7 @@ const dummyBrowserConfig: Partial & { 'filter_journeys.match': '', 'filter_journeys.tags': [], ignore_https_errors: false, - 'throttling.is_enabled': true, - 'throttling.download_speed': '5', - 'throttling.upload_speed': '3', - 'throttling.latency': '20', - 'throttling.config': '5d/3u/20l', + throttling: { value: { download: '5', upload: '3', latency: '20' }, label: 'test', id: 'test' }, id: '75cdd125-5b62-4459-870c-46f59bf37e89', config_id: '75cdd125-5b62-4459-870c-46f59bf37e89', fields: { config_id: '75cdd125-5b62-4459-870c-46f59bf37e89', run_once: true }, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts index 0d66c944a1544..c1c6fa238b6c6 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts @@ -13,7 +13,11 @@ import { ProjectMonitor, PrivateLocation, } from '../../../../common/runtime_types'; -import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; +import { + DEFAULT_FIELDS, + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '../../../../common/constants/monitor_defaults'; import { normalizeProjectMonitors } from '.'; describe('browser normalizers', () => { @@ -143,11 +147,6 @@ describe('browser normalizers', () => { 'service.name': '', 'source.project.content': 'test content 1', tags: ['tag1', 'tag2'], - 'throttling.config': '5d/10u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '10', params: '', type: 'browser', project_id: projectId, @@ -157,6 +156,15 @@ describe('browser normalizers', () => { timeout: null, id: '', hash: testHash, + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '5', + latency: '20', + upload: '10', + }, + }, }, unsupportedKeys: [], errors: [], @@ -198,11 +206,6 @@ describe('browser normalizers', () => { 'service.name': '', 'source.project.content': 'test content 2', tags: ['tag3', 'tag4'], - 'throttling.config': '10d/15u/18l', - 'throttling.download_speed': '10', - 'throttling.is_enabled': true, - 'throttling.latency': '18', - 'throttling.upload_speed': '15', type: 'browser', project_id: projectId, namespace: 'test_space', @@ -211,6 +214,15 @@ describe('browser normalizers', () => { timeout: null, id: '', hash: testHash, + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '10', + latency: '18', + upload: '15', + }, + }, }, unsupportedKeys: [], errors: [], @@ -257,11 +269,6 @@ describe('browser normalizers', () => { 'service.name': '', 'source.project.content': 'test content 3', tags: ['tag3', 'tag4'], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': false, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', type: 'browser', project_id: projectId, namespace: 'test_space', @@ -270,6 +277,147 @@ describe('browser normalizers', () => { timeout: null, id: '', hash: testHash, + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.NO_THROTTLING], + }, + unsupportedKeys: [], + errors: [], + }, + ]); + }); + + it('handles defined throttling values', () => { + const actual = normalizeProjectMonitors({ + locations, + privateLocations, + monitors: [ + { + ...monitors[0], + throttling: { + download: 9, + upload: 0.75, + latency: 170, + }, + }, + ], + projectId, + namespace: 'test-space', + version: '8.5.0', + }); + expect(actual).toEqual([ + { + normalizedFields: { + ...DEFAULT_FIELDS[DataStream.BROWSER], + journey_id: 'test-id-1', + ignore_https_errors: true, + origin: 'project', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + }, + ], + name: 'test-name-1', + schedule: { + number: '3', + unit: 'm', + }, + screenshots: 'off', + 'service.name': '', + 'source.project.content': 'test content 1', + tags: ['tag1', 'tag2'], + params: '', + type: 'browser', + project_id: projectId, + namespace: 'test_space', + original_space: 'test-space', + custom_heartbeat_id: 'test-id-1-test-project-id-test-space', + timeout: null, + id: '', + hash: testHash, + throttling: { + id: '4g', + label: '4G', + value: { + download: '9', + latency: '170', + upload: '0.75', + }, + }, + }, + unsupportedKeys: [], + errors: [], + }, + ]); + }); + + it('handles custom throttling values', () => { + const actual = normalizeProjectMonitors({ + locations, + privateLocations, + monitors: [ + { + ...monitors[0], + throttling: { + download: 10, + upload: 5, + latency: 30, + }, + }, + ], + projectId, + namespace: 'test-space', + version: '8.5.0', + }); + expect(actual).toEqual([ + { + normalizedFields: { + ...DEFAULT_FIELDS[DataStream.BROWSER], + journey_id: 'test-id-1', + ignore_https_errors: true, + origin: 'project', + locations: [ + { + geo: { + lat: 33.333, + lon: 73.333, + }, + id: 'us_central', + isServiceManaged: true, + label: 'Test Location', + }, + ], + name: 'test-name-1', + schedule: { + number: '3', + unit: 'm', + }, + screenshots: 'off', + 'service.name': '', + 'source.project.content': 'test content 1', + tags: ['tag1', 'tag2'], + params: '', + type: 'browser', + project_id: projectId, + namespace: 'test_space', + original_space: 'test-space', + custom_heartbeat_id: 'test-id-1-test-project-id-test-space', + timeout: null, + id: '', + hash: testHash, + throttling: { + id: 'custom', + label: 'Custom', + value: { + download: '10', + latency: '30', + upload: '5', + }, + }, }, unsupportedKeys: [], errors: [], diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts index b8c6448b9e6e0..7ddf0cb01af91 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts @@ -10,8 +10,16 @@ import { ConfigKey, DataStream, FormMonitorType, + ProjectMonitor, + ThrottlingConfig, } from '../../../../common/runtime_types'; -import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; +import { + PROFILE_VALUES_ENUM, + DEFAULT_FIELDS, + PROFILES_MAP, + PROFILE_VALUES, + CUSTOM_LABEL, +} from '../../../../common/constants/monitor_defaults'; import { NormalizedProjectProps, NormalizerResult, @@ -38,33 +46,15 @@ export const getNormalizeBrowserFields = ({ version, }); + const throttling = normalizeThrottling(monitor.throttling); + const normalizedFields = { ...commonFields, [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, [ConfigKey.SOURCE_PROJECT_CONTENT]: monitor.content || defaultFields[ConfigKey.SOURCE_PROJECT_CONTENT], - [ConfigKey.THROTTLING_CONFIG]: - typeof monitor.throttling !== 'boolean' - ? `${monitor.throttling?.download}d/${monitor.throttling?.upload}u/${monitor.throttling?.latency}l` - : defaultFields[ConfigKey.THROTTLING_CONFIG], - [ConfigKey.DOWNLOAD_SPEED]: `${ - typeof monitor.throttling !== 'boolean' - ? monitor.throttling?.download - : defaultFields[ConfigKey.DOWNLOAD_SPEED] - }`, - [ConfigKey.UPLOAD_SPEED]: `${ - typeof monitor.throttling !== 'boolean' - ? monitor.throttling?.upload - : defaultFields[ConfigKey.UPLOAD_SPEED] - }`, - [ConfigKey.IS_THROTTLING_ENABLED]: - Boolean(monitor.throttling) ?? defaultFields[ConfigKey.IS_THROTTLING_ENABLED], - [ConfigKey.LATENCY]: `${ - typeof monitor.throttling !== 'boolean' - ? monitor.throttling?.latency - : defaultFields[ConfigKey.LATENCY] - }`, + [ConfigKey.THROTTLING_CONFIG]: throttling, [ConfigKey.IGNORE_HTTPS_ERRORS]: monitor.ignoreHTTPSErrors || defaultFields[ConfigKey.IGNORE_HTTPS_ERRORS], [ConfigKey.SCREENSHOTS]: monitor.screenshot || defaultFields[ConfigKey.SCREENSHOTS], @@ -89,3 +79,40 @@ export const getNormalizeBrowserFields = ({ errors, }; }; + +export const normalizeThrottling = ( + monitorThrottling: ProjectMonitor['throttling'] +): ThrottlingConfig => { + const defaultFields = DEFAULT_FIELDS[DataStream.BROWSER]; + + let throttling = defaultFields[ConfigKey.THROTTLING_CONFIG]; + if (typeof monitorThrottling === 'boolean' && !monitorThrottling) { + throttling = PROFILES_MAP[PROFILE_VALUES_ENUM.NO_THROTTLING]; + } + if (typeof monitorThrottling === 'object') { + const { download, upload, latency } = monitorThrottling; + const matchedProfile = PROFILE_VALUES.find(({ value }) => { + return ( + Number(value?.download) === download && + Number(value?.upload) === upload && + Number(value?.latency) === latency + ); + }); + + if (matchedProfile) { + return matchedProfile; + } else { + return { + id: PROFILE_VALUES_ENUM.CUSTOM, + label: CUSTOM_LABEL, + value: { + download: String(download), + upload: String(upload), + latency: String(latency), + }, + }; + } + } + + return throttling; +}; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts index cd7ac7a26f6dd..0045cc711f9bc 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts @@ -7,11 +7,11 @@ import { get } from 'lodash'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { + CodeEditorMode, ConfigKey, DataStream, FormMonitorType, HTTPFields, - Mode, TLSVersion, } from '../../../../common/runtime_types/monitor_management'; import { @@ -70,6 +70,11 @@ export const getNormalizeHTTPFields = ({ (yamlConfig as Record)[ConfigKey.REQUEST_BODY_CHECK] as string, defaultFields[ConfigKey.REQUEST_BODY_CHECK] ), + [ConfigKey.RESPONSE_BODY_MAX_BYTES]: `${get( + yamlConfig, + ConfigKey.RESPONSE_BODY_MAX_BYTES, + defaultFields[ConfigKey.RESPONSE_BODY_MAX_BYTES] + )}`, [ConfigKey.TLS_VERSION]: get(monitor, ConfigKey.TLS_VERSION) ? (getOptionalListField(get(monitor, ConfigKey.TLS_VERSION)) as TLSVersion[]) : defaultFields[ConfigKey.TLS_VERSION], @@ -94,14 +99,14 @@ export const getRequestBodyField = ( defaultValue: HTTPFields[ConfigKey.REQUEST_BODY_CHECK] ): HTTPFields[ConfigKey.REQUEST_BODY_CHECK] => { let parsedValue: string; - let type: Mode; + let type: CodeEditorMode; if (typeof value === 'object') { parsedValue = JSON.stringify(value); - type = Mode.JSON; + type = CodeEditorMode.JSON; } else { parsedValue = value; - type = Mode.PLAINTEXT; + type = CodeEditorMode.PLAINTEXT; } return { type, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts index 800b8cfa0ac5e..385ec449d6c1a 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts @@ -485,11 +485,6 @@ const payloadData = [ 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', timeout: null, type: 'browser', 'url.port': null, @@ -540,11 +535,6 @@ const payloadData = [ 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', timeout: null, type: 'browser', 'url.port': null, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts index c284e18dac56d..a7ae725337b06 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts @@ -554,11 +554,6 @@ const payloadData = [ 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', timeout: null, type: 'browser', 'url.port': null, @@ -609,11 +604,6 @@ const payloadData = [ 'ssl.verification_mode': 'full', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', timeout: null, type: 'browser', 'url.port': null, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.test.ts index 22ec8e8a3213e..700f339516215 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.test.ts @@ -14,6 +14,24 @@ import { UptimeServerSetup } from '../legacy_uptime/lib/adapters'; import { ServiceConfig } from '../../common/config'; import axios from 'axios'; import { LocationStatus, PublicLocations } from '../../common/runtime_types'; +import { LicenseGetResponse } from '@elastic/elasticsearch/lib/api/types'; + +const licenseMock: LicenseGetResponse = { + license: { + status: 'active', + uid: '1d34eb9f-e66f-47d1-8d24-cd60d187587a', + type: 'trial', + issue_date: '2022-05-05T14:25:00.732Z', + issue_date_in_millis: 165176070074432, + expiry_date: '2022-06-04T14:25:00.732Z', + expiry_date_in_millis: 165435270073332, + max_nodes: 1000, + max_resource_units: null, + issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', + issuer: 'elasticsearch', + start_date_in_millis: -1, + }, +}; jest.mock('axios', () => jest.fn()); jest.mock('@kbn/server-http-tools', () => ({ @@ -167,7 +185,7 @@ describe('callAPI', () => { await apiClient.callAPI('POST', { monitors: testMonitors, output, - licenseLevel: 'trial', + license: licenseMock.license, }); expect(spy).toHaveBeenCalledTimes(3); @@ -181,7 +199,7 @@ describe('callAPI', () => { monitor.locations.some((loc: any) => loc.id === 'us_central') ), output, - licenseLevel: 'trial', + license: licenseMock.license, }, 'POST', devUrl @@ -195,7 +213,7 @@ describe('callAPI', () => { monitor.locations.some((loc: any) => loc.id === 'us_central_qa') ), output, - licenseLevel: 'trial', + license: licenseMock.license, }, 'POST', 'https://qa.service.elstc.co' @@ -209,7 +227,7 @@ describe('callAPI', () => { monitor.locations.some((loc: any) => loc.id === 'us_central_staging') ), output, - licenseLevel: 'trial', + license: licenseMock.license, }, 'POST', 'https://qa.service.stg.co' @@ -223,6 +241,7 @@ describe('callAPI', () => { output, stack_version: '8.7.0', license_level: 'trial', + license_issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', }, headers: { Authorization: 'Basic ZGV2OjEyMzQ1', @@ -242,6 +261,7 @@ describe('callAPI', () => { output, stack_version: '8.7.0', license_level: 'trial', + license_issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', }, headers: { Authorization: 'Basic ZGV2OjEyMzQ1', @@ -261,6 +281,7 @@ describe('callAPI', () => { output, stack_version: '8.7.0', license_level: 'trial', + license_issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', }, headers: { Authorization: 'Basic ZGV2OjEyMzQ1', @@ -324,7 +345,7 @@ describe('callAPI', () => { await apiClient.callAPI('POST', { monitors: testMonitors, output, - licenseLevel: 'platinum', + license: licenseMock.license, }); expect(axiosSpy).toHaveBeenNthCalledWith(1, { @@ -333,7 +354,8 @@ describe('callAPI', () => { is_edit: undefined, output, stack_version: '8.7.0', - license_level: 'platinum', + license_level: 'trial', + license_issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', }, headers: { 'x-kibana-version': '8.7.0', @@ -376,7 +398,7 @@ describe('callAPI', () => { await apiClient.runOnce({ monitors: testMonitors, output, - licenseLevel: 'trial', + license: licenseMock.license, }); expect(axiosSpy).toHaveBeenNthCalledWith(1, { @@ -386,6 +408,7 @@ describe('callAPI', () => { output, stack_version: '8.7.0', license_level: 'trial', + license_issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', }, headers: { 'x-kibana-version': '8.7.0', @@ -418,7 +441,11 @@ describe('callAPI', () => { manifestUrl: 'http://localhost:8080/api/manifest', tls: { certificate: 'test-certificate', key: 'test-key' } as any, }, - { isDev: true, stackVersion: '8.7.0' } as UptimeServerSetup + { + isDev: true, + stackVersion: '8.7.0', + cloud: { cloudId: 'test-id', deploymentId: 'deployment-id' }, + } as UptimeServerSetup ); apiClient.locations = testLocations; @@ -428,7 +455,7 @@ describe('callAPI', () => { await apiClient.syncMonitors({ monitors: testMonitors, output, - licenseLevel: 'trial', + license: licenseMock.license, }); expect(axiosSpy).toHaveBeenNthCalledWith(1, { @@ -438,6 +465,9 @@ describe('callAPI', () => { output, stack_version: '8.7.0', license_level: 'trial', + license_issued_to: '2c515bd215ce444441f83ffd36a9d3d2546', + cloud_id: 'test-id', + deployment_id: 'deployment-id', }, headers: { 'x-kibana-version': '8.7.0', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.ts b/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.ts index bfc872f3680a8..876f0270c20ce 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/service_api_client.ts @@ -11,6 +11,7 @@ import { catchError, tap } from 'rxjs/operators'; import * as https from 'https'; import { SslConfig } from '@kbn/server-http-tools'; import { Logger } from '@kbn/core/server'; +import { LicenseGetLicenseInformation } from '@elastic/elasticsearch/lib/api/types'; import { UptimeServerSetup } from '../legacy_uptime/lib/adapters'; import { sendErrorTelemetryEvents } from '../routes/telemetry/monitor_upgrade_sender'; import { MonitorFields, PublicLocations, ServiceLocationErrors } from '../../common/runtime_types'; @@ -27,7 +28,7 @@ export interface ServiceData { }; endpoint?: 'monitors' | 'runOnce' | 'sync'; isEdit?: boolean; - licenseLevel: string; + license: LicenseGetLicenseInformation; } export class ServiceAPIClient { @@ -142,7 +143,7 @@ export class ServiceAPIClient { async callAPI( method: 'POST' | 'PUT' | 'DELETE', - { monitors: allMonitors, output, endpoint, isEdit, licenseLevel }: ServiceData + { monitors: allMonitors, output, endpoint, isEdit, license }: ServiceData ) { if (this.username === TEST_SERVICE_USERNAME) { // we don't want to call service while local integration tests are running @@ -159,7 +160,7 @@ export class ServiceAPIClient { ); if (locMonitors.length > 0) { const promise = this.callServiceEndpoint( - { monitors: locMonitors, isEdit, endpoint, output, licenseLevel }, + { monitors: locMonitors, isEdit, endpoint, output, license }, method, url ); @@ -200,7 +201,7 @@ export class ServiceAPIClient { } async callServiceEndpoint( - { monitors, output, endpoint = 'monitors', isEdit, licenseLevel }: ServiceData, + { monitors, output, endpoint = 'monitors', isEdit, license }: ServiceData, method: 'POST' | 'PUT' | 'DELETE', baseUrl: string ) { @@ -233,7 +234,10 @@ export class ServiceAPIClient { output, stack_version: this.stackVersion, is_edit: isEdit, - license_level: licenseLevel, + license_level: license.type, + license_issued_to: license.issued_to, + deployment_id: this.server.cloud?.deploymentId, + cloud_id: this.server.cloud?.cloudId, }, headers: authHeader, httpsAgent: this.getHttpsAgent(baseUrl), diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts index 9ec587cab93c5..35e31add55aa0 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts @@ -100,7 +100,9 @@ describe('SyntheticsService', () => { status: LocationStatus.GA, }; }); - serverMock.config = { service: { devUrl: 'http://localhost' } }; + serverMock.config = { + service: { devUrl: 'http://localhost', manifestUrl: 'https://test-manifest.com' }, + }; if (serverMock.savedObjectsClient) { serverMock.savedObjectsClient.find = jest.fn().mockResolvedValue({ saved_objects: [ diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts index dd3af249aa3e7..e4ef7cbbb6e67 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts @@ -174,7 +174,7 @@ export class SyntheticsService { service.isAllowed = allowed; service.signupUrl = signupUrl; - if (service.isAllowed) { + if (service.isAllowed && service.config.manifestUrl) { service.setupIndexTemplates(); await service.pushConfigs(); } @@ -307,7 +307,7 @@ export class SyntheticsService { this.syncErrors = await this.apiClient.post({ monitors, output, - licenseLevel: license.type, + license, }); } return this.syncErrors; @@ -329,7 +329,7 @@ export class SyntheticsService { monitors, output, isEdit, - licenseLevel: license.type, + license, }; this.syncErrors = await this.apiClient.put(data); @@ -349,7 +349,7 @@ export class SyntheticsService { subject.subscribe(async (monitors) => { try { - if (monitors.length === 0) { + if (monitors.length === 0 || !this.config.manifestUrl) { return; } @@ -372,7 +372,7 @@ export class SyntheticsService { service.syncErrors = await this.apiClient.syncMonitors({ monitors, output, - licenseLevel: license.type, + license, }); } catch (e) { sendErrorTelemetryEvents(service.logger, service.server.telemetry, { @@ -406,7 +406,7 @@ export class SyntheticsService { return await this.apiClient.runOnce({ monitors, output, - licenseLevel: license.type, + license, }); } catch (e) { this.logger.error(e); @@ -429,7 +429,7 @@ export class SyntheticsService { const data = { output, monitors: this.formatConfigs(configs), - licenseLevel: license.type, + license, }; return await this.apiClient.delete(data); } @@ -453,7 +453,7 @@ export class SyntheticsService { const data = { output, monitors, - licenseLevel: license.type, + license, }; return await this.apiClient.delete(data); } diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index a02e14d577b35..61b7fe8d8d19c 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -78,6 +78,7 @@ "@kbn/alerts-as-data-utils", "@kbn/exploratory-view-plugin", "@kbn/observability-shared-plugin", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/task_manager/server/index.ts b/x-pack/plugins/task_manager/server/index.ts index f489566a56491..174686529959e 100644 --- a/x-pack/plugins/task_manager/server/index.ts +++ b/x-pack/plugins/task_manager/server/index.ts @@ -50,36 +50,49 @@ export const config: PluginConfigDescriptor = { exposeToUsage: { max_workers: true, }, - deprecations: () => [ - (settings, fromPath, addDeprecation) => { - const taskManager = get(settings, fromPath); - if (taskManager?.index) { - addDeprecation({ - level: 'critical', - configPath: `${fromPath}.index`, - documentationUrl: 'https://ela.st/kbn-remove-legacy-multitenancy', - message: `"${fromPath}.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`, - correctiveActions: { - manualSteps: [ - `If you rely on this setting to achieve multitenancy you should use Spaces, cross-cluster replication, or cross-cluster search instead.`, - `To migrate to Spaces, we encourage using saved object management to export your saved objects from a tenant into the default tenant in a space.`, - ], - }, - }); - } - if (taskManager?.max_workers > MAX_WORKERS_LIMIT) { - addDeprecation({ - level: 'critical', - configPath: `${fromPath}.max_workers`, - message: `setting "${fromPath}.max_workers" (${taskManager?.max_workers}) greater than ${MAX_WORKERS_LIMIT} is deprecated.`, - correctiveActions: { - manualSteps: [ - `Maximum allowed value of "${fromPath}.max_workers" is ${MAX_WORKERS_LIMIT}.` + - `Replace "${fromPath}.max_workers: ${taskManager?.max_workers}" with (${MAX_WORKERS_LIMIT}).`, - ], - }, - }); - } - }, - ], + deprecations: ({ deprecate }) => { + return [ + deprecate('ephemeral_tasks.enabled', 'a future version', { + level: 'warning', + message: `Configuring "xpack.task_manager.ephemeral_tasks.enabled" is deprecated and will be removed in a future version. Remove this setting to increase task execution resiliency.`, + }), + deprecate('ephemeral_tasks.request_capacity', 'a future version', { + level: 'warning', + message: `Configuring "xpack.task_manager.ephemeral_tasks.request_capacity" is deprecated and will be removed in a future version. Remove this setting to increase task execution resiliency.`, + }), + (settings, fromPath, addDeprecation) => { + const taskManager = get(settings, fromPath); + if (taskManager?.index) { + addDeprecation({ + level: 'critical', + configPath: `${fromPath}.index`, + documentationUrl: 'https://ela.st/kbn-remove-legacy-multitenancy', + message: `"${fromPath}.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`, + correctiveActions: { + manualSteps: [ + `If you rely on this setting to achieve multitenancy you should use Spaces, cross-cluster replication, or cross-cluster search instead.`, + `To migrate to Spaces, we encourage using saved object management to export your saved objects from a tenant into the default tenant in a space.`, + ], + }, + }); + } + }, + (settings, fromPath, addDeprecation) => { + const taskManager = get(settings, fromPath); + if (taskManager?.max_workers > MAX_WORKERS_LIMIT) { + addDeprecation({ + level: 'critical', + configPath: `${fromPath}.max_workers`, + message: `setting "${fromPath}.max_workers" (${taskManager?.max_workers}) greater than ${MAX_WORKERS_LIMIT} is deprecated.`, + correctiveActions: { + manualSteps: [ + `Maximum allowed value of "${fromPath}.max_workers" is ${MAX_WORKERS_LIMIT}.` + + `Replace "${fromPath}.max_workers: ${taskManager?.max_workers}" with (${MAX_WORKERS_LIMIT}).`, + ], + }, + }); + } + }, + ]; + }, }; diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 1fb28465f5bce..98b6d711b5539 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -127,7 +127,7 @@ export class TaskManagerPlugin config: this.config!, usageCounter: this.usageCounter!, kibanaVersion: this.kibanaVersion, - kibanaIndexName: core.savedObjects.getKibanaIndex(), + kibanaIndexName: core.savedObjects.getDefaultIndex(), getClusterClient: () => startServicesPromise.then(({ elasticsearch }) => elasticsearch.client), shouldRunTasks: this.shouldRunBackgroundTasks, @@ -141,7 +141,7 @@ export class TaskManagerPlugin config: this.config!, usageCounter: this.usageCounter!, kibanaVersion: this.kibanaVersion, - kibanaIndexName: core.savedObjects.getKibanaIndex(), + kibanaIndexName: core.savedObjects.getDefaultIndex(), getClusterClient: () => startServicesPromise.then(({ elasticsearch }) => elasticsearch.client), }); diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 594373a71e188..88270d7102caa 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -5454,6 +5454,38 @@ } } }, + "vulnerabilities": { + "properties": { + "doc_count": { + "type": "long" + }, + "deleted": { + "type": "long" + }, + "size_in_bytes": { + "type": "long" + }, + "last_doc_timestamp": { + "type": "date" + } + } + }, + "latest_vulnerabilities": { + "properties": { + "doc_count": { + "type": "long" + }, + "deleted": { + "type": "long" + }, + "size_in_bytes": { + "type": "long" + }, + "last_doc_timestamp": { + "type": "date" + } + } + }, "score": { "properties": { "doc_count": { diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts index 3aec85af09261..bd63e464bf64f 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts @@ -49,6 +49,7 @@ export const CTI_ROW_RENDERER_FIELDS = [ export const TIMELINE_EVENTS_FIELDS = [ ALERT_RULE_CONSUMER, '@timestamp', + 'kibana.alert.ancestors.index', 'kibana.alert.workflow_status', 'kibana.alert.group.id', 'kibana.alert.original_time', @@ -230,6 +231,7 @@ export const TIMELINE_EVENTS_FIELDS = [ 'process.entry_leader.entity_id', 'process.entry_leader.name', 'process.entry_leader.pid', + 'process.entry_leader.start', 'process.session_leader.entity_id', 'process.session_leader.name', 'process.session_leader.pid', diff --git a/x-pack/plugins/transform/common/api_schemas/reauthorize_transforms.ts b/x-pack/plugins/transform/common/api_schemas/reauthorize_transforms.ts new file mode 100644 index 0000000000000..463366aa06826 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/reauthorize_transforms.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. + */ + +import { TypeOf } from '@kbn/config-schema'; + +import { transformIdsSchema, CommonResponseStatusSchema } from './common'; + +export const reauthorizeTransformsRequestSchema = transformIdsSchema; +export type ReauthorizeTransformsRequestSchema = TypeOf; +export type ReauthorizeTransformsResponseSchema = CommonResponseStatusSchema; diff --git a/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts b/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts index c00e79220e5b0..e9fa6cd7bc6d7 100644 --- a/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts +++ b/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts @@ -71,6 +71,7 @@ describe('has_privilege_factory', () => { canDeleteTransform: true, canGetTransform: true, canPreviewTransform: true, + canReauthorizeTransform: true, canResetTransform: true, canScheduleNowTransform: true, canStartStopTransform: true, @@ -96,6 +97,7 @@ describe('has_privilege_factory', () => { canDeleteTransform: false, canGetTransform: true, canPreviewTransform: false, + canReauthorizeTransform: false, canResetTransform: false, canScheduleNowTransform: false, canStartStopTransform: false, @@ -124,6 +126,7 @@ describe('has_privilege_factory', () => { canGetTransform: false, canPreviewTransform: false, canResetTransform: false, + canReauthorizeTransform: false, canScheduleNowTransform: false, canStartStopTransform: false, canUseTransformAlerts: false, diff --git a/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts b/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts index f9afe59355c01..972f8e727f50d 100644 --- a/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts +++ b/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts @@ -17,6 +17,7 @@ export interface TransformCapabilities { canDeleteTransform: boolean; canPreviewTransform: boolean; canCreateTransform: boolean; + canReauthorizeTransform: boolean; canScheduleNowTransform: boolean; canStartStopTransform: boolean; canCreateTransformAlerts: boolean; @@ -30,6 +31,7 @@ export const INITIAL_CAPABILITIES = Object.freeze({ canDeleteTransform: false, canPreviewTransform: false, canCreateTransform: false, + canReauthorizeTransform: false, canScheduleNowTransform: false, canStartStopTransform: false, canCreateTransformAlerts: false, @@ -130,6 +132,8 @@ export const getPrivilegesAndCapabilities = ( capabilities.canScheduleNowTransform = capabilities.canStartStopTransform; + capabilities.canReauthorizeTransform = capabilities.canStartStopTransform; + return { privileges: privilegesResult, capabilities }; }; // create the text for button's tooltips if the user @@ -170,6 +174,16 @@ export function createCapabilityFailureMessage( } ); break; + + case 'canReauthorizeTransform': + message = i18n.translate( + 'xpack.transform.capability.noPermission.reauthorizeTransformTooltip', + { + defaultMessage: 'You do not have permission to reauthorize transforms.', + } + ); + break; + case 'canDeleteTransform': message = i18n.translate('xpack.transform.capability.noPermission.deleteTransformTooltip', { defaultMessage: 'You do not have permission to delete transforms.', diff --git a/x-pack/plugins/transform/common/types/transform_stats.ts b/x-pack/plugins/transform/common/types/transform_stats.ts index c1194ea8f1018..4134662552a90 100644 --- a/x-pack/plugins/transform/common/types/transform_stats.ts +++ b/x-pack/plugins/transform/common/types/transform_stats.ts @@ -11,6 +11,7 @@ import { type TransformHealth, type TransformState, TRANSFORM_STATE } from '../c import { TransformId } from './transform'; export interface TransformHealthIssue { + type: string; issue: string; details?: string; count: number; diff --git a/x-pack/plugins/transform/common/utils/transform_api_key.ts b/x-pack/plugins/transform/common/utils/transform_api_key.ts new file mode 100644 index 0000000000000..66187ef318ced --- /dev/null +++ b/x-pack/plugins/transform/common/utils/transform_api_key.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GrantAPIKeyResult } from '@kbn/security-plugin/server'; +import type { SecurityCreateApiKeyResponse } from '@elastic/elasticsearch/lib/api/types'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +export interface TransformAPIKey extends GrantAPIKeyResult { + /** + * Generated encoded API key used for headers + */ + encoded: string; +} + +export interface SecondaryAuthorizationHeader { + headers?: { 'es-secondary-authorization': string | string[] }; +} + +export function isTransformApiKey(arg: any): arg is TransformAPIKey { + return isPopulatedObject(arg, ['api_key', 'encoded']) && typeof arg.encoded === 'string'; +} + +export function generateTransformSecondaryAuthHeaders( + apiKeyWithCurrentUserPermission: + | GrantAPIKeyResult + | null + | undefined + | SecurityCreateApiKeyResponse +): SecondaryAuthorizationHeader | undefined { + return isTransformApiKey(apiKeyWithCurrentUserPermission) + ? { + headers: { + 'es-secondary-authorization': `ApiKey ${apiKeyWithCurrentUserPermission.encoded}`, + }, + } + : undefined; +} diff --git a/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx b/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx index 62a88a51b7b27..3922c32df2181 100644 --- a/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx +++ b/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx @@ -6,18 +6,21 @@ */ import { useContext } from 'react'; +import { of } from 'rxjs'; +import type { + IKibanaSearchResponse, + IKibanaSearchRequest, + ISearchGeneric, +} from '@kbn/data-plugin/public'; import type { ScopedHistory } from '@kbn/core/public'; - import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { savedObjectsPluginMock } from '@kbn/saved-objects-plugin/public/mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; - import { SharePluginStart } from '@kbn/share-plugin/public'; - import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; @@ -35,6 +38,36 @@ const dataViewsStart = dataViewPluginMocks.createStartContract(); // Replace mock to support syntax using `.then()` as used in transform code. coreStart.savedObjects.client.find = jest.fn().mockResolvedValue({ savedObjects: [] }); +// Replace mock to support tests for `use_index_data`. +dataStart.search.search = jest.fn(({ params }: IKibanaSearchRequest) => { + const hits = []; + + // simulate a cross cluster search result + // against a cluster that doesn't support fields + if (params.index.includes(':')) { + hits.push({ + _id: 'the-doc', + _index: 'the-index', + }); + } + + return of({ + rawResponse: { + hits: { + hits, + total: { + value: 0, + relation: 'eq', + }, + max_score: 0, + }, + timed_out: false, + took: 10, + _shards: { total: 1, successful: 1, failed: 0, skipped: 0 }, + }, + }); +}) as ISearchGeneric; + const appDependencies: AppDependencies = { application: coreStart.application, charts: chartPluginMock.createStartContract(), diff --git a/x-pack/plugins/transform/public/app/common/reauthorization_utils.ts b/x-pack/plugins/transform/public/app/common/reauthorization_utils.ts new file mode 100644 index 0000000000000..d32383ccd683e --- /dev/null +++ b/x-pack/plugins/transform/public/app/common/reauthorization_utils.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 { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { TransformHealthIssue } from '../../../common/types/transform_stats'; +import { TRANSFORM_HEALTH } from '../../../common/constants'; +import type { TransformListRow } from './transform_list'; + +export const needsReauthorization = (transform: Partial) => { + return ( + isPopulatedObject(transform.config?.authorization, ['api_key']) && + isPopulatedObject(transform.stats) && + transform.stats.health.status === TRANSFORM_HEALTH.red && + transform.stats.health.issues?.find( + (issue) => (issue as TransformHealthIssue).issue === 'Privileges check failed' + ) !== undefined + ); +}; diff --git a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts index 61e6baf5c250e..41564d393913c 100644 --- a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - import type { IHttpFetchError } from '@kbn/core-http-browser'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; @@ -135,32 +133,6 @@ const apiFactory = () => ({ ): Promise { return Promise.resolve({ messages: [], total: 0 }); }, - async esSearch(payload: any): Promise { - const hits = []; - - // simulate a cross cluster search result - // against a cluster that doesn't support fields - if (payload.index.includes(':')) { - hits.push({ - _id: 'the-doc', - _index: 'the-index', - }); - } - - return Promise.resolve({ - hits: { - hits, - total: { - value: 0, - relation: 'eq', - }, - max_score: 0, - }, - timed_out: false, - took: 10, - _shards: { total: 1, successful: 1, failed: 0, skipped: 0 }, - }); - }, async getEsIndices(): Promise { return Promise.resolve([]); diff --git a/x-pack/plugins/transform/public/app/hooks/use_api.ts b/x-pack/plugins/transform/public/app/hooks/use_api.ts index 44df1941d8d9f..3b39d39a3a7bc 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_api.ts @@ -7,12 +7,14 @@ import { useMemo } from 'react'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - import type { IHttpFetchError } from '@kbn/core-http-browser'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { + ReauthorizeTransformsRequestSchema, + ReauthorizeTransformsResponseSchema, +} from '../../../common/api_schemas/reauthorize_transforms'; import type { GetTransformsAuditMessagesResponseSchema } from '../../../common/api_schemas/audit_messages'; import type { DeleteTransformsRequestSchema, @@ -166,6 +168,18 @@ export const useApi = () => { return e; } }, + async reauthorizeTransforms( + reqBody: ReauthorizeTransformsRequestSchema + ): Promise { + try { + return await http.post(`${API_BASE_PATH}reauthorize_transforms`, { + body: JSON.stringify(reqBody), + }); + } catch (e) { + return e; + } + }, + async resetTransforms( reqBody: ResetTransformsRequestSchema ): Promise { @@ -228,13 +242,6 @@ export const useApi = () => { return e; } }, - async esSearch(payload: any): Promise { - try { - return await http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) }); - } catch (e) { - return e; - } - }, async getEsIndices(): Promise { try { return await http.get(`/api/index_management/indices`); diff --git a/x-pack/plugins/transform/public/app/hooks/use_data_search.ts b/x-pack/plugins/transform/public/app/hooks/use_data_search.ts new file mode 100644 index 0000000000000..af4bb440f9e24 --- /dev/null +++ b/x-pack/plugins/transform/public/app/hooks/use_data_search.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback } from 'react'; +import { lastValueFrom } from 'rxjs'; + +import type { IKibanaSearchRequest } from '@kbn/data-plugin/common'; + +import { useAppDependencies } from '../app_dependencies'; + +export const useDataSearch = () => { + const { data } = useAppDependencies(); + + return useCallback( + async (esSearchRequestParams: IKibanaSearchRequest['params'], abortSignal?: AbortSignal) => { + try { + const { rawResponse: resp } = await lastValueFrom( + data.search.search( + { + params: esSearchRequestParams, + }, + { abortSignal } + ) + ); + + return resp; + } catch (error) { + if (error.name === 'AbortError') { + // ignore abort errors + } else { + return error; + } + } + }, + [data] + ); +}; diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index f90faf53e87b5..25318fc9e2903 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import type { DeleteTransformStatus, DeleteTransformsRequestSchema, @@ -24,7 +25,6 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const { http, data: { dataViews: dataViewsContract }, - ml: { extractErrorMessage }, application: { capabilities }, } = useAppDependencies(); const toastNotifications = useToastNotifications(); @@ -62,7 +62,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { ); } }, - [dataViewsContract, toastNotifications, extractErrorMessage] + [dataViewsContract, toastNotifications] ); const checkUserIndexPermission = useCallback(async () => { diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index f4d10b5562001..97005c11c3661 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -33,6 +33,7 @@ import type { StepDefineExposedState } from '../sections/create_transform/compon import { SearchItems } from './use_search_items'; import { useApi } from './use_api'; +import { useDataSearch } from './use_data_search'; export const useIndexData = ( dataView: SearchItems['dataView'], @@ -43,6 +44,7 @@ export const useIndexData = ( const indexPattern = useMemo(() => dataView.getIndexPattern(), [dataView]); const api = useApi(); + const dataSearch = useDataSearch(); const toastNotifications = useToastNotifications(); const { ml: { @@ -78,56 +80,62 @@ export const useIndexData = ( }, }; - // Fetch 500 random documents to determine populated fields. - // This is a workaround to avoid passing potentially thousands of unpopulated fields - // (for example, as part of filebeat/metricbeat/ECS based indices) - // to the data grid component which would significantly slow down the page. - const fetchDataGridSampleDocuments = async function () { - setErrorMessage(''); - setStatus(INDEX_STATUS.LOADING); - - const esSearchRequest = { - index: indexPattern, - body: { - fields: ['*'], - _source: false, - query: { - function_score: { - query: defaultQuery, - random_score: {}, + useEffect(() => { + const abortController = new AbortController(); + + // Fetch 500 random documents to determine populated fields. + // This is a workaround to avoid passing potentially thousands of unpopulated fields + // (for example, as part of filebeat/metricbeat/ECS based indices) + // to the data grid component which would significantly slow down the page. + const fetchDataGridSampleDocuments = async function () { + setErrorMessage(''); + setStatus(INDEX_STATUS.LOADING); + + const esSearchRequest = { + index: indexPattern, + body: { + fields: ['*'], + _source: false, + query: { + function_score: { + query: defaultQuery, + random_score: {}, + }, }, + size: 500, }, - size: 500, - }, - }; + }; - const resp = await api.esSearch(esSearchRequest); + const resp = await dataSearch(esSearchRequest, abortController.signal); - if (!isEsSearchResponse(resp)) { - setErrorMessage(getErrorMessage(resp)); - setStatus(INDEX_STATUS.ERROR); - return; - } + if (!isEsSearchResponse(resp)) { + setErrorMessage(getErrorMessage(resp)); + setStatus(INDEX_STATUS.ERROR); + return; + } - const isCrossClusterSearch = indexPattern.includes(':'); - const isMissingFields = resp.hits.hits.every((d) => typeof d.fields === 'undefined'); + const isCrossClusterSearch = indexPattern.includes(':'); + const isMissingFields = resp.hits.hits.every((d) => typeof d.fields === 'undefined'); - const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); + const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); - // Get all field names for each returned doc and flatten it - // to a list of unique field names used across all docs. - const allDataViewFields = getFieldsFromKibanaIndexPattern(dataView); - const populatedFields = [...new Set(docs.map(Object.keys).flat(1))] - .filter((d) => allDataViewFields.includes(d)) - .sort(); + // Get all field names for each returned doc and flatten it + // to a list of unique field names used across all docs. + const allDataViewFields = getFieldsFromKibanaIndexPattern(dataView); + const populatedFields = [...new Set(docs.map(Object.keys).flat(1))] + .filter((d) => allDataViewFields.includes(d)) + .sort(); - setCcsWarning(isCrossClusterSearch && isMissingFields); - setStatus(INDEX_STATUS.LOADED); - setDataViewFields(populatedFields); - }; + setCcsWarning(isCrossClusterSearch && isMissingFields); + setStatus(INDEX_STATUS.LOADED); + setDataViewFields(populatedFields); + }; - useEffect(() => { fetchDataGridSampleDocuments(); + + return () => { + abortController.abort(); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [timeRangeMs]); @@ -190,96 +198,62 @@ export const useIndexData = ( // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify([query, timeRangeMs])]); - const fetchDataGridData = async function () { - setErrorMessage(''); - setStatus(INDEX_STATUS.LOADING); - - const sort: EsSorting = sortingColumns.reduce((s, column) => { - s[column.id] = { order: column.direction }; - return s; - }, {} as EsSorting); - - const esSearchRequest = { - index: indexPattern, - body: { - fields: ['*'], - _source: false, - query: isDefaultQuery(query) ? defaultQuery : queryWithBaseFilterCriteria, - from: pagination.pageIndex * pagination.pageSize, - size: pagination.pageSize, - ...(Object.keys(sort).length > 0 ? { sort } : {}), - ...(isRuntimeMappings(combinedRuntimeMappings) - ? { runtime_mappings: combinedRuntimeMappings } - : {}), - }, - }; - const resp = await api.esSearch(esSearchRequest); - - if (!isEsSearchResponse(resp)) { - setErrorMessage(getErrorMessage(resp)); - setStatus(INDEX_STATUS.ERROR); - return; - } - - const isCrossClusterSearch = indexPattern.includes(':'); - const isMissingFields = resp.hits.hits.every((d) => typeof d.fields === 'undefined'); + useEffect(() => { + const abortController = new AbortController(); + + const fetchDataGridData = async function () { + setErrorMessage(''); + setStatus(INDEX_STATUS.LOADING); + + const sort: EsSorting = sortingColumns.reduce((s, column) => { + s[column.id] = { order: column.direction }; + return s; + }, {} as EsSorting); + + const esSearchRequest = { + index: indexPattern, + body: { + fields: ['*'], + _source: false, + query: isDefaultQuery(query) ? defaultQuery : queryWithBaseFilterCriteria, + from: pagination.pageIndex * pagination.pageSize, + size: pagination.pageSize, + ...(Object.keys(sort).length > 0 ? { sort } : {}), + ...(isRuntimeMappings(combinedRuntimeMappings) + ? { runtime_mappings: combinedRuntimeMappings } + : {}), + }, + }; + const resp = await dataSearch(esSearchRequest, abortController.signal); - const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); + if (!isEsSearchResponse(resp)) { + setErrorMessage(getErrorMessage(resp)); + setStatus(INDEX_STATUS.ERROR); + return; + } - setCcsWarning(isCrossClusterSearch && isMissingFields); - setRowCountInfo({ - rowCount: typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total!.value, - rowCountRelation: - typeof resp.hits.total === 'number' - ? ('eq' as estypes.SearchTotalHitsRelation) - : resp.hits.total!.relation, - }); - setTableItems(docs); - setStatus(INDEX_STATUS.LOADED); - }; + const isCrossClusterSearch = indexPattern.includes(':'); + const isMissingFields = resp.hits.hits.every((d) => typeof d.fields === 'undefined'); - const fetchColumnChartsData = async function () { - const allDataViewFieldNames = new Set(dataView.fields.map((f) => f.name)); - const columnChartsData = await api.getHistogramsForFields( - indexPattern, - columns - .filter((cT) => dataGrid.visibleColumns.includes(cT.id)) - .map((cT) => { - // If a column field name has a corresponding keyword field, - // fetch the keyword field instead to be able to do aggregations. - const fieldName = cT.id; - return hasKeywordDuplicate(fieldName, allDataViewFieldNames) - ? { - fieldName: `${fieldName}.keyword`, - type: getFieldType(undefined), - } - : { - fieldName, - type: getFieldType(cT.schema), - }; - }), - isDefaultQuery(query) ? defaultQuery : queryWithBaseFilterCriteria, - combinedRuntimeMappings - ); - - if (!isFieldHistogramsResponseSchema(columnChartsData)) { - showDataGridColumnChartErrorMessageToast(columnChartsData, toastNotifications); - return; - } + const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); - setColumnCharts( - // revert field names with `.keyword` used to do aggregations to their original column name - columnChartsData.map((d) => ({ - ...d, - ...(isKeywordDuplicate(d.id, allDataViewFieldNames) - ? { id: removeKeywordPostfix(d.id) } - : {}), - })) - ); - }; + setCcsWarning(isCrossClusterSearch && isMissingFields); + setRowCountInfo({ + rowCount: typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total!.value, + rowCountRelation: + typeof resp.hits.total === 'number' + ? ('eq' as estypes.SearchTotalHitsRelation) + : resp.hits.total!.relation, + }); + setTableItems(docs); + setStatus(INDEX_STATUS.LOADED); + }; - useEffect(() => { fetchDataGridData(); + + return () => { + abortController.abort(); + }; // custom comparison // eslint-disable-next-line react-hooks/exhaustive-deps }, [ @@ -296,6 +270,46 @@ export const useIndexData = ( ]); useEffect(() => { + const fetchColumnChartsData = async function () { + const allDataViewFieldNames = new Set(dataView.fields.map((f) => f.name)); + const columnChartsData = await api.getHistogramsForFields( + indexPattern, + columns + .filter((cT) => dataGrid.visibleColumns.includes(cT.id)) + .map((cT) => { + // If a column field name has a corresponding keyword field, + // fetch the keyword field instead to be able to do aggregations. + const fieldName = cT.id; + return hasKeywordDuplicate(fieldName, allDataViewFieldNames) + ? { + fieldName: `${fieldName}.keyword`, + type: getFieldType(undefined), + } + : { + fieldName, + type: getFieldType(cT.schema), + }; + }), + isDefaultQuery(query) ? defaultQuery : queryWithBaseFilterCriteria, + combinedRuntimeMappings + ); + + if (!isFieldHistogramsResponseSchema(columnChartsData)) { + showDataGridColumnChartErrorMessageToast(columnChartsData, toastNotifications); + return; + } + + setColumnCharts( + // revert field names with `.keyword` used to do aggregations to their original column name + columnChartsData.map((d) => ({ + ...d, + ...(isKeywordDuplicate(d.id, allDataViewFieldNames) + ? { id: removeKeywordPostfix(d.id) } + : {}), + })) + ); + }; + if (chartsVisible) { fetchColumnChartsData(); } diff --git a/x-pack/plugins/transform/public/app/hooks/use_reauthorize_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_reauthorize_transform.tsx new file mode 100644 index 0000000000000..af6018c35cecc --- /dev/null +++ b/x-pack/plugins/transform/public/app/hooks/use_reauthorize_transform.tsx @@ -0,0 +1,81 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; + +import type { StartTransformsRequestSchema } from '../../../common/api_schemas/start_transforms'; +import { isStartTransformsResponseSchema } from '../../../common/api_schemas/type_guards'; + +import { getErrorMessage } from '../../../common/utils/errors'; + +import { useAppDependencies, useToastNotifications } from '../app_dependencies'; +import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common'; +import { ToastNotificationText } from '../components'; + +import { useApi } from './use_api'; + +export const useReauthorizeTransforms = () => { + const { overlays, theme } = useAppDependencies(); + const toastNotifications = useToastNotifications(); + const api = useApi(); + + return async (transformsInfo: StartTransformsRequestSchema) => { + const results = await api.reauthorizeTransforms(transformsInfo); + + if (!isStartTransformsResponseSchema(results)) { + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.transform.stepCreateForm.reauthorizeTransformResponseSchemaErrorMessage', + { + defaultMessage: 'An error occurred calling the reauthorize transforms request.', + } + ), + text: toMountPoint( + , + { theme$: theme.theme$ } + ), + }); + return; + } + + for (const transformId in results) { + // hasOwnProperty check to ensure only properties on object itself, and not its prototypes + if (results.hasOwnProperty(transformId)) { + const result = results[transformId]; + if (result.success === true) { + toastNotifications.addSuccess( + i18n.translate('xpack.transform.transformList.reauthorizeTransformSuccessMessage', { + defaultMessage: 'Request to reauthorize transform {transformId} acknowledged.', + values: { transformId }, + }) + ); + } else { + toastNotifications.addError(new Error(JSON.stringify(result.error!.caused_by, null, 2)), { + title: i18n.translate( + 'xpack.transform.transformList.reauthorizeTransformErrorMessage', + { + defaultMessage: 'An error occurred reauthorizing the transform {transformId}', + values: { transformId }, + } + ), + toastMessage: result.error!.reason, + }); + } + } + } + + refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH); + }; +}; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx index c23d6ed475efc..e0a52978c0b4a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx @@ -25,8 +25,8 @@ interface SourceSearchBarProps { } export const SourceSearchBar: FC = ({ dataView, searchBar }) => { const { - actions: { searchChangeHandler, searchSubmitHandler, setErrorMessage }, - state: { errorMessage, searchInput }, + actions: { searchChangeHandler, searchSubmitHandler, setQueryErrorMessage }, + state: { queryErrorMessage, searchInput }, } = searchBar; const { @@ -44,7 +44,7 @@ export const SourceSearchBar: FC = ({ dataView, searchBar return ( setErrorMessage(undefined)} + closePopover={() => setQueryErrorMessage(undefined)} input={ = ({ dataView, searchBar }} /> } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar', { - defaultMessage: 'Invalid query: {errorMessage}', + defaultMessage: 'Invalid query: {queryErrorMessage}', values: { - errorMessage: errorMessage?.message.split('\n')[0], + queryErrorMessage: queryErrorMessage?.message.split('\n')[0], }, })} diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx index 11f9dadbb359c..6749786865083 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx @@ -5,22 +5,27 @@ * 2.0. */ +import { debounce } from 'lodash'; import React, { useCallback, useContext, useEffect, useState } from 'react'; +import useUpdateEffect from 'react-use/lib/useUpdateEffect'; + import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n-react'; -import { debounce } from 'lodash'; -import useUpdateEffect from 'react-use/lib/useUpdateEffect'; import { i18n } from '@kbn/i18n'; + +import { useDataSearch } from '../../../../../../../hooks/use_data_search'; import { isEsSearchResponseWithAggregations, isMultiBucketAggregate, } from '../../../../../../../../../common/api_schemas/type_guards'; -import { useApi } from '../../../../../../../hooks'; import { CreateTransformWizardContext } from '../../../../wizard/wizard'; -import { FilterAggConfigTerm } from '../types'; import { useToastNotifications } from '../../../../../../../app_dependencies'; +import { FilterAggConfigTerm } from '../types'; + /** * Form component for the term filter aggregation. */ @@ -29,16 +34,39 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm onChange, selectedField, }) => { - const api = useApi(); const { dataView, runtimeMappings } = useContext(CreateTransformWizardContext); + const dataSearch = useDataSearch(); const toastNotifications = useToastNotifications(); const [options, setOptions] = useState([]); const [isLoading, setIsLoading] = useState(true); + const [searchValue, setSearchValue] = useState(''); + + const onSearchChange = (newSearchValue: string) => { + setSearchValue(newSearchValue); + }; + + const updateConfig = useCallback( + (update) => { + onChange({ + config: { + ...config, + ...update, + }, + }); + }, + [config, onChange] + ); + + useEffect(() => { + const abortController = new AbortController(); + + const fetchOptions = debounce(async () => { + if (selectedField === undefined) return; + + setIsLoading(true); + setOptions([]); - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - const fetchOptions = useCallback( - debounce(async (searchValue: string) => { const esSearchRequest = { index: dataView!.title, body: { @@ -62,7 +90,7 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm }, }; - const response = await api.esSearch(esSearchRequest); + const response = await dataSearch(esSearchRequest, abortController.signal); setIsLoading(false); @@ -88,42 +116,21 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm .buckets as estypes.AggregationsSignificantLongTermsBucket[] ).map((value) => ({ label: value.key + '' })) ); - }, 600), - [selectedField] - ); - - const onSearchChange = useCallback( - async (searchValue) => { - if (selectedField === undefined) return; + }, 600); - setIsLoading(true); - setOptions([]); + fetchOptions(); - await fetchOptions(searchValue); - }, - [fetchOptions, selectedField] - ); - - const updateConfig = useCallback( - (update) => { - onChange({ - config: { - ...config, - ...update, - }, - }); - }, - [config, onChange] - ); - - useEffect(() => { - // Simulate initial load. - onSearchChange(''); return () => { // make sure the ongoing request is canceled fetchOptions.cancel(); + abortController.abort(); }; - // eslint-disable-next-line react-hooks/exhaustive-deps + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + }, [selectedField]); + + useEffect(() => { + // Simulate initial load. + onSearchChange(''); }, []); useUpdateEffect(() => { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts index 775401decef35..157ab0051e631 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts @@ -13,4 +13,4 @@ export { getDefaultAggregationConfig } from './get_default_aggregation_config'; export { getDefaultGroupByConfig } from './get_default_group_by_config'; export { getDefaultStepDefineState } from './get_default_step_define_state'; export { getPivotDropdownOptions } from './get_pivot_dropdown_options'; -export type { ErrorMessage, Field, StepDefineExposedState } from './types'; +export type { Field, StepDefineExposedState } from './types'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts index 682001a937381..2e66b2187a9e0 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts @@ -28,11 +28,6 @@ import { import { LatestFunctionConfig } from '../../../../../../../common/api_schemas/transforms'; import { RUNTIME_FIELD_TYPES } from '../../../../../../../common/shared_imports'; -export interface ErrorMessage { - query: string; - message: string; -} - export interface Field { name: EsFieldName; type: KBN_FIELD_TYPES | TIME_SERIES_METRIC_TYPES.COUNTER; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts index e8d56fc002981..62f8661f0ca49 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts @@ -9,11 +9,11 @@ import { useState } from 'react'; import { toElasticsearchQuery, fromKueryExpression, luceneStringToDsl } from '@kbn/es-query'; import type { Query } from '@kbn/es-query'; +import type { QueryErrorMessage } from '@kbn/ml-error-utils'; import { getTransformConfigQuery } from '../../../../../common'; import { - ErrorMessage, StepDefineExposedState, QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE_LUCENE, @@ -43,7 +43,9 @@ export const useSearchBar = ( const [searchQuery, setSearchQuery] = useState(defaults.searchQuery); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); const searchChangeHandler = (query: Query) => setSearchInput(query); const searchSubmitHandler = (query: Query) => { @@ -61,7 +63,7 @@ export const useSearchBar = ( return; } } catch (e) { - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } }; @@ -71,14 +73,14 @@ export const useSearchBar = ( actions: { searchChangeHandler, searchSubmitHandler, - setErrorMessage, + setQueryErrorMessage, setSearchInput, setSearchLanguage, setSearchQuery, setSearchString, }, state: { - errorMessage, + queryErrorMessage, transformConfigQuery, searchInput, searchLanguage, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/index.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/index.ts new file mode 100644 index 0000000000000..bbbe18d9f6acf --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/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 { useReauthorizeAction } from './use_reauthorize_action'; +export { ReauthorizeActionModal } from './reauthorize_action_modal'; +export { isReauthorizeActionDisabled, ReauthorizeActionName } from './reauthorize_action_name'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/reauthorize_action_modal.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/reauthorize_action_modal.tsx new file mode 100644 index 0000000000000..9e848fdd8323e --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/reauthorize_action_modal.tsx @@ -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 React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EUI_MODAL_CONFIRM_BUTTON, EuiConfirmModal } from '@elastic/eui'; +import type { ReauthorizeAction } from './use_reauthorize_action'; + +export const ReauthorizeActionModal: FC = ({ + closeModal, + items, + reauthorizeAndCloseModal, +}) => { + const isBulkAction = items.length > 1; + + const bulkReauthorizeModalTitle = i18n.translate( + 'xpack.transform.transformList.bulkReauthorizeModalTitle', + { + defaultMessage: 'Reauthorize {count} {count, plural, one {transform} other {transforms}}?', + values: { count: items && items.length }, + } + ); + const reauthorizeModalTitle = i18n.translate( + 'xpack.transform.transformList.reauthorizeModalTitle', + { + defaultMessage: 'Reauthorize {transformId}?', + values: { transformId: items[0] && items[0].config.id }, + } + ); + + return ( + +

    + {i18n.translate('xpack.transform.transformList.reauthorizeModalBody', { + defaultMessage: + 'Your current roles are used to update and start the transform. Starting a transform increases search and indexing load in your cluster. If excessive load is experienced, stop the transform.', + })} +

    +
    + ); +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/reauthorize_action_name.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/reauthorize_action_name.tsx new file mode 100644 index 0000000000000..e07ae03ec46b0 --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/reauthorize_action_name.tsx @@ -0,0 +1,84 @@ +/* + * 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, { FC, useContext } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiToolTip, EuiText } from '@elastic/eui'; +import { needsReauthorization } from '../../../../common/reauthorization_utils'; +import { + AuthorizationContext, + createCapabilityFailureMessage, +} from '../../../../lib/authorization'; +import { TransformListRow } from '../../../../common'; + +export const reauthorizeActionNameText = i18n.translate( + 'xpack.transform.transformList.reauthorizeActionNameText', + { + defaultMessage: 'Reauthorize', + } +); + +export const isReauthorizeActionDisabled = ( + items: TransformListRow[], + canStartStopTransform: boolean, + transformNodes: number +) => { + return ( + !canStartStopTransform || + items.length === 0 || + transformNodes === 0 || + !items.some(needsReauthorization) + ); +}; + +export interface ReauthorizeActionNameProps { + items: TransformListRow[]; + forceDisable?: boolean; + transformNodes: number; +} +export const ReauthorizeActionName: FC = ({ + items, + forceDisable, + transformNodes, +}) => { + const { canStartStopTransform } = useContext(AuthorizationContext).capabilities; + + // Disable start for batch transforms which have completed. + const someNeedsReauthorization = items.some(needsReauthorization); + + const actionIsDisabled = isReauthorizeActionDisabled( + items, + canStartStopTransform, + transformNodes + ); + + let content: string | undefined; + if (actionIsDisabled && items.length > 0) { + if (!canStartStopTransform && someNeedsReauthorization) { + content = createCapabilityFailureMessage('canReauthorizeTransform'); + } + if (!someNeedsReauthorization) { + content = i18n.translate( + 'xpack.transform.transformList.reauthorizeBulkActionDisabledToolTipContent', + { + defaultMessage: 'One or more selected transforms must require reauthorization.', + } + ); + } + } + + const text = {reauthorizeActionNameText}; + if ((forceDisable === true || actionIsDisabled) && content !== undefined) { + return ( + + {text} + + ); + } + + return <>{text}; +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/sort_transforms_to_reauthorize.test.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/sort_transforms_to_reauthorize.test.ts new file mode 100644 index 0000000000000..3c0d1b7ed7bde --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/sort_transforms_to_reauthorize.test.ts @@ -0,0 +1,108 @@ +/* + * 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 { sortTransformsToReauthorize } from './sort_transforms_to_reauthorize'; + +describe('sortTransformsToReauthorize', () => { + test('should work with multiple transforms with or without config or _meta defined', () => { + const transforms = [ + { + id: 'transform0', + config: {}, + }, + { + id: 'transformA-2', + config: { + _meta: { + run_as_kibana_system: false, + managed_by: 'fleet', + managed: true, + order: 2, + package: { + name: 'packageA', + }, + installed_by: 'transform_user', + }, + }, + }, + { + id: 'transformA-0', + config: { + _meta: { + run_as_kibana_system: false, + managed_by: 'fleet', + managed: true, + package: { + name: 'packageA', + }, + installed_by: 'transform_user', + }, + }, + }, + { + id: 'transformB-2', + config: { + _meta: { + run_as_kibana_system: false, + managed_by: 'fleet', + managed: true, + order: 2, + package: { + name: 'packageB', + }, + installed_by: 'transform_user', + }, + }, + }, + { + id: 'transformB-1', + config: { + _meta: { + run_as_kibana_system: false, + managed_by: 'fleet', + managed: true, + order: 1, + package: { + name: 'packageB', + }, + installed_by: 'transform_user', + }, + }, + }, + ]; + // @ts-ignore transforms is partial of TransformListRow + const { transformIds, shouldInstallSequentially } = sortTransformsToReauthorize(transforms); + expect(transformIds.map((t) => t.id)).toEqual([ + 'transform0', + 'transformA-0', + 'transformA-2', + 'transformB-1', + 'transformB-2', + ]); + expect(shouldInstallSequentially).toEqual(true); + }); + + test('should return shouldInstallSequentially: false if none of the transforms have order specified', () => { + const transforms = [ + { + id: 'transform3', + config: {}, + }, + { + id: 'transform2', + config: {}, + }, + { + id: 'transform1', + config: {}, + }, + ]; + // @ts-ignore transforms is partial of TransformListRow + const { transformIds, shouldInstallSequentially } = sortTransformsToReauthorize(transforms); + expect(transformIds.map((t) => t.id)).toEqual(['transform3', 'transform2', 'transform1']); + expect(shouldInstallSequentially).toEqual(false); + }); +}); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/sort_transforms_to_reauthorize.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/sort_transforms_to_reauthorize.ts new file mode 100644 index 0000000000000..00803929d2952 --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/sort_transforms_to_reauthorize.ts @@ -0,0 +1,42 @@ +/* + * 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 { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { sortBy } from 'lodash'; +import { TransformListRow } from '../../../../common'; + +// For transforms, some might be part of a integration package +// A transform might be dependent on the results of another transform +// If transforms have _meta.order specified, install sequentially (sort by package and install order) +export const sortTransformsToReauthorize = (transforms: TransformListRow[]) => { + let shouldInstallSequentially = false; + const mappedTransforms = transforms.map((t) => { + let packageName = ''; + let order = Number.MIN_VALUE; + if (isPopulatedObject(t?.config?._meta, ['order'])) { + if (typeof t.config._meta.order === 'number') { + shouldInstallSequentially = true; + order = t?.config?._meta.order; + } + if ( + isPopulatedObject(t.config._meta.package, ['name']) && + typeof t.config._meta.package.name === 'string' + ) { + packageName = t.config._meta.package.name; + } + } + + return { id: t.id, order, packageName }; + }); + + return { + transformIds: shouldInstallSequentially + ? sortBy(mappedTransforms, ['packageName', 'order']).map((t) => ({ id: t.id })) + : mappedTransforms.map((t) => ({ id: t.id })), + shouldInstallSequentially, + }; +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/use_reauthorize_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/use_reauthorize_action.tsx new file mode 100644 index 0000000000000..086f5451d53be --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_reauthorize/use_reauthorize_action.tsx @@ -0,0 +1,76 @@ +/* + * 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, { useContext, useMemo, useState } from 'react'; + +import { sortTransformsToReauthorize } from './sort_transforms_to_reauthorize'; +import { needsReauthorization } from '../../../../common/reauthorization_utils'; +import { useReauthorizeTransforms } from '../../../../hooks/use_reauthorize_transform'; +import { + isReauthorizeActionDisabled, + ReauthorizeActionName, + reauthorizeActionNameText, +} from './reauthorize_action_name'; + +import { TransformListAction, TransformListRow } from '../../../../common'; +import { AuthorizationContext } from '../../../../lib/authorization'; + +export type ReauthorizeAction = ReturnType; +export const useReauthorizeAction = (forceDisable: boolean, transformNodes: number) => { + const { canStartStopTransform } = useContext(AuthorizationContext).capabilities; + + const reauthorizeTransforms = useReauthorizeTransforms(); + + const [isModalVisible, setModalVisible] = useState(false); + const [items, setItems] = useState([]); + + const closeModal = () => setModalVisible(false); + + const reauthorizeAndCloseModal = () => { + setModalVisible(false); + const { transformIds } = sortTransformsToReauthorize(items); + reauthorizeTransforms(transformIds); + }; + + const openModal = (newItems: TransformListRow[]) => { + if (Array.isArray(newItems)) { + setItems(newItems); + setModalVisible(true); + } + }; + + const action: TransformListAction = useMemo( + () => ({ + name: (item: TransformListRow) => ( + + ), + available: (item: TransformListRow) => needsReauthorization(item), + enabled: (item: TransformListRow) => + !isReauthorizeActionDisabled([item], canStartStopTransform, transformNodes), + description: reauthorizeActionNameText, + icon: 'alert', + type: 'icon', + color: 'warning', + onClick: (item: TransformListRow) => openModal([item]), + 'data-test-subj': 'transformActionReauthorize', + }), + [canStartStopTransform, forceDisable, transformNodes] + ); + + return { + action, + closeModal, + isModalVisible, + items, + openModal, + reauthorizeAndCloseModal, + }; +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx index 7843516bc5a6e..e6c279e56f3c3 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx @@ -24,6 +24,12 @@ import { EuiSearchBarProps, } from '@elastic/eui'; +import { + isReauthorizeActionDisabled, + ReauthorizeActionModal, + ReauthorizeActionName, + useReauthorizeAction, +} from '../action_reauthorize'; import type { TransformId } from '../../../../../../common/types/transform'; import { @@ -108,6 +114,7 @@ export const TransformList: FC = ({ const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false); const bulkStartAction = useStartAction(false, transformNodes); const bulkDeleteAction = useDeleteAction(false); + const bulkReauthorizeAction = useReauthorizeAction(false, transformNodes); const bulkResetAction = useResetAction(false); const bulkStopAction = useStopAction(false); const bulkScheduleNowAction = useScheduleNowAction(false, transformNodes); @@ -221,6 +228,20 @@ export const TransformList: FC = ({
    , +
    + { + bulkReauthorizeAction.openModal(transformSelection); + }} + disabled={isReauthorizeActionDisabled( + transformSelection, + capabilities.canStartStopTransform, + transformNodes + )} + > + + +
    ,
    { @@ -325,6 +346,9 @@ export const TransformList: FC = ({ {/* Bulk Action Modals */} {bulkStartAction.isModalVisible && } {bulkDeleteAction.isModalVisible && } + {bulkReauthorizeAction.isModalVisible && ( + + )} {bulkResetAction.isModalVisible && } {bulkStopAction.isModalVisible && } diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx index 4c1e34583ce5a..d620a60e7a861 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx @@ -34,6 +34,7 @@ describe('Transform: Transform List Actions', () => { 'transformActionEdit', 'transformActionClone', 'transformActionDelete', + 'transformActionReauthorize', 'transformActionReset', ]); }); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx index 26ee8b9fd9111..b46628aeb4eff 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { EuiTableActionsColumnType } from '@elastic/eui'; +import { ReauthorizeActionModal, useReauthorizeAction } from '../action_reauthorize'; import { TransformListRow } from '../../../../common'; import { useCloneAction } from '../action_clone'; @@ -37,6 +38,7 @@ export const useActions = ({ const deleteAction = useDeleteAction(forceDisable); const discoverAction = useDiscoverAction(forceDisable); const editAction = useEditAction(forceDisable, transformNodes); + const reauthorizeAction = useReauthorizeAction(forceDisable, transformNodes); const resetAction = useResetAction(forceDisable); const scheduleNowAction = useScheduleNowAction(forceDisable, transformNodes); const startAction = useStartAction(forceDisable, transformNodes); @@ -49,7 +51,7 @@ export const useActions = ({ {resetAction.isModalVisible && } {startAction.isModalVisible && } {stopAction.isModalVisible && } - + {reauthorizeAction.isModalVisible && } {deleteAction.isModalVisible && } @@ -63,6 +65,7 @@ export const useActions = ({ editAction.action, cloneAction.action, deleteAction.action, + reauthorizeAction.action, resetAction.action, ], }; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx index cf3ffa98b0334..db77b3e306da2 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { @@ -24,6 +24,8 @@ import { EuiIcon, } from '@elastic/eui'; +import { AuthorizationContext } from '../../../../lib/authorization'; +import { needsReauthorization } from '../../../../common/reauthorization_utils'; import { isLatestTransform, isPivotTransform, @@ -38,12 +40,20 @@ import { isManagedTransform } from '../../../../common/managed_transforms_utils' import { TransformHealthColoredDot } from './transform_health_colored_dot'; import { TransformTaskStateBadge } from './transform_task_state_badge'; +const TRANSFORM_INSUFFICIENT_PERMISSIONS_MSG = i18n.translate( + 'xpack.transform.transformList.needsReauthorizationBadge.insufficientPermissions', + { + defaultMessage: 'This transform was created with insufficient permissions.', + } +); export const useColumns = ( expandedRowItemIds: TransformId[], setExpandedRowItemIds: React.Dispatch>, transformNodes: number, transformSelection: TransformListRow[] ) => { + const { canStartStopTransform } = useContext(AuthorizationContext).capabilities; + const { actions, modals } = useActions({ forceDisable: transformSelection.length > 0, transformNodes, @@ -150,7 +160,31 @@ export const useColumns = ( ), width: '30px', render: (item) => { - return Array.isArray(item.alerting_rules) ? ( + const needsReauth = needsReauthorization(item); + + const actionMsg = canStartStopTransform + ? i18n.translate( + 'xpack.transform.transformList.needsReauthorizationBadge.reauthorizeTooltip', + { + defaultMessage: 'Reauthorize to start transforms.', + } + ) + : i18n.translate( + 'xpack.transform.transformList.needsReauthorizationBadge.contactAdminTooltip', + { + defaultMessage: 'Contact your administrator to request the required permissions.', + } + ); + const needsReauthTooltipIcon = needsReauth ? ( + <> + + + +   + + ) : null; + + const alertingRulesTooltipIcon = Array.isArray(item.alerting_rules) ? ( ); + return ( + <> + {needsReauthTooltipIcon} + {alertingRulesTooltipIcon} + + ); }, }, { diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx index 6e2c1cc612235..3961f83ecffd0 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useEffect, useState } from 'react'; +import React, { FC, useContext, useEffect, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -23,6 +23,8 @@ import { EuiCallOut, EuiButton, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { needsReauthorization } from '../../common/reauthorization_utils'; import { APP_GET_TRANSFORM_CLUSTER_PRIVILEGES, TRANSFORM_STATE, @@ -32,7 +34,7 @@ import { useRefreshTransformList, TransformListRow } from '../../common'; import { useDocumentationLinks } from '../../hooks/use_documentation_links'; import { useDeleteTransforms, useGetTransforms } from '../../hooks'; import { RedirectToCreateTransform } from '../../common/navigation'; -import { PrivilegesWrapper } from '../../lib/authorization'; +import { AuthorizationContext, PrivilegesWrapper } from '../../lib/authorization'; import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation'; import { useRefreshInterval } from './components/transform_list/use_refresh_interval'; @@ -77,6 +79,48 @@ export const TransformManagement: FC = () => { // Call useRefreshInterval() after the subscription above is set up. useRefreshInterval(setBlockRefresh); + const { canStartStopTransform } = useContext(AuthorizationContext).capabilities; + + const unauthorizedTransformsWarning = useMemo(() => { + const unauthorizedCnt = transforms.filter((t) => needsReauthorization(t)).length; + + if (!unauthorizedCnt) return null; + + const insufficientPermissionsMsg = i18n.translate( + 'xpack.transform.transformList.unauthorizedTransformsCallout.insufficientPermissionsMsg', + { + defaultMessage: + '{unauthorizedCnt, plural, one {A transform was created with insufficient permissions.} other {# transforms were created with insufficient permissions.}}', + values: { unauthorizedCnt }, + } + ); + const actionMsg = canStartStopTransform + ? i18n.translate( + 'xpack.transform.transformList.unauthorizedTransformsCallout.reauthorizeMsg', + { + defaultMessage: + 'Reauthorize to start {unauthorizedCnt, plural, one {transform} other {# transforms}}.', + values: { unauthorizedCnt }, + } + ) + : i18n.translate( + 'xpack.transform.transformList.unauthorizedTransformsCallout.contactAdminMsg', + { + defaultMessage: 'Contact your administrator to request the required permissions.', + } + ); + return ( + <> + + + + ); + }, [transforms, canStartStopTransform]); + const [isSearchSelectionVisible, setIsSearchSelectionVisible] = useState(false); const [savedObjectId, setSavedObjectId] = useState(null); @@ -131,6 +175,8 @@ export const TransformManagement: FC = () => { {!isInitialized && } {isInitialized && ( <> + {unauthorizedTransformsWarning} + {typeof errorMessage !== 'undefined' && ( diff --git a/x-pack/plugins/transform/server/plugin.ts b/x-pack/plugins/transform/server/plugin.ts index ab2bb0769e002..95d80f3545c42 100644 --- a/x-pack/plugins/transform/server/plugin.ts +++ b/x-pack/plugins/transform/server/plugin.ts @@ -58,7 +58,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { ], }); - getStartServices().then(([coreStart, { dataViews }]) => { + getStartServices().then(([coreStart, { dataViews, security: securityStart }]) => { const license = new License({ pluginId: PLUGIN.id, minimumLicenseType: PLUGIN.minimumLicenseType, @@ -75,6 +75,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { license, dataViews, coreStart, + security: securityStart, }); }); diff --git a/x-pack/plugins/transform/server/routes/api/error_utils.ts b/x-pack/plugins/transform/server/routes/api/error_utils.ts index 6f02b1978506f..9054ee7aa3b23 100644 --- a/x-pack/plugins/transform/server/routes/api/error_utils.ts +++ b/x-pack/plugins/transform/server/routes/api/error_utils.ts @@ -142,7 +142,7 @@ export function wrapEsError(err: any, statusCodeToMessageMap: Record( + { + path: addBasePath('reauthorize_transforms'), + validate: { + body: reauthorizeTransformsRequestSchema, + }, + }, + license.guardApiRoute( + async (ctx, req, res) => { + try { + const transformsInfo = req.body; + const { elasticsearch } = coreStart; + const esClient = elasticsearch.client.asScoped(req).asCurrentUser; + + let apiKeyWithCurrentUserPermission; + + // If security is not enabled or available, user should not have the need to reauthorize + // in that case, start anyway + if (securityStart) { + apiKeyWithCurrentUserPermission = await securityStart.authc.apiKeys.grantAsInternalUser( + req, + { + name: `auto-generated-transform-api-key`, + role_descriptors: {}, + } + ); + } + const secondaryAuth = generateTransformSecondaryAuthHeaders( + apiKeyWithCurrentUserPermission + ); + + const authorizedTransforms = await reauthorizeAndStartTransforms( + transformsInfo, + esClient, + { + ...(secondaryAuth ? secondaryAuth : {}), + } + ); + return res.ok({ body: authorizedTransforms }); + } catch (e) { + return res.customError(wrapError(wrapEsError(e))); + } + } + ) + ); + /** * @apiGroup Transforms * @@ -466,33 +528,6 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { ) ); - /** - * @apiGroup Transforms - * - * @api {post} /api/transform/es_search Transform ES Search Proxy - * @apiName PostTransformEsSearchProxy - * @apiDescription ES Search Proxy - * - * @apiSchema (body) any - */ - router.post( - { - path: addBasePath('es_search'), - validate: { - body: schema.maybe(schema.any()), - }, - }, - license.guardApiRoute(async (ctx, req, res) => { - try { - const esClient = (await ctx.core).elasticsearch.client; - const body = await esClient.asCurrentUser.search(req.body, { maxRetries: 0 }); - return res.ok({ body }); - } catch (e) { - return res.customError(wrapError(wrapEsError(e))); - } - }) - ); - registerTransformsAuditMessagesRoutes(routeDependencies); registerTransformNodesRoutes(routeDependencies); } @@ -860,3 +895,44 @@ async function scheduleNowTransforms( } return results; } + +async function reauthorizeAndStartTransforms( + transformsInfo: ReauthorizeTransformsRequestSchema, + esClient: ElasticsearchClient, + options?: TransportRequestOptions +) { + const results: ReauthorizeTransformsResponseSchema = {}; + + for (const transformInfo of transformsInfo) { + const transformId = transformInfo.id; + try { + await esClient.transform.updateTransform( + { + body: {}, + transform_id: transformId, + }, + options ?? {} + ); + + await esClient.transform.startTransform( + { + transform_id: transformId, + }, + { ignore: [409] } + ); + + results[transformId] = { success: true }; + } catch (e) { + if (isRequestTimeout(e)) { + return fillResultsWithTimeouts({ + results, + id: transformId, + items: transformsInfo, + action: TRANSFORM_ACTIONS.REAUTHORIZE, + }); + } + results[transformId] = { success: false, error: e.meta.body.error }; + } + } + return results; +} diff --git a/x-pack/plugins/transform/server/types.ts b/x-pack/plugins/transform/server/types.ts index 012f819fce88f..6aa17d59db353 100644 --- a/x-pack/plugins/transform/server/types.ts +++ b/x-pack/plugins/transform/server/types.ts @@ -33,4 +33,5 @@ export interface RouteDependencies { license: License; coreStart: CoreStart; dataViews: DataViewsServerPluginStart; + security?: SecurityPluginStart; } diff --git a/x-pack/plugins/transform/tsconfig.json b/x-pack/plugins/transform/tsconfig.json index 6be7c1581f949..657cb1b34448d 100644 --- a/x-pack/plugins/transform/tsconfig.json +++ b/x-pack/plugins/transform/tsconfig.json @@ -58,7 +58,8 @@ "@kbn/saved-objects-finder-plugin", "@kbn/ml-route-utils", "@kbn/core-lifecycle-server", - "@kbn/security-plugin" + "@kbn/security-plugin", + "@kbn/ml-error-utils" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index b8e6993bf9d6a..d3bfc3f941a3a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -1090,7 +1090,6 @@ "dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName} a été ajouté", "dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName} a été ajouté", "dashboard.listing.createNewDashboard.newToKibanaDescription": "Vous êtes nouveau sur Kibana ? {sampleDataInstallLink} pour découvrir l'application.", - "dashboard.listing.unsaved.discardAria": "Ignorer les modifications apportées à {title}", "dashboard.listing.unsaved.editAria": "Continuer à modifier {title}", "dashboard.listing.unsaved.unsavedChangesTitle": "Vous avez des modifications non enregistrées dans le {dash} suivant :", "dashboard.loadingError.dashboardGridErrorMessage": "Impossible de charger le tableau de bord : {message}", @@ -1127,10 +1126,7 @@ "dashboard.dashboardPageTitle": "Tableaux de bord", "dashboard.dashboardWasSavedSuccessMessage": "Le tableau de bord \"{dashTitle}\" a été enregistré.", "dashboard.deleteError.toastDescription": "Erreur rencontrée lors de la suppression du tableau de bord", - "dashboard.discardChangesConfirmModal.cancelButtonLabel": "Annuler", - "dashboard.discardChangesConfirmModal.confirmButtonLabel": "Abandonner les modifications", "dashboard.discardChangesConfirmModal.discardChangesDescription": "Une fois les modifications ignorées, vous ne pourrez pas les récupérer.", - "dashboard.discardChangesConfirmModal.discardChangesTitle": "Ignorer les modifications apportées au tableau de bord ?", "dashboard.editingToolbar.addControlButtonTitle": "Ajouter un contrôle", "dashboard.editingToolbar.addTimeSliderControlButtonTitle": "Ajouter un contrôle de curseur temporel", "dashboard.editingToolbar.controlsButtonTitle": "Contrôles", @@ -1164,7 +1160,6 @@ "dashboard.listing.readonlyNoItemsTitle": "Aucun tableau de bord à afficher", "dashboard.listing.table.entityName": "tableau de bord", "dashboard.listing.table.entityNamePlural": "tableaux de bord", - "dashboard.listing.unsaved.discardTitle": "Abandonner les modifications", "dashboard.listing.unsaved.editTitle": "Poursuivre les modifications", "dashboard.listing.unsaved.loading": "Chargement", "dashboard.loadingError.dashboardNotFound": "Le tableau de bord demandé est introuvable.", @@ -2147,19 +2142,15 @@ "discover.docTable.totalDocuments": "{totalDocuments} documents", "discover.dscTour.stepAddFields.description": "Cliquez sur {plusIcon} pour ajouter les champs qui vous intéressent.", "discover.dscTour.stepExpand.description": "Cliquez sur {expandIcon} pour afficher, comparer et filtrer les documents.", - "discover.field.title": "{fieldName} ({fieldDisplayName})", "discover.fieldChooser.detailViews.existsInRecordsText": "Existe dans {value} / {totalValue} enregistrements", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "Exclure {field} : \"{value}\"", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "Filtrer sur {field} : \"{value}\"", "discover.fieldChooser.detailViews.valueOfRecordsText": "{value} / {totalValue} enregistrements", - "discover.fieldChooser.discoverField.addButtonAriaLabel": "Ajouter {field} au tableau", - "discover.fieldChooser.discoverField.removeButtonAriaLabel": "Retirer {field} du tableau", "discover.fieldChooser.fieldCalculator.fieldIsNotPresentInDocumentsErrorMessage": "Ce champ est présent dans votre mapping Elasticsearch, mais pas dans les {hitsLength} documents affichés dans le tableau des documents. Cependant, vous pouvez toujours le consulter ou effectuer une recherche dessus.", "discover.grid.copyClipboardButtonTitle": "Copier la valeur de {column}", "discover.grid.copyColumnValuesToClipboard.toastTitle": "Valeurs de la colonne \"{column}\" copiées dans le presse-papiers", "discover.grid.filterForAria": "Filtrer sur cette {value}", "discover.grid.filterOutAria": "Exclure cette {value}", - "discover.gridSampleSize.description": "Vous voyez les {sampleSize} premiers documents qui correspondent à votre recherche. Pour modifier cette valeur, accédez à {advancedSettingsLink}.", "discover.howToSeeOtherMatchingDocumentsDescription": "Voici les {sampleSize} premiers documents correspondant à votre recherche. Veuillez affiner cette dernière pour en voir davantage.", "discover.noMatchRoute.bannerText": "L'application Discover ne reconnaît pas cet itinéraire : {route}", "discover.noResults.kqlExamples.kqlDescription": "En savoir plus sur {kqlLink}", @@ -2337,8 +2328,6 @@ "discover.embeddable.inspectorRequestDataTitle": "Données", "discover.embeddable.inspectorRequestDescription": "Cette requête interroge Elasticsearch afin de récupérer les données pour la recherche.", "discover.embeddable.search.displayName": "rechercher", - "discover.field.mappingConflict": "Ce champ est défini avec plusieurs types (chaîne, entier, etc.) dans les différents index qui correspondent à ce modèle. Vous pouvez toujours utiliser ce champ conflictuel, mais il sera indisponible pour les fonctions qui nécessitent que Kibana en connaisse le type. Pour corriger ce problème, vous devrez réindexer vos données.", - "discover.field.mappingConflict.title": "Conflit de mapping", "discover.fieldChooser.addField.label": "Ajouter un champ", "discover.fieldChooser.availableFieldsTooltip": "Champs disponibles pour l'affichage dans le tableau.", "discover.fieldChooser.detailViews.emptyStringText": "Chaîne vide", @@ -2381,7 +2370,6 @@ "discover.grid.tableRow.viewSurroundingDocumentsLinkTextSimple": "Documents relatifs", "discover.grid.tableRow.viewText": "Afficher :", "discover.grid.viewDoc": "Afficher/Masquer les détails de la boîte de dialogue", - "discover.gridSampleSize.advancedSettingsLinkLabel": "Paramètres avancés", "discover.helpMenu.appName": "Découverte", "discover.inspectorRequestDataTitleDocuments": "Documents", "discover.inspectorRequestDescriptionDocument": "Cette requête interroge Elasticsearch afin de récupérer les documents.", @@ -2492,8 +2480,6 @@ "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataBody": "Les index correspondant au modèle {pattern} ne seront pas vérifiés, car une erreur s'est produite : {error}", "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataTitle": "Les index correspondant au modèle {pattern} ne seront pas vérifiés", "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingUnallowedValuesBody": "Un problème est survenu lors du chargement des valeurs non autorisées : {error}", - "ecsDataQualityDashboard.errorLoadingEcsMetadataLabel": "Erreur lors du chargement des métadonnées ECS : {details}", - "ecsDataQualityDashboard.errorLoadingEcsVersionLabel": "Erreur lors du chargement de la version ECS : {details}", "ecsDataQualityDashboard.errorLoadingIlmExplainLabel": "Erreur lors du chargement d'ILM Explain : {details}", "ecsDataQualityDashboard.errorLoadingMappingsLabel": "Erreur lors du chargement des mappings pour {patternOrIndexName} : {details}", "ecsDataQualityDashboard.errorLoadingStatsLabel": "Erreur lors du chargement des statistiques : {details}", @@ -2527,8 +2513,6 @@ "ecsDataQualityDashboard.cancelButton": "Annuler", "ecsDataQualityDashboard.checkAllButton": "Tout vérifier", "ecsDataQualityDashboard.coldDescription": "L'index n'est plus mis à jour et il est interrogé peu fréquemment. Les informations doivent toujours être interrogeables, mais il est acceptable que ces requêtes soient plus lentes.", - "ecsDataQualityDashboard.collapseButtonLabelClosed": "Fermé", - "ecsDataQualityDashboard.collapseButtonLabelOpen": "Ouvrir", "ecsDataQualityDashboard.compareFieldsTable.documentValuesActualColumn": "Valeurs du document (réelles)", "ecsDataQualityDashboard.compareFieldsTable.ecsDescriptionColumn": "Description ECS", "ecsDataQualityDashboard.compareFieldsTable.ecsMappingTypeColumn": "Type de mapping ECS", @@ -2650,7 +2634,6 @@ "ecsDataQualityDashboard.summaryTable.passedTooltip": "Approuvé", "ecsDataQualityDashboard.summaryTable.resultColumn": "Résultat", "ecsDataQualityDashboard.summaryTable.thisIndexHasNotBeenCheckedTooltip": "Cet index n'a pas été vérifié", - "ecsDataQualityDashboard.takeActionMenu.takeActionButton": "Entreprendre une action", "ecsDataQualityDashboard.technicalPreviewBadge": "Version d'évaluation technique", "ecsDataQualityDashboard.timestampDescriptionLabel": "Date/heure d'origine de l'événement. Il s'agit des date et heure extraites de l'événement, représentant généralement le moment auquel l'événement a été généré par la source. Si la source de l'événement ne comporte pas d'horodatage original, cette valeur est habituellement remplie la première fois que l'événement a été reçu par le pipeline. Champs requis pour tous les événements.", "ecsDataQualityDashboard.toasts.copiedErrorsToastTitle": "Erreurs copiées dans le presse-papiers", @@ -5822,7 +5805,6 @@ "unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel": "Appliquer la requête enregistrée", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "Annuler", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "Supprimer", - "unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel": "Remplacer par la requête enregistrée sélectionnée", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "Enregistrer en tant que nouvelle requête enregistrée", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "Enregistrer en tant que nouvelle", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "Enregistrer les modifications", @@ -6822,7 +6804,6 @@ "visualizations.missedDataView.errorMessage": "Impossible de trouver {type} : {id}", "visualizations.newChart.conditionalMessage.newLibrary": "Passer à la bibliothèque {type} dans {link}", "visualizations.newGaugeChart.notificationMessage": "La nouvelle bibliothèque de graphiques de jauge ne prend pas encore en charge l'agrégation de graphiques fractionnés. {conditionalMessage}", - "visualizations.newHeatmapChart.notificationMessage": "La nouvelle bibliothèque de graphiques de cartes thermiques ne prend pas encore en charge l'agrégation de graphiques fractionnés. {conditionalMessage}", "visualizations.newVisWizard.newVisTypeTitle": "Nouveau {visTypeName}", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, one {type trouvé} other {types trouvés}}", "visualizations.noMatchRoute.bannerText": "L'application Visualize ne reconnaît pas cet itinéraire : {route}.", @@ -7047,13 +7028,11 @@ "xpack.aiops.logCategorization.emptyPromptBody": "Loguez les messages de groupes d'analyse de modèle dans les catégories courantes.", "xpack.aiops.logCategorization.emptyPromptTitle": "Sélectionner un champ de texte et cliquez sur Exécuter la catégorisation pour démarrer l'analyse", "xpack.aiops.logCategorization.errorLoadingCategories": "Erreur lors du chargement des catégories", - "xpack.aiops.logCategorization.filterOutInDiscover": "Filtrer dans Discover", "xpack.aiops.logCategorization.noCategoriesBody": "Assurez-vous que le champ sélectionné est rempli dans la plage temporelle sélectionnée.", "xpack.aiops.logCategorization.noCategoriesTitle": "Aucune catégorie trouvée", "xpack.aiops.logCategorization.noDocsBody": "Assurez-vous que la plage temporelle sélectionnée contient des documents.", "xpack.aiops.logCategorization.noDocsTitle": "Aucun document trouvé", "xpack.aiops.logCategorization.runButton": "Exécuter la catégorisation", - "xpack.aiops.logCategorization.showInDiscover": "Les afficher dans Discover", "xpack.aiops.miniHistogram.noDataLabel": "S. O.", "xpack.aiops.progressAriaLabel": "Progression", "xpack.aiops.rerunAnalysisButtonTitle": "Relancer l’analyse", @@ -7113,11 +7092,8 @@ "xpack.apm.agentExplorerInstanceTable.noServiceNodeName.tooltip.linkToDocs": "Vous pouvez configurer le nom du nœud de service via {seeDocs}.", "xpack.apm.agentExplorerTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1 version} other {# versions}}", "xpack.apm.alerts.anomalySeverity.scoreDetailsDescription": "score {value} {value, select, critical {} other {et supérieur}}", - "xpack.apm.alertTypes.errorCount.reason": "Le nombre d'erreurs est {measured} dans le dernier {interval} pour {serviceName}. Alerte lorsque > {threshold}.", "xpack.apm.alertTypes.minimumWindowSize.description": "La valeur minimale recommandée est {sizeValue} {sizeUnit}. Elle permet de s'assurer que l'alerte comporte suffisamment de données à évaluer. Si vous choisissez une valeur trop basse, l'alerte ne se déclenchera peut-être pas comme prévu.", - "xpack.apm.alertTypes.transactionDuration.reason": "La latence de {aggregationType} est {measured} dans le dernier {interval} pour {serviceName}. Alerte lorsque > {threshold}.", "xpack.apm.alertTypes.transactionDurationAnomaly.reason": "Une anomalie {severityLevel} avec un score de {measured} a été détectée dans le dernier {interval} pour {serviceName}.", - "xpack.apm.alertTypes.transactionErrorRate.reason": "L'échec des transactions est {measured} dans le dernier {interval} pour {serviceName}. Alerte lorsque > {threshold}.", "xpack.apm.anomalyDetection.createJobs.failed.text": "Une erreur est survenue lors de la création d'une ou de plusieurs tâches de détection des anomalies pour les environnements de service APM [{environments}]. Erreur : \"{errorMessage}\"", "xpack.apm.anomalyDetection.createJobs.succeeded.text": "Tâches de détection des anomalies créées avec succès pour les environnements de service APM [{environments}]. Le démarrage de l'analyse du trafic à la recherche d'anomalies par le Machine Learning va prendre un certain temps.", "xpack.apm.anomalyDetectionSetup.notEnabledForEnvironmentText": "La détection des anomalies n'est pas encore activée pour l'environnement \"{currentEnvironment}\". Cliquez pour continuer la configuration.", @@ -7171,7 +7147,6 @@ "xpack.apm.serviceOveriew.errorsTableOccurrences": "{occurrences} occ.", "xpack.apm.serviceOverview.embeddedMap.error.toastDescription": "L'usine incorporable ayant l'ID \"{embeddableFactoryId}\" est introuvable.", "xpack.apm.serviceOverview.embeddedMap.subtitle": "Carte affichant le nombre total de {currentMap} en fonction du pays et de la région", - "xpack.apm.serviceOverview.lensFlyout.topValues": "{BUCKET_SIZE} valeurs les plus élevées de {metric}", "xpack.apm.serviceOverview.mobileCallOutText": "Il s'agit d'un service mobile, qui est actuellement disponible en tant que version d'évaluation technique. Vous pouvez nous aider à améliorer l'expérience en nous envoyant des commentaires. {feedbackLink}.", "xpack.apm.servicesTable.environmentCount": "{environmentCount, plural, one {1 environnement} other {# environnements}}", "xpack.apm.settings.agentKeys.apiKeysDisabledErrorDescription": "Contactez votre administrateur système et reportez-vous à {link} pour activer les clés d'API.", @@ -7553,7 +7528,6 @@ "xpack.apm.dataView.autoCreateDisabled": "La création automatique des vues de données a été désactivée via l'option de configuration \"autoCreateApmDataView\"", "xpack.apm.dataView.noApmData": "Aucune donnée APM", "xpack.apm.dependecyOperationDetailView.header.backLinkLabel": "Toutes les opérations", - "xpack.apm.dependencies.kueryBarPlaceholder": "Rechercher dans les indicateurs de dépendance (par ex., span.destination.service.resource:elasticsearch)", "xpack.apm.dependenciesInventory.dependencyTableColumn": "Dépendance", "xpack.apm.dependenciesTable.columnErrorRate": "Taux de transactions ayant échoué", "xpack.apm.dependenciesTable.columnErrorRateTip": "Le pourcentage de transactions ayant échoué pour le service sélectionné. Les transactions du serveur HTTP avec un code du statut 4xx (erreur du client) ne sont pas considérées comme des échecs, car l'appelant, et non le serveur, a provoqué l'échec.", @@ -8101,17 +8075,11 @@ "xpack.apm.serviceOverview.latencyColumnDefaultLabel": "Latence", "xpack.apm.serviceOverview.latencyColumnP95Label": "Latence (95e)", "xpack.apm.serviceOverview.latencyColumnP99Label": "Latence (99e)", - "xpack.apm.serviceOverview.lensFlyout.countRecords": "Nombre d'enregistrements", "xpack.apm.serviceOverview.loadingText": "Chargement…", "xpack.apm.serviceOverview.mobileCallOutLink": "Donner un retour", "xpack.apm.serviceOverview.mobileCallOutTitle": "APM mobile", - "xpack.apm.serviceOverview.mostUsed.appVersion": "Version de l'application", - "xpack.apm.serviceOverview.mostUsed.device": "Appareils", - "xpack.apm.serviceOverview.mostUsed.nct": "Type de connexion réseau", - "xpack.apm.serviceOverview.mostUsed.osVersion": "Version du système d'exploitation", "xpack.apm.serviceOverview.mostUsedTitle": "Le plus utilisé", "xpack.apm.serviceOverview.noResultsText": "Aucune instance trouvée", - "xpack.apm.serviceOverview.openInLens": "Ouvrir dans Lens", "xpack.apm.serviceOverview.throughtputChartTitle": "Rendement", "xpack.apm.serviceOverview.tpmHelp": "Le rendement est mesuré en transactions par minute (tpm).", "xpack.apm.serviceOverview.transactionsTableColumnErrorRate": "Taux de transactions ayant échoué", @@ -10639,8 +10607,6 @@ "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "Ajouter une intégration KSPM", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "En savoir plus sur le niveau de sécurité du cloud", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "Détectez les erreurs de configuration de sécurité dans vos ressources cloud !", - "xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "Échec des résultats", - "xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "Réussite des résultats", "xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "Afficher le tableau de bord CSP", "xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "Afficher les résultats CSP ", "xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "Afficher les règles CSP ", @@ -11145,8 +11111,6 @@ "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.caption": "Afficher l'index {indexName}", "xpack.enterpriseSearch.content.engineList.deleteEngine.successToast.title": "{engineName} a été supprimé", "xpack.enterpriseSearch.content.engines.createEngine.headerSubTitle": "Un moteur permet à vos utilisateurs d'interroger les données de vos index. Pour en savoir plus, explorez notre {enginesDocsLink} !", - "xpack.enterpriseSearch.content.engines.description": "Les moteurs vous permettent d'interroger les données indexées avec un ensemble complet d'outils de pertinence, d'analyse et de personnalisation. Pour en savoir plus sur le fonctionnement des moteurs dans Enterprise Search, {documentationUrl}", - "xpack.enterpriseSearch.content.engines.enginesList.description": "Affichage de {from}-{to} sur {total}", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.subTitle": "Afficher les index associés à {engineName}", "xpack.enterpriseSearch.content.enginesList.table.column.view.indices": "{indicesCount, number} {indicesCount, plural, other {index}}", "xpack.enterpriseSearch.content.index.connector.syncRules.description": "Ajoutez une règle de synchronisation pour personnaliser les données qui sont synchronisées à partir de {indexName}. Tout est inclus par défaut, et les documents sont validés par rapport à l'ensemble des règles d'indexation configurées, en commençant par le haut de la liste.", @@ -11176,17 +11140,10 @@ "xpack.enterpriseSearch.content.indices.pipelines.successToastDeleteMlPipeline.title": "Pipeline d'inférence de Machine Learning \"{pipelineName}\" supprimé", "xpack.enterpriseSearch.content.indices.pipelines.successToastDetachMlPipeline.title": "Pipeline d'inférence de Machine Learning détaché de \"{pipelineName}\"", "xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.unmanaged.description": "Modifier ce pipeline à partir de {ingestPipelines} dans Gestion de la Suite", - "xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description": "Recherchez dans votre contenu {name} avec Enterprise Search.", - "xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage": "Vous recherchez d'autres connecteurs ? {workplaceSearchLink} ou {buildYourOwnConnectorLink}.", - "xpack.enterpriseSearch.content.indices.selectConnector.successToast.title": "Votre index utilisera maintenant le connecteur natif {connectorName}.", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.alreadyExists.error": "Un index portant le nom {indexName} existe déjà", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.isInvalid.error": "{indexName} n'est pas un nom d'index valide", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineOne": "Votre index sera nommé : {indexName}", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description": "Un index supprimé appelé {indexName} était, à l'origine, lié à une configuration de connecteur. Voulez-vous remplacer cette configuration de connecteur par la nouvelle ?", - "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content": "À l'aide de notre infrastructure de connecteur et de nos exemples de clients de connecteur, vous pouvez accélérer l'ingestion vers Elasticsearch {bulkApiDocLink} pour n'importe quelle source de données. Une fois l'index créé, le système vous guidera pour accéder à l'infrastructure du connecteur et connecter votre premier client.", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content": "Une fois que vous avez créé votre connecteur, votre contenu est prêt. Créez votre première expérience de recherche avec {elasticsearchLink} ou bien explorez les outils d'expérience de recherche fournis par {appSearchLink}. Nous vous conseillons de créer un {searchEngineLink} pour bénéficier du meilleur équilibre entre puissance et simplicité.", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.content": "Fournissez un nom d'index unique et définissez éventuellement un {languageAnalyzerDocLink} pour l'index. Cet index contiendra le contenu de la source de données et il est optimisé avec les mappings de champ par défaut pour les expériences de recherche correspondantes.", - "xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content": "Faites votre choix dans notre catalogue de connecteurs natifs pour commencer à extraire un contenu interrogeable de sources de données prises en charge telles que MongoDB. Si vous devez personnaliser le comportement d'un connecteur, vous pouvez toujours déployer la version client du connecteur autogéré et l'enregistrer via le workflow {buildAConnectorLabel}.", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.description": "Affichage de {results} sur {total}. Nombre maximal de résultats de recherche de {maximum} documents.", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage": "Documents par page : {docPerPage}", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option": "{docCount} documents", @@ -12246,23 +12203,9 @@ "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "Terminé", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "Générer une clé en lecture seule", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "Créer une clé d'API en lecture seule pour le moteur", - "xpack.enterpriseSearch.content.engine.api.pageTitle": "API", - "xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "Elastic ne stocke pas les clés d’API. Une fois la clé générée, vous ne pourrez la visualiser qu'une seule fois. Veillez à l'enregistrer dans un endroit sûr. Si vous n'y avez plus accès, vous devrez générer une nouvelle clé d’API à partir de cet écran.", - "xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "Créer une clé d'API", - "xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "Découvrez plus d'informations sur les clés d'API.", - "xpack.enterpriseSearch.content.engine.api.step1.title": "Générer et enregistrer la clé d'API", - "xpack.enterpriseSearch.content.engine.api.step1.viewKeysButton": "Afficher les clés", - "xpack.enterpriseSearch.content.engine.api.step2.copyEndpointDescription": "Utilisez cette URL pour accéder aux points de terminaison d'API de votre moteur.", - "xpack.enterpriseSearch.content.engine.api.step2.title": "Copier le point de terminaison de votre moteur", "xpack.enterpriseSearch.content.engine.api.step3.curlTitle": "cURL", "xpack.enterpriseSearch.content.engine.api.step3.intro": "Découvrez comment effectuer l'intégration avec votre moteur à l'aide des clients de langage gérés par Elastic pour vous aider à créer votre expérience de recherche.", "xpack.enterpriseSearch.content.engine.api.step3.searchUITitle": "Search UI", - "xpack.enterpriseSearch.content.engine.api.step3.title": "Découvrir comment appeler vos points de terminaison", - "xpack.enterpriseSearch.content.engine.api.step4.copy": "Votre moteur fournit des données d'analyse de base dans le cadre de cette installation. Pour recevoir des indicateurs plus granulaires et personnalisés, intégrez notre script Behavioral Analytics dans votre plateforme.", - "xpack.enterpriseSearch.content.engine.api.step4.learnHowLink": "Découvrez comment faire", - "xpack.enterpriseSearch.content.engine.api.step4.title": "(Facultatif) Booster vos analyses", - "xpack.enterpriseSearch.content.engine.headerActions.actionsButton.ariaLabel": "Bouton de menu d'actions du moteur", - "xpack.enterpriseSearch.content.engine.headerActions.delete": "Supprimer ce moteur", "xpack.enterpriseSearch.content.engine.indices.actions.columnTitle": "Actions", "xpack.enterpriseSearch.content.engine.indices.actions.removeIndex.title": "Retirer cet index du moteur", "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.title": "Afficher cet index", @@ -12274,7 +12217,6 @@ "xpack.enterpriseSearch.content.engine.indices.docsCount.columnTitle": "Nombre de documents", "xpack.enterpriseSearch.content.engine.indices.health.columnTitle": "Intégrité des index", "xpack.enterpriseSearch.content.engine.indices.name.columnTitle": "Nom de l'index", - "xpack.enterpriseSearch.content.engine.indices.pageTitle": "Index", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.description": "L'index ne sera pas supprimé. Vous pourrez l'ajouter de nouveau à ce moteur ultérieurement.", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.text": "Oui, retirer cet index", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.title": "Retirer cet index du moteur", @@ -12282,12 +12224,10 @@ "xpack.enterpriseSearch.content.engine.indicesSelect.docsLabel": "Documents :", "xpack.enterpriseSearch.content.engine.schema.field_name.columnTitle": "Nom du champ", "xpack.enterpriseSearch.content.engine.schema.field_type.columnTitle": "Type du champ", - "xpack.enterpriseSearch.content.engine.schema.pageTitle": "Schéma", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title": "Oui, supprimer ce moteur ", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description": "La suppression de votre moteur ne pourra pas être annulée. Vos index ne seront pas affectés. ", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.title": "Supprimer définitivement ce moteur ?", "xpack.enterpriseSearch.content.engineList.table.column.actions.deleteEngineLabel": "Supprimer ce moteur", - "xpack.enterpriseSearch.content.engines.breadcrumb": "Moteurs", "xpack.enterpriseSearch.content.engines.createEngine.header.createError.title": "Erreur lors de la création du moteur", "xpack.enterpriseSearch.content.engines.createEngine.header.docsLink": "Documentation sur les moteurs", "xpack.enterpriseSearch.content.engines.createEngine.headerTitle": "Créer un moteur", @@ -12295,15 +12235,9 @@ "xpack.enterpriseSearch.content.engines.createEngine.nameEngine.title": "Nommer votre moteur", "xpack.enterpriseSearch.content.engines.createEngine.selectIndices.title": "Sélectionner les index", "xpack.enterpriseSearch.content.engines.createEngine.submit": "Créer ce moteur", - "xpack.enterpriseSearch.content.engines.createEngineButtonLabel": "Créer un moteur", - "xpack.enterpriseSearch.content.engines.documentation": "explorer notre documentation sur les moteurs", "xpack.enterpriseSearch.content.engines.enginesList.empty.description": "Lançons-nous dans la création de votre premier moteur.", "xpack.enterpriseSearch.content.engines.enginesList.empty.title": "Créer votre premier moteur", "xpack.enterpriseSearch.content.engines.indices.addIndicesFlyout.updateError.title": "Erreur lors de la mise à jour du moteur", - "xpack.enterpriseSearch.content.engines.searchBar.ariaLabel": "Moteurs de recherche", - "xpack.enterpriseSearch.content.engines.searchPlaceholder": "Moteurs de recherche", - "xpack.enterpriseSearch.content.engines.searchPlaceholder.description": "Localisez un moteur en fonction de son nom ou de ses index inclus.", - "xpack.enterpriseSearch.content.engines.title": "Moteurs", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.docsCount.columnTitle": "Nombre de documents", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.health.columnTitle": "Intégrité des index", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.name.columnTitle": "Nom de l'index", @@ -12360,7 +12294,6 @@ "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceDescription": "Supprimer automatiquement l'espace supplémentaire de vos documents", "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceLabel": "Réduire l'espace", "xpack.enterpriseSearch.content.index.pipelines.settings.runMlInferenceDescrition": "Améliorer vos données à l'aide de modèles de ML entraînés compatibles", - "xpack.enterpriseSearch.content.index.searchEngines.createEngine": "Créer un moteur App Search", "xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip": "Vous ne pouvez pas créer de moteurs à partir d'index masqués.", "xpack.enterpriseSearch.content.index.searchEngines.label": "Moteurs de recherche", "xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "Afficher les moteurs App Search", @@ -12611,12 +12544,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.message": "Message d'erreur", "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.timestamp": "Horodatage", "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.title": "Erreurs d'inférence", - "xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel": "créez-les vous-même", "xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel": "Documentation", "xpack.enterpriseSearch.content.indices.selectConnector.description": "Lancez-vous en sélectionnant le connecteur que vous souhaiteriez configurer pour extraire, indexer et synchroniser les données à partir de votre source de données dans votre index de recherche nouvellement créé.", - "xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel": "Sélectionner et configurer", "xpack.enterpriseSearch.content.indices.selectConnector.title": "Sélectionner un connecteur", - "xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel": "Afficher d'autres intégrations dans Workplace Search", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.attach": "Attacher", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create": "Créer un pipeline", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title": "Configurer", @@ -12642,42 +12572,19 @@ "xpack.enterpriseSearch.content.nativeConnectors.mongodb.name": "MongoDB", "xpack.enterpriseSearch.content.nativeConnectors.mysql.name": "MySQL", "xpack.enterpriseSearch.content.navTitle": "Contenu", - "xpack.enterpriseSearch.content.new_index.successToast.button.label": "Créer un moteur", - "xpack.enterpriseSearch.content.new_index.successToast.description": "Vous pouvez utiliser des moteurs App Search pour créer une expérience de recherche pour votre nouvel index Elasticsearch.", "xpack.enterpriseSearch.content.new_index.successToast.title": "Index créé avec succès", "xpack.enterpriseSearch.content.newIndex.breadcrumb": "Nouvel index de recherche", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description": "Ajouter des documents par programmation en se connectant avec l'API", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer": "Un peu de développement nécessaire", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label": "Utiliser l’API", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description": "Utiliser le cadre des connecteurs pour créer rapidement des connecteurs pour des sources de données personnalisées", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer": "Développement nécessaire", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label": "Créer un connecteur", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description": "Découvrir, extraire, indexer et synchroniser tout le contenu de votre site web", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer": "Aucun développement nécessaire", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label": "Utiliser le robot d'indexation", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description": "Configurer un connecteur pour extraire, indexer et synchroniser tout votre contenu des sources de données prises en charge ", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer": "Aucun développement nécessaire", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label": "Utiliser un connecteur", "xpack.enterpriseSearch.content.newIndex.emptyState.description": "Les données que vous ajoutez dans Enterprise Search sont appelées \"index de recherche\", et vous pouvez effectuer des recherches à l'intérieur à la fois dans App Search et dans Workplace Search. Maintenant, vous pouvez utiliser vos connecteurs dans App Search et vos robots d'indexation dans Workplace Search.", "xpack.enterpriseSearch.content.newIndex.emptyState.footer.link": "Lisez les documents", "xpack.enterpriseSearch.content.newIndex.emptyState.footer.title": "Vous souhaitez en savoir plus sur les index de recherche ?", "xpack.enterpriseSearch.content.newIndex.emptyState.title": "Sélectionner une méthode d'ingestion", - "xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content": "Générez une clé d’API et consultez la documentation pour envoyer des documents au point de terminaison de l’API Elasticsearch. Des clients linguistiques sont disponibles pour une intégration simplifiée.", - "xpack.enterpriseSearch.content.newIndex.methodApi.title": "Indexer avec l’API", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink": "API Bulk", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title": "Créer et configurer un connecteur", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title": "Configurer un connecteur", - "xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content": "Configurez les domaines que vous souhaitez indexer et, lorsque vous êtes prêt, déclenchez votre première indexation. Laissez Enterprise Search faire le reste.", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.createIndex.buttonText": "Créer un index", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputHelpText": "La langue peut être modifiée ultérieurement, mais ce changement peut nécessiter une réindexation.", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputLabel": "Analyseur linguistique", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineTwo": "Les noms doivent être en minuscules et ne peuvent pas contenir d'espaces ni de caractères spéciaux.", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel": "Nom de l'index", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder": "Définir un nom pour votre index", - "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText": "Consulter la documentation", "xpack.enterpriseSearch.content.newIndex.pageTitle": "Nouvel index de recherche", - "xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description": "Créez un index Elasticsearch optimisé pour la recherche en sélectionnant une méthode d'ingestion adaptée à votre cas d'utilisation.", - "xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title": "Sélectionner une méthode d'ingestion", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label": "Annuler", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label": "Remplacer la configuration", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title": "Remplacer un connecteur existant", @@ -12685,16 +12592,6 @@ "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.genericError": "Nous n'avons pas pu créer votre index", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.indexAlreadyExists": "L'index existe déjà.", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.unauthorizedError": "Vous n'êtes pas autorisé à créer ce connecteur", - "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title": "Créer un connecteur", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink": "App Search", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink": "Elasticsearch", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink": "moteur de recherche", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title": "Créer une expérience de recherche", - "xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title": "Configurer les paramètres d’ingestion", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title": "Indexer avec le robot d'indexation", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink": "analyseur linguistique", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.title": "Créer un index Elasticsearch", - "xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title": "Indexer à l'aide d'un connecteur", "xpack.enterpriseSearch.content.newIndex.types.api": "Point de terminaison d'API", "xpack.enterpriseSearch.content.newIndex.types.connector": "Connecteur", "xpack.enterpriseSearch.content.newIndex.types.crawler": "Robot d'indexation", @@ -12800,7 +12697,6 @@ "xpack.enterpriseSearch.content.searchIndices.syncStatus.columnTitle": "Statut", "xpack.enterpriseSearch.content.settings.breadcrumb": "Paramètres", "xpack.enterpriseSearch.content.settings.contactExtraction.label": "Extraction du contenu", - "xpack.enterpriseSearch.content.settings.contactExtraction.link": "En savoir plus sur l'extraction de contenu", "xpack.enterpriseSearch.content.settings.contentExtraction.description": "Autoriser tous les mécanismes d'ingestion de votre déploiement Enterprise Search à extraire le contenu interrogeable des fichiers binaires tels que les documents PDF et Word. Ce paramètre s'applique à tous les nouveaux index Elasticsearch créés par un mécanisme d'ingestion Enterprise Search.", "xpack.enterpriseSearch.content.settings.contentExtraction.descriptionTwo": "Vous pouvez également activer ou désactiver cette fonctionnalité pour un index spécifique sur la page de configuration de l'index.", "xpack.enterpriseSearch.content.settings.contentExtraction.title": "Extraction de contenu de l'ensemble du déploiement", @@ -12814,7 +12710,6 @@ "xpack.enterpriseSearch.content.settings.whitespaceReduction.deploymentHeaderTitle": "Réduction d'espaces sur l'ensemble du déploiement", "xpack.enterpriseSearch.content.settings.whiteSpaceReduction.description": "La réduction d'espaces supprimera le contenu de texte intégral des espaces par défaut.", "xpack.enterpriseSearch.content.settings.whitespaceReduction.label": "Réduction d'espaces", - "xpack.enterpriseSearch.content.settings.whitespaceReduction.link": "En savoir plus sur la réduction d'espaces", "xpack.enterpriseSearch.content.shared.result.header.metadata.deleteDocument": "Supprimer le document", "xpack.enterpriseSearch.content.shared.result.header.metadata.title": "Métadonnées du document", "xpack.enterpriseSearch.content.sources.basicRulesTable.includeEverythingMessage": "Inclure tout le reste à partir de cette source", @@ -13095,8 +12990,6 @@ "xpack.enterpriseSearch.emailLabel": "E-mail", "xpack.enterpriseSearch.emptyState.description": "Votre contenu est stocké dans un index Elasticsearch. Commencez par créer un index Elasticsearch et sélectionnez une méthode d'ingestion. Les options comprennent le robot d'indexation Elastic, les intégrations de données tierces ou l'utilisation des points de terminaison d'API Elasticsearch.", "xpack.enterpriseSearch.emptyState.description.line2": "Qu’il s’agisse de créer une expérience de recherche avec App Search ou Elasticsearch, vous pouvez commencer ici.", - "xpack.enterpriseSearch.engines.engine.notFound.action1": "Retour aux moteurs", - "xpack.enterpriseSearch.engines.navTitle": "Moteurs", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.description": "Effectuez des recherches sur tout, partout. Offrez à vos équipes débordées une expérience de recherche innovante et puissante facilement mise en œuvre. Intégrez rapidement une fonction de recherche préréglée à votre site web, à votre application ou à votre lieu de travail. Effectuez des recherches simples sur tout.", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.notConfigured": "Enterprise Search n'est pas encore configuré dans votre instance Kibana.", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt": "Premiers pas avec Enterprise Search", @@ -13184,9 +13077,6 @@ "xpack.enterpriseSearch.nav.contentSettingsTitle": "Paramètres", "xpack.enterpriseSearch.nav.contentTitle": "Contenu", "xpack.enterpriseSearch.nav.elasticsearchTitle": "Elasticsearch", - "xpack.enterpriseSearch.nav.engine.apiTitle": "API", - "xpack.enterpriseSearch.nav.engine.indicesTitle": "Index", - "xpack.enterpriseSearch.nav.engine.schemaTitle": "Schéma", "xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle": "Aperçu", "xpack.enterpriseSearch.nav.searchExperiencesTitle": "Expériences de recherche", "xpack.enterpriseSearch.nav.searchIndicesTitle": "Index", @@ -17427,29 +17317,19 @@ "xpack.infra.hostsViewPage.landing.introTitle": "Présentation : Analyse de l'hôte", "xpack.infra.hostsViewPage.landing.learnMore": "En savoir plus", "xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "Il s'agit d'une version préliminaire de la fonctionnalité, et nous souhaiterions vivement connaître votre avis tandis que nous continuons\n à la développer et à l'améliorer. Pour accéder à cette fonctionnalité, il suffit de l'activer ci-dessous. Ne passez pas à côté\n de cette nouvelle et puissante fonctionnalité ajoutée à notre plateforme... Essayez-la aujourd'hui même !", - "xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title": "Utilisation CPU sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.cpu.subtitle": "Moyenne", "xpack.infra.hostsViewPage.metricTrend.cpu.title": "Utilisation CPU", "xpack.infra.hostsViewPage.metricTrend.cpu.tooltip": "Moyenne de pourcentage de temps CPU utilisé dans les états autres que Inactif et IOWait, normalisée par le nombre de cœurs de processeur. Inclut le temps passé à la fois sur l'espace utilisateur et sur l'espace du noyau. 100 % signifie que tous les processeurs de l'hôte sont occupés.", - "xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title": "Utilisation CPU sur la durée.", "xpack.infra.hostsViewPage.metricTrend.hostCount.title": "Hôtes", "xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "Nombre d'hôtes renvoyé par vos critères de recherche actuels.", - "xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle": "Utilisation de la mémoire sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.memory.subtitle": "Moyenne", "xpack.infra.hostsViewPage.metricTrend.memory.title": "Utilisation mémoire", "xpack.infra.hostsViewPage.metricTrend.memory.tooltip": "Moyenne de pourcentage d'utilisation de la mémoire principale, en excluant le cache de pages. Cela inclut la mémoire résidente pour tous les processus, plus la mémoire utilisée par les structures et le code du noyau, à l'exception du cache de pages. Un niveau élevé indique une situation de saturation de la mémoire pour un hôte. 100 % signifie que la mémoire principale est entièrement remplie par de la mémoire ne pouvant pas être récupérée, sauf en l'échangeant.", - "xpack.infra.hostsViewPage.metricTrend.rx.a11y.description": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.rx.a11y.title": "Réseau entrant (RX) sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.rx.subtitle": "Moyenne", "xpack.infra.hostsViewPage.metricTrend.rx.title": "Réseau entrant (RX)", "xpack.infra.hostsViewPage.metricTrend.rx.tooltip": "Nombre d'octets qui ont été reçus par seconde sur les interfaces publiques des hôtes.", - "xpack.infra.hostsViewPage.metricTrend.tx.a11.title": "Utilisation de réseau sortant (TX) sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.tx.a11y.description": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.", - "xpack.infra.hostsViewPage.metricTrend.tx.subtitle": "Moyenne", "xpack.infra.hostsViewPage.metricTrend.tx.title": "Réseau sortant (TX)", "xpack.infra.hostsViewPage.metricTrend.tx.tooltip": "Nombre d'octets qui ont été envoyés par seconde sur les interfaces publiques des hôtes", + "xpack.infra.hostsViewPage.metricTrend.subtitle.average": "Moyenne", + "xpack.infra.hostsViewPage.metricTrend.subtitle.average.limit": "Moyenne (of {limit} hosts)", + "xpack.infra.hostsViewPage.metricTrend.subtitle.hostCount.limit": "Limited to {limit}", "xpack.infra.hostsViewPage.table.averageMemoryTotalColumnHeader": "Total de la mémoire (moy.)", "xpack.infra.hostsViewPage.table.averageMemoryUsageColumnHeader": "Utilisation de la mémoire (moy.)", "xpack.infra.hostsViewPage.table.averageRxColumnHeader": "RX (moy.)", @@ -19537,10 +19417,7 @@ "xpack.lens.indexPattern.emptyFieldsLabel": "Champs vides", "xpack.lens.indexPattern.enableAccuracyMode": "Activer le mode de précision", "xpack.lens.indexPattern.fieldExploreInDiscover": "Explorer dans Discover", - "xpack.lens.indexPattern.fieldItemTooltip": "Effectuez un glisser-déposer pour visualiser.", "xpack.lens.indexPattern.fieldPlaceholder": "Champ", - "xpack.lens.indexPattern.fieldStatsButtonEmptyLabel": "Ce champ ne comporte aucune donnée mais vous pouvez toujours effectuer un glisser-déposer pour visualiser.", - "xpack.lens.indexPattern.fieldStatsButtonLabel": "Cliquez pour obtenir un aperçu du champ, ou effectuez un glisser-déposer pour visualiser.", "xpack.lens.indexPattern.fieldStatsNoData": "Lens ne peut pas créer de visualisation avec ce champ, car il ne contient pas de données. Pour créer une visualisation, glissez-déposez un autre champ.", "xpack.lens.indexPattern.filterBy.clickToEdit": "Cliquer pour modifier", "xpack.lens.indexPattern.filterBy.emptyFilterQuery": "(vide)", @@ -20416,15 +20293,11 @@ "xpack.maps.addLayerPanel.addLayer": "Ajouter un calque", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "Changer de calque", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "Annuler", - "xpack.maps.aggs.defaultCountLabel": "compte", "xpack.maps.attribution.addBtnAriaLabel": "Ajouter une attribution", "xpack.maps.attribution.addBtnLabel": "Ajouter une attribution", - "xpack.maps.attribution.applyBtnLabel": "Appliquer", "xpack.maps.attribution.attributionFormLabel": "Attribution", "xpack.maps.attribution.clearBtnAriaLabel": "Effacer l'attribution", - "xpack.maps.attribution.clearBtnLabel": "Effacer", "xpack.maps.attribution.editBtnAriaLabel": "Modifier l'attribution", - "xpack.maps.attribution.editBtnLabel": "Modifier", "xpack.maps.attribution.labelFieldLabel": "Étiquette", "xpack.maps.attribution.urlLabel": "Lien", "xpack.maps.badge.readOnly.text": "Lecture seule", @@ -20661,7 +20534,6 @@ "xpack.maps.mapActions.removeFeatureError": "Impossible de retirer la fonctionnalité de l’index.", "xpack.maps.mapListing.entityName": "carte", "xpack.maps.mapListing.entityNamePlural": "cartes", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "Impossible de charger les cartes", "xpack.maps.mapSavedObjectLabel": "Carte", "xpack.maps.mapSettingsPanel.addCustomIcon": "Ajouter une icône personnalisée", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "Ajuster automatiquement la carte aux limites de données", @@ -20697,15 +20569,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "Champ", "xpack.maps.metricsEditor.selectFieldPlaceholder": "Sélectionner un champ", "xpack.maps.metricsEditor.selectPercentileLabel": "Centile", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "Moyenne", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "Compte unique", - "xpack.maps.metricSelect.countDropDownOptionLabel": "Décompte", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "Max.", - "xpack.maps.metricSelect.minDropDownOptionLabel": "Min.", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "Centile", "xpack.maps.metricSelect.selectAggregationPlaceholder": "Sélectionner une agrégation", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "Somme", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "Premier terme", "xpack.maps.mvtSource.addFieldLabel": "Ajouter", "xpack.maps.mvtSource.fieldPlaceholderText": "Nom du champ", "xpack.maps.mvtSource.numberFieldLabel": "numéro", @@ -21515,7 +21379,6 @@ "xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomAggregationIntervalLabel": "(intervalle d'agrégation : {focusAggInt}, étendue du compartiment : {bucketSpan})", "xpack.ml.trainedModels.modelsList.deleteModal.header": "Supprimer {modelsCount, plural, one {{modelId}} other {# modèles}} ?", "xpack.ml.trainedModels.modelsList.fetchDeletionErrorMessage": "La suppression {modelsCount, plural, one {du modèle} other {des modèles}} a échoué", - "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "Arrêter le modèle {modelId} ?", "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, one {# modèle sélectionné} other {# modèles sélectionnés}}", "xpack.ml.trainedModels.modelsList.startDeployment.modalTitle": "Démarrer le déploiement de {modelId}", "xpack.ml.trainedModels.modelsList.startFailed": "Impossible de démarrer \"{modelId}\"", @@ -26591,9 +26454,6 @@ "xpack.reporting.diagnostic.browserMissingFonts": "Le navigateur n'a pas réussi à localiser de police par défaut. Consultez {url} pour corriger ce problème.", "xpack.reporting.diagnostic.noUsableSandbox": "Impossible d'utiliser la sandbox Chromium. Vous pouvez la désactiver à vos risques et périls avec \"xpack.screenshotting.browser.chromium.disableSandbox\". Veuillez consulter {url}", "xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "Impossible de déchiffrer les données de la tâche de reporting. Veuillez vous assurer que {encryptionKey} est défini et générez à nouveau ce rapport. {err}", - "generateCsv.esErrorMessage": "Réponse {statusCode} reçue d'Elasticsearch : {message}", - "generateCsv.incorrectRowCount": "Une erreur a été rencontrée avec le nombre de lignes CSV générées à partir de la recherche : {expected} prévues, {received} reçues.", - "generateCsv.unknownErrorMessage": "Une erreur inconnue est survenue : {message}", "xpack.reporting.jobResponse.errorHandler.notAuthorized": "Désolé, vous n'êtes pas autorisé à afficher ou supprimer les rapports {jobtype}", "xpack.reporting.jobsQuery.deleteError": "Impossible de supprimer le rapport : {error}", "xpack.reporting.jobStatusDetail.attemptXofY": "Tentative {attempts} sur {max_attempts}.", @@ -26650,9 +26510,6 @@ "xpack.reporting.diagnostic.screenshotFailureMessage": "Impossible d'effectuer une capture d'écran de votre installation Kibana.", "xpack.reporting.errorHandler.unknownError": "Erreur inconnue", "xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "Les en-têtes de tâche sont manquants", - "generateCsv.authenticationExpired.partialResultsMessage": "Ce rapport contient des résultats CSV partiels, car le token d'authentification a expiré. Exportez une quantité moindre de données ou augmentez le délai d'expiration du token d'authentification.", - "generateCsv.csvUnableToClosePit": "Impossible de fermer le point temporel utilisé pour la recherche. Vérifiez les logs de serveur Kibana.", - "generateCsv.escapedFormulaValues": "Le CSV peut contenir des formules dont les valeurs sont précédées d'un caractère d'échappement", "xpack.reporting.jobCreatedBy.unknownUserPlaceholderText": "Inconnu", "xpack.reporting.jobResponse.errorHandler.unknownError": "Erreur inconnue", "xpack.reporting.jobStatusDetail.deprecatedText": "Il s'agit d'un type d'exportation déclassé. L'automatisation de ce rapport devra être à nouveau créée pour une question de compatibilité avec les futures versions de Kibana.", @@ -26789,6 +26646,12 @@ "xpack.reporting.uiSettings.validate.customLogo.badFile": "Désolé, ce fichier ne convient pas. Veuillez essayer un autre fichier image.", "xpack.reporting.uiSettings.validate.customLogo.tooLarge": "Désolé, ce fichier est trop volumineux. Le fichier image doit être inférieur à 200 kilo-octets.", "xpack.reporting.userAccessError.learnMoreLink": "En savoir plus", + "generateCsv.esErrorMessage": "Réponse {statusCode} reçue d'Elasticsearch : {message}", + "generateCsv.incorrectRowCount": "Une erreur a été rencontrée avec le nombre de lignes CSV générées à partir de la recherche : {expected} prévues, {received} reçues.", + "generateCsv.unknownErrorMessage": "Une erreur inconnue est survenue : {message}", + "generateCsv.authenticationExpired.partialResultsMessage": "Ce rapport contient des résultats CSV partiels, car le token d'authentification a expiré. Exportez une quantité moindre de données ou augmentez le délai d'expiration du token d'authentification.", + "generateCsv.csvUnableToClosePit": "Impossible de fermer le point temporel utilisé pour la recherche. Vérifiez les logs de serveur Kibana.", + "generateCsv.escapedFormulaValues": "Le CSV peut contenir des formules dont les valeurs sont précédées d'un caractère d'échappement", "xpack.rollupJobs.create.errors.dateHistogramIntervalInvalidCalendarIntervalSuggestion": "1{unit}", "xpack.rollupJobs.create.errors.idSameAsCloned": "Le nom doit être différent du nom cloné : \"{clonedId}\".", "xpack.rollupJobs.create.errors.indexPatternIllegalCharacters": "Supprimez les caractères {characterList} de votre modèle d'indexation.", @@ -27442,7 +27305,6 @@ "xpack.security.management.editRole.featureTable.featureVisibilityTitle": "Personnaliser les privilèges des fonctionnalités", "xpack.security.management.editRole.featureTable.managementCategoryHelpText": "L'accès à Stack Management est déterminé à la fois par les privilèges Elasticsearch et les privilèges Kibana, et il ne peut pas être explicitement désactivé.", "xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "La fonctionnalité possède des privilèges de sous-fonctionnalités personnalisés. Développez cette ligne pour en savoir plus.", - "xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "Supprimer un privilège d'index", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryEditorAriaLabel": "Éditeur de requêtes des documents accordés", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "Requête des documents accordés", "xpack.security.management.editRole.indexPrivilegeForm.grantReadPrivilegesLabel": "Accorder des privilèges de lecture à des documents spécifiques", @@ -27926,7 +27788,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "Désactivation réussie de {totalRules, plural, =1 {{totalRules} règle} other {{totalRules} règles}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "Impossible de dupliquer {rulesCount, plural, =1 {# règle} other {# règles}}.", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "Vous dupliquez {rulesCount, plural, one {# règle sélectionnée} other {# règles sélectionnées}}. Veuillez choisir comment dupliquer les exceptions existantes", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "Dupliquer {rulesCount, plural, one {la règle} other {les règles}} avec les exceptions ?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "Dupliquer {rulesCount, plural, one {la règle et ses} other {les règles et leurs}} exceptions", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "Dupliquer uniquement {rulesCount, plural, one {la règle} other {les règles}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "Duplication réussie de {totalRules, plural, =1 {{totalRules} règle} other {{totalRules} règles}}", @@ -27995,7 +27856,6 @@ "xpack.securitySolution.endpoint.hostIsolation.successfulIsolation.cases": "Cette action a été attachée {caseCount, plural, one {au cas suivant} other {aux cas suivants}} :", "xpack.securitySolution.endpoint.hostIsolation.unisolate.successfulMessage": "La libération de l'hôte {hostName} a été soumise avec succès", "xpack.securitySolution.endpoint.hostIsolation.unIsolateThisHost": "{hostName} est actuellement {isolated}. Voulez-vous vraiment {unisolate} cet hôte ?", - "xpack.securitySolution.endpoint.hostIsolationStatus.multiplePendingActions": "{count} {count, plural, one {action} other {actions}} en attente", "xpack.securitySolution.endpoint.list.hostStatusValue": "{hostStatus, select, healthy {Sain} unhealthy {Défectueux} updating {En cours de mise à jour} offline {Hors ligne} inactive {Inactif} unenrolled {Désinscrit} other {Défectueux}}", "xpack.securitySolution.endpoint.list.policy.revisionNumber": "rév. {revNumber}", "xpack.securitySolution.endpoint.list.totalCount": "Affichage de {totalItemCount, plural, one {# point de terminaison} other {# points de terminaison}}", @@ -28071,7 +27931,6 @@ "xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle": "Nom de {riskEntity}", "xpack.securitySolution.entityAnalytics.riskDashboard.riskClassificationTitle": "Classification de risque de {riskEntity}", "xpack.securitySolution.entityAnalytics.riskDashboard.riskToolTip": "La classification de risque de {riskEntity} est déterminée par le score de risque de {riskEntityLowercase}. Les {riskEntity} classées comme Critique ou Élevée sont indiquées comme étant à risque.", - "xpack.securitySolution.event.reason.reasonRendererTitle": "Outils de rendu d'événement : {eventRendererName} ", "xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel": "Le champ {field} est un objet, et il est composé de champs imbriqués qui peuvent être ajoutés en tant que colonne", "xpack.securitySolution.eventDetails.viewColumnCheckboxAriaLabel": "Afficher la colonne {field}", "xpack.securitySolution.eventFilter.flyoutForm.creationSuccessToastTitle": "\"{name}\" a été ajouté à la liste de filtres d'événements.", @@ -29119,9 +28978,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "Sélectionner des index de menaces", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "Au minimum un modèle d'indexation est requis.", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "Tous les résultats", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "Sélectionnez le moment auquel les actions automatiques doivent être effectuées si une règle est évaluée comme vraie.", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "Sélectionnez le moment auquel les actions automatiques doivent être effectuées si une règle est évaluée comme vraie. Cette fréquence ne s'applique pas aux actions de réponse.", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "Fréquence des actions", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "Impossible de créer des actions de règle. Vous ne disposez pas des autorisations \"Lire\" pour le plug-in \"Actions\".", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "Créer et activer la règle", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "Créer la règle sans l’activer", @@ -30079,12 +29935,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " Si vous dupliquez les exceptions, la liste des exceptions partagée sera dupliquée par référence et l'exception de la règle par défaut sera copiée et créée comme une nouvelle exception", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "Règles dupliquées", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "Dupliquer", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "La fréquence des actions que vous sélectionnez ci-dessous est appliquée à toutes les actions (nouvelles et existantes) pour toutes les règles sélectionnées.", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "Ajouter des actions sur les règles", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "Écraser toutes les actions sur les règles sélectionnées", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "Les variables de règle peuvent affecter uniquement certaines règles sélectionnées, en fonction des types de règle (par exemple, \\u007b\\u007bcontext.rule.threshold\\u007d\\u007d affichera uniquement les valeurs des règles de seuil).", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "Sélectionnez le moment auquel les actions automatiques doivent être effectuées si une règle est évaluée comme vraie.", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "Fréquence des actions", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "Enregistrer", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "Appliquer le modèle de chronologie", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "Aucun", @@ -30395,15 +30248,6 @@ "xpack.securitySolution.endpoint.hostisolation.unisolate": "libération", "xpack.securitySolution.endpoint.hostIsolation.unisolateHost": "Libérer l'hôte", "xpack.securitySolution.endpoint.hostIsolationExceptions.fleetIntegration.title": "Exceptions d'isolation de l'hôte", - "xpack.securitySolution.endpoint.hostIsolationStatus.isIsolating": "Isolation", - "xpack.securitySolution.endpoint.hostIsolationStatus.isolated": "Isolé", - "xpack.securitySolution.endpoint.hostIsolationStatus.isUnIsolating": "Libération", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingActions": "Actions en attente :", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingIsolate": "Isoler", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingKillProcess": "Arrêter le processus", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingRunningProcesses": "Processus", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingSuspendProcess": "Suspendre le processus", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingUnIsolate": "Libération", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.environments": "protéger vos points de terminaison traditionnels ou vos environnements cloud dynamiques", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.seeDocumentationLink": "documentation", "xpack.securitySolution.endpoint.list.actionmenu": "Ouvrir", @@ -31004,10 +30848,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "Fermer", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "Sélectionner ou glisser-déposer plusieurs fichiers", "xpack.securitySolution.exceptions.exceptionListsImportButton": "Importer la liste", - "xpack.securitySolution.exceptions.exportModalCancelButton": "Annuler", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "Exporter", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "Inclure les exceptions ayant expiré", - "xpack.securitySolution.exceptions.exportModalTitle": "Exporter la liste d'exceptions", "xpack.securitySolution.exceptions.fetchError": "Erreur lors de la récupération de la liste d'exceptions", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "Erreur lors de la récupération des références d'exceptions", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "Supprimer une exception à une règle", @@ -31219,7 +31059,6 @@ "xpack.securitySolution.host.details.overview.platformTitle": "Plateforme", "xpack.securitySolution.host.details.overview.regionTitle": "Région", "xpack.securitySolution.host.details.versionLabel": "Version", - "xpack.securitySolution.hostIsolation.agentStatuses.empty": "-", "xpack.securitySolution.hostIsolationExceptions.cardActionDeleteLabel": "Supprimer l'exception", "xpack.securitySolution.hostIsolationExceptions.cardActionEditLabel": "Modifier l'exception", "xpack.securitySolution.hostIsolationExceptions.deleteModtalTitle": "Supprimer l'exception d'isolation de l'hôte", @@ -32448,7 +32287,6 @@ "xpack.sessionView.metadataDetailsTab.cloud": "Cloud", "xpack.sessionView.metadataDetailsTab.container": "Conteneur", "xpack.sessionView.metadataDetailsTab.host": "Système d'exploitation de l'hôte", - "xpack.sessionView.metadataDetailsTab.metadata": "Métadonnées", "xpack.sessionView.metadataDetailsTab.orchestrator": "Orchestrateur", "xpack.sessionView.networkTooltip": "Alerte réseau", "xpack.sessionView.output": "Sortie", @@ -34602,8 +34440,6 @@ "xpack.synthetics.coreVitals.fcpTooltip": "First Contentful Paint (FCP) se concentre sur le rendu initial et mesure la durée entre le début du chargement d'une page et le moment où une partie du contenu de la page s'affiche à l'écran.", "xpack.synthetics.coreVitals.lcp.help": "Largest Contentful Paint mesure les performances de chargement. Pour offrir une expérience agréable aux utilisateurs, le LCP doit survenir dans les 2,5 secondes du début de chargement de la page.", "xpack.synthetics.createMonitor.pageHeader.title": "Créer le moniteur", - "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalLabel": "Préversion technique", - "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalTooltip": "Prévisualisez la méthode la plus rapide permettant de créer des scripts de monitoring Elastic Synthetics avec notre enregistreur Elastic Synthetics", "xpack.synthetics.createPackagePolicy.stepConfigure.headerField.addHeader.label": "Ajouter un en-tête", "xpack.synthetics.createPackagePolicy.stepConfigure.inputVarFieldOptionalLabel": "Facultatif", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.closeButtonLabel": "Fermer le menu volant du script", @@ -34929,7 +34765,6 @@ "xpack.synthetics.monitorConfig.textAssertion.label": "Assertion de texte", "xpack.synthetics.monitorConfig.throttling.helpText": "Simulez la régulation du réseau (téléchargement, chargement, latence). D'autres options seront ajoutées dans une prochaine version.", "xpack.synthetics.monitorConfig.throttling.label": "Profil de connexion", - "xpack.synthetics.monitorConfig.throttling.options.default": "Par défaut", "xpack.synthetics.monitorConfig.timeout.formatError": "Le délai d'expiration n'est pas valide.", "xpack.synthetics.monitorConfig.timeout.greaterThan0Error": "Le délai d'expiration doit être supérieur ou égal à 0.", "xpack.synthetics.monitorConfig.timeout.helpText": "Temps total autorisé pour tester la connexion et l'échange de données.", @@ -34967,7 +34802,6 @@ "xpack.synthetics.monitorDetails.statusBar.pingType.icmp": "ICMP", "xpack.synthetics.monitorDetails.statusBar.pingType.tcp": "TCP", "xpack.synthetics.monitorDetails.summary.availability": "Disponibilité", - "xpack.synthetics.monitorDetails.summary.avgDuration": "Durée moy.", "xpack.synthetics.monitorDetails.summary.brushArea": "Brosser une zone pour une plus haute fidélité", "xpack.synthetics.monitorDetails.summary.complete": "Terminé", "xpack.synthetics.monitorDetails.summary.duration": "Durée", @@ -35073,12 +34907,9 @@ "xpack.synthetics.monitorManagement.apiKey.label": "Clé d'API", "xpack.synthetics.monitorManagement.apiKeyWarning.label": "Cette clé d’API ne sera affichée qu'une seule fois. Veuillez en conserver une copie pour vos propres dossiers.", "xpack.synthetics.monitorManagement.areYouSure": "Voulez-vous vraiment supprimer cet emplacement ?", - "xpack.synthetics.monitorManagement.callout.apiKeyMissing": "La Gestion des moniteurs est actuellement désactivée en raison d'une clé d'API manquante", "xpack.synthetics.monitorManagement.callout.description.disabled": "La Gestion des moniteurs est actuellement désactivée. Pour exécuter vos moniteurs sur le service Synthetics géré par Elastic, activez la Gestion des moniteurs. Vos moniteurs existants ont été suspendus.", - "xpack.synthetics.monitorManagement.callout.description.invalidKey": "La Gestion des moniteurs est actuellement désactivée. Pour exécuter vos moniteurs dans l'un des emplacements de tests gérés globaux d'Elastic, vous devez ré-activer la Gestion des moniteurs.", "xpack.synthetics.monitorManagement.callout.disabled": "La Gestion des moniteurs est désactivée", "xpack.synthetics.monitorManagement.callout.disabled.adminContact": "Veuillez contacter votre administrateur pour activer la Gestion des moniteurs.", - "xpack.synthetics.monitorManagement.callout.disabledCallout.invalidKey": "Contactez votre administrateur pour activer la Gestion des moniteurs.", "xpack.synthetics.monitorManagement.cancelLabel": "Annuler", "xpack.synthetics.monitorManagement.cannotSaveIntegration": "Vous n'êtes pas autorisé à mettre à jour les intégrations. Des autorisations d'écriture pour les intégrations sont requises.", "xpack.synthetics.monitorManagement.closeButtonLabel": "Fermer", @@ -35099,8 +34930,6 @@ "xpack.synthetics.monitorManagement.editMonitorError": "Erreur lors du chargement de la liste Gestion des moniteurs", "xpack.synthetics.monitorManagement.editMonitorError.description": "Les paramètres de Gestion des moniteurs n'ont pas pu être chargés. Veuillez contacter le support technique.", "xpack.synthetics.monitorManagement.emptyState.enablement": "Activez la Gestion des moniteurs pour exécuter des moniteurs légers et basés sur un navigateur réel à partir d'emplacements de test hébergés dans le monde entier. L'activation de la Gestion des moniteurs générera une clé d'API pour autoriser le service Synthetics à mettre à jour votre cluster Elasticsearch.", - "xpack.synthetics.monitorManagement.emptyState.enablement.disabled.title": "La Gestion des moniteurs est désactivée", - "xpack.synthetics.monitorManagement.emptyState.enablement.disabledDescription": "La Gestion des moniteurs est actuellement désactivée. La Gestion des moniteurs vous permet d'exécuter des moniteurs légers et basés sur un navigateur réel à partir d'emplacements de test hébergés dans le monde entier. Pour activer la Gestion des moniteurs, veuillez contacter un administrateur.", "xpack.synthetics.monitorManagement.emptyState.enablement.doc": "Lisez les documents", "xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title": "Activer la Gestion des moniteurs", "xpack.synthetics.monitorManagement.emptyState.enablement.learnMore": "Envie d'en savoir plus ?", @@ -35129,7 +34958,6 @@ "xpack.synthetics.monitorManagement.locationName": "Nom de l’emplacement", "xpack.synthetics.monitorManagement.locationsLabel": "Emplacements", "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel": "Chargement de la liste Gestion des moniteurs", - "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.invalidKey": "En savoir plus", "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.learnMore": "En savoir plus.", "xpack.synthetics.monitorManagement.monitorAddedSuccessMessage": "Moniteur ajouté avec succès.", "xpack.synthetics.monitorManagement.monitorEditedSuccessMessage": "Moniteur mis à jour.", @@ -35169,10 +34997,7 @@ "xpack.synthetics.monitorManagement.startAddingLocationsDescription": "Les emplacements privés vous permettent d'exploiter des moniteurs depuis vos propres locaux. Ils nécessitent un agent Elastic et une politique d'agent que vous pouvez contrôler et maintenir via Fleet.", "xpack.synthetics.monitorManagement.steps": "Étapes", "xpack.synthetics.monitorManagement.summary.heading": "Résumé", - "xpack.synthetics.monitorManagement.syntheticsDisabledFailure": "La Gestion des moniteurs n'a pas pu être désactivée. Veuillez contacter le support technique.", "xpack.synthetics.monitorManagement.syntheticsDisabledSuccess": "Gestion des moniteurs désactivée avec succès.", - "xpack.synthetics.monitorManagement.syntheticsEnabledFailure": "La Gestion des moniteurs n'a pas pu être activée. Veuillez contacter le support technique.", - "xpack.synthetics.monitorManagement.syntheticsEnableLabel.invalidKey": "Activer la Gestion des moniteurs", "xpack.synthetics.monitorManagement.syntheticsEnableLabel.management": "Activer la Gestion des moniteurs", "xpack.synthetics.monitorManagement.syntheticsEnableSuccess": "Gestion des moniteurs activée avec succès.", "xpack.synthetics.monitorManagement.testResult": "Résultat du test", @@ -35188,7 +35013,6 @@ "xpack.synthetics.monitorManagement.websiteUrlLabel": "URL de site web", "xpack.synthetics.monitorManagement.websiteUrlPlaceholder": "Entrer l'URL d'un site web", "xpack.synthetics.monitorOverviewTab.title": "Aperçu", - "xpack.synthetics.monitors.management.betaLabel": "Cette fonctionnalité est en version bêta et susceptible d'être modifiée. La conception et le code sont moins matures que les fonctionnalités officielles en disponibilité générale et sont fournis tels quels sans aucune garantie. Les fonctionnalités en version bêta ne sont pas soumises à l'accord de niveau de service des fonctionnalités officielles en disponibilité générale.", "xpack.synthetics.monitors.pageHeader.createButton.label": "Créer le moniteur", "xpack.synthetics.monitors.pageHeader.title": "Moniteurs", "xpack.synthetics.monitorsPage.errors": "Erreurs", @@ -35274,7 +35098,6 @@ "xpack.synthetics.overview.groupPopover.project.label": "Projet", "xpack.synthetics.overview.groupPopover.tag.label": "Balise", "xpack.synthetics.overview.heading": "Moniteurs", - "xpack.synthetics.overview.headingBeta": " (bêta)", "xpack.synthetics.overview.headingBetaSection": "Synthetics", "xpack.synthetics.overview.monitors.label": "Moniteurs", "xpack.synthetics.overview.noMonitorsFoundContent": "Essayez d'affiner votre recherche.", @@ -35690,7 +35513,7 @@ "xpack.transform.stepCreateForm.duplicateDataViewErrorMessage": "Une erreur est survenue lors de la création de la vue de données Kibana {dataViewName} : La vue de données existe déjà.", "xpack.transform.stepCreateForm.startTransformErrorMessage": "Une erreur s'est produite lors du démarrage de la transformation {transformId} :", "xpack.transform.stepCreateForm.startTransformSuccessMessage": "La requête pour démarrer la transformation {transformId} a été reconnue.", - "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "Requête non valide : {errorMessage}", + "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "Requête non valide : {queryErrorMessage}", "xpack.transform.stepDefineForm.queryPlaceholderKql": "Par exemple, {example}", "xpack.transform.stepDefineForm.queryPlaceholderLucene": "Par exemple, {example}", "xpack.transform.stepDefineForm.runtimeFieldsListLabel": "{runtimeFields}", @@ -36405,7 +36228,6 @@ "xpack.triggersActionsUI.ruleSnoozeScheduler.recurYearly": "Annuel", "xpack.triggersActionsUI.ruleSnoozeScheduler.repeatIntervalLabel": "Chaque", "xpack.triggersActionsUI.ruleSnoozeScheduler.repeatLabel": "Répéter", - "xpack.triggersActionsUI.ruleSnoozeScheduler.reucrringSwitch": "Rendre récurrent", "xpack.triggersActionsUI.ruleSnoozeScheduler.saveSchedule": "Enregistrer le calendrier", "xpack.triggersActionsUI.ruleSnoozeScheduler.timezoneLabel": "Fuseau horaire", "xpack.triggersActionsUI.rulesSettings.flapping.alertFlappingDetection": "Détection de bagotement d'alerte", @@ -37934,7 +37756,6 @@ "visTypeHeatmap.scaleTypes.linearText": "Linéaire", "visTypeHeatmap.scaleTypes.logText": "Logarithmique", "visTypeHeatmap.scaleTypes.squareRootText": "Racine carrée", - "visTypeHeatmap.splitTitle.tooltip": "l'agrégation de graphique divisé n’est pas encore compatible avec la nouvelle bibliothèque de graphiques. Veuillez activer le paramètre avancé de la bibliothèque de graphiques héritée pour les cartes thermiques afin d'utiliser l'agrégation de graphique divisé.", "visTypeMarkdown.function.font.help": "Paramètres de police.", "visTypeMarkdown.function.help": "Visualisation Markdown", "visTypeMarkdown.function.markdown.help": "Markdown à rendre", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 7d3317df1bf37..3c27b1d843c3b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1090,7 +1090,6 @@ "dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName}が追加されました", "dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName}が追加されました", "dashboard.listing.createNewDashboard.newToKibanaDescription": "Kibana は初心者ですか?{sampleDataInstallLink}してお試しください。", - "dashboard.listing.unsaved.discardAria": "{title}の変更を破棄", "dashboard.listing.unsaved.editAria": "{title}の編集を続行", "dashboard.listing.unsaved.unsavedChangesTitle": "次の{dash}には保存されていない変更があります:", "dashboard.loadingError.dashboardGridErrorMessage": "ダッシュボードが読み込めません:{message}", @@ -1127,10 +1126,7 @@ "dashboard.dashboardPageTitle": "ダッシュボード", "dashboard.dashboardWasSavedSuccessMessage": "ダッシュボード「{dashTitle}」が保存されました。", "dashboard.deleteError.toastDescription": "ダッシュボードの削除中にエラーが発生しました", - "dashboard.discardChangesConfirmModal.cancelButtonLabel": "キャンセル", - "dashboard.discardChangesConfirmModal.confirmButtonLabel": "変更を破棄", "dashboard.discardChangesConfirmModal.discardChangesDescription": "変更を破棄すると、元に戻すことはできません。", - "dashboard.discardChangesConfirmModal.discardChangesTitle": "ダッシュボードへの変更を破棄しますか?", "dashboard.editingToolbar.addControlButtonTitle": "コントロールを追加", "dashboard.editingToolbar.addTimeSliderControlButtonTitle": "時間スライダーコントロールを追加", "dashboard.editingToolbar.controlsButtonTitle": "コントロール", @@ -1164,7 +1160,6 @@ "dashboard.listing.readonlyNoItemsTitle": "表示するダッシュボードがありません", "dashboard.listing.table.entityName": "ダッシュボード", "dashboard.listing.table.entityNamePlural": "ダッシュボード", - "dashboard.listing.unsaved.discardTitle": "変更を破棄", "dashboard.listing.unsaved.editTitle": "編集を続行", "dashboard.listing.unsaved.loading": "読み込み中", "dashboard.loadingError.dashboardNotFound": "リクエストされたダッシュボードが見つかりませんでした。", @@ -2147,19 +2142,15 @@ "discover.docTable.totalDocuments": "{totalDocuments}ドキュメント", "discover.dscTour.stepAddFields.description": "{plusIcon}をクリックして、関心があるフィールドを追加します。", "discover.dscTour.stepExpand.description": "{expandIcon}をクリックすると、ドキュメントを表示、比較、フィルタリングできます。", - "discover.field.title": "{fieldName}({fieldDisplayName})", "discover.fieldChooser.detailViews.existsInRecordsText": "{value} / {totalValue}レコードに存在します", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "{field}を除外:\"{value}\"", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "{field}のフィルター:\"{value}\"", "discover.fieldChooser.detailViews.valueOfRecordsText": "{value} / {totalValue}レコード", - "discover.fieldChooser.discoverField.addButtonAriaLabel": "{field}をテーブルに追加", - "discover.fieldChooser.discoverField.removeButtonAriaLabel": "{field}をテーブルから削除", "discover.fieldChooser.fieldCalculator.fieldIsNotPresentInDocumentsErrorMessage": "このフィールドはElasticsearchマッピングに表示されますが、ドキュメントテーブルの{hitsLength}件のドキュメントには含まれません。可視化や検索は可能な場合があります。", "discover.grid.copyClipboardButtonTitle": "{column}の値をコピー", "discover.grid.copyColumnValuesToClipboard.toastTitle": "\"{column}\"列の値がクリップボードにコピーされました", "discover.grid.filterForAria": "この{value}でフィルターを適用", "discover.grid.filterOutAria": "この{value}を除外", - "discover.gridSampleSize.description": "検索と一致する最初の{sampleSize}ドキュメントを表示しています。この値を変更するには、{advancedSettingsLink}に移動します。", "discover.howToSeeOtherMatchingDocumentsDescription": "これらは検索条件に一致した初めの{sampleSize}件のドキュメントです。他の結果を表示するには検索条件を絞ってください。", "discover.noMatchRoute.bannerText": "Discoverアプリケーションはこのルートを認識できません:{route}", "discover.noResults.kqlExamples.kqlDescription": "{kqlLink}の詳細", @@ -2337,8 +2328,6 @@ "discover.embeddable.inspectorRequestDataTitle": "データ", "discover.embeddable.inspectorRequestDescription": "このリクエストはElasticsearchにクエリをかけ、検索データを取得します。", "discover.embeddable.search.displayName": "検索", - "discover.field.mappingConflict": "このフィールドは、このパターンと一致するインデックス全体に対して複数の型(文字列、整数など)として定義されています。この競合フィールドを使用することはできますが、Kibana で型を認識する必要がある関数では使用できません。この問題を修正するにはデータのレンダリングが必要です。", - "discover.field.mappingConflict.title": "マッピングの矛盾", "discover.fieldChooser.addField.label": "フィールドを追加", "discover.fieldChooser.availableFieldsTooltip": "フィールドをテーブルに表示できます。", "discover.fieldChooser.detailViews.emptyStringText": "空の文字列", @@ -2381,7 +2370,6 @@ "discover.grid.tableRow.viewSurroundingDocumentsLinkTextSimple": "周りのドキュメント", "discover.grid.tableRow.viewText": "表示:", "discover.grid.viewDoc": "詳細ダイアログを切り替え", - "discover.gridSampleSize.advancedSettingsLinkLabel": "高度な設定", "discover.helpMenu.appName": "Discover", "discover.inspectorRequestDataTitleDocuments": "ドキュメント", "discover.inspectorRequestDescriptionDocument": "このリクエストはElasticsearchにクエリをかけ、ドキュメントを取得します。", @@ -2492,8 +2480,6 @@ "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataBody": "次のエラーが発生したため、{pattern}パターンと一致するインデックスはチェックされません:{error}", "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataTitle": "{pattern}パターンと一致するインデックスはチェックされません", "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingUnallowedValuesBody": "許可されていない値の読み込み中に問題が発生しました:{error}", - "ecsDataQualityDashboard.errorLoadingEcsMetadataLabel": "ECSメタデータの読み込みエラー:{details}", - "ecsDataQualityDashboard.errorLoadingEcsVersionLabel": "ECSバージョンの読み込みエラー:{details}", "ecsDataQualityDashboard.errorLoadingIlmExplainLabel": "ILM Explainの読み込みエラー:{details}", "ecsDataQualityDashboard.errorLoadingMappingsLabel": "{patternOrIndexName}のマッピングの読み込みエラー:{details}", "ecsDataQualityDashboard.errorLoadingStatsLabel": "統計情報の読み込みエラー:{details}", @@ -2527,8 +2513,6 @@ "ecsDataQualityDashboard.cancelButton": "キャンセル", "ecsDataQualityDashboard.checkAllButton": "すべて確認", "ecsDataQualityDashboard.coldDescription": "インデックスは更新されず、頻繁に照会されません。情報はまだ検索可能でなければなりませんが、クエリが低速でも問題ありません。", - "ecsDataQualityDashboard.collapseButtonLabelClosed": "終了", - "ecsDataQualityDashboard.collapseButtonLabelOpen": "開く", "ecsDataQualityDashboard.compareFieldsTable.documentValuesActualColumn": "ドキュメント値(実際)", "ecsDataQualityDashboard.compareFieldsTable.ecsDescriptionColumn": "ECS説明", "ecsDataQualityDashboard.compareFieldsTable.ecsMappingTypeColumn": "ECSマッピングタイプ", @@ -2650,7 +2634,6 @@ "ecsDataQualityDashboard.summaryTable.passedTooltip": "合格", "ecsDataQualityDashboard.summaryTable.resultColumn": "結果", "ecsDataQualityDashboard.summaryTable.thisIndexHasNotBeenCheckedTooltip": "このインデックスは確認されていません", - "ecsDataQualityDashboard.takeActionMenu.takeActionButton": "アクションを実行", "ecsDataQualityDashboard.technicalPreviewBadge": "テクニカルプレビュー", "ecsDataQualityDashboard.timestampDescriptionLabel": "イベントが生成された日時これはイベントから抽出された日時で、一般的にはイベントがソースから生成された日時を表します。イベントソースに元のタイムスタンプがない場合は、通常、この値はイベントがパイプラインによって受信された最初の日時が入力されます。すべてのイベントの必須フィールドです。", "ecsDataQualityDashboard.toasts.copiedErrorsToastTitle": "エラーをクリップボードにコピーしました", @@ -5823,7 +5806,6 @@ "unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel": "保存されたクエリの適用", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "キャンセル", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "削除", - "unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel": "選択した保存されたクエリで置換", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "新規保存クエリを保存", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "新規保存", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "変更を保存", @@ -6823,7 +6805,6 @@ "visualizations.missedDataView.errorMessage": "{type}が見つかりませんでした:{id}", "visualizations.newChart.conditionalMessage.newLibrary": "{link}で{type}ライブラリに切り替える", "visualizations.newGaugeChart.notificationMessage": "新しいゲージグラフライブラリはまだ分割グラフアグリゲーションをサポートしていません。{conditionalMessage}", - "visualizations.newHeatmapChart.notificationMessage": "新しいヒートマップグラフライブラリはまだ分割グラフアグリゲーションをサポートしていません。{conditionalMessage}", "visualizations.newVisWizard.newVisTypeTitle": "新規{visTypeName}", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {タイプ}}が見つかりました", "visualizations.noMatchRoute.bannerText": "Visualizeアプリケーションはこのルートを認識できません。{route}", @@ -7048,13 +7029,11 @@ "xpack.aiops.logCategorization.emptyPromptBody": "ログパターン分析では、メッセージが共通のカテゴリーにグループ化されます。", "xpack.aiops.logCategorization.emptyPromptTitle": "テキストフィールドを選択し、[カテゴリー分けの実行]をクリックすると、分析が開始します", "xpack.aiops.logCategorization.errorLoadingCategories": "カテゴリーの読み込みエラー", - "xpack.aiops.logCategorization.filterOutInDiscover": "Discoverで除外", "xpack.aiops.logCategorization.noCategoriesBody": "選択したフィールドが選択した時間範囲で入力されていることを確認してください。", "xpack.aiops.logCategorization.noCategoriesTitle": "カテゴリーが見つかりません", "xpack.aiops.logCategorization.noDocsBody": "選択した日付範囲にドキュメントが含まれていることを確認してください。", "xpack.aiops.logCategorization.noDocsTitle": "ドキュメントが見つかりませんでした", "xpack.aiops.logCategorization.runButton": "カテゴリー分けの実行", - "xpack.aiops.logCategorization.showInDiscover": "Discoverでこれらを表示", "xpack.aiops.miniHistogram.noDataLabel": "N/A", "xpack.aiops.progressAriaLabel": "進捗", "xpack.aiops.rerunAnalysisButtonTitle": "分析を再実行", @@ -7114,11 +7093,8 @@ "xpack.apm.agentExplorerInstanceTable.noServiceNodeName.tooltip.linkToDocs": "{seeDocs}を使用してサービスノード名を構成できます。", "xpack.apm.agentExplorerTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, other {#個のバージョン}}", "xpack.apm.alerts.anomalySeverity.scoreDetailsDescription": "スコア{value}{value, select, critical {} other {以上}}", - "xpack.apm.alertTypes.errorCount.reason": "エラーカウントは{serviceName}の最後の{interval}で{measured}です。> {threshold}のときにアラートを通知します。", "xpack.apm.alertTypes.minimumWindowSize.description": "推奨される最小値は{sizeValue} {sizeUnit}です。これにより、アラートに評価する十分なデータがあることが保証されます。低すぎる値を選択した場合、アラートが想定通りに実行されない可能性があります。", - "xpack.apm.alertTypes.transactionDuration.reason": "{serviceName}の{aggregationType}のレイテンシは{measured}で最後の{interval}で測定されます。> {threshold}のときにアラートを通知します。", "xpack.apm.alertTypes.transactionDurationAnomaly.reason": "{severityLevel}の異常が{measured}のスコアで{serviceName}の最後の{interval}で検出されました。", - "xpack.apm.alertTypes.transactionErrorRate.reason": "{serviceName}の失敗したトランザクションは最後の{interval}で{measured}されます。> {threshold}のときにアラートを通知します。", "xpack.apm.anomalyDetection.createJobs.failed.text": "APMサービス環境[{environments}]用に1つ以上の異常検知ジョブを作成しているときに問題が発生しました。エラー「{errorMessage}」", "xpack.apm.anomalyDetection.createJobs.succeeded.text": "APMサービス環境[{environments}]の異常検知ジョブが正常に作成されました。機械学習がトラフィック異常値の分析を開始するには、少し時間がかかります。", "xpack.apm.anomalyDetectionSetup.notEnabledForEnvironmentText": "「{currentEnvironment}」環境では、まだ異常検知が有効ではありません。クリックすると、セットアップを続行します。", @@ -7172,7 +7148,6 @@ "xpack.apm.serviceOveriew.errorsTableOccurrences": "{occurrences}件。", "xpack.apm.serviceOverview.embeddedMap.error.toastDescription": "id {embeddableFactoryId}の埋め込み可能ファクトリが見つかりました。", "xpack.apm.serviceOverview.embeddedMap.subtitle": "国と地域別に基づく{currentMap}の総数を示した地図", - "xpack.apm.serviceOverview.lensFlyout.topValues": "{metric}の上位の{BUCKET_SIZE}値", "xpack.apm.serviceOverview.mobileCallOutText": "これはモバイルサービスであり、現在はテクニカルプレビューとしてリリースされています。フィードバックを送信して、エクスペリエンスの改善にご協力ください。{feedbackLink}", "xpack.apm.servicesTable.environmentCount": "{environmentCount, plural, other {#個の環境}}", "xpack.apm.settings.agentKeys.apiKeysDisabledErrorDescription": "システム管理者に連絡し、{link}を伝えてAPIキーを有効にしてください。", @@ -7554,7 +7529,6 @@ "xpack.apm.dataView.autoCreateDisabled": "データビューの自動作成は、「autoCreateApmDataView」構成オプションによって無効化されています", "xpack.apm.dataView.noApmData": "APMデータがありません", "xpack.apm.dependecyOperationDetailView.header.backLinkLabel": "すべての演算", - "xpack.apm.dependencies.kueryBarPlaceholder": "依存関係メトリックを検索(例:span.destination.service.resource:elasticsearch)", "xpack.apm.dependenciesInventory.dependencyTableColumn": "依存関係", "xpack.apm.dependenciesTable.columnErrorRate": "失敗したトランザクション率", "xpack.apm.dependenciesTable.columnErrorRateTip": "選択したサービスの失敗したトランザクションの割合。4xxステータスコード(クライアントエラー)のHTTPサーバートランザクションは、サーバーではなく呼び出し側が失敗の原因であるため、失敗と見なされません。", @@ -8101,17 +8075,11 @@ "xpack.apm.serviceOverview.latencyColumnDefaultLabel": "レイテンシ", "xpack.apm.serviceOverview.latencyColumnP95Label": "レイテンシ(95 番目)", "xpack.apm.serviceOverview.latencyColumnP99Label": "レイテンシ(99 番目)", - "xpack.apm.serviceOverview.lensFlyout.countRecords": "レコード数", "xpack.apm.serviceOverview.loadingText": "読み込み中…", "xpack.apm.serviceOverview.mobileCallOutLink": "フィードバックを作成する", "xpack.apm.serviceOverview.mobileCallOutTitle": "モバイルAPM", - "xpack.apm.serviceOverview.mostUsed.appVersion": "アプリバージョン", - "xpack.apm.serviceOverview.mostUsed.device": "デバイス", - "xpack.apm.serviceOverview.mostUsed.nct": "ネットワーク接続タイプ", - "xpack.apm.serviceOverview.mostUsed.osVersion": "OSバージョン", "xpack.apm.serviceOverview.mostUsedTitle": "最も使用されている", "xpack.apm.serviceOverview.noResultsText": "インスタンスが見つかりません", - "xpack.apm.serviceOverview.openInLens": "Lensで開く", "xpack.apm.serviceOverview.throughtputChartTitle": "スループット", "xpack.apm.serviceOverview.tpmHelp": "スループットは1分あたりのトランザクション数(tpm)で測定されます。", "xpack.apm.serviceOverview.transactionsTableColumnErrorRate": "失敗したトランザクション率", @@ -10639,8 +10607,6 @@ "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "KSPM統合の追加", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "クラウドセキュリティ態勢の詳細をご覧ください", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "クラウドリソースでセキュリティの誤った構成を検出します。", - "xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "失敗した調査結果", - "xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "合格した調査結果", "xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "CSPダッシュボードを表示", "xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "CSP調査結果を表示 ", "xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "CSPルールを表示 ", @@ -11144,8 +11110,6 @@ "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.caption": "インデックス{indexName}を表示", "xpack.enterpriseSearch.content.engineList.deleteEngine.successToast.title": "{engineName}が削除されました。", "xpack.enterpriseSearch.content.engines.createEngine.headerSubTitle": "エンジンは、ユーザーがインデックス内のデータを照会することを可能にします。詳細については、{enginesDocsLink}を探索してください。", - "xpack.enterpriseSearch.content.engines.description": "エンジンは、関連性、分析、パーソナライゼーションツールの完全なセットで、インデックスされたデータを照会することを可能にします。エンタープライズサーチにおけるエンジンの仕組みについて詳しく知るには{documentationUrl}", - "xpack.enterpriseSearch.content.engines.enginesList.description": "{total}件中{from}-{to}件を表示中", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.subTitle": "{engineName}に関連付けられたインデックスを表示", "xpack.enterpriseSearch.content.enginesList.table.column.view.indices": "{indicesCount, number} {indicesCount, plural, other {インデックス}}", "xpack.enterpriseSearch.content.index.connector.syncRules.description": "同期ルールを追加して、{indexName}から同期されるデータをカスタマイズします。デフォルトではすべてが含まれます。ドキュメントは、リストの最上位から下に向かって、構成されたインデックスルールのセットに対して検証されます。", @@ -11175,17 +11139,10 @@ "xpack.enterpriseSearch.content.indices.pipelines.successToastDeleteMlPipeline.title": "機械学習推論パイプライン\"{pipelineName}\"を削除しました", "xpack.enterpriseSearch.content.indices.pipelines.successToastDetachMlPipeline.title": "機械学習推論パイプラインを\"{pipelineName}\"からデタッチしました", "xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.unmanaged.description": "スタック管理で{ingestPipelines}からこのパイプラインを編集", - "xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description": "エンタープライズサーチで{name}コンテンツを検索します。", - "xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage": "その他のコネクターをお探しの場合は、{workplaceSearchLink}または{buildYourOwnConnectorLink}。", - "xpack.enterpriseSearch.content.indices.selectConnector.successToast.title": "インデックスは{connectorName}ネイティブコネクターを使用します。", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.alreadyExists.error": "名前{indexName}のインデックスがすでに存在します", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.isInvalid.error": "{indexName}は無効なインデックス名です", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineOne": "インデックスは次の名前になります:{indexName}", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description": "削除されたインデックス{indexName}は、既存のコネクター構成に関連付けられていました。既存のコネクター構成を新しいコネクター構成で置き換えますか?", - "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content": "コネクターフレームワークとコネクタークライアントの例を使用すると、あらゆるデータソースでElasticsearch {bulkApiDocLink}への取り込みを高速化できます。インデックスを作成した後、コネクターフレームワークにアクセスし、最初のコネクタークライアントを接続するための手順が示されます。", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content": "コネクターを作成すると、コンテンツの準備が完了します。{elasticsearchLink}で最初の検索エクスペリエンスを構築するか、{appSearchLink}で提供されている検索エクスペリエンスツールを使用します。柔軟性の高い能力とターンキーのシンプル性を両立するために、{searchEngineLink}を作成することをお勧めします。", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.content": "一意のインデックス名を指定し、任意でインデックスのデフォルト{languageAnalyzerDocLink}を設定します。このインデックスには、データソースコンテンツが格納されます。また、デフォルトフィールドマッピングで最適化され、関連する検索エクスペリエンスを実現します。", - "xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content": "ネイティブコネクターのカタログから選択し、MongoDBなどのサポートされているデータソースから検索可能なコンテンツの抽出を開始します。コネクターの動作をカスタマイズする必要がある場合は、セルフマネージドコネクタークライアントバージョンを常にデプロイし、{buildAConnectorLabel}ワークフロー経由で登録できます。", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.description": "{total}件中{results}件を表示中。{maximum}ドキュメントが検索結果の最大数です。", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage": "毎秒あたりのドキュメント:{docPerPage}", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option": "{docCount}ドキュメント", @@ -12245,23 +12202,9 @@ "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "完了", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "読み取り専用キーを生成", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "エンジン読み取り専用APIキーを作成", - "xpack.enterpriseSearch.content.engine.api.pageTitle": "API", - "xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "ElasticはAPIキーを保存しません。生成後は、1回だけキーを表示できます。必ず安全に保管してください。アクセスできなくなった場合は、この画面から新しいAPIキーを生成する必要があります。", - "xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "APIキーを作成", - "xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "APIキーの詳細をご覧ください。", - "xpack.enterpriseSearch.content.engine.api.step1.title": "APIキーを生成して保存", - "xpack.enterpriseSearch.content.engine.api.step1.viewKeysButton": "キーを表示", - "xpack.enterpriseSearch.content.engine.api.step2.copyEndpointDescription": "このURLを使用して、エンジンのAPIエンドポイントにアクセスします。", - "xpack.enterpriseSearch.content.engine.api.step2.title": "エンジンのエンドポイントをコピー", "xpack.enterpriseSearch.content.engine.api.step3.curlTitle": "cURL", "xpack.enterpriseSearch.content.engine.api.step3.intro": "Elasticで管理されている言語クライアントが使用されたエンジンと統合し、検索エクスペリエンスを構築する方法をご覧ください。", "xpack.enterpriseSearch.content.engine.api.step3.searchUITitle": "Search UI", - "xpack.enterpriseSearch.content.engine.api.step3.title": "エンドポイントを呼び出す方法をご覧ください", - "xpack.enterpriseSearch.content.engine.api.step4.copy": "エンジンは、このインストールの一部として、基本分析データを提供します。さらに粒度の高いカスタムメトリックを利用するには、ご使用のプラットフォームで当社の行動分析を統合してください。", - "xpack.enterpriseSearch.content.engine.api.step4.learnHowLink": "方法を学習", - "xpack.enterpriseSearch.content.engine.api.step4.title": "(任意)分析を強化", - "xpack.enterpriseSearch.content.engine.headerActions.actionsButton.ariaLabel": "エンジンアクションメニューボタン", - "xpack.enterpriseSearch.content.engine.headerActions.delete": "このエンジンを削除", "xpack.enterpriseSearch.content.engine.indices.actions.columnTitle": "アクション", "xpack.enterpriseSearch.content.engine.indices.actions.removeIndex.title": "このインデックスをエンジンから削除", "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.title": "このインデックスを表示", @@ -12273,7 +12216,6 @@ "xpack.enterpriseSearch.content.engine.indices.docsCount.columnTitle": "ドキュメント数", "xpack.enterpriseSearch.content.engine.indices.health.columnTitle": "インデックス正常性", "xpack.enterpriseSearch.content.engine.indices.name.columnTitle": "インデックス名", - "xpack.enterpriseSearch.content.engine.indices.pageTitle": "インデックス", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.description": "インデックスは削除されません。後からこのエンジンに追加することができます。", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.text": "はい。このインデックスを削除します", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.title": "このインデックスをエンジンから削除", @@ -12281,12 +12223,10 @@ "xpack.enterpriseSearch.content.engine.indicesSelect.docsLabel": "ドキュメント:", "xpack.enterpriseSearch.content.engine.schema.field_name.columnTitle": "フィールド名", "xpack.enterpriseSearch.content.engine.schema.field_type.columnTitle": "フィールド型", - "xpack.enterpriseSearch.content.engine.schema.pageTitle": "スキーマ", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title": "はい。このエンジンを削除 ", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description": "エンジンを削除すると、元に戻せません。インデックスには影響しません。", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.title": "このエンジンを完全に削除しますか?", "xpack.enterpriseSearch.content.engineList.table.column.actions.deleteEngineLabel": "このエンジンを削除", - "xpack.enterpriseSearch.content.engines.breadcrumb": "エンジン", "xpack.enterpriseSearch.content.engines.createEngine.header.createError.title": "エンジンの作成エラー", "xpack.enterpriseSearch.content.engines.createEngine.header.docsLink": "エンジンドキュメント", "xpack.enterpriseSearch.content.engines.createEngine.headerTitle": "エンジンを作成", @@ -12294,15 +12234,9 @@ "xpack.enterpriseSearch.content.engines.createEngine.nameEngine.title": "エンジン名を指定", "xpack.enterpriseSearch.content.engines.createEngine.selectIndices.title": "インデックスを選択", "xpack.enterpriseSearch.content.engines.createEngine.submit": "このエンジンを削除", - "xpack.enterpriseSearch.content.engines.createEngineButtonLabel": "エンジンを作成", - "xpack.enterpriseSearch.content.engines.documentation": "エンジンドキュメントを読む", "xpack.enterpriseSearch.content.engines.enginesList.empty.description": "最初のエンジンの作成を説明します。", "xpack.enterpriseSearch.content.engines.enginesList.empty.title": "初めてのエンジンの作成", "xpack.enterpriseSearch.content.engines.indices.addIndicesFlyout.updateError.title": "エンジンの更新エラー", - "xpack.enterpriseSearch.content.engines.searchBar.ariaLabel": "検索エンジン", - "xpack.enterpriseSearch.content.engines.searchPlaceholder": "検索エンジン", - "xpack.enterpriseSearch.content.engines.searchPlaceholder.description": "名前または含まれているインデックスでエンジンを検索します。", - "xpack.enterpriseSearch.content.engines.title": "エンジン", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.docsCount.columnTitle": "ドキュメント数", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.health.columnTitle": "インデックス正常性", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.name.columnTitle": "インデックス名", @@ -12359,7 +12293,6 @@ "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceDescription": "ドキュメントの不要な空白を自動的に削除", "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceLabel": "空白の削除", "xpack.enterpriseSearch.content.index.pipelines.settings.runMlInferenceDescrition": "互換性がある学習済みMLモデルを使用してデータを強化", - "xpack.enterpriseSearch.content.index.searchEngines.createEngine": "App Searchエンジンの作成", "xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip": "非表示のインデックスからエンジンを作成することはできません。", "xpack.enterpriseSearch.content.index.searchEngines.label": "検索エンジン", "xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "App Searchエンジンを表示", @@ -12610,12 +12543,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.message": "エラーメッセージ", "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.timestamp": "タイムスタンプ", "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.title": "推論エラー", - "xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel": "世界に1つ", "xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel": "ドキュメント", "xpack.enterpriseSearch.content.indices.selectConnector.description": "まず、データソースから、新しく作成された検索インデックスに、データを抽出、インデックス、同期するように構成するコネクターを選択します。", - "xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel": "選択して構成", "xpack.enterpriseSearch.content.indices.selectConnector.title": "コネクターを選択", - "xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel": "Workplace Searchで追加の統合を表示", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.attach": "接続", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create": "パイプラインの作成", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title": "構成", @@ -12641,42 +12571,19 @@ "xpack.enterpriseSearch.content.nativeConnectors.mongodb.name": "MongoDB", "xpack.enterpriseSearch.content.nativeConnectors.mysql.name": "MySQL", "xpack.enterpriseSearch.content.navTitle": "コンテンツ", - "xpack.enterpriseSearch.content.new_index.successToast.button.label": "エンジンを作成", - "xpack.enterpriseSearch.content.new_index.successToast.description": "App Searchエンジンを使用して、新しいElasticsearchインデックスの検索エクスペリエンスを構築できます。", "xpack.enterpriseSearch.content.new_index.successToast.title": "インデックスが正常に作成されました", "xpack.enterpriseSearch.content.newIndex.breadcrumb": "新しい検索インデックス", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description": "APIに接続してプログラムでドキュメントを追加", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer": "ある程度の開発が必要です", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label": "APIを使用", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description": "コネクターフレームワークを使用すると、カスタムデータソースのコネクターをすばやく構築できます。", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer": "開発が必要です", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label": "コネクターを作成", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description": "すべてのWebサイトコンテンツを検出、抽出、インデックス作成、同期", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer": "開発は不要です", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label": "Webクローラーを使用", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description": "サポートされているデータソースから、すべてのコンテンツを抽出、インデックス、同期するように、コネクターを設定 ", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer": "開発は不要です", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label": "コネクターを使用", "xpack.enterpriseSearch.content.newIndex.emptyState.description": "エンタープライズ サーチで追加したデータは検索インデックスと呼ばれ、App SearchとWorkplace Searchの両方で検索可能です。App SearchのコネクターとWorkplace SearchのWebクローラーを使用できます。", "xpack.enterpriseSearch.content.newIndex.emptyState.footer.link": "ドキュメントを読む", "xpack.enterpriseSearch.content.newIndex.emptyState.footer.title": "検索インデックスの詳細", "xpack.enterpriseSearch.content.newIndex.emptyState.title": "インジェスチョン方法を選択", - "xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content": "APIキーを生成し、ドキュメントを参照して、ドキュメントをElasticsearch APIエンドポイントに送信します。合理化された統合では、言語クライアントを使用できます。", - "xpack.enterpriseSearch.content.newIndex.methodApi.title": "APIを使用してインデックス", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink": "Bulk API", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title": "コネクターを作成して構成", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title": "コネクターを構成", - "xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content": "クローリングするドメインを構成し、準備が完了したら、最初のクローリングをトリガーします。その後の処理は、エンタープライズ サーチで自動的に実行されます。", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.createIndex.buttonText": "インデックスの作成", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputHelpText": "言語は後から変更できますが、再インデックスが必要になる場合があります", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputLabel": "言語アナライザー", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineTwo": "名前は小文字で入力してください。スペースや特殊文字は使用できません。", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel": "インデックス名", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder": "インデックスの名前を設定", - "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText": "ドキュメントを表示", "xpack.enterpriseSearch.content.newIndex.pageTitle": "新しい検索インデックス", - "xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description": "ユースケースのインジェスチョン方法を選択し、検索が最適化されたElasticsearchインデックスを作成します。", - "xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title": "インジェスチョン方法を選択", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label": "キャンセル", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label": "構成を置換", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title": "既存のコネクターを置換", @@ -12684,16 +12591,6 @@ "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.genericError": "インデックスを作成できませんでした", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.indexAlreadyExists": "このインデックスはすでに存在します", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.unauthorizedError": "このコネクターを作成する権限がありません", - "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title": "コネクターを作成", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink": "App Search", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink": "Elasticsearch", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink": "検索エンジン", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title": "検索エクスペリエンスを構築", - "xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title": "インジェスチョン設定を構成", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title": "Webクローラーを使用してインデックス", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink": "言語アナライザー", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.title": "Elasticsearchインデックスを作成", - "xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title": "コネクターを使用したインデックス作成", "xpack.enterpriseSearch.content.newIndex.types.api": "APIエンドポイント", "xpack.enterpriseSearch.content.newIndex.types.connector": "コネクター", "xpack.enterpriseSearch.content.newIndex.types.crawler": "Webクローラー", @@ -12799,7 +12696,6 @@ "xpack.enterpriseSearch.content.searchIndices.syncStatus.columnTitle": "ステータス", "xpack.enterpriseSearch.content.settings.breadcrumb": "設定", "xpack.enterpriseSearch.content.settings.contactExtraction.label": "コンテンツ抽出", - "xpack.enterpriseSearch.content.settings.contactExtraction.link": "コンテンツ抽出の詳細", "xpack.enterpriseSearch.content.settings.contentExtraction.description": "エンタープライズ サーチデプロイですべてのインジェストメソッドで、PDFやWordドキュメントなどのバイナリファイルから検索可能なコンテンツを抽出できます。この設定は、エンタープライズ サーチインジェストメカニズムで作成されたすべての新しいElasticsearchインデックスに適用されます。", "xpack.enterpriseSearch.content.settings.contentExtraction.descriptionTwo": "インデックスの構成ページで、特定のインデックスに対して、この機能を有効化または無効化することもできます。", "xpack.enterpriseSearch.content.settings.contentExtraction.title": "デプロイレベルのコンテンツ抽出", @@ -12813,7 +12709,6 @@ "xpack.enterpriseSearch.content.settings.whitespaceReduction.deploymentHeaderTitle": "デプロイレベルの空白削除", "xpack.enterpriseSearch.content.settings.whiteSpaceReduction.description": "空白削除では、デフォルトで全文コンテンツから空白を削除します。", "xpack.enterpriseSearch.content.settings.whitespaceReduction.label": "空白削除", - "xpack.enterpriseSearch.content.settings.whitespaceReduction.link": "空白削除の詳細", "xpack.enterpriseSearch.content.shared.result.header.metadata.deleteDocument": "ドキュメントを削除", "xpack.enterpriseSearch.content.shared.result.header.metadata.title": "ドキュメントメタデータ", "xpack.enterpriseSearch.content.sources.basicRulesTable.includeEverythingMessage": "このソースの他のすべての項目を含める", @@ -13094,8 +12989,6 @@ "xpack.enterpriseSearch.emailLabel": "メール", "xpack.enterpriseSearch.emptyState.description": "コンテンツはElasticsearchインデックスに保存されます。まず、Elasticsearchインデックスを作成し、インジェスチョン方法を選択します。オプションには、Elastic Webクローラー、サードパーティデータ統合、Elasticsearch APIエンドポイントの使用があります。", "xpack.enterpriseSearch.emptyState.description.line2": "App SearchまたはElasticsearchのどちらで検索エクスペリエンスを構築しても、これが最初のステップです。", - "xpack.enterpriseSearch.engines.engine.notFound.action1": "エンジンに戻る", - "xpack.enterpriseSearch.engines.navTitle": "エンジン", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.description": "場所を問わず、何でも検索。組織を支える多忙なチームのために、パワフルでモダンな検索エクスペリエンスを簡単に導入できます。Webサイトやアプリ、ワークプレイスに事前調整済みの検索をすばやく追加しましょう。何でもシンプルに検索できます。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.notConfigured": "エンタープライズサーチはまだKibanaインスタンスで構成されていません。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt": "エンタープライズ サーチの基本操作", @@ -13183,9 +13076,6 @@ "xpack.enterpriseSearch.nav.contentSettingsTitle": "設定", "xpack.enterpriseSearch.nav.contentTitle": "コンテンツ", "xpack.enterpriseSearch.nav.elasticsearchTitle": "Elasticsearch", - "xpack.enterpriseSearch.nav.engine.apiTitle": "API", - "xpack.enterpriseSearch.nav.engine.indicesTitle": "インデックス", - "xpack.enterpriseSearch.nav.engine.schemaTitle": "スキーマ", "xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle": "概要", "xpack.enterpriseSearch.nav.searchExperiencesTitle": "検索エクスペリエンス", "xpack.enterpriseSearch.nav.searchIndicesTitle": "インデックス", @@ -17426,29 +17316,19 @@ "xpack.infra.hostsViewPage.landing.introTitle": "導入:ホスト分析", "xpack.infra.hostsViewPage.landing.learnMore": "詳細", "xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "この機能は初期バージョンであり、今後継続する中で、開発、改善するうえで皆様からのフィードバックをお願いします\n 。機能にアクセスするには、以下を有効にします。プラットフォームに\n 追加されたこの強力な新機能をお見逃しなく。今すぐお試しください!", - "xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description": "主要なメトリックの経時的な傾向を示す折れ線グラフ。", - "xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title": "経時的なCPU使用状況。", - "xpack.infra.hostsViewPage.metricTrend.cpu.subtitle": "平均", "xpack.infra.hostsViewPage.metricTrend.cpu.title": "CPU 使用状況", "xpack.infra.hostsViewPage.metricTrend.cpu.tooltip": "アイドルおよびIOWait以外の状態で費やされたCPU時間の割合の平均値を、CPUコア数で正規化したもの。ユーザースペースとカーネルスペースの両方で費やされた時間が含まれます。100%はホストのすべてのCPUがビジー状態であることを示します。", - "xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title": "経時的なCPU使用状況。", "xpack.infra.hostsViewPage.metricTrend.hostCount.title": "ホスト", "xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "現在の検索条件から返されたホストの数です。", - "xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription": "主要なメトリックの経時的な傾向を示す折れ線グラフ。", - "xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle": "経時的なメモリ使用状況。", - "xpack.infra.hostsViewPage.metricTrend.memory.subtitle": "平均", "xpack.infra.hostsViewPage.metricTrend.memory.title": "メモリー使用状況", "xpack.infra.hostsViewPage.metricTrend.memory.tooltip": "ページキャッシュを除いたメインメモリの割合の平均値。これには、すべてのプロセスの常駐メモリと、ページキャッシュを離れてカーネル構造とコードによって使用されるメモリが含まれます。高レベルは、ホストのメモリが飽和状態にあることを示します。100%とは、メインメモリがすべてスワップアウト以外の、再利用不可能なメモリで満たされていることを意味します。", - "xpack.infra.hostsViewPage.metricTrend.rx.a11y.description": "主要なメトリックの経時的な傾向を示す折れ線グラフ。", - "xpack.infra.hostsViewPage.metricTrend.rx.a11y.title": "経時的なネットワーク受信(RX)。", - "xpack.infra.hostsViewPage.metricTrend.rx.subtitle": "平均", "xpack.infra.hostsViewPage.metricTrend.rx.title": "ネットワーク受信(RX)", "xpack.infra.hostsViewPage.metricTrend.rx.tooltip": "ホストのパブリックインターフェースで1秒間に受信したバイト数。", - "xpack.infra.hostsViewPage.metricTrend.tx.a11.title": "経時的なネットワーク送信(TX)。", - "xpack.infra.hostsViewPage.metricTrend.tx.a11y.description": "主要なメトリックの経時的な傾向を示す折れ線グラフ。", - "xpack.infra.hostsViewPage.metricTrend.tx.subtitle": "平均", "xpack.infra.hostsViewPage.metricTrend.tx.title": "ネットワーク送信(TX)", "xpack.infra.hostsViewPage.metricTrend.tx.tooltip": "ホストのパブリックインターフェースで1秒間に送信したバイト数", + "xpack.infra.hostsViewPage.metricTrend.subtitle.average": "平均", + "xpack.infra.hostsViewPage.metricTrend.subtitle.average.limit": "平均 (of {limit} hosts)", + "xpack.infra.hostsViewPage.metricTrend.subtitle.hostCount.limit": "Limited to {limit}", "xpack.infra.hostsViewPage.table.averageMemoryTotalColumnHeader": "メモリー合計(平均)", "xpack.infra.hostsViewPage.table.averageMemoryUsageColumnHeader": "メモリー使用状況(平均)", "xpack.infra.hostsViewPage.table.averageRxColumnHeader": "RX(平均)", @@ -19537,10 +19417,7 @@ "xpack.lens.indexPattern.emptyFieldsLabel": "空のフィールド", "xpack.lens.indexPattern.enableAccuracyMode": "精度モードを有効にする", "xpack.lens.indexPattern.fieldExploreInDiscover": "Discoverで探索", - "xpack.lens.indexPattern.fieldItemTooltip": "可視化するには、ドラッグアンドドロップします。", "xpack.lens.indexPattern.fieldPlaceholder": "フィールド", - "xpack.lens.indexPattern.fieldStatsButtonEmptyLabel": "このフィールドにはデータがありませんが、ドラッグアンドドロップで可視化できます。", - "xpack.lens.indexPattern.fieldStatsButtonLabel": "フィールドプレビューを表示するには、クリックします。可視化するには、ドラッグアンドドロップします。", "xpack.lens.indexPattern.fieldStatsNoData": "Lensはこのフィールドのビジュアライゼーションを作成できません。フィールドにデータがありません。ビジュアライゼーションを作成するには、別のフィールドをドラッグします。", "xpack.lens.indexPattern.filterBy.clickToEdit": "クリックして編集", "xpack.lens.indexPattern.filterBy.emptyFilterQuery": "(空)", @@ -20416,15 +20293,11 @@ "xpack.maps.addLayerPanel.addLayer": "レイヤーを追加", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "レイヤーを変更", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "キャンセル", - "xpack.maps.aggs.defaultCountLabel": "カウント", "xpack.maps.attribution.addBtnAriaLabel": "属性を追加", "xpack.maps.attribution.addBtnLabel": "属性を追加", - "xpack.maps.attribution.applyBtnLabel": "適用", "xpack.maps.attribution.attributionFormLabel": "属性", "xpack.maps.attribution.clearBtnAriaLabel": "属性を消去", - "xpack.maps.attribution.clearBtnLabel": "クリア", "xpack.maps.attribution.editBtnAriaLabel": "属性を編集", - "xpack.maps.attribution.editBtnLabel": "編集", "xpack.maps.attribution.labelFieldLabel": "ラベル", "xpack.maps.attribution.urlLabel": "リンク", "xpack.maps.badge.readOnly.text": "読み取り専用", @@ -20661,7 +20534,6 @@ "xpack.maps.mapActions.removeFeatureError": "インデックスから機能を削除できません。", "xpack.maps.mapListing.entityName": "マップ", "xpack.maps.mapListing.entityNamePlural": "マップ", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "マップを読み込めません", "xpack.maps.mapSavedObjectLabel": "マップ", "xpack.maps.mapSettingsPanel.addCustomIcon": "カスタムアイコンを追加", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "自動的にマップをデータ境界に合わせる", @@ -20697,15 +20569,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "フィールド", "xpack.maps.metricsEditor.selectFieldPlaceholder": "フィールドを選択", "xpack.maps.metricsEditor.selectPercentileLabel": "パーセンタイル", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "平均", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "ユニークカウント", - "xpack.maps.metricSelect.countDropDownOptionLabel": "カウント", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "最高", - "xpack.maps.metricSelect.minDropDownOptionLabel": "最低", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "パーセンタイル", "xpack.maps.metricSelect.selectAggregationPlaceholder": "集約を選択", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "合計", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "トップ用語", "xpack.maps.mvtSource.addFieldLabel": "追加", "xpack.maps.mvtSource.fieldPlaceholderText": "フィールド名", "xpack.maps.mvtSource.numberFieldLabel": "数字", @@ -21502,7 +21366,6 @@ "xpack.ml.timeSeriesExplorer.timeSeriesChart.updatedAnnotationNotificationMessage": "ID {jobId}のジョブの注釈が更新されました。", "xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomAggregationIntervalLabel": "(アグリゲーション間隔:{focusAggInt}、バケットスパン:{bucketSpan})", "xpack.ml.trainedModels.modelsList.fetchDeletionErrorMessage": "{modelsCount, plural, other {モデル}}の削除が失敗しました", - "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "モデル{modelId}を停止しますか?", "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, other {#個のモデル}}を選択済み", "xpack.ml.trainedModels.modelsList.startDeployment.modalTitle": "{modelId}デプロイを開始", "xpack.ml.trainedModels.modelsList.startFailed": "\"{modelId}\"の開始に失敗しました", @@ -23493,7 +23356,7 @@ "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "フィルターリストを表示するパーミッションがありません", "xpack.ml.ruleEditor.scopeSection.scopeTitle": "範囲", "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "ルールを作成", - "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ", + "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ", "xpack.ml.ruleEditor.typicalAppliesTypeText": "通常", "xpack.ml.sampleDataLinkLabel": "ML ジョブ", "xpack.ml.selectDataViewLabel": "データビューを選択", @@ -26572,9 +26435,6 @@ "xpack.reporting.diagnostic.browserMissingFonts": "ブラウザーはデフォルトフォントを検索できませんでした。この問題を修正するには、{url}を参照してください。", "xpack.reporting.diagnostic.noUsableSandbox": "Chromiumサンドボックスを使用できません。これは「xpack.screenshotting.browser.chromium.disableSandbox」で無効にすることができます。この作業はご自身の責任で行ってください。{url}を参照してください", "xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました。{encryptionKey} が設定されていることを確認してこのレポートを再生成してください。{err}", - "generateCsv.esErrorMessage": "Elasticsearchから{statusCode}応答を受け取りました:{message}", - "generateCsv.incorrectRowCount": "検索から生成されたCSVの行数でエラーが発生しました。正しい行数:{expected}、実際の行数:{received}。", - "generateCsv.unknownErrorMessage": "不明なエラーが発生しました:{message}", "xpack.reporting.jobResponse.errorHandler.notAuthorized": "{jobtype}レポートを表示または削除する権限がありません", "xpack.reporting.jobsQuery.deleteError": "レポートを削除できません:{error}", "xpack.reporting.jobStatusDetail.attemptXofY": "{max_attempts}回中{attempts}回試行します。", @@ -26631,9 +26491,6 @@ "xpack.reporting.diagnostic.screenshotFailureMessage": "Kibanaインストールのスクリーンショットを作成できませんでした。", "xpack.reporting.errorHandler.unknownError": "不明なエラー", "xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "ジョブヘッダーがありません", - "generateCsv.authenticationExpired.partialResultsMessage": "認証トークが有効期限切れのため、このレポートには一部のCSVの結果が含まれています。少ない量のデータをエクスポートするか、認証トークンのタイムアウトを大きくします。", - "generateCsv.csvUnableToClosePit": "検索に使用したPoint-In-Timeを閉じることができません。Kibanaサーバーログを確認してください。", - "generateCsv.escapedFormulaValues": "CSVには、値がエスケープされた式が含まれる場合があります", "xpack.reporting.jobCreatedBy.unknownUserPlaceholderText": "不明", "xpack.reporting.jobResponse.errorHandler.unknownError": "不明なエラー", "xpack.reporting.jobStatusDetail.deprecatedText": "これは廃止予定のエクスポートタイプです。将来のバージョンのKibanaとの互換性のためには、このレポートの自動化を再作成する必要があります。", @@ -26770,6 +26627,12 @@ "xpack.reporting.uiSettings.validate.customLogo.badFile": "このファイルは動作しません。別の画像ファイルを試してください。", "xpack.reporting.uiSettings.validate.customLogo.tooLarge": "このファイルは大きすぎます。画像ファイルは200キロバイト未満でなければなりません。", "xpack.reporting.userAccessError.learnMoreLink": "詳細", + "generateCsv.esErrorMessage": "Elasticsearchから{statusCode}応答を受け取りました:{message}", + "generateCsv.incorrectRowCount": "検索から生成されたCSVの行数でエラーが発生しました。正しい行数:{expected}、実際の行数:{received}。", + "generateCsv.unknownErrorMessage": "不明なエラーが発生しました:{message}", + "generateCsv.authenticationExpired.partialResultsMessage": "認証トークが有効期限切れのため、このレポートには一部のCSVの結果が含まれています。少ない量のデータをエクスポートするか、認証トークンのタイムアウトを大きくします。", + "generateCsv.csvUnableToClosePit": "検索に使用したPoint-In-Timeを閉じることができません。Kibanaサーバーログを確認してください。", + "generateCsv.escapedFormulaValues": "CSVには、値がエスケープされた式が含まれる場合があります", "xpack.rollupJobs.create.errors.dateHistogramIntervalInvalidCalendarIntervalSuggestion": "1{unit}", "xpack.rollupJobs.create.errors.idSameAsCloned": "名前はクローン名「{clonedId}」と同じにできません。", "xpack.rollupJobs.create.errors.indexPatternIllegalCharacters": "インデックスパターンから {characterList} を削除してください。", @@ -27422,7 +27285,6 @@ "xpack.security.management.editRole.featureTable.featureVisibilityTitle": "機能権限をカスタマイズ", "xpack.security.management.editRole.featureTable.managementCategoryHelpText": "スタック管理へのアクセスは、ElasticsearchとKibanaの両方の権限によって決まり、明示的に無効にすることはできません。", "xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "機能でサブ機能の権限がカスタマイズされています。この行を展開すると詳細が表示されます。", - "xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "インデックスの権限を削除", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryEditorAriaLabel": "提供されたドキュメントのクエリエディター", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "提供されたドキュメントのクエリ", "xpack.security.management.editRole.indexPrivilegeForm.grantReadPrivilegesLabel": "特定のドキュメントの読み込み権限を提供", @@ -27906,7 +27768,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "{totalRules, plural, =1 {{totalRules}個のルール} other {{totalRules}個のルール}}が正常に無効にされました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "{rulesCount, plural, =1 {#個のルール} other {#個のルール}}を複製できませんでした。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "{rulesCount, plural, other {#個の選択したルール}}を複製しています。既存の例外を複製する方法を選択してください", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "{rulesCount, plural, other {ルール}}と例外を複製しますか?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "{rulesCount, plural, other {ルール}}と例外を複製", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "{rulesCount, plural, other {ルール}}のみを複製", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "{totalRules, plural, =1 {{totalRules}個のルール} other {{totalRules}個のルール}}が正常に複製されました", @@ -27974,7 +27835,6 @@ "xpack.securitySolution.endpoint.hostIsolation.successfulIsolation.cases": "このアクションは次の{caseCount, plural, other {ケース}}に関連付けられました:", "xpack.securitySolution.endpoint.hostIsolation.unisolate.successfulMessage": "ホスト{hostName}でのリリースは正常に送信されました", "xpack.securitySolution.endpoint.hostIsolation.unIsolateThisHost": "{hostName}は現在{isolated}されています。このホストを{unisolate}しますか?", - "xpack.securitySolution.endpoint.hostIsolationStatus.multiplePendingActions": "{count}個の{count, plural, other {アクション}}が保留中", "xpack.securitySolution.endpoint.list.hostStatusValue": "{hostStatus, select, healthy {正常} unhealthy {異常} updating {更新中} offline {オフライン} inactive {非アクティブ} unenrolled {登録解除済み} other {異常}}", "xpack.securitySolution.endpoint.list.policy.revisionNumber": "rev. {revNumber}", "xpack.securitySolution.endpoint.list.totalCount": "{totalItemCount, plural, other {#個のエンドポイント}}を表示中", @@ -28050,7 +27910,6 @@ "xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle": "{riskEntity}名", "xpack.securitySolution.entityAnalytics.riskDashboard.riskClassificationTitle": "{riskEntity}リスク分類", "xpack.securitySolution.entityAnalytics.riskDashboard.riskToolTip": "{riskEntity}リスク分類は、{riskEntityLowercase}リスクスコアによって決定されます。「重大」または「高」に分類された{riskEntity}は、リスクが高いことが表示されます。", - "xpack.securitySolution.event.reason.reasonRendererTitle": "イベントレンダラー:{eventRendererName} ", "xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel": "{field}フィールドはオブジェクトであり、列として追加できるネストされたフィールドに分解されます", "xpack.securitySolution.eventDetails.viewColumnCheckboxAriaLabel": "{field}列を表示", "xpack.securitySolution.eventFilter.flyoutForm.creationSuccessToastTitle": "\"{name}\"がイベントフィルターリストに追加されました。", @@ -29098,9 +28957,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "脅威インデックスを選択", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "インデックスパターンが最低1つ必要です。", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "すべての結果", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。この頻度は対応アクションには適用されません。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "アクション頻度", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "ルールアクションを作成できません。「Actions」プラグインの「読み取り」アクセス権がありません。", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "ルールを作成して有効にする", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "有効にせずにルールを作成", @@ -30058,12 +29914,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " 例外を複製する場合は、参照によって共有例外リストが複製されます。それから、デフォルトルール例外がコピーされ、新しい例外が作成されます", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "ルールが複製されました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "複製", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "以下で選択したアクション頻度は、すべての選択したルールのすべてのアクション(新規と既存のアクション)に適用されます。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "ルールアクションを追加", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "すべての選択したルールアクションを上書き", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "ルールタイプによっては、ルール変数が選択舌一部のルールにのみ影響する場合があります(例:\\u007b\\u007bcontext.rule.threshold\\u007d\\u007dはしきい値ルールの値のみを表示します)。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "アクション頻度", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "保存", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "タイムラインテンプレートを適用", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "なし", @@ -30374,15 +30227,6 @@ "xpack.securitySolution.endpoint.hostisolation.unisolate": "リリース", "xpack.securitySolution.endpoint.hostIsolation.unisolateHost": "ホストのリリース", "xpack.securitySolution.endpoint.hostIsolationExceptions.fleetIntegration.title": "ホスト分離例外", - "xpack.securitySolution.endpoint.hostIsolationStatus.isIsolating": "分離中", - "xpack.securitySolution.endpoint.hostIsolationStatus.isolated": "分離済み", - "xpack.securitySolution.endpoint.hostIsolationStatus.isUnIsolating": "リリース中", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingActions": "保留中のアクション:", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingIsolate": "分離", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingKillProcess": "プロセスを終了", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingRunningProcesses": "プロセス", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingSuspendProcess": "プロセスを一時停止", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingUnIsolate": "リリース", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.environments": "従来のエンドポイントや動的クラウド環境を保護", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.seeDocumentationLink": "ドキュメンテーション", "xpack.securitySolution.endpoint.list.actionmenu": "開く", @@ -30983,10 +30827,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "閉じる", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "複数のファイルを選択するかドラッグしてください", "xpack.securitySolution.exceptions.exceptionListsImportButton": "リストをインポート", - "xpack.securitySolution.exceptions.exportModalCancelButton": "キャンセル", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "エクスポート", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "有効期限切れの例外を含める", - "xpack.securitySolution.exceptions.exportModalTitle": "例外リストのエクスポート", "xpack.securitySolution.exceptions.fetchError": "例外リストの取得エラー", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "例外参照の取得エラー", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "ルール例外の削除", @@ -31198,7 +31038,6 @@ "xpack.securitySolution.host.details.overview.platformTitle": "プラットフォーム", "xpack.securitySolution.host.details.overview.regionTitle": "地域", "xpack.securitySolution.host.details.versionLabel": "バージョン", - "xpack.securitySolution.hostIsolation.agentStatuses.empty": "-", "xpack.securitySolution.hostIsolationExceptions.cardActionDeleteLabel": "例外の削除", "xpack.securitySolution.hostIsolationExceptions.cardActionEditLabel": "例外の編集", "xpack.securitySolution.hostIsolationExceptions.deleteModtalTitle": "ホスト分離例外を削除", @@ -32427,7 +32266,6 @@ "xpack.sessionView.metadataDetailsTab.cloud": "クラウド", "xpack.sessionView.metadataDetailsTab.container": "コンテナー", "xpack.sessionView.metadataDetailsTab.host": "ホストOS", - "xpack.sessionView.metadataDetailsTab.metadata": "メタデータ", "xpack.sessionView.metadataDetailsTab.orchestrator": "オーケストレーター", "xpack.sessionView.networkTooltip": "ネットワークアラート", "xpack.sessionView.output": "アウトプット", @@ -34581,8 +34419,6 @@ "xpack.synthetics.coreVitals.fcpTooltip": "初回コンテンツの描画(FCP)は初期のレンダリングに集中し、ページの読み込みが開始してから、ページのコンテンツのいずれかの部分が画面に表示されるときまでの時間を測定します。", "xpack.synthetics.coreVitals.lcp.help": "最大コンテンツの描画(LCP)は読み込みパフォーマンスを計測します。優れたユーザーエクスペリエンスを実現するには、ページの読み込みが開始した後、2.5秒以内にLCPが実行されるようにしてください。", "xpack.synthetics.createMonitor.pageHeader.title": "監視の作成", - "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalLabel": "テクニカルプレビュー", - "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalTooltip": "Elastic Synthetics Recorderを使用してElastic Synthetics監視スクリプトを作成する最も簡単な方法をプレビュー", "xpack.synthetics.createPackagePolicy.stepConfigure.headerField.addHeader.label": "ヘッダーを追加", "xpack.synthetics.createPackagePolicy.stepConfigure.inputVarFieldOptionalLabel": "オプション", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.closeButtonLabel": "スクリプトフライアウトを閉じる", @@ -34908,7 +34744,6 @@ "xpack.synthetics.monitorConfig.textAssertion.label": "テキストアサーション", "xpack.synthetics.monitorConfig.throttling.helpText": "ネットワークスロットリングをシミュレートします(ダウンロード、アップロード、レイテンシ)。今後のバージョンではその他のオプションが追加されます。", "xpack.synthetics.monitorConfig.throttling.label": "接続プロファイル", - "xpack.synthetics.monitorConfig.throttling.options.default": "デフォルト", "xpack.synthetics.monitorConfig.timeout.formatError": "タイムアウトが無効です。", "xpack.synthetics.monitorConfig.timeout.greaterThan0Error": "タイムアウトは0以上でなければなりません。", "xpack.synthetics.monitorConfig.timeout.helpText": "接続のテストとデータの交換に許可された合計時間。", @@ -34946,7 +34781,6 @@ "xpack.synthetics.monitorDetails.statusBar.pingType.icmp": "ICMP", "xpack.synthetics.monitorDetails.statusBar.pingType.tcp": "TCP", "xpack.synthetics.monitorDetails.summary.availability": "可用性", - "xpack.synthetics.monitorDetails.summary.avgDuration": "平均期間", "xpack.synthetics.monitorDetails.summary.brushArea": "信頼度を高めるためにエリアを精査", "xpack.synthetics.monitorDetails.summary.complete": "完了", "xpack.synthetics.monitorDetails.summary.duration": "期間", @@ -35052,12 +34886,9 @@ "xpack.synthetics.monitorManagement.apiKey.label": "API キー", "xpack.synthetics.monitorManagement.apiKeyWarning.label": "このAPIキーは1回だけ表示されます。自分の記録用にコピーして保管してください。", "xpack.synthetics.monitorManagement.areYouSure": "この場所を削除しますか?", - "xpack.synthetics.monitorManagement.callout.apiKeyMissing": "現在、APIキーがないため、モニター管理は無効です", "xpack.synthetics.monitorManagement.callout.description.disabled": "モニター管理は現在無効です。Elasticで管理されたSyntheticsサービスでモニターを実行するには、モニター管理を有効にします。既存のモニターが一時停止しています。", - "xpack.synthetics.monitorManagement.callout.description.invalidKey": "モニター管理は現在無効です。Elasticのグローバル管理されたテストロケーションのいずれかでモニターを実行するには、モニター管理を再有効化する必要があります。", "xpack.synthetics.monitorManagement.callout.disabled": "モニター管理が無効です", "xpack.synthetics.monitorManagement.callout.disabled.adminContact": "モニター管理を有効にするには、管理者に連絡してください。", - "xpack.synthetics.monitorManagement.callout.disabledCallout.invalidKey": "モニター管理を有効にするには、管理者に連絡してください。", "xpack.synthetics.monitorManagement.cancelLabel": "キャンセル", "xpack.synthetics.monitorManagement.cannotSaveIntegration": "統合を更新する権限がありません。統合書き込み権限が必要です。", "xpack.synthetics.monitorManagement.closeButtonLabel": "閉じる", @@ -35078,8 +34909,6 @@ "xpack.synthetics.monitorManagement.editMonitorError": "モニター管理の読み込みエラー", "xpack.synthetics.monitorManagement.editMonitorError.description": "モニター管理設定を読み込めませんでした。サポートに問い合わせてください。", "xpack.synthetics.monitorManagement.emptyState.enablement": "モニター管理を有効にすると、世界中のホスティングされたテスト場所から軽量でリアルなブラウザーモニターを実行できます。モニター管理を有効にすると、APIキーが生成され、SyntheticsサービスはElasticsearchクラスターに書き込むことができます。", - "xpack.synthetics.monitorManagement.emptyState.enablement.disabled.title": "モニター管理が無効です", - "xpack.synthetics.monitorManagement.emptyState.enablement.disabledDescription": "モニター管理は現在無効です。モニター管理では、世界中のホスティングされたテスト場所から軽量でリアルなブラウザーモニターを実行できます。モニター管理を有効にするには、管理者に連絡してください。", "xpack.synthetics.monitorManagement.emptyState.enablement.doc": "ドキュメントを読む", "xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title": "モニター管理を有効にする", "xpack.synthetics.monitorManagement.emptyState.enablement.learnMore": "詳細について", @@ -35108,7 +34937,6 @@ "xpack.synthetics.monitorManagement.locationName": "場所名", "xpack.synthetics.monitorManagement.locationsLabel": "場所", "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel": "モニター管理を読み込んでいます", - "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.invalidKey": "詳細", "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.learnMore": "詳細情報", "xpack.synthetics.monitorManagement.monitorAddedSuccessMessage": "モニターが正常に追加されました。", "xpack.synthetics.monitorManagement.monitorEditedSuccessMessage": "モニターは正常に更新されました。", @@ -35148,10 +34976,7 @@ "xpack.synthetics.monitorManagement.startAddingLocationsDescription": "非公開の場所では、独自の施設からモニターを実行できます。Fleet経由で制御および保守できるElasticエージェントとエージェントポリシーが必要です。", "xpack.synthetics.monitorManagement.steps": "ステップ", "xpack.synthetics.monitorManagement.summary.heading": "まとめ", - "xpack.synthetics.monitorManagement.syntheticsDisabledFailure": "モニター管理を無効にできませんでした。サポートに問い合わせてください。", "xpack.synthetics.monitorManagement.syntheticsDisabledSuccess": "モニター管理は正常に無効にされました。", - "xpack.synthetics.monitorManagement.syntheticsEnabledFailure": "モニター管理を有効にできませんでした。サポートに問い合わせてください。", - "xpack.synthetics.monitorManagement.syntheticsEnableLabel.invalidKey": "モニター管理を有効にする", "xpack.synthetics.monitorManagement.syntheticsEnableLabel.management": "モニター管理を有効にする", "xpack.synthetics.monitorManagement.syntheticsEnableSuccess": "モニター管理は正常に有効にされました。", "xpack.synthetics.monitorManagement.testResult": "テスト結果", @@ -35167,7 +34992,6 @@ "xpack.synthetics.monitorManagement.websiteUrlLabel": "WebサイトのURL", "xpack.synthetics.monitorManagement.websiteUrlPlaceholder": "WebサイトURLを入力", "xpack.synthetics.monitorOverviewTab.title": "概要", - "xpack.synthetics.monitors.management.betaLabel": "この機能はベータ段階で、変更される可能性があります。デザインとコードは正式に一般公開された機能より完成度が低く、現状のまま保証なしで提供されています。ベータ機能は、正式に一般公開された機能に適用されるサポートサービスレベル契約の対象外です。", "xpack.synthetics.monitors.pageHeader.createButton.label": "監視の作成", "xpack.synthetics.monitors.pageHeader.title": "監視", "xpack.synthetics.monitorsPage.errors": "エラー", @@ -35253,7 +35077,6 @@ "xpack.synthetics.overview.groupPopover.project.label": "プロジェクト", "xpack.synthetics.overview.groupPopover.tag.label": "タグ", "xpack.synthetics.overview.heading": "監視", - "xpack.synthetics.overview.headingBeta": " (ベータ)", "xpack.synthetics.overview.headingBetaSection": "Synthetics", "xpack.synthetics.overview.monitors.label": "監視", "xpack.synthetics.overview.noMonitorsFoundContent": "検索を更新してください。", @@ -35669,7 +35492,7 @@ "xpack.transform.stepCreateForm.duplicateDataViewErrorMessage": "Kibanaデータビュー{dataViewName}の作成中にエラーが発生しました:データビューはすでに存在します。", "xpack.transform.stepCreateForm.startTransformErrorMessage": "変換 {transformId} の開始中にエラーが発生しました。", "xpack.transform.stepCreateForm.startTransformSuccessMessage": "変換 {transformId} の開始リクエストが受け付けられました。", - "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "無効なクエリ:{errorMessage}", + "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "無効なクエリ:{queryErrorMessage}", "xpack.transform.stepDefineForm.queryPlaceholderKql": "例: {example}.", "xpack.transform.stepDefineForm.queryPlaceholderLucene": "例: {example}.", "xpack.transform.stepDefineForm.runtimeFieldsListLabel": "{runtimeFields}", @@ -36373,7 +36196,6 @@ "xpack.triggersActionsUI.ruleSnoozeScheduler.recurYearly": "年ごと", "xpack.triggersActionsUI.ruleSnoozeScheduler.repeatIntervalLabel": "毎", "xpack.triggersActionsUI.ruleSnoozeScheduler.repeatLabel": "繰り返し", - "xpack.triggersActionsUI.ruleSnoozeScheduler.reucrringSwitch": "繰り返しにする", "xpack.triggersActionsUI.ruleSnoozeScheduler.saveSchedule": "スケジュールを保存", "xpack.triggersActionsUI.ruleSnoozeScheduler.timezoneLabel": "タイムゾーン", "xpack.triggersActionsUI.rulesSettings.flapping.alertFlappingDetection": "アラートフラップ検出", @@ -37902,7 +37724,6 @@ "visTypeHeatmap.scaleTypes.linearText": "線形", "visTypeHeatmap.scaleTypes.logText": "ログ", "visTypeHeatmap.scaleTypes.squareRootText": "平方根", - "visTypeHeatmap.splitTitle.tooltip": "分割グラフアグリゲーションは、新しいグラフライブラリでまだサポートされていません。分割グラフアグリゲーションを使用するには、ヒートマップのレガシーグラフライブラリと詳細設定を有効にしてください。", "visTypeMarkdown.function.font.help": "フォント設定です。", "visTypeMarkdown.function.help": "マークダウンビジュアライゼーション", "visTypeMarkdown.function.markdown.help": "レンダリングするマークダウン", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index dce328063bf26..7d6d834b7a36a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1090,7 +1090,6 @@ "dashboard.addPanel.newEmbeddableAddedSuccessMessageTitle": "{savedObjectName} 已添加", "dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName} 已添加", "dashboard.listing.createNewDashboard.newToKibanaDescription": "Kibana 新手?{sampleDataInstallLink}来试用一下。", - "dashboard.listing.unsaved.discardAria": "丢弃对 {title} 的更改", "dashboard.listing.unsaved.editAria": "继续编辑 {title}", "dashboard.listing.unsaved.unsavedChangesTitle": "在以下 {dash} 中有未保存更改:", "dashboard.loadingError.dashboardGridErrorMessage": "无法加载仪表板:{message}", @@ -1127,10 +1126,7 @@ "dashboard.dashboardPageTitle": "仪表板", "dashboard.dashboardWasSavedSuccessMessage": "仪表板“{dashTitle}”已保存", "dashboard.deleteError.toastDescription": "删除仪表板时发生错误", - "dashboard.discardChangesConfirmModal.cancelButtonLabel": "取消", - "dashboard.discardChangesConfirmModal.confirmButtonLabel": "放弃更改", "dashboard.discardChangesConfirmModal.discardChangesDescription": "放弃更改后,它们将无法恢复。", - "dashboard.discardChangesConfirmModal.discardChangesTitle": "放弃对仪表板所做的更改?", "dashboard.editingToolbar.addControlButtonTitle": "添加控件", "dashboard.editingToolbar.addTimeSliderControlButtonTitle": "添加时间滑块控件", "dashboard.editingToolbar.controlsButtonTitle": "控件", @@ -1164,7 +1160,6 @@ "dashboard.listing.readonlyNoItemsTitle": "没有可查看的仪表板", "dashboard.listing.table.entityName": "仪表板", "dashboard.listing.table.entityNamePlural": "仪表板", - "dashboard.listing.unsaved.discardTitle": "放弃更改", "dashboard.listing.unsaved.editTitle": "继续编辑", "dashboard.listing.unsaved.loading": "正在加载", "dashboard.loadingError.dashboardNotFound": "找不到请求的仪表板。", @@ -2147,19 +2142,15 @@ "discover.docTable.totalDocuments": "{totalDocuments} 个文档", "discover.dscTour.stepAddFields.description": "单击 {plusIcon} 以添加您感兴趣的字段。", "discover.dscTour.stepExpand.description": "单击 {expandIcon} 以查看、比较和筛选文档。", - "discover.field.title": "{fieldName} ({fieldDisplayName})", "discover.fieldChooser.detailViews.existsInRecordsText": "存在于 {value}/{totalValue} 条记录中", "discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel": "筛除 {field}“{value}”", "discover.fieldChooser.detailViews.filterValueButtonAriaLabel": "筛留 {field}“{value}”", "discover.fieldChooser.detailViews.valueOfRecordsText": "{value}/{totalValue} 条记录", - "discover.fieldChooser.discoverField.addButtonAriaLabel": "将 {field} 添加到表中", - "discover.fieldChooser.discoverField.removeButtonAriaLabel": "从表中移除 {field}", "discover.fieldChooser.fieldCalculator.fieldIsNotPresentInDocumentsErrorMessage": "此字段在您的 Elasticsearch 映射中,但不在文档表中显示的 {hitsLength} 个文档中。您可能仍能够基于它可视化或搜索。", "discover.grid.copyClipboardButtonTitle": "复制 {column} 的值", "discover.grid.copyColumnValuesToClipboard.toastTitle": "“{column}”列的值已复制到剪贴板", "discover.grid.filterForAria": "筛留此 {value}", "discover.grid.filterOutAria": "筛除此 {value}", - "discover.gridSampleSize.description": "您正查看与您的搜索相匹配的前 {sampleSize} 个文档。要更改此值,请前往 {advancedSettingsLink}", "discover.howToSeeOtherMatchingDocumentsDescription": "以下是匹配您的搜索的前 {sampleSize} 个文档,请优化您的搜索以查看其他文档。", "discover.noMatchRoute.bannerText": "Discover 应用程序无法识别此路由:{route}", "discover.noResults.kqlExamples.kqlDescription": "详细了解 {kqlLink}", @@ -2337,8 +2328,6 @@ "discover.embeddable.inspectorRequestDataTitle": "数据", "discover.embeddable.inspectorRequestDescription": "此请求将查询 Elasticsearch 以获取搜索的数据。", "discover.embeddable.search.displayName": "搜索", - "discover.field.mappingConflict": "此字段在匹配此模式的各个索引中已定义为若干类型(字符串、整数等)。您可能仍可以使用此冲突字段,但它无法用于需要 Kibana 知道其类型的函数。要解决此问题,需要重新索引您的数据。", - "discover.field.mappingConflict.title": "映射冲突", "discover.fieldChooser.addField.label": "添加字段", "discover.fieldChooser.availableFieldsTooltip": "适用于在表中显示的字段。", "discover.fieldChooser.detailViews.emptyStringText": "空字符串", @@ -2381,7 +2370,6 @@ "discover.grid.tableRow.viewSurroundingDocumentsLinkTextSimple": "周围文档", "discover.grid.tableRow.viewText": "视图:", "discover.grid.viewDoc": "切换具有详情的对话框", - "discover.gridSampleSize.advancedSettingsLinkLabel": "高级设置", "discover.helpMenu.appName": "Discover", "discover.inspectorRequestDataTitleDocuments": "文档", "discover.inspectorRequestDescriptionDocument": "此请求将查询 Elasticsearch 以获取文档。", @@ -2492,8 +2480,6 @@ "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataBody": "将不会检查与 {pattern} 模式匹配的索引,因为出现错误:{error}", "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingMetadataTitle": "将不会检查与 {pattern} 模式匹配的索引", "ecsDataQualityDashboard.emptyErrorPrompt.errorLoadingUnallowedValuesBody": "加载不允许使用的值时出现错误:{error}", - "ecsDataQualityDashboard.errorLoadingEcsMetadataLabel": "加载 ECS 元数据时出错:{details}", - "ecsDataQualityDashboard.errorLoadingEcsVersionLabel": "加载 ECS 版本时出错:{details}", "ecsDataQualityDashboard.errorLoadingIlmExplainLabel": "加载 ILM 说明时出错:{details}", "ecsDataQualityDashboard.errorLoadingMappingsLabel": "加载 {patternOrIndexName} 的映射时出错:{details}", "ecsDataQualityDashboard.errorLoadingStatsLabel": "加载统计信息时出错:{details}", @@ -2527,8 +2513,6 @@ "ecsDataQualityDashboard.cancelButton": "取消", "ecsDataQualityDashboard.checkAllButton": "全部选中", "ecsDataQualityDashboard.coldDescription": "该索引不再进行更新,且不被经常查询。这些信息仍需能够搜索,但查询速度快慢并不重要。", - "ecsDataQualityDashboard.collapseButtonLabelClosed": "已关闭", - "ecsDataQualityDashboard.collapseButtonLabelOpen": "打开", "ecsDataQualityDashboard.compareFieldsTable.documentValuesActualColumn": "文档值(实际)", "ecsDataQualityDashboard.compareFieldsTable.ecsDescriptionColumn": "ECS 描述", "ecsDataQualityDashboard.compareFieldsTable.ecsMappingTypeColumn": "ECS 映射类型", @@ -2650,7 +2634,6 @@ "ecsDataQualityDashboard.summaryTable.passedTooltip": "通过", "ecsDataQualityDashboard.summaryTable.resultColumn": "结果", "ecsDataQualityDashboard.summaryTable.thisIndexHasNotBeenCheckedTooltip": "尚未检查此索引", - "ecsDataQualityDashboard.takeActionMenu.takeActionButton": "采取操作", "ecsDataQualityDashboard.technicalPreviewBadge": "技术预览", "ecsDataQualityDashboard.timestampDescriptionLabel": "事件发生时的日期/时间。这是从事件中提取的日期/时间,通常表示源生成事件的时间。如果事件源没有原始时间戳,通常会在管道首次收到事件时填充此值。所有事件的必填字段。", "ecsDataQualityDashboard.toasts.copiedErrorsToastTitle": "已将错误复制到剪贴板", @@ -5823,7 +5806,6 @@ "unifiedSearch.search.searchBar.savedQueryPopoverApplyFilterSetLabel": "应用已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionCancelButtonText": "取消", "unifiedSearch.search.searchBar.savedQueryPopoverConfirmDeletionConfirmButtonText": "删除", - "unifiedSearch.search.searchBar.savedQueryPopoverReplaceFilterSetLabel": "替换为选定已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "另存为新的已保存查询", "unifiedSearch.search.searchBar.savedQueryPopoverSaveAsNewButtonText": "另存为新的", "unifiedSearch.search.searchBar.savedQueryPopoverSaveChangesButtonText": "保存更改", @@ -6823,7 +6805,6 @@ "visualizations.missedDataView.errorMessage": "找不到 {type}:{id}", "visualizations.newChart.conditionalMessage.newLibrary": "切换到{link}中的{type}库", "visualizations.newGaugeChart.notificationMessage": "新的仪表盘图表库尚不支持拆分图表聚合。{conditionalMessage}", - "visualizations.newHeatmapChart.notificationMessage": "新的热图图表库尚不支持拆分图表聚合。{conditionalMessage}", "visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {类型}}已找到", "visualizations.noMatchRoute.bannerText": "Visualize 应用程序无法识别此路由:{route}。", @@ -7048,13 +7029,11 @@ "xpack.aiops.logCategorization.emptyPromptBody": "日志模式分析会将消息分组到常用类别。", "xpack.aiops.logCategorization.emptyPromptTitle": "选择文本字段,然后单击“运行归类”以开始分析", "xpack.aiops.logCategorization.errorLoadingCategories": "加载类别时出错", - "xpack.aiops.logCategorization.filterOutInDiscover": "在 Discover 中筛除", "xpack.aiops.logCategorization.noCategoriesBody": "确保在选定时间范围内填充所选字段。", "xpack.aiops.logCategorization.noCategoriesTitle": "找不到类别", "xpack.aiops.logCategorization.noDocsBody": "确保选定时间范围包含文档。", "xpack.aiops.logCategorization.noDocsTitle": "找不到文档", "xpack.aiops.logCategorization.runButton": "运行归类", - "xpack.aiops.logCategorization.showInDiscover": "在 Discover 中显示这些项", "xpack.aiops.miniHistogram.noDataLabel": "不可用", "xpack.aiops.progressAriaLabel": "进度", "xpack.aiops.rerunAnalysisButtonTitle": "重新运行分析", @@ -7114,10 +7093,8 @@ "xpack.apm.agentExplorerInstanceTable.noServiceNodeName.tooltip.linkToDocs": "您可以通过 {seeDocs} 配置服务节点名称。", "xpack.apm.agentExplorerTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, other {# 个版本}}", "xpack.apm.alerts.anomalySeverity.scoreDetailsDescription": "分数 {value} {value, select, critical {} other {及以上}}", - "xpack.apm.alertTypes.errorCount.reason": "对于 {serviceName},过去 {interval}的错误计数为 {measured}。大于 {threshold} 时告警。", "xpack.apm.alertTypes.minimumWindowSize.description": "建议的最小值为 {sizeValue} {sizeUnit}这是为了确保告警具有足够的待评估数据。如果选择的值太小,可能无法按预期触发告警。", "xpack.apm.alertTypes.transactionDurationAnomaly.reason": "对于 {serviceName},过去 {interval}检测到分数为 {measured} 的 {severityLevel} 异常。", - "xpack.apm.alertTypes.transactionErrorRate.reason": "对于 {serviceName},过去 {interval}的失败事务数为 {measured}。大于 {threshold} 时告警。", "xpack.apm.anomalyDetection.createJobs.failed.text": "为 APM 服务环境 [{environments}] 创建一个或多个异常检测作业时出现问题。错误:“{errorMessage}”", "xpack.apm.anomalyDetection.createJobs.succeeded.text": "APM 服务环境 [{environments}] 的异常检测作业已成功创建。Machine Learning 要过一些时间才会开始分析流量以发现异常。", "xpack.apm.anomalyDetectionSetup.notEnabledForEnvironmentText": "尚未针对环境“{currentEnvironment}”启用异常检测。单击可继续设置。", @@ -7171,7 +7148,6 @@ "xpack.apm.serviceOveriew.errorsTableOccurrences": "{occurrences} 次", "xpack.apm.serviceOverview.embeddedMap.error.toastDescription": "未找到 ID 为“{embeddableFactoryId}”的可嵌入工厂。", "xpack.apm.serviceOverview.embeddedMap.subtitle": "根据国家和区域显示 {currentMap} 总数的地图", - "xpack.apm.serviceOverview.lensFlyout.topValues": "{metric} 的排名前 {BUCKET_SIZE} 的值", "xpack.apm.serviceOverview.mobileCallOutText": "这是一项移动服务,它当前以技术预览的形式发布。您可以通过提供反馈来帮助我们改进体验。{feedbackLink}。", "xpack.apm.servicesTable.environmentCount": "{environmentCount, plural, one {1 个环境} other {# 个环境}}", "xpack.apm.settings.agentKeys.apiKeysDisabledErrorDescription": "请联系您的系统管理员并参阅{link}以启用 API 密钥。", @@ -7553,7 +7529,6 @@ "xpack.apm.dataView.autoCreateDisabled": "已通过“autoCreateApmDataView”配置选项禁止自动创建数据视图", "xpack.apm.dataView.noApmData": "无 APM 数据", "xpack.apm.dependecyOperationDetailView.header.backLinkLabel": "所有操作", - "xpack.apm.dependencies.kueryBarPlaceholder": "搜索依赖项指标(例如,span.destination.service.resource:elasticsearch)", "xpack.apm.dependenciesInventory.dependencyTableColumn": "依赖项", "xpack.apm.dependenciesTable.columnErrorRate": "失败事务率", "xpack.apm.dependenciesTable.columnErrorRateTip": "选定服务的失败事务百分比。状态代码为 4xx 的 HTTP 服务器事务(客户端错误)不会视为失败,因为是调用方而不是服务器造成了失败。", @@ -8101,17 +8076,11 @@ "xpack.apm.serviceOverview.latencyColumnDefaultLabel": "延迟", "xpack.apm.serviceOverview.latencyColumnP95Label": "延迟(第 95 个)", "xpack.apm.serviceOverview.latencyColumnP99Label": "延迟(第 99 个)", - "xpack.apm.serviceOverview.lensFlyout.countRecords": "记录计数", "xpack.apm.serviceOverview.loadingText": "正在加载……", "xpack.apm.serviceOverview.mobileCallOutLink": "反馈", "xpack.apm.serviceOverview.mobileCallOutTitle": "移动 APM", - "xpack.apm.serviceOverview.mostUsed.appVersion": "应用版本", - "xpack.apm.serviceOverview.mostUsed.device": "设备", - "xpack.apm.serviceOverview.mostUsed.nct": "网络连接类型", - "xpack.apm.serviceOverview.mostUsed.osVersion": "操作系统版本", "xpack.apm.serviceOverview.mostUsedTitle": "最常用", "xpack.apm.serviceOverview.noResultsText": "未找到实例", - "xpack.apm.serviceOverview.openInLens": "在 Lens 中打开", "xpack.apm.serviceOverview.throughtputChartTitle": "吞吐量", "xpack.apm.serviceOverview.tpmHelp": "吞吐量按每分钟事务数 (tpm) 来度量。", "xpack.apm.serviceOverview.transactionsTableColumnErrorRate": "失败事务率", @@ -10639,8 +10608,6 @@ "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "添加 KSPM 集成", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "了解有关云安全态势的详情", "xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "在云资源中检测安全配置错误!", - "xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "失败的结果", - "xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "通过的结果", "xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "查看 CSP 仪表板", "xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "查看 CSP 结果 ", "xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "查看 CSP 规则 ", @@ -11145,8 +11112,6 @@ "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.caption": "查看索引 {indexName}", "xpack.enterpriseSearch.content.engineList.deleteEngine.successToast.title": "{engineName} 已删除", "xpack.enterpriseSearch.content.engines.createEngine.headerSubTitle": "引擎允许您的用户在索引中查询数据。请浏览我们的 {enginesDocsLink} 了解详情!", - "xpack.enterpriseSearch.content.engines.description": "引擎允许您通过一整套相关性、分析和个性化工具查询索引数据。有关引擎在 Enterprise Search 中的工作机制的详情,{documentationUrl}", - "xpack.enterpriseSearch.content.engines.enginesList.description": "正在显示第 {from}-{to} 个(共 {total} 个)", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.subTitle": "查看与 {engineName} 关联的索引", "xpack.enterpriseSearch.content.enginesList.table.column.view.indices": "{indicesCount, number} 个{indicesCount, plural, other {索引}}", "xpack.enterpriseSearch.content.index.connector.syncRules.description": "添加同步规则以定制要从 {indexName} 同步哪些数据。默认包括所有内容,并根据从上到下列出的已配置索引规则集验证文档。", @@ -11176,17 +11141,10 @@ "xpack.enterpriseSearch.content.indices.pipelines.successToastDeleteMlPipeline.title": "已删除 Machine Learning 推理管道“{pipelineName}”", "xpack.enterpriseSearch.content.indices.pipelines.successToastDetachMlPipeline.title": "已从“{pipelineName}”中分离 Machine Learning 推理管道", "xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.unmanaged.description": "从 Stack Management 中的 {ingestPipelines} 编辑此管道", - "xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description": "使用 Enterprise Search 搜索您的 {name} 内容。", - "xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage": "正在寻找更多连接器?{workplaceSearchLink}或{buildYourOwnConnectorLink}", - "xpack.enterpriseSearch.content.indices.selectConnector.successToast.title": "您的索引现在将使用 {connectorName} 本机连接器。", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.alreadyExists.error": "名为 {indexName} 的索引已存在", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.isInvalid.error": "{indexName} 为无效索引名称", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineOne": "您的索引将命名为:{indexName}", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description": "名为 {indexName} 的已删除索引最初绑定到现有连接器配置。是否要将现有连接器配置替换成新的?", - "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content": "使用我们的连接器框架和连接器客户端示例,您将能够加速任何数据源的 Elasticsearch {bulkApiDocLink} 采集。创建索引后,将引导您完成各个步骤,以访问连接器框架并连接到您的首个连接器客户端。", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content": "构建连接器后,您的内容将准备就绪。通过 {elasticsearchLink} 构建您的首次搜索体验,或浏览 {appSearchLink} 提供的搜索体验工具。我们建议您创建 {searchEngineLink},以实现灵活的功能与一站式简便性的完美平衡。", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.content": "提供唯一的索引名称,并为索引设置默认的 {languageAnalyzerDocLink}(可选)。此索引将存放您的数据源内容,并通过默认字段映射进行优化,以提供相关搜索体验。", - "xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content": "从我们的本机连接器目录中进行选择,以开始从 MongoDB 等受支持的数据源中提取可搜索内容。如果需要定制连接器的行为,您始终可以部署自我管理的连接器客户端版本并通过 {buildAConnectorLabel} 工作流进行注册。", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.description": "显示 {results} 个,共 {total} 个。搜索结果最多包含 {maximum} 个文档。", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage": "每页文档数:{docPerPage}", "xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option": "{docCount} 个文档", @@ -12246,23 +12204,9 @@ "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.done": "完成", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.generateButton": "生成只读密钥", "xpack.enterpriseSearch.content.engine.api.generateEngineApiKeyModal.title": "创建引擎只读 API 密钥", - "xpack.enterpriseSearch.content.engine.api.pageTitle": "API", - "xpack.enterpriseSearch.content.engine.api.step1.apiKeyWarning": "Elastic 不会存储 API 密钥。一旦生成,您只能查看密钥一次。请确保将其保存在某个安全位置。如果失去它的访问权限,您需要从此屏幕生成新的 API 密钥。", - "xpack.enterpriseSearch.content.engine.api.step1.createAPIKeyButton": "创建 API 密钥", - "xpack.enterpriseSearch.content.engine.api.step1.learnMoreLink": "详细了解 API 密钥。", - "xpack.enterpriseSearch.content.engine.api.step1.title": "生成并保存 API 密钥", - "xpack.enterpriseSearch.content.engine.api.step1.viewKeysButton": "查看密钥", - "xpack.enterpriseSearch.content.engine.api.step2.copyEndpointDescription": "使用此 URL 访问您引擎的 API 终端。", - "xpack.enterpriseSearch.content.engine.api.step2.title": "复制您引擎的终端", "xpack.enterpriseSearch.content.engine.api.step3.curlTitle": "cURL", "xpack.enterpriseSearch.content.engine.api.step3.intro": "了解如何将您的引擎与由 Elastic 维护的语言客户端进行集成,以帮助构建搜索体验。", "xpack.enterpriseSearch.content.engine.api.step3.searchUITitle": "搜索 UI", - "xpack.enterpriseSearch.content.engine.api.step3.title": "了解如何调用终端", - "xpack.enterpriseSearch.content.engine.api.step4.copy": "您的引擎作为此安装的一部分提供了基本分析数据。要接收更多粒度化的定制指标,请在您的平台上集成我们的行为分析脚本。", - "xpack.enterpriseSearch.content.engine.api.step4.learnHowLink": "了解操作方法", - "xpack.enterpriseSearch.content.engine.api.step4.title": "(可选)强化分析", - "xpack.enterpriseSearch.content.engine.headerActions.actionsButton.ariaLabel": "引擎操作菜单按钮", - "xpack.enterpriseSearch.content.engine.headerActions.delete": "删除此引擎", "xpack.enterpriseSearch.content.engine.indices.actions.columnTitle": "操作", "xpack.enterpriseSearch.content.engine.indices.actions.removeIndex.title": "从引擎中移除此索引", "xpack.enterpriseSearch.content.engine.indices.actions.viewIndex.title": "查看此索引", @@ -12274,7 +12218,6 @@ "xpack.enterpriseSearch.content.engine.indices.docsCount.columnTitle": "文档计数", "xpack.enterpriseSearch.content.engine.indices.health.columnTitle": "索引运行状况", "xpack.enterpriseSearch.content.engine.indices.name.columnTitle": "索引名称", - "xpack.enterpriseSearch.content.engine.indices.pageTitle": "索引", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.description": "这不会删除该索引。您可以在稍后将其重新添加到此引擎。", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.text": "是,移除此索引", "xpack.enterpriseSearch.content.engine.indices.removeIndexConfirm.title": "从引擎中移除此索引", @@ -12282,12 +12225,10 @@ "xpack.enterpriseSearch.content.engine.indicesSelect.docsLabel": "文档:", "xpack.enterpriseSearch.content.engine.schema.field_name.columnTitle": "字段名称", "xpack.enterpriseSearch.content.engine.schema.field_type.columnTitle": "字段类型", - "xpack.enterpriseSearch.content.engine.schema.pageTitle": "架构", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.confirmButton.title": "是,删除此引擎 ", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.delete.description": "删除引擎是不可逆操作。您的索引不会受到影响。", "xpack.enterpriseSearch.content.engineList.deleteEngineModal.title": "永久删除此引擎?", "xpack.enterpriseSearch.content.engineList.table.column.actions.deleteEngineLabel": "删除此引擎", - "xpack.enterpriseSearch.content.engines.breadcrumb": "引擎", "xpack.enterpriseSearch.content.engines.createEngine.header.createError.title": "创建引擎时出错", "xpack.enterpriseSearch.content.engines.createEngine.header.docsLink": "引擎文档", "xpack.enterpriseSearch.content.engines.createEngine.headerTitle": "创建引擎", @@ -12295,15 +12236,9 @@ "xpack.enterpriseSearch.content.engines.createEngine.nameEngine.title": "命名您的引擎", "xpack.enterpriseSearch.content.engines.createEngine.selectIndices.title": "选择索引", "xpack.enterpriseSearch.content.engines.createEngine.submit": "创建此引擎", - "xpack.enterpriseSearch.content.engines.createEngineButtonLabel": "创建引擎", - "xpack.enterpriseSearch.content.engines.documentation": "浏览我们的引擎文档", "xpack.enterpriseSearch.content.engines.enginesList.empty.description": "下面我们指导您创建首个引擎。", "xpack.enterpriseSearch.content.engines.enginesList.empty.title": "创建您的首个引擎", "xpack.enterpriseSearch.content.engines.indices.addIndicesFlyout.updateError.title": "更新引擎时出错", - "xpack.enterpriseSearch.content.engines.searchBar.ariaLabel": "搜索引擎", - "xpack.enterpriseSearch.content.engines.searchPlaceholder": "搜索引擎", - "xpack.enterpriseSearch.content.engines.searchPlaceholder.description": "通过名称或根据其包含的索引查找引擎。", - "xpack.enterpriseSearch.content.engines.title": "引擎", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.docsCount.columnTitle": "文档计数", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.health.columnTitle": "索引运行状况", "xpack.enterpriseSearch.content.enginesList.indicesFlyout.table.name.columnTitle": "索引名称", @@ -12360,7 +12295,6 @@ "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceDescription": "自动剪裁文档中的额外空白", "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceLabel": "减少空白", "xpack.enterpriseSearch.content.index.pipelines.settings.runMlInferenceDescrition": "使用兼容的已训练 ML 模型增强您的数据", - "xpack.enterpriseSearch.content.index.searchEngines.createEngine": "创建 App Search 引擎", "xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip": "无法从隐藏的索引中创建引擎。", "xpack.enterpriseSearch.content.index.searchEngines.label": "搜索引擎", "xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "查看 App Search 引擎", @@ -12611,12 +12545,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.message": "错误消息", "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.timestamp": "时间戳", "xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.title": "推理错误", - "xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel": "自行构建", "xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel": "文档", "xpack.enterpriseSearch.content.indices.selectConnector.description": "通过选择要配置以从数据源中提取、索引数据并将其同步到您新建的搜索索引的连接器,从而开始使用。", - "xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel": "选择并配置", "xpack.enterpriseSearch.content.indices.selectConnector.title": "选择连接器", - "xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel": "在 Workplace Search 中查看其他集成", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.attach": "附加", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create": "创建管道", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title": "配置", @@ -12642,42 +12573,19 @@ "xpack.enterpriseSearch.content.nativeConnectors.mongodb.name": "MongoDB", "xpack.enterpriseSearch.content.nativeConnectors.mysql.name": "MySQL", "xpack.enterpriseSearch.content.navTitle": "内容", - "xpack.enterpriseSearch.content.new_index.successToast.button.label": "创建引擎", - "xpack.enterpriseSearch.content.new_index.successToast.description": "您可以使用 App Search 引擎为您的新 Elasticsearch 索引构建搜索体验。", "xpack.enterpriseSearch.content.new_index.successToast.title": "已成功创建索引", "xpack.enterpriseSearch.content.newIndex.breadcrumb": "新搜索索引", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description": "通过连接 API 以编程方式添加文档", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer": "需要一些开发", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label": "使用 API", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description": "使用连接器框架为定制数据源快速构建连接器", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer": "需要开发", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label": "构建连接器", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description": "发现、提取、索引和同步所有网站内容", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer": "无需开发", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label": "使用网络爬虫", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description": "配置连接器以从受支持的数据源提取、索引和同步所有内容 ", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer": "无需开发", - "xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label": "使用连接器", "xpack.enterpriseSearch.content.newIndex.emptyState.description": "您在 Enterprise Search 中添加的数据称为搜索索引,您可在 App Search 和 Workplace Search 中搜索这些数据。现在,您可以在 App Search 中使用连接器,在 Workplace Search 中使用网络爬虫。", "xpack.enterpriseSearch.content.newIndex.emptyState.footer.link": "阅读文档", "xpack.enterpriseSearch.content.newIndex.emptyState.footer.title": "想要详细了解搜索索引?", "xpack.enterpriseSearch.content.newIndex.emptyState.title": "选择采集方法", - "xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content": "生成 API 密钥并查看文档,以便将文档发布到 Elasticsearch API 终端。语言客户端可用于精简集成。", - "xpack.enterpriseSearch.content.newIndex.methodApi.title": "使用 API 编制索引", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink": "批量 API", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title": "构建并配置连接器", - "xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title": "配置连接器", - "xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content": "配置您要爬网的域,并在准备就绪后触发第一次爬网。让 Enterprise Search 执行其余操作。", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.createIndex.buttonText": "创建索引", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputHelpText": "可以在稍后更改语言,但可能需要重新索引", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputLabel": "语言分析器", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineTwo": "名称应为小写,并且不能包含空格或特殊字符。", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel": "索引名称", "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder": "设置索引的名称", - "xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText": "查看文档", "xpack.enterpriseSearch.content.newIndex.pageTitle": "新搜索索引", - "xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description": "通过为您的用例选择采集方法来创建搜索优化的 Elasticsearch 索引。", - "xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title": "选择采集方法", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label": "取消", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label": "替换配置", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title": "替换现有连接器", @@ -12685,16 +12593,6 @@ "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.genericError": "无法创建您的索引", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.indexAlreadyExists": "此索引已存在", "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.unauthorizedError": "您无权创建此连接器", - "xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title": "构建连接器", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink": "App Search", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink": "Elasticsearch", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink": "搜索引擎", - "xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title": "构建搜索体验", - "xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title": "配置采集设置", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title": "使用网络爬虫编制索引", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink": "语言分析器", - "xpack.enterpriseSearch.content.newIndex.steps.createIndex.title": "创建 Elasticsearch 索引", - "xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title": "使用连接器进行索引", "xpack.enterpriseSearch.content.newIndex.types.api": "API 终端", "xpack.enterpriseSearch.content.newIndex.types.connector": "连接器", "xpack.enterpriseSearch.content.newIndex.types.crawler": "网络爬虫", @@ -12800,7 +12698,6 @@ "xpack.enterpriseSearch.content.searchIndices.syncStatus.columnTitle": "状态", "xpack.enterpriseSearch.content.settings.breadcrumb": "设置", "xpack.enterpriseSearch.content.settings.contactExtraction.label": "内容提取", - "xpack.enterpriseSearch.content.settings.contactExtraction.link": "详细了解内容提取", "xpack.enterpriseSearch.content.settings.contentExtraction.description": "允许您的 Enterprise Search 部署上的所有采集机制从 PDF 和 Word 文档等二进制文件中提取可搜索内容。此设置适用于由 Enterprise Search 采集机制创建的所有新 Elasticsearch 索引。", "xpack.enterpriseSearch.content.settings.contentExtraction.descriptionTwo": "您还可以在索引的配置页面针对特定索引启用或禁用此功能。", "xpack.enterpriseSearch.content.settings.contentExtraction.title": "部署广泛内容提取", @@ -12814,7 +12711,6 @@ "xpack.enterpriseSearch.content.settings.whitespaceReduction.deploymentHeaderTitle": "部署广泛的空白缩减", "xpack.enterpriseSearch.content.settings.whiteSpaceReduction.description": "默认情况下,空白缩减将清除空白的全文本内容。", "xpack.enterpriseSearch.content.settings.whitespaceReduction.label": "空白缩减", - "xpack.enterpriseSearch.content.settings.whitespaceReduction.link": "详细了解空白缩减", "xpack.enterpriseSearch.content.shared.result.header.metadata.deleteDocument": "删除文档", "xpack.enterpriseSearch.content.shared.result.header.metadata.title": "文档元数据", "xpack.enterpriseSearch.content.sources.basicRulesTable.includeEverythingMessage": "包括来自此源的所有其他内容", @@ -13095,8 +12991,6 @@ "xpack.enterpriseSearch.emailLabel": "电子邮件", "xpack.enterpriseSearch.emptyState.description": "您的内容存储在 Elasticsearch 索引中。通过创建 Elasticsearch 索引并选择采集方法开始使用。选项包括 Elastic 网络爬虫、第三方数据集成或使用 Elasticsearch API 终端。", "xpack.enterpriseSearch.emptyState.description.line2": "无论是使用 App Search 还是 Elasticsearch 构建搜索体验,您都可以从此处立即开始。", - "xpack.enterpriseSearch.engines.engine.notFound.action1": "返回到引擎", - "xpack.enterpriseSearch.engines.navTitle": "引擎", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.description": "随时随地进行全面搜索。为工作繁忙的团队轻松实现强大的现代搜索体验。将预先调整的搜索功能快速添加到您的网站、应用或工作区。全面搜索就是这么简单。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.notConfigured": "企业搜索尚未在您的 Kibana 实例中配置。", "xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt": "企业搜索入门", @@ -13184,9 +13078,6 @@ "xpack.enterpriseSearch.nav.contentSettingsTitle": "设置", "xpack.enterpriseSearch.nav.contentTitle": "内容", "xpack.enterpriseSearch.nav.elasticsearchTitle": "Elasticsearch", - "xpack.enterpriseSearch.nav.engine.apiTitle": "API", - "xpack.enterpriseSearch.nav.engine.indicesTitle": "索引", - "xpack.enterpriseSearch.nav.engine.schemaTitle": "架构", "xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle": "概览", "xpack.enterpriseSearch.nav.searchExperiencesTitle": "搜索体验", "xpack.enterpriseSearch.nav.searchIndicesTitle": "索引", @@ -17427,29 +17318,19 @@ "xpack.infra.hostsViewPage.landing.introTitle": "即将引入:主机分析", "xpack.infra.hostsViewPage.landing.learnMore": "了解详情", "xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "这是早期版本的功能,我们需要您的反馈,\n 以便继续开发和改进该功能。要访问该功能,直接在下面启用即可。请抓紧时间,\n 了解新添加到我们平台中的这项强大功能 - 立即试用!", - "xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description": "显示一段时间的主要指标趋势的折线图。", - "xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title": "一段时间的 CPU 使用率。", - "xpack.infra.hostsViewPage.metricTrend.cpu.subtitle": "平均值", "xpack.infra.hostsViewPage.metricTrend.cpu.title": "CPU 使用", "xpack.infra.hostsViewPage.metricTrend.cpu.tooltip": "CPU 在空闲和 IOWait 状态以外所花费时间的平均百分比,按 CPU 核心数进行标准化。包括在用户空间和内核空间上花费的时间。100% 表示主机的所有 CPU 都处于忙碌状态。", - "xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title": "一段时间的 CPU 使用率。", "xpack.infra.hostsViewPage.metricTrend.hostCount.title": "主机", "xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "当前搜索条件返回的主机数。", - "xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription": "显示一段时间的主要指标趋势的折线图。", - "xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle": "一段时间的内存使用率。", - "xpack.infra.hostsViewPage.metricTrend.memory.subtitle": "平均值", "xpack.infra.hostsViewPage.metricTrend.memory.title": "内存使用", "xpack.infra.hostsViewPage.metricTrend.memory.tooltip": "主内存使用率(不包括页面缓存)的平均百分比。这包括所有进程的常驻内存,加上由内核结构和代码使用的内存,但不包括页面缓存。高比率表明主机出现内存饱和情况。100% 表示主内存被完全占用,除了进行换出外无法回收内存。", - "xpack.infra.hostsViewPage.metricTrend.rx.a11y.description": "显示一段时间的主要指标趋势的折线图。", - "xpack.infra.hostsViewPage.metricTrend.rx.a11y.title": "一段时间的网络入站数据 (RX)。", - "xpack.infra.hostsViewPage.metricTrend.rx.subtitle": "平均值", "xpack.infra.hostsViewPage.metricTrend.rx.title": "网络入站数据 (RX)", "xpack.infra.hostsViewPage.metricTrend.rx.tooltip": "主机的公共接口上每秒接收的字节数。", - "xpack.infra.hostsViewPage.metricTrend.tx.a11.title": "一段时间的网络出站数据 (TX) 使用量。", - "xpack.infra.hostsViewPage.metricTrend.tx.a11y.description": "显示一段时间的主要指标趋势的折线图。", - "xpack.infra.hostsViewPage.metricTrend.tx.subtitle": "平均值", "xpack.infra.hostsViewPage.metricTrend.tx.title": "网络出站数据 (TX)", "xpack.infra.hostsViewPage.metricTrend.tx.tooltip": "主机的公共接口上每秒发送的字节数", + "xpack.infra.hostsViewPage.metricTrend.subtitle.average": "平均值", + "xpack.infra.hostsViewPage.metricTrend.subtitle.average.limit": "平均值 (of {limit} hosts)", + "xpack.infra.hostsViewPage.metricTrend.subtitle.hostCount.limit": "Limited to {limit}", "xpack.infra.hostsViewPage.table.averageMemoryTotalColumnHeader": "内存合计(平均值)", "xpack.infra.hostsViewPage.table.averageMemoryUsageColumnHeader": "内存使用率(平均值)", "xpack.infra.hostsViewPage.table.averageRxColumnHeader": "RX(平均值)", @@ -19538,10 +19419,7 @@ "xpack.lens.indexPattern.emptyFieldsLabel": "空字段", "xpack.lens.indexPattern.enableAccuracyMode": "启用准确性模式", "xpack.lens.indexPattern.fieldExploreInDiscover": "在 Discover 中浏览", - "xpack.lens.indexPattern.fieldItemTooltip": "拖放以可视化。", "xpack.lens.indexPattern.fieldPlaceholder": "字段", - "xpack.lens.indexPattern.fieldStatsButtonEmptyLabel": "此字段不包含任何数据,但您仍然可以拖放以进行可视化。", - "xpack.lens.indexPattern.fieldStatsButtonLabel": "单击以进行字段预览,或拖放以进行可视化。", "xpack.lens.indexPattern.fieldStatsNoData": "Lens 无法使用此字段创建可视化,因为其中未包含数据。要创建可视化,请拖放其他字段。", "xpack.lens.indexPattern.filterBy.clickToEdit": "单击以编辑", "xpack.lens.indexPattern.filterBy.emptyFilterQuery": "(空)", @@ -20416,15 +20294,11 @@ "xpack.maps.addLayerPanel.addLayer": "添加图层", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "更改图层", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "取消", - "xpack.maps.aggs.defaultCountLabel": "计数", "xpack.maps.attribution.addBtnAriaLabel": "添加归因", "xpack.maps.attribution.addBtnLabel": "添加归因", - "xpack.maps.attribution.applyBtnLabel": "应用", "xpack.maps.attribution.attributionFormLabel": "归因", "xpack.maps.attribution.clearBtnAriaLabel": "清除归因", - "xpack.maps.attribution.clearBtnLabel": "清除", "xpack.maps.attribution.editBtnAriaLabel": "编辑归因", - "xpack.maps.attribution.editBtnLabel": "编辑", "xpack.maps.attribution.labelFieldLabel": "标签", "xpack.maps.attribution.urlLabel": "链接", "xpack.maps.badge.readOnly.text": "只读", @@ -20661,7 +20535,6 @@ "xpack.maps.mapActions.removeFeatureError": "无法从索引中移除特征。", "xpack.maps.mapListing.entityName": "地图", "xpack.maps.mapListing.entityNamePlural": "地图", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "无法加载地图", "xpack.maps.mapSavedObjectLabel": "地图", "xpack.maps.mapSettingsPanel.addCustomIcon": "添加定制图标", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "使地图自动适应数据边界", @@ -20697,15 +20570,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "字段", "xpack.maps.metricsEditor.selectFieldPlaceholder": "选择字段", "xpack.maps.metricsEditor.selectPercentileLabel": "百分位数", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "平均值", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "唯一计数", - "xpack.maps.metricSelect.countDropDownOptionLabel": "计数", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "最大值", - "xpack.maps.metricSelect.minDropDownOptionLabel": "最小值", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "百分位数", "xpack.maps.metricSelect.selectAggregationPlaceholder": "选择聚合", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "求和", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "热门词", "xpack.maps.mvtSource.addFieldLabel": "添加", "xpack.maps.mvtSource.fieldPlaceholderText": "字段名称", "xpack.maps.mvtSource.numberFieldLabel": "数字", @@ -21513,7 +21378,6 @@ "xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomAggregationIntervalLabel": "(聚合时间间隔:{focusAggInt},存储桶跨度:{bucketSpan})", "xpack.ml.trainedModels.modelsList.deleteModal.header": "删除 {modelsCount, plural, one {{modelId}} other {# 个模型}}?", "xpack.ml.trainedModels.modelsList.fetchDeletionErrorMessage": "{modelsCount, plural, other {模型}}删除失败", - "xpack.ml.trainedModels.modelsList.forceStopDialog.title": "停止模型 {modelId}", "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, other {# 个模型}}已选择", "xpack.ml.trainedModels.modelsList.startDeployment.modalTitle": "启动 {modelId} 部署", "xpack.ml.trainedModels.modelsList.startFailed": "无法启动“{modelId}”", @@ -23505,7 +23369,7 @@ "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "您无权查看筛选列表", "xpack.ml.ruleEditor.scopeSection.scopeTitle": "范围", "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "创建规则", - "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ", + "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ", "xpack.ml.ruleEditor.typicalAppliesTypeText": "典型", "xpack.ml.sampleDataLinkLabel": "ML 作业", "xpack.ml.selectDataViewLabel": "选择数据视图", @@ -26588,9 +26452,6 @@ "xpack.reporting.diagnostic.browserMissingFonts": "浏览器找不到默认字体。请参见 {url} 以解决此问题。", "xpack.reporting.diagnostic.noUsableSandbox": "无法使用 Chromium 沙盒。您自行承担使用“xpack.screenshotting.browser.chromium.disableSandbox”禁用此项的风险。请参见 {url}", "xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}", - "generateCsv.esErrorMessage": "从 Elasticsearch 收到 {statusCode} 响应:{message}", - "generateCsv.incorrectRowCount": "从搜索生成的 CSV 行数出现错误:应为 {expected},但收到 {received}。", - "generateCsv.unknownErrorMessage": "出现未知错误:{message}", "xpack.reporting.jobResponse.errorHandler.notAuthorized": "抱歉,您无权查看或删除 {jobtype} 报告", "xpack.reporting.jobsQuery.deleteError": "无法删除报告:{error}", "xpack.reporting.jobStatusDetail.attemptXofY": "尝试 {attempts} 次,最多可尝试 {max_attempts} 次。", @@ -26647,9 +26508,6 @@ "xpack.reporting.diagnostic.screenshotFailureMessage": "我们无法拍摄 Kibana 安装的屏幕截图。", "xpack.reporting.errorHandler.unknownError": "未知错误", "xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "作业标头缺失", - "generateCsv.authenticationExpired.partialResultsMessage": "此报告包含部分 CSV 结果,因为身份验证令牌已过期。导出更少量的数据,或增加身份验证令牌的超时时间。", - "generateCsv.csvUnableToClosePit": "无法关闭用于搜索的时间点。查看 Kibana 服务器日志。", - "generateCsv.escapedFormulaValues": "CSV 可能包含值已转义的公式", "xpack.reporting.jobCreatedBy.unknownUserPlaceholderText": "未知", "xpack.reporting.jobResponse.errorHandler.unknownError": "未知错误", "xpack.reporting.jobStatusDetail.deprecatedText": "这是已弃用的导出类型。此报告的自动化将需要重新创建,才能与未来版本的 Kibana 兼容。", @@ -26786,6 +26644,12 @@ "xpack.reporting.uiSettings.validate.customLogo.badFile": "抱歉,该文件无效。请尝试其他图像文件。", "xpack.reporting.uiSettings.validate.customLogo.tooLarge": "抱歉,该文件过大。图像文件必须小于 200 千字节。", "xpack.reporting.userAccessError.learnMoreLink": "了解详情", + "generateCsv.esErrorMessage": "从 Elasticsearch 收到 {statusCode} 响应:{message}", + "generateCsv.incorrectRowCount": "从搜索生成的 CSV 行数出现错误:应为 {expected},但收到 {received}。", + "generateCsv.unknownErrorMessage": "出现未知错误:{message}", + "generateCsv.authenticationExpired.partialResultsMessage": "此报告包含部分 CSV 结果,因为身份验证令牌已过期。导出更少量的数据,或增加身份验证令牌的超时时间。", + "generateCsv.csvUnableToClosePit": "无法关闭用于搜索的时间点。查看 Kibana 服务器日志。", + "generateCsv.escapedFormulaValues": "CSV 可能包含值已转义的公式", "xpack.rollupJobs.create.errors.dateHistogramIntervalInvalidCalendarIntervalSuggestion": "1{unit}", "xpack.rollupJobs.create.errors.idSameAsCloned": "名称不能与克隆名称相同:“{clonedId}。", "xpack.rollupJobs.create.errors.indexPatternIllegalCharacters": "从索引模式中删除 {characterList} 字符。", @@ -27437,7 +27301,6 @@ "xpack.security.management.editRole.featureTable.featureVisibilityTitle": "定制功能权限", "xpack.security.management.editRole.featureTable.managementCategoryHelpText": "对堆栈管理的访问由 Elasticsearch 和 Kibana 权限共同决定,并且不能显式禁用。", "xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "功能已定制子功能权限。展开此行以了解更多信息。", - "xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "删除索引权限", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryEditorAriaLabel": "已授权文档查询编辑器", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "已授权文档查询", "xpack.security.management.editRole.indexPrivilegeForm.grantReadPrivilegesLabel": "授予特定文档的读取权限", @@ -27921,7 +27784,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "已成功禁用 {totalRules, plural, other {{totalRules} 个规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "无法复制 {rulesCount, plural, other {# 个规则}}。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "您正在复制 {rulesCount, plural, other {# 个选定规则}},请选择您希望如何复制现有例外", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "复制存在例外的{rulesCount, plural, other {规则}}?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "复制{rulesCount, plural, other {规则}}及其例外", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "仅复制{rulesCount, plural, other {规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "已成功复制 {totalRules, plural, other {{totalRules} 个规则}}", @@ -27990,7 +27852,6 @@ "xpack.securitySolution.endpoint.hostIsolation.successfulIsolation.cases": "此操作已附加到以下{caseCount, plural, other {案例}}:", "xpack.securitySolution.endpoint.hostIsolation.unisolate.successfulMessage": "已成功提交主机 {hostName} 的释放", "xpack.securitySolution.endpoint.hostIsolation.unIsolateThisHost": "{hostName} 当前 {isolated}。是否确定要{unisolate}此主机?", - "xpack.securitySolution.endpoint.hostIsolationStatus.multiplePendingActions": "{count} 个{count, plural, other {操作}}未决", "xpack.securitySolution.endpoint.list.hostStatusValue": "{hostStatus, select, healthy {运行正常} unhealthy {运行不正常} updating {正在更新} offline {脱机} inactive {非活动} unenrolled {未注册} other {运行不正常}}", "xpack.securitySolution.endpoint.list.policy.revisionNumber": "rev. {revNumber}", "xpack.securitySolution.endpoint.list.totalCount": "正在显示 {totalItemCount, plural, other {# 个终端}}", @@ -28066,7 +27927,6 @@ "xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle": "{riskEntity} 名称", "xpack.securitySolution.entityAnalytics.riskDashboard.riskClassificationTitle": "{riskEntity} 风险分类", "xpack.securitySolution.entityAnalytics.riskDashboard.riskToolTip": "{riskEntity} 风险分类由 {riskEntityLowercase} 风险分数决定。分类为紧急或高的{riskEntity}主机即表示存在风险。", - "xpack.securitySolution.event.reason.reasonRendererTitle": "事件呈现器:{eventRendererName}", "xpack.securitySolution.eventDetails.nestedColumnCheckboxAriaLabel": "{field} 字段是对象,并分解为可以添加为列的嵌套字段", "xpack.securitySolution.eventDetails.viewColumnCheckboxAriaLabel": "查看 {field} 列", "xpack.securitySolution.eventFilter.flyoutForm.creationSuccessToastTitle": "“{name}”已添加到事件筛选列表。", @@ -29114,9 +28974,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "选择威胁索引", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "至少需要一种索引模式。", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "所有结果", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "选择在规则评估为 true 时应执行自动操作的时间。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "选择在规则评估为 true 时应执行自动操作的时间。此频率不适用于响应操作。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "操作频率", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "无法创建规则操作。您对“操作”插件没有“读”权限。", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "创建并启用规则", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "创建规则但不启用", @@ -30074,12 +29931,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " 如果您复制例外,则会通过引用复制共享例外列表,然后复制默认规则例外,并将其创建为新例外", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "规则已复制", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "复制", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "您在下面选择的操作频率将应用于所有选定规则的所有操作(新操作和现有操作)。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "添加规则操作", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "覆盖所有选定规则操作", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "基于规则类型,规则变量可能仅影响您选择的某些规则(例如,\\u007b\\u007bcontext.rule.threshold\\u007d\\u007d 将仅显示阈值规则的值)。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "选择在规则评估为 true 时应执行自动操作的时间。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "操作频率", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "保存", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "应用时间线模板", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "无", @@ -30390,15 +30244,6 @@ "xpack.securitySolution.endpoint.hostisolation.unisolate": "释放", "xpack.securitySolution.endpoint.hostIsolation.unisolateHost": "释放主机", "xpack.securitySolution.endpoint.hostIsolationExceptions.fleetIntegration.title": "主机隔离例外", - "xpack.securitySolution.endpoint.hostIsolationStatus.isIsolating": "正在隔离", - "xpack.securitySolution.endpoint.hostIsolationStatus.isolated": "已隔离", - "xpack.securitySolution.endpoint.hostIsolationStatus.isUnIsolating": "正在释放", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingActions": "未决操作:", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingIsolate": "隔离", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingKillProcess": "结束进程", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingRunningProcesses": "进程", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingSuspendProcess": "挂起进程", - "xpack.securitySolution.endpoint.hostIsolationStatus.tooltipPendingUnIsolate": "释放", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.environments": "保护您的传统终端或动态云环境", "xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.seeDocumentationLink": "文档", "xpack.securitySolution.endpoint.list.actionmenu": "打开", @@ -30999,10 +30844,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "关闭", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "选择或拖放多个文件", "xpack.securitySolution.exceptions.exceptionListsImportButton": "导入列表", - "xpack.securitySolution.exceptions.exportModalCancelButton": "取消", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "导出", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "包括已过期例外", - "xpack.securitySolution.exceptions.exportModalTitle": "导出例外列表", "xpack.securitySolution.exceptions.fetchError": "提取例外列表时出错", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "提取例外引用时出错", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "删除规则例外", @@ -31214,7 +31055,6 @@ "xpack.securitySolution.host.details.overview.platformTitle": "平台", "xpack.securitySolution.host.details.overview.regionTitle": "地区", "xpack.securitySolution.host.details.versionLabel": "版本", - "xpack.securitySolution.hostIsolation.agentStatuses.empty": "-", "xpack.securitySolution.hostIsolationExceptions.cardActionDeleteLabel": "删除例外", "xpack.securitySolution.hostIsolationExceptions.cardActionEditLabel": "编辑例外", "xpack.securitySolution.hostIsolationExceptions.deleteModtalTitle": "删除主机隔离例外", @@ -32443,7 +32283,6 @@ "xpack.sessionView.metadataDetailsTab.cloud": "云", "xpack.sessionView.metadataDetailsTab.container": "容器", "xpack.sessionView.metadataDetailsTab.host": "主机 OS", - "xpack.sessionView.metadataDetailsTab.metadata": "元数据", "xpack.sessionView.metadataDetailsTab.orchestrator": "Orchestrator", "xpack.sessionView.networkTooltip": "网络告警", "xpack.sessionView.output": "输出", @@ -34597,8 +34436,6 @@ "xpack.synthetics.coreVitals.fcpTooltip": "首次内容绘制 (FCP) 侧重于初始渲染,并测量从页面开始加载到页面内容的任何部分显示在屏幕的时间。", "xpack.synthetics.coreVitals.lcp.help": "最大内容绘制用于衡量加载性能。为了提供良好的用户体验,LCP 应在页面首次开始加载后的 2.5 秒内执行。", "xpack.synthetics.createMonitor.pageHeader.title": "创建监测", - "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalLabel": "技术预览", - "xpack.synthetics.createPackagePolicy.stepConfigure.browser.scriptRecorder.experimentalTooltip": "预览通过 Elastic Synthetics 记录器创建 Elastic Synthetics 监测脚本的最快方式", "xpack.synthetics.createPackagePolicy.stepConfigure.headerField.addHeader.label": "添加标头", "xpack.synthetics.createPackagePolicy.stepConfigure.inputVarFieldOptionalLabel": "可选", "xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.closeButtonLabel": "关闭脚本浮出控件", @@ -34924,7 +34761,6 @@ "xpack.synthetics.monitorConfig.textAssertion.label": "文本断言", "xpack.synthetics.monitorConfig.throttling.helpText": "模拟网络限制(下载、上传、延迟)。将在未来版本中添加更多选项。", "xpack.synthetics.monitorConfig.throttling.label": "连接配置文件", - "xpack.synthetics.monitorConfig.throttling.options.default": "默认", "xpack.synthetics.monitorConfig.timeout.formatError": "超时无效。", "xpack.synthetics.monitorConfig.timeout.greaterThan0Error": "超时必须大于或等于 0。", "xpack.synthetics.monitorConfig.timeout.helpText": "允许用于测试连接并交换数据的总时间。", @@ -34962,7 +34798,6 @@ "xpack.synthetics.monitorDetails.statusBar.pingType.icmp": "ICMP", "xpack.synthetics.monitorDetails.statusBar.pingType.tcp": "TCP", "xpack.synthetics.monitorDetails.summary.availability": "可用性", - "xpack.synthetics.monitorDetails.summary.avgDuration": "平均持续时间", "xpack.synthetics.monitorDetails.summary.brushArea": "轻刷某个区域以提高保真度", "xpack.synthetics.monitorDetails.summary.complete": "已完成", "xpack.synthetics.monitorDetails.summary.duration": "持续时间", @@ -35068,12 +34903,9 @@ "xpack.synthetics.monitorManagement.apiKey.label": "API 密钥", "xpack.synthetics.monitorManagement.apiKeyWarning.label": "此 API 密钥仅显示一次。请保留副本作为您自己的记录。", "xpack.synthetics.monitorManagement.areYouSure": "是否确定要删除此位置?", - "xpack.synthetics.monitorManagement.callout.apiKeyMissing": "由于缺少 API 密钥,监测管理当前已禁用", "xpack.synthetics.monitorManagement.callout.description.disabled": "监测管理当前处于禁用状态。要在 Elastic 托管 Synthetics 服务上运行监测,请启用监测管理。现有监测已暂停。", - "xpack.synthetics.monitorManagement.callout.description.invalidKey": "监测管理当前处于禁用状态。要在 Elastic 的全球托管测试位置之一运行监测,您需要重新启用监测管理。", "xpack.synthetics.monitorManagement.callout.disabled": "已禁用监测管理", "xpack.synthetics.monitorManagement.callout.disabled.adminContact": "请联系管理员启用监测管理。", - "xpack.synthetics.monitorManagement.callout.disabledCallout.invalidKey": "请联系管理员启用监测管理。", "xpack.synthetics.monitorManagement.cancelLabel": "取消", "xpack.synthetics.monitorManagement.cannotSaveIntegration": "您无权更新集成。需要集成写入权限。", "xpack.synthetics.monitorManagement.closeButtonLabel": "关闭", @@ -35094,8 +34926,6 @@ "xpack.synthetics.monitorManagement.editMonitorError": "加载监测管理时出错", "xpack.synthetics.monitorManagement.editMonitorError.description": "无法加载监测管理设置。请联系支持人员。", "xpack.synthetics.monitorManagement.emptyState.enablement": "启用监测管理以从全球托管测试地点运行轻量级、真正的浏览器监测。启用监测管理将生成 API 密钥,以便 Synthetics 服务回写 Elasticsearch 集群。", - "xpack.synthetics.monitorManagement.emptyState.enablement.disabled.title": "已禁用监测管理", - "xpack.synthetics.monitorManagement.emptyState.enablement.disabledDescription": "监测管理当前处于禁用状态。通过监测管理,您可以从全球托管测试地点运行轻量级、真正的浏览器监测。要启用监测管理,请与管理员联系。", "xpack.synthetics.monitorManagement.emptyState.enablement.doc": "阅读文档", "xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title": "启用监测管理", "xpack.synthetics.monitorManagement.emptyState.enablement.learnMore": "希望了解详情?", @@ -35124,7 +34954,6 @@ "xpack.synthetics.monitorManagement.locationName": "位置名称", "xpack.synthetics.monitorManagement.locationsLabel": "位置", "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel": "正在加载监测管理", - "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.invalidKey": "了解详情", "xpack.synthetics.monitorManagement.manageMonitorLoadingLabel.callout.learnMore": "了解详情。", "xpack.synthetics.monitorManagement.monitorAddedSuccessMessage": "已成功添加监测。", "xpack.synthetics.monitorManagement.monitorEditedSuccessMessage": "已成功更新监测。", @@ -35164,10 +34993,7 @@ "xpack.synthetics.monitorManagement.startAddingLocationsDescription": "专用位置供您从自己的场所运行监测。它们需要可以通过 Fleet 进行控制和维护的 Elastic 代理和代理策略。", "xpack.synthetics.monitorManagement.steps": "步长", "xpack.synthetics.monitorManagement.summary.heading": "摘要", - "xpack.synthetics.monitorManagement.syntheticsDisabledFailure": "无法禁用监测管理。请联系支持人员。", "xpack.synthetics.monitorManagement.syntheticsDisabledSuccess": "已成功禁用监测管理。", - "xpack.synthetics.monitorManagement.syntheticsEnabledFailure": "无法启用监测管理。请联系支持人员。", - "xpack.synthetics.monitorManagement.syntheticsEnableLabel.invalidKey": "启用监测管理", "xpack.synthetics.monitorManagement.syntheticsEnableLabel.management": "启用监测管理", "xpack.synthetics.monitorManagement.syntheticsEnableSuccess": "已成功启用监测管理。", "xpack.synthetics.monitorManagement.testResult": "测试结果", @@ -35183,7 +35009,6 @@ "xpack.synthetics.monitorManagement.websiteUrlLabel": "网站 URL", "xpack.synthetics.monitorManagement.websiteUrlPlaceholder": "输入网站 URL", "xpack.synthetics.monitorOverviewTab.title": "概览", - "xpack.synthetics.monitors.management.betaLabel": "此功能为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能的支持服务水平协议约束。", "xpack.synthetics.monitors.pageHeader.createButton.label": "创建监测", "xpack.synthetics.monitors.pageHeader.title": "监测", "xpack.synthetics.monitorsPage.errors": "错误", @@ -35269,7 +35094,6 @@ "xpack.synthetics.overview.groupPopover.project.label": "项目", "xpack.synthetics.overview.groupPopover.tag.label": "标签", "xpack.synthetics.overview.heading": "监测", - "xpack.synthetics.overview.headingBeta": " (公测版)", "xpack.synthetics.overview.headingBetaSection": "Synthetics", "xpack.synthetics.overview.monitors.label": "监测", "xpack.synthetics.overview.noMonitorsFoundContent": "请尝试优化您的搜索。", @@ -35685,7 +35509,7 @@ "xpack.transform.stepCreateForm.duplicateDataViewErrorMessage": "创建 Kibana 数据视图 {dataViewName} 时发生错误:数据视图已存在。", "xpack.transform.stepCreateForm.startTransformErrorMessage": "启动转换 {transformId} 时发生错误:", "xpack.transform.stepCreateForm.startTransformSuccessMessage": "启动转换 {transformId} 的请求已确认。", - "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "无效查询:{errorMessage}", + "xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar": "无效查询:{queryErrorMessage}", "xpack.transform.stepDefineForm.queryPlaceholderKql": "例如,{example}", "xpack.transform.stepDefineForm.queryPlaceholderLucene": "例如,{example}", "xpack.transform.stepDefineForm.runtimeFieldsListLabel": "{runtimeFields}", @@ -36400,7 +36224,6 @@ "xpack.triggersActionsUI.ruleSnoozeScheduler.recurYearly": "每年", "xpack.triggersActionsUI.ruleSnoozeScheduler.repeatIntervalLabel": "每", "xpack.triggersActionsUI.ruleSnoozeScheduler.repeatLabel": "重复", - "xpack.triggersActionsUI.ruleSnoozeScheduler.reucrringSwitch": "设为定期", "xpack.triggersActionsUI.ruleSnoozeScheduler.saveSchedule": "保存计划", "xpack.triggersActionsUI.ruleSnoozeScheduler.timezoneLabel": "时区", "xpack.triggersActionsUI.rulesSettings.flapping.alertFlappingDetection": "告警摆动检测", @@ -37929,7 +37752,6 @@ "visTypeHeatmap.scaleTypes.linearText": "线性", "visTypeHeatmap.scaleTypes.logText": "对数", "visTypeHeatmap.scaleTypes.squareRootText": "平方根", - "visTypeHeatmap.splitTitle.tooltip": "新图表库尚不支持拆分图表聚合。请启用热图旧版图表库高级设置以使用拆分图表聚合。", "visTypeMarkdown.function.font.help": "字体设置。", "visTypeMarkdown.function.help": "Markdown 可视化", "visTypeMarkdown.function.markdown.help": "要渲染的 Markdown", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 19b66ff62d524..22ead5ca4d2fe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -68,6 +68,7 @@ export const RULE_EXECUTION_LOG_COLUMN_IDS = [ 'es_search_duration', 'schedule_delay', 'timed_out', + 'maintenance_window_ids', ] as const; export const RULE_EXECUTION_LOG_DURATION_COLUMNS = [ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx index df067b43c32c7..b9f33ab40c6fc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_aggregations.test.tsx @@ -68,6 +68,7 @@ describe('useLoadRuleAggregations', () => { ruleExecutionStatuses: [], ruleLastRunOutcomes: [], ruleStatuses: [], + ruleParams: {}, tags: [], }, enabled: true, @@ -105,6 +106,7 @@ describe('useLoadRuleAggregations', () => { types: ['type1', 'type2'], actionTypes: ['action1', 'action2'], ruleExecutionStatuses: ['status1', 'status2'], + ruleParams: {}, ruleStatuses: ['enabled', 'snoozed'] as RuleStatus[], tags: ['tag1', 'tag2'], ruleLastRunOutcomes: ['outcome1', 'outcome2'], @@ -145,6 +147,7 @@ describe('useLoadRuleAggregations', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx index ff674f677783e..1342746223889 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules.test.tsx @@ -272,6 +272,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -328,6 +329,7 @@ describe('useLoadRules', () => { types: ['type1', 'type2'], actionTypes: ['action1', 'action2'], ruleExecutionStatuses: ['status1', 'status2'], + ruleParams: {}, ruleLastRunOutcomes: ['outcome1', 'outcome2'], ruleStatuses: ['enabled', 'snoozed'] as RuleStatus[], tags: ['tag1', 'tag2'], @@ -355,6 +357,7 @@ describe('useLoadRules', () => { actionTypesFilter: ['action1', 'action2'], ruleExecutionStatusesFilter: ['status1', 'status2'], ruleLastRunOutcomesFilter: ['outcome1', 'outcome2'], + ruleParamsFilter: {}, ruleStatusesFilter: ['enabled', 'snoozed'], tagsFilter: ['tag1', 'tag2'], sort: { field: 'name', direction: 'asc' }, @@ -378,6 +381,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -415,6 +419,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -444,6 +449,7 @@ describe('useLoadRules', () => { types: [], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], @@ -477,6 +483,7 @@ describe('useLoadRules', () => { types: ['some-kind-of-filter'], actionTypes: [], ruleExecutionStatuses: [], + ruleParams: {}, ruleLastRunOutcomes: [], ruleStatuses: [], tags: [], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts index 8ffb59a7806ad..7a30b00b94c94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rules_query.ts @@ -44,6 +44,7 @@ export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { filters.actionTypes, filters.ruleStatuses, filters.ruleLastRunOutcomes, + filters.ruleParams, page, sort, { @@ -59,6 +60,7 @@ export const useLoadRulesQuery = (props: UseLoadRulesQueryProps) => { actionTypesFilter: filters.actionTypes, ruleExecutionStatusesFilter: filters.ruleExecutionStatuses, ruleLastRunOutcomesFilter: filters.ruleLastRunOutcomes, + ruleParamsFilter: filters.ruleParams, ruleStatusesFilter: filters.ruleStatuses, tagsFilter: filters.tags, sort, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts index 11a2ae2588c29..06b8a176b16e5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts @@ -6,7 +6,7 @@ */ import { RuleExecutionStatus } from '@kbn/alerting-plugin/common'; import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; -import { Rule, RuleAction, ResolvedRule, RuleLastRun } from '../../../types'; +import type { Rule, RuleAction, ResolvedRule, RuleLastRun } from '../../../types'; const transformAction: RewriteRequestCase = ({ uuid, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index ed56bcdb3ca7d..d8b8c85ad26a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -36,7 +36,7 @@ const rewriteBodyRequest: RewriteResponseCase = ({ throttle: frequency!.throttle, summary: frequency!.summary, }, - alertsFilter, + alerts_filter: alertsFilter, })), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts index 8159501b3d4b4..ea704891523b8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/map_filters_to_kuery_node.ts @@ -13,6 +13,7 @@ export const mapFiltersToKueryNode = ({ actionTypesFilter, ruleExecutionStatusesFilter, ruleLastRunOutcomesFilter, + ruleParamsFilter, ruleStatusesFilter, tagsFilter, searchText, @@ -22,6 +23,7 @@ export const mapFiltersToKueryNode = ({ tagsFilter?: string[]; ruleExecutionStatusesFilter?: string[]; ruleLastRunOutcomesFilter?: string[]; + ruleParamsFilter?: Record; ruleStatusesFilter?: RuleStatus[]; searchText?: string; }): KueryNode | null => { @@ -63,6 +65,19 @@ export const mapFiltersToKueryNode = ({ ); } + if (ruleParamsFilter && Object.keys(ruleParamsFilter).length) { + filterKueryNode.push( + nodeBuilder.and( + Object.keys(ruleParamsFilter).map((ruleParam) => + nodeBuilder.is( + `alert.attributes.params.${ruleParam}`, + String(ruleParamsFilter[ruleParam]) + ) + ) + ) + ); + } + if (ruleStatusesFilter && ruleStatusesFilter.length) { const snoozedFilter = nodeBuilder.or([ fromKueryExpression('alert.attributes.muteAll: true'), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts index 43beb66b40f9e..889f2634eacc9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts @@ -28,6 +28,7 @@ describe('loadRuleSummary', () => { lastRun: '2021-04-01T22:18:27.609Z', muteAll: false, name: 'test', + revision: 0, ruleTypeId: '.index-threshold', status: 'OK', statusEndDate: '2021-04-01T22:19:25.174Z', @@ -55,6 +56,7 @@ describe('loadRuleSummary', () => { last_run: '2021-04-01T22:18:27.609Z', mute_all: false, name: 'test', + revision: 0, rule_type_id: '.index-threshold', status: 'OK', status_end_date: '2021-04-01T22:19:25.174Z', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts index cce9fd3e5ac97..989bd49289dde 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_helpers.ts @@ -19,6 +19,7 @@ export interface LoadRulesProps { tagsFilter?: string[]; ruleExecutionStatusesFilter?: string[]; ruleLastRunOutcomesFilter?: string[]; + ruleParamsFilter?: Record; ruleStatusesFilter?: RuleStatus[]; sort?: Sorting; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts index d7c56388b4e9e..f7e2bd6a8177e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rules_kuery_filter.ts @@ -19,6 +19,7 @@ export async function loadRulesWithKueryFilter({ actionTypesFilter, ruleExecutionStatusesFilter, ruleLastRunOutcomesFilter, + ruleParamsFilter, ruleStatusesFilter, tagsFilter, sort = { field: 'name', direction: 'asc' }, @@ -34,6 +35,7 @@ export async function loadRulesWithKueryFilter({ tagsFilter, ruleExecutionStatusesFilter, ruleLastRunOutcomesFilter, + ruleParamsFilter, ruleStatusesFilter, searchText, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index 15097eb03f156..ee2418d22262b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -26,7 +26,7 @@ const rewriteBodyRequest: RewriteResponseCase = ({ actions, ... throttle: frequency!.throttle, summary: frequency!.summary, }, - alertsFilter, + alerts_filter: alertsFilter, ...(uuid && { uuid }), })), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx new file mode 100644 index 0000000000000..3cd22cc70a429 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx @@ -0,0 +1,96 @@ +/* + * 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, useCallback, useMemo, useEffect } from 'react'; +import { Filter } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import { EuiSwitch, EuiSpacer } from '@elastic/eui'; +import { AlertsFilter } from '@kbn/alerting-plugin/common'; +import deepEqual from 'fast-deep-equal'; +import { AlertsSearchBar } from '../alerts_search_bar'; + +interface ActionAlertsFilterQueryProps { + state?: AlertsFilter['query']; + onChange: (update?: AlertsFilter['query']) => void; +} + +export const ActionAlertsFilterQuery: React.FC = ({ + state, + onChange, +}) => { + const [query, setQuery] = useState(state ?? { kql: '', filters: [] }); + + const queryEnabled = useMemo(() => Boolean(state), [state]); + + useEffect(() => { + const nextState = queryEnabled ? query : undefined; + if (!deepEqual(state, nextState)) onChange(nextState); + }, [queryEnabled, query, state, onChange]); + + const toggleQuery = useCallback( + () => onChange(state ? undefined : query), + [state, query, onChange] + ); + const updateQuery = useCallback( + (update: Partial) => { + setQuery({ + ...query, + ...update, + }); + }, + [query, setQuery] + ); + + const onQueryChange = useCallback( + ({ query: newQuery }) => updateQuery({ kql: newQuery }), + [updateQuery] + ); + + const onFiltersUpdated = useCallback( + (filters: Filter[]) => updateQuery({ filters }), + [updateQuery] + ); + + return ( + <> + + {queryEnabled && ( + <> + + + + )} + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx index a4c3edfda3344..ef73b3ab305c0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx @@ -17,7 +17,7 @@ jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ })); describe('action_alerts_filter_timeframe', () => { - async function setup(timeframe: AlertsFilterTimeframe | null) { + async function setup(timeframe?: AlertsFilterTimeframe) { const wrapper = mountWithIntl( {}} /> ); @@ -32,7 +32,7 @@ describe('action_alerts_filter_timeframe', () => { } it('renders an unchecked switch when passed a null timeframe', async () => { - const wrapper = await setup(null); + const wrapper = await setup(); const alertsFilterTimeframeToggle = wrapper.find( '[data-test-subj="alertsFilterTimeframeToggle"]' diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx index a8e276f81535f..05fcd8fba77a7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx @@ -24,8 +24,8 @@ import { AlertsFilterTimeframe, IsoWeekday } from '@kbn/alerting-plugin/common'; import { I18N_WEEKDAY_OPTIONS_DDD, ISO_WEEKDAYS } from '../../../common/constants'; interface ActionAlertsFilterTimeframeProps { - state: AlertsFilterTimeframe | null; - onChange: (update: AlertsFilterTimeframe | null) => void; + state?: AlertsFilterTimeframe; + onChange: (update?: AlertsFilterTimeframe) => void; } const TIMEZONE_OPTIONS = moment.tz?.names().map((n) => ({ label: n })) ?? [{ label: 'UTC' }]; @@ -46,14 +46,14 @@ const useDefaultTimezone = () => { return kibanaTz; }; -const useTimeframe = (initialTimeframe: AlertsFilterTimeframe | null) => { +const useTimeframe = (initialTimeframe?: AlertsFilterTimeframe) => { const timezone = useDefaultTimezone(); const DEFAULT_TIMEFRAME = { days: [], timezone, hours: { start: '00:00', - end: '24:00', + end: '23:59', }, }; return useState(initialTimeframe || DEFAULT_TIMEFRAME); @@ -79,12 +79,12 @@ export const ActionAlertsFilterTimeframe: React.FC { - const nextState = timeframeEnabled ? timeframe : null; + const nextState = timeframeEnabled ? timeframe : undefined; if (!deepEqual(state, nextState)) onChange(nextState); }, [timeframeEnabled, timeframe, state, onChange]); const toggleTimeframe = useCallback( - () => onChange(state ? null : timeframe), + () => onChange(state ? undefined : timeframe), [state, timeframe, onChange] ); const updateTimeframe = useCallback( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 18a8a577b7e0f..5be6a91698833 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -351,7 +351,7 @@ describe('action_form', () => { (initialAlert.actions[index] = { ...initialAlert.actions[index], alertsFilter: { - ...(initialAlert.actions[index].alertsFilter ?? { query: null, timeframe: null }), + ...initialAlert.actions[index].alertsFilter, [key]: value, }, }) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 6b7ede2f01747..25feead9c518e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -228,7 +228,8 @@ export const ActionForm = ({ return; } setIsAddActionPanelOpen(false); - const actionTypeConnectors = connectors.filter( + const allowGroupConnector = (actionTypeModel?.subtype ?? []).map((atm) => atm.id); + let actionTypeConnectors = connectors.filter( (field) => field.actionTypeId === actionTypeModel.id ); @@ -241,7 +242,22 @@ export const ActionForm = ({ frequency: defaultRuleFrequency, }); setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); + } else { + actionTypeConnectors = connectors.filter((field) => + allowGroupConnector.includes(field.actionTypeId) + ); + if (actionTypeConnectors.length > 0) { + actions.push({ + id: '', + actionTypeId: actionTypeConnectors[0].actionTypeId, + group: defaultActionGroupId, + params: {}, + frequency: DEFAULT_FREQUENCY, + }); + setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); + } } + if (actionTypeConnectors.length === 0) { // if no connectors exists or all connectors is already assigned an action under current alert // set actionType as id to be able to create new connector within the alert form @@ -263,7 +279,7 @@ export const ActionForm = ({ const preconfiguredConnectors = connectors.filter((connector) => connector.isPreconfigured); actionTypeNodes = actionTypeRegistry .list() - .filter((item) => actionTypesIndex[item.id]) + .filter((item) => actionTypesIndex[item.id] && !item.hideInUi) .filter((item) => !!item.actionParamsFields) .sort((a, b) => actionTypeCompare(actionTypesIndex[a.id], actionTypesIndex[b.id], preconfiguredConnectors) @@ -378,6 +394,27 @@ export const ActionForm = ({ }} onSelectConnector={(connectorId: string) => { setActionIdByIndex(connectorId, index); + const newConnector = connectors.find((connector) => connector.id === connectorId); + if (newConnector && newConnector.actionTypeId) { + const actionTypeRegistered = actionTypeRegistry.get(newConnector.actionTypeId); + if (actionTypeRegistered.convertParamsBetweenGroups) { + const updatedActions = actions.map((_item: RuleAction, i: number) => { + if (i === index) { + return { + ..._item, + actionTypeId: newConnector.actionTypeId, + id: connectorId, + params: + actionTypeRegistered.convertParamsBetweenGroups != null + ? actionTypeRegistered.convertParamsBetweenGroups(_item.params) + : {}, + }; + } + return _item; + }); + setActions(updatedActions); + } + } }} /> ); @@ -407,6 +444,31 @@ export const ActionForm = ({ }} onConnectorSelected={(id: string) => { setActionIdByIndex(id, index); + const newConnector = connectors.find((connector) => connector.id === id); + if ( + newConnector && + actionConnector && + newConnector.actionTypeId !== actionConnector.actionTypeId + ) { + const actionTypeRegistered = actionTypeRegistry.get(newConnector.actionTypeId); + if (actionTypeRegistered.convertParamsBetweenGroups) { + const updatedActions = actions.map((_item: RuleAction, i: number) => { + if (i === index) { + return { + ..._item, + actionTypeId: newConnector.actionTypeId, + id, + params: + actionTypeRegistered.convertParamsBetweenGroups != null + ? actionTypeRegistered.convertParamsBetweenGroups(_item.params) + : {}, + }; + } + return _item; + }); + setActions(updatedActions); + } + } }} actionTypeRegistry={actionTypeRegistry} onDeleteAction={() => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 38d267d014824..a5222ac091800 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -61,6 +61,7 @@ import { ConnectorsSelection } from './connectors_selection'; import { ActionNotifyWhen } from './action_notify_when'; import { validateParamsForWarnings } from '../../lib/validate_params_for_warnings'; import { ActionAlertsFilterTimeframe } from './action_alerts_filter_timeframe'; +import { ActionAlertsFilterQuery } from './action_alerts_filter_query'; export type ActionTypeFormProps = { actionItem: RuleAction; @@ -307,6 +308,7 @@ export const ActionTypeForm = ({ const actionTypeRegistered = actionTypeRegistry.get(actionConnector.actionTypeId); if (!actionTypeRegistered) return null; + const allowGroupConnector = (actionTypeRegistered?.subtype ?? []).map((atr) => atr.id); const showActionGroupErrorIcon = (): boolean => { return !isOpen && some(actionParamsErrors.errors, (error) => !isEmpty(error)); @@ -361,6 +363,7 @@ export const ActionTypeForm = ({ } > {!hideNotifyWhen && } + setActionAlertsFilterProperty('query', query, index)} + /> + setActionAlertsFilterProperty('timeframe', timeframe, index)} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx index 370e61b9fe5dc..029993a95c7ce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx @@ -59,8 +59,8 @@ describe('connector_add_flyout', () => { }, actionConnectorFields: null, }); - actionTypeRegistry.get.mockReturnValueOnce(actionType); - loadActionTypes.mockResolvedValueOnce([ + actionTypeRegistry.get.mockReturnValue(actionType); + loadActionTypes.mockResolvedValue([ { id: actionType.id, enabled: true, @@ -137,8 +137,8 @@ describe('connector_add_flyout', () => { }, actionConnectorFields: null, }); - actionTypeRegistry.get.mockReturnValueOnce(actionType); - loadActionTypes.mockResolvedValueOnce([ + actionTypeRegistry.get.mockReturnValue(actionType); + loadActionTypes.mockResolvedValue([ { id: actionType.id, enabled: false, @@ -180,8 +180,8 @@ describe('connector_add_flyout', () => { }, actionConnectorFields: null, }); - actionTypeRegistry.get.mockReturnValueOnce(actionType); - loadActionTypes.mockResolvedValueOnce([ + actionTypeRegistry.get.mockReturnValue(actionType); + loadActionTypes.mockResolvedValue([ { id: actionType.id, enabled: false, @@ -221,8 +221,8 @@ describe('connector_add_flyout', () => { actionConnectorFields: null, isExperimental: false, }); - actionTypeRegistry.get.mockReturnValueOnce(actionType); - loadActionTypes.mockResolvedValueOnce([ + actionTypeRegistry.get.mockReturnValue(actionType); + loadActionTypes.mockResolvedValue([ { id: actionType.id, enabled: false, @@ -263,8 +263,8 @@ describe('connector_add_flyout', () => { actionConnectorFields: null, isExperimental: true, }); - actionTypeRegistry.get.mockReturnValueOnce(actionType); - loadActionTypes.mockResolvedValueOnce([ + actionTypeRegistry.get.mockReturnValue(actionType); + loadActionTypes.mockResolvedValue([ { id: actionType.id, enabled: false, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx index 60b153badffe6..f4717bb512a0c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx @@ -22,6 +22,7 @@ interface Props { onActionTypeChange: (actionType: ActionType) => void; featureId?: string; setHasActionsUpgradeableByTrial?: (value: boolean) => void; + setAllActionTypes?: (actionsType: ActionTypeIndex) => void; actionTypeRegistry: ActionTypeRegistryContract; } @@ -29,6 +30,7 @@ export const ActionTypeMenu = ({ onActionTypeChange, featureId, setHasActionsUpgradeableByTrial, + setAllActionTypes, actionTypeRegistry, }: Props) => { const { @@ -37,7 +39,6 @@ export const ActionTypeMenu = ({ } = useKibana().services; const [loadingActionTypes, setLoadingActionTypes] = useState(false); const [actionTypesIndex, setActionTypesIndex] = useState(undefined); - useEffect(() => { (async () => { try { @@ -50,6 +51,9 @@ export const ActionTypeMenu = ({ index[actionTypeItem.id] = actionTypeItem; } setActionTypesIndex(index); + if (setAllActionTypes) { + setAllActionTypes(index); + } // determine if there are actions disabled by license that that // would be enabled by upgrading to gold or trial if (setHasActionsUpgradeableByTrial) { @@ -73,9 +77,13 @@ export const ActionTypeMenu = ({ })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const registeredActionTypes = Object.entries(actionTypesIndex ?? []) - .filter(([id, details]) => actionTypeRegistry.has(id) && details.enabledInConfig === true) + .filter( + ([id, details]) => + actionTypeRegistry.has(id) && + details.enabledInConfig === true && + !actionTypeRegistry.get(id).hideInUi + ) .map(([id, actionType]) => { const actionTypeModel = actionTypeRegistry.get(id); return { @@ -100,7 +108,9 @@ export const ActionTypeMenu = ({ title={item.name} description={item.selectMessage} isDisabled={!checkEnabledResult.isEnabled} - onClick={() => onActionTypeChange(item.actionType)} + onClick={() => { + onActionTypeChange(item.actionType); + }} /> ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx index 530d41a4c27d1..ec5e4ef01ceec 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx @@ -65,6 +65,7 @@ export const AddConnectorInline = ({ ? actionTypesIndex[actionItem.actionTypeId].name : actionItem.actionTypeId; const actionTypeRegistered = actionTypeRegistry.get(actionItem.actionTypeId); + const allowGroupConnector = (actionTypeRegistered?.subtype ?? []).map((subtype) => subtype.id); const connectorDropdownErrors = useMemo( () => [`Unable to load ${actionTypeRegistered.actionTypeTitle} connector`], [actionTypeRegistered.actionTypeTitle] @@ -90,7 +91,12 @@ export const AddConnectorInline = ({ ); useEffect(() => { - const filteredConnectors = getValidConnectors(connectors, actionItem, actionTypesIndex); + const filteredConnectors = getValidConnectors( + connectors, + actionItem, + actionTypesIndex, + allowGroupConnector + ); if (filteredConnectors.length > 0) { setHasConnectors(true); @@ -134,6 +140,7 @@ export const AddConnectorInline = ({ actionTypeRegistered={actionTypeRegistered} connectors={connectors} onConnectorSelected={onSelectConnector} + allowGroupConnector={allowGroupConnector} /> ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx index 4f704af65827d..1c2e1ae1e7318 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx @@ -19,16 +19,26 @@ import { EuiIcon, EuiFlexGroup, EuiBetaBadge, + EuiButtonGroup, + EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import './connector_add_modal.scss'; import { betaBadgeProps } from './beta_badge_props'; import { hasSaveActionsCapability } from '../../lib/capabilities'; -import { ActionType, ActionConnector, ActionTypeRegistryContract } from '../../../types'; +import { + ActionType, + ActionConnector, + ActionTypeRegistryContract, + ActionTypeModel, + ActionTypeIndex, +} from '../../../types'; import { useKibana } from '../../../common/lib/kibana'; import { useCreateConnector } from '../../hooks/use_create_connector'; -import { ConnectorForm, ConnectorFormState } from './connector_form'; +import { ConnectorForm, ConnectorFormState, ResetForm } from './connector_form'; import { ConnectorFormSchema } from './types'; +import { loadActionTypes } from '../../lib/action_connector_api'; +import { SectionLoading } from '../../components'; export interface ConnectorAddModalProps { actionType: ActionType; @@ -38,27 +48,74 @@ export interface ConnectorAddModalProps { } const ConnectorAddModal = ({ - actionType, + actionType: tempActionType, onClose, postSaveEventHandler, actionTypeRegistry, }: ConnectorAddModalProps) => { const { application: { capabilities }, + http, + notifications: { toasts }, } = useKibana().services; - + const [actionType, setActionType] = useState(tempActionType); + const [loadingActionTypes, setLoadingActionTypes] = useState(false); + const [allActionTypes, setAllActionTypes] = useState(undefined); const { isLoading: isSavingConnector, createConnector } = useCreateConnector(); const isMounted = useRef(false); - const initialConnector = { - actionTypeId: actionType.id, + const [initialConnector, setInitialConnector] = useState({ + actionTypeId: actionType?.id ?? '', isDeprecated: false, - isMissingSecrets: false, config: {}, secrets: {}, - }; + isMissingSecrets: false, + }); const canSave = hasSaveActionsCapability(capabilities); const actionTypeModel = actionTypeRegistry.get(actionType.id); + const groupActionTypeModel: Array = + actionTypeModel && actionTypeModel.subtype + ? (actionTypeModel?.subtype ?? []).map((subtypeAction) => ({ + ...actionTypeRegistry.get(subtypeAction.id), + name: subtypeAction.name, + })) + : []; + + const groupActionButtons = groupActionTypeModel.map((gAction) => ({ + id: gAction.id, + label: gAction.name, + 'data-test-subj': `${gAction.id}Button`, + })); + + const resetConnectorForm = useRef(); + + const setResetForm = (reset: ResetForm) => { + resetConnectorForm.current = reset; + }; + + const onChangeGroupAction = (id: string) => { + if (allActionTypes && allActionTypes[id]) { + setActionType(allActionTypes[id]); + setInitialConnector({ + actionTypeId: id, + isDeprecated: false, + config: {}, + secrets: {}, + isMissingSecrets: false, + }); + if (resetConnectorForm.current) { + resetConnectorForm.current({ + resetValues: true, + defaultValue: { + actionTypeId: id, + isDeprecated: false, + config: {}, + secrets: {}, + }, + }); + } + } + }; const [preSubmitValidationErrorMessage, setPreSubmitValidationErrorMessage] = useState(null); @@ -131,8 +188,39 @@ const ConnectorAddModal = ({ }; }, []); + useEffect(() => { + (async () => { + try { + setLoadingActionTypes(true); + const availableActionTypes = await loadActionTypes({ http }); + setLoadingActionTypes(false); + + const index: ActionTypeIndex = {}; + for (const actionTypeItem of availableActionTypes) { + index[actionTypeItem.id] = actionTypeItem; + } + setAllActionTypes(index); + } catch (e) { + if (toasts) { + toasts.addDanger({ + title: i18n.translate( + 'xpack.triggersActionsUI.sections.actionsConnectorsList.unableToLoadConnectorTypesMessage', + { defaultMessage: 'Unable to load connector types' } + ), + }); + } + } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( - + {actionTypeModel && actionTypeModel.iconClass ? ( @@ -167,13 +255,40 @@ const ConnectorAddModal = ({ - - {preSubmitValidationErrorMessage} + {loadingActionTypes ? ( + + + + ) : ( + <> + {groupActionTypeModel && ( + <> + + + + )} + + {preSubmitValidationErrorMessage} + + )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.tsx index b0d8072dae652..d9e59ac19db94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.tsx @@ -27,6 +27,16 @@ export interface ConnectorFormState { preSubmitValidator: ConnectorValidationFunc | null; } +export type ResetForm = ( + options?: + | { + resetValues?: boolean | undefined; + defaultValue?: + | Partial, Record>> + | undefined; + } + | undefined +) => void; interface Props { actionTypeModel: ActionTypeModel | null; connector: ConnectorFormSchema & { isMissingSecrets: boolean }; @@ -35,6 +45,7 @@ interface Props { onChange?: (state: ConnectorFormState) => void; /** Handler to receive update on the form "isModified" state */ onFormModifiedChange?: (isModified: boolean) => void; + setResetForm?: (value: ResetForm) => void; } /** * The serializer and deserializer are needed to transform the headers of @@ -101,13 +112,14 @@ const ConnectorFormComponent: React.FC = ({ isEdit, onChange, onFormModifiedChange, + setResetForm, }) => { const { form } = useForm({ defaultValue: connector, serializer: formSerializer, deserializer: formDeserializer, }); - const { submit, isValid: isFormValid, isSubmitted, isSubmitting } = form; + const { submit, isValid: isFormValid, isSubmitted, isSubmitting, reset } = form; const [preSubmitValidator, setPreSubmitValidator] = useState( null ); @@ -133,6 +145,13 @@ const ConnectorFormComponent: React.FC = ({ } }, [isFormModified, onFormModifiedChange]); + useEffect(() => { + if (setResetForm) { + setResetForm(reset); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reset]); + return (
    getValidConnectors(connectors, actionItem, actionTypesIndex), - [actionItem, actionTypesIndex, connectors] + () => getValidConnectors(connectors, actionItem, actionTypesIndex, allowGroupConnector), + [actionItem, actionTypesIndex, allowGroupConnector, connectors] ); const selectedConnectors = useMemo( - () => getValueOfSelectedConnector(actionItem.id, validConnectors, actionTypeRegistered), - [actionItem.id, validConnectors, actionTypeRegistered] + () => + getValueOfSelectedConnector( + actionItem.id, + validConnectors, + actionTypeRegistered, + allowGroupConnector + ), + [actionItem.id, validConnectors, actionTypeRegistered, allowGroupConnector] ); const options = useMemo( @@ -83,10 +91,15 @@ function ConnectorsSelectionComponent({ const getValueOfSelectedConnector = ( actionItemId: string, connectors: ActionConnector[], - actionTypeRegistered: ActionTypeModel + actionTypeRegistered: ActionTypeModel, + allowGroupConnector: string[] = [] ): Array> => { - const selectedConnector = connectors.find((connector) => connector.id === actionItemId); - + let selectedConnector = connectors.find((connector) => connector.id === actionItemId); + if (allowGroupConnector.length > 0 && !selectedConnector) { + selectedConnector = connectors.find((connector) => + allowGroupConnector.includes(connector.actionTypeId) + ); + } if (!selectedConnector) { return []; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.tsx index 32bd7931aecc8..5cf6f6f8de69b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.tsx @@ -6,21 +6,30 @@ */ import React, { memo, ReactNode, useCallback, useEffect, useRef, useState } from 'react'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiFlyout, EuiFlyoutBody } from '@elastic/eui'; +import { + EuiButton, + EuiButtonGroup, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiSpacer, +} from '@elastic/eui'; -import { getConnectorCompatibility } from '@kbn/actions-plugin/common'; +import { getConnectorCompatibility, UptimeConnectorFeatureId } from '@kbn/actions-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { ActionConnector, ActionType, ActionTypeModel, + ActionTypeIndex, ActionTypeRegistryContract, } from '../../../../types'; import { hasSaveActionsCapability } from '../../../lib/capabilities'; import { useKibana } from '../../../../common/lib/kibana'; import { ActionTypeMenu } from '../action_type_menu'; import { useCreateConnector } from '../../../hooks/use_create_connector'; -import { ConnectorForm, ConnectorFormState } from '../connector_form'; +import { ConnectorForm, ConnectorFormState, ResetForm } from '../connector_form'; import { ConnectorFormSchema } from '../types'; import { FlyoutHeader } from './header'; import { FlyoutFooter } from './footer'; @@ -47,6 +56,7 @@ const CreateConnectorFlyoutComponent: React.FC = ({ const { isLoading: isSavingConnector, createConnector } = useCreateConnector(); const isMounted = useRef(false); + const [allActionTypes, setAllActionTypes] = useState(undefined); const [actionType, setActionType] = useState(null); const [hasActionsUpgradeableByTrial, setHasActionsUpgradeableByTrial] = useState(false); const canSave = hasSaveActionsCapability(capabilities); @@ -78,6 +88,47 @@ const CreateConnectorFlyoutComponent: React.FC = ({ const actionTypeModel: ActionTypeModel | null = actionType != null ? actionTypeRegistry.get(actionType.id) : null; + /* Future Developer + * We are excluding `UptimeConnectorFeatureId` because as this time Synthetics won't work + * with slack API on their UI, We need to add an ISSUE here so they can fix it + */ + const groupActionTypeModel: Array = + actionTypeModel && actionTypeModel.subtype && featureId !== UptimeConnectorFeatureId + ? (actionTypeModel?.subtype ?? []).map((subtypeAction) => ({ + ...actionTypeRegistry.get(subtypeAction.id), + name: subtypeAction.name, + })) + : []; + + const groupActionButtons = groupActionTypeModel.map((gAction) => ({ + id: gAction.id, + label: gAction.name, + 'data-test-subj': `${gAction.id}Button`, + })); + + const resetConnectorForm = useRef(); + + const setResetForm = (reset: ResetForm) => { + resetConnectorForm.current = reset; + }; + + const onChangeGroupAction = (id: string) => { + if (allActionTypes && allActionTypes[id]) { + setActionType(allActionTypes[id]); + if (resetConnectorForm.current) { + resetConnectorForm.current({ + resetValues: true, + defaultValue: { + actionTypeId: id, + isDeprecated: false, + config: {}, + secrets: {}, + }, + }); + } + } + }; + const validateAndCreateConnector = useCallback(async () => { setPreSubmitValidationErrorMessage(null); @@ -166,14 +217,29 @@ const CreateConnectorFlyoutComponent: React.FC = ({ > {hasConnectorTypeSelected ? ( <> + {groupActionTypeModel && ( + <> + + + + )} {!!preSubmitValidationErrorMessage &&

    {preSubmitValidationErrorMessage}

    } - <> @@ -220,6 +286,7 @@ const CreateConnectorFlyoutComponent: React.FC = ({ featureId={featureId} onActionTypeChange={setActionType} setHasActionsUpgradeableByTrial={setHasActionsUpgradeableByTrial} + setAllActionTypes={setAllActionTypes} actionTypeRegistry={actionTypeRegistry} /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx index 41893e785f5bf..78039c753a276 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx @@ -35,14 +35,14 @@ const renderWithSecretFields = ({ describe('EncryptedFieldsCallout', () => { const isCreateTests: Array<[number, string]> = [ - [1, 'Remember value label0. You must reenter it each time you edit the connector.'], + [1, 'Remember your label0 value. You must reenter it each time you edit the connector.'], [ 2, - 'Remember values label0 and label1. You must reenter them each time you edit the connector.', + 'Remember your label0 and label1 values. You must reenter them each time you edit the connector.', ], [ 3, - 'Remember values label0, label1, and label2. You must reenter them each time you edit the connector.', + 'Remember your label0, label1, and label2 values. You must reenter them each time you edit the connector.', ], ]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx index 7bc0fdbc0703c..35cbb473c6a3f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx @@ -104,7 +104,7 @@ const EncryptedFieldsCalloutComponent: React.FC = ( { values: { secretFieldsLabel, encryptedFieldsLength: totalSecretFields }, defaultMessage: - 'Remember value{encryptedFieldsLength, plural, one {} other {s}} {secretFieldsLabel}. You must reenter {encryptedFieldsLength, plural, one {it} other {them}} each time you edit the connector.', + 'Remember your {secretFieldsLabel} {encryptedFieldsLength, plural, one {value} other {values}}. You must reenter {encryptedFieldsLength, plural, one {it} other {them}} each time you edit the connector.', } )} dataTestSubj="create-connector-secrets-callout" diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index a03c34c90962e..9edde99574ca1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -234,6 +234,7 @@ const ActionsConnectorsList: React.FunctionComponent = () => { <> editItem(item, EditConnectorTabs.Configuration)} key={item.id} disabled={actionTypesIndex ? !actionTypesIndex[item.actionTypeId]?.enabled : true} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 6a170363d34a8..49b1f9905cd7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -19,9 +19,16 @@ export function AlertsSearchBar({ appName, featureIds, query, + filters, onQueryChange, + onFiltersUpdated, rangeFrom, rangeTo, + showFilterBar = false, + showDatePicker = true, + showSubmitButton = true, + placeholder = SEARCH_BAR_PLACEHOLDER, + submitOnBlur = false, }: AlertsSearchBarProps) { const { unifiedSearch: { @@ -52,14 +59,20 @@ export function AlertsSearchBar({ ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts index 2c94c250c168a..b7616333c199a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { Filter } from '@kbn/es-query'; import { ValidFeatureId } from '@kbn/rule-data-utils'; export type QueryLanguageType = 'lucene' | 'kuery'; @@ -15,8 +16,15 @@ export interface AlertsSearchBarProps { rangeFrom?: string; rangeTo?: string; query?: string; + filters?: Filter[]; + showFilterBar?: boolean; + showDatePicker?: boolean; + showSubmitButton?: boolean; + placeholder?: string; + submitOnBlur?: boolean; onQueryChange: (query: { dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; query?: string; }) => void; + onFiltersUpdated?: (filters: Filter[]) => void; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx index f9d209549da0c..23fac59fca208 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/bulk_actions/bulk_actions.test.tsx @@ -691,7 +691,8 @@ describe('AlertsTable.BulkActions', () => { ).toBeTruthy(); }); - describe('and clear the selection is clicked', () => { + // FLAKY: https://github.com/elastic/kibana/issues/154970 + describe.skip('and clear the selection is clicked', () => { it('should turn off the toolbar', async () => { const props = { ...tablePropsWithBulkActions, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx index 0ec383c84f6e6..32e5c0e83d297 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx @@ -111,6 +111,16 @@ describe('rule_event_log_list_cell_renderer', () => { expect(wrapper.find(EuiIcon).props().color).toEqual('gray'); }); + it('renders maintenance window correctly', () => { + const wrapper = shallow( + + ); + expect(wrapper.text()).toEqual('test-id-1, test-id-2'); + }); + it('links to rules on the correct space', () => { const wrapper1 = shallow( {value ? 'true' : 'false'}; } + if (columnId === 'maintenance_window_ids') { + return <>{Array.isArray(value) ? value.join(', ') : ''}; + } + return <>{value}; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts index 6543d74ecd7a2..af09d984f9417 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/connectors.ts @@ -10,13 +10,15 @@ import { ActionConnector, ActionTypeIndex, RuleAction } from '../../../types'; export const getValidConnectors = ( connectors: ActionConnector[], actionItem: RuleAction, - actionTypesIndex: ActionTypeIndex + actionTypesIndex: ActionTypeIndex, + allowGroupConnector: string[] = [] ): ActionConnector[] => { const actionType = actionTypesIndex[actionItem.actionTypeId]; return connectors.filter( (connector) => - connector.actionTypeId === actionItem.actionTypeId && + (allowGroupConnector.includes(connector.actionTypeId) || + connector.actionTypeId === actionItem.actionTypeId) && // include only enabled by config connectors or preconfigured (actionType?.enabledInConfig || connector.isPreconfigured) ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx index f3775e5de5066..821d4edf16c66 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx @@ -481,6 +481,7 @@ function mockRuleSummary(overloads: Partial = {}): RuleSummary { throttle: '', enabled: true, errorMessages: [], + revision: 0, statusStartDate: fake2MinutesAgo.toISOString(), statusEndDate: fakeNow.toISOString(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx index 81c5e0d7d7e90..8b99cc910d8da 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx @@ -197,6 +197,7 @@ export function alertToListItem( isMuted, sortPriority, flapping: alert.flapping, + ...(alert.maintenanceWindowIds ? { maintenanceWindowIds: alert.maintenanceWindowIds } : {}), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx index 3b3142181d8e7..5a6fad3b939a2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx @@ -91,6 +91,21 @@ const alertsTableColumns = ( width: '80px', 'data-test-subj': 'alertsTableCell-duration', }, + { + field: 'maintenanceWindowIds', + width: '250px', + render: (value: string[]) => { + return Array.isArray(value) ? value.join(', ') : ''; + }, + name: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.alertsList.columns.maintenanceWindowIds', + { + defaultMessage: 'Maintenance windows', + } + ), + sortable: false, + 'data-test-subj': 'alertsTableCell-maintenanceWindowIds', + }, { field: '', align: RIGHT_ALIGNMENT, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx index 7df89d799b2ae..7965f58fa8420 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx @@ -610,6 +610,20 @@ export const RuleEventLogListTable = ( ), isSortable: getIsColumnSortable('timed_out'), }, + { + id: 'maintenance_window_ids', + displayAsText: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.maintenanceWindowIds', + { + defaultMessage: 'Maintenance windows', + } + ), + actions: { + showSortAsc: false, + showSortDesc: false, + }, + isSortable: getIsColumnSortable('maintenance_window_ids'), + }, ], [getPaginatedRowIndex, onFlyoutOpen, onFilterChange, hasRuleNames, showFromAllSpaces, logs] ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx index 117bca134f0f9..1826894ef70f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx @@ -162,6 +162,7 @@ function mockRuleSummary(overloads: Partial = {}): any { throttle: null, enabled: true, errorMessages: [], + revision: 0, statusStartDate: fake2MinutesAgo.toISOString(), statusEndDate: fakeNow.toISOString(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts index 4146ac8006325..d72e381c170ab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts @@ -95,6 +95,7 @@ export function mockRuleSummary(overloads: Partial = {}): RuleSumma throttle: '', enabled: true, errorMessages: [], + revision: 0, statusStartDate: '2022-03-21T07:40:46-07:00', statusEndDate: '2022-03-25T07:40:46-07:00', alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts index c469b61690c3a..f66f648051715 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts @@ -14,4 +14,5 @@ export interface AlertListItem { isMuted: boolean; sortPriority: number; flapping: boolean; + maintenanceWindowIds?: string[]; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts index 8ac7c38276233..54f3871928fb3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts @@ -13,6 +13,7 @@ import { IntervalSchedule, RuleActionAlertsFilterProperty, } from '@kbn/alerting-plugin/common'; +import { isEmpty } from 'lodash/fp'; import { Rule, RuleAction } from '../../../types'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; @@ -237,12 +238,18 @@ export const ruleReducer = ( return state; } else { const oldAction = rule.actions.splice(index, 1)[0]; + const { alertsFilter, ...rest } = oldAction; + const updatedAlertsFilter = { ...alertsFilter }; + + if (value) { + updatedAlertsFilter[key] = value; + } else { + delete updatedAlertsFilter[key]; + } + const updatedAction = { - ...oldAction, - alertsFilter: { - ...(oldAction.alertsFilter ?? { timeframe: null, query: null }), - [key]: value, - }, + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), }; rule.actions.splice(index, 0, updatedAction); return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge_with_api.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge_with_api.tsx index d4cd600f98ee5..238a88ba951f1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge_with_api.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/notify_badge/notify_badge_with_api.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useKibana } from '../../../../../common/lib/kibana'; import { SnoozeSchedule } from '../../../../../types'; import { loadRule } from '../../../../lib/rule_api/get_rule'; @@ -24,6 +24,13 @@ export const RulesListNotifyBadgeWithApi: React.FunctionComponent< const [ruleSnoozeInfo, setRuleSnoozeInfo] = useState(rule); + // This helps to fix problems related to rule prop updates. As component handles the loading state via isLoading prop + // rule prop is obviously not ready atm so when it's ready ruleSnoozeInfo won't be updated without useEffect so + // incorrect state will be shown. + useEffect(() => { + setRuleSnoozeInfo(rule); + }, [rule]); + const onSnoozeRule = useCallback( (snoozeSchedule: SnoozeSchedule) => { return snoozeRuleApi({ http, id: ruleSnoozeInfo.id, snoozeSchedule }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_snooze/scheduler.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_snooze/scheduler.tsx index 3c65a2afba551..1520255f21a80 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_snooze/scheduler.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_snooze/scheduler.tsx @@ -330,8 +330,8 @@ const RuleSnoozeSchedulerPanel: React.FunctionComponent = ({ setIsRecurring(!isRecurring)} checked={isRecurring} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 0bf80a0eb83cd..5bb454c0c29c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -8,7 +8,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { i18n } from '@kbn/i18n'; -import { capitalize, isEmpty, sortBy } from 'lodash'; +import { capitalize, isEmpty, isEqual, sortBy } from 'lodash'; import { KueryNode } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { @@ -75,6 +75,7 @@ import './rules_list.scss'; import { CreateRuleButton } from './create_rule_button'; import { ManageLicenseModal } from './manage_license_modal'; import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; +import { RulesListClearRuleFilterBanner } from './rules_list_clear_rule_filter_banner'; import { RulesListTable, convertRulesToTableItems } from './rules_list_table'; import { RulesListDocLink } from './rules_list_doc_link'; import { UpdateApiKeyModalConfirmation } from '../../../components/update_api_key_modal_confirmation'; @@ -107,23 +108,26 @@ const RuleEdit = lazy(() => import('../../rule_form/rule_edit')); export interface RulesListProps { filteredRuleTypes?: string[]; - showActionFilter?: boolean; + lastResponseFilter?: string[]; + lastRunOutcomeFilter?: string[]; + refresh?: Date; ruleDetailsRoute?: string; + ruleParamFilter?: Record; + rulesListKey?: string; + searchFilter?: string; + showActionFilter?: boolean; showCreateRuleButtonInPrompt?: boolean; - setHeaderActions?: (components?: React.ReactNode[]) => void; + showSearchBar?: boolean; statusFilter?: RuleStatus[]; - onStatusFilterChange?: (status: RuleStatus[]) => void; - lastResponseFilter?: string[]; + typeFilter?: string[]; + visibleColumns?: string[]; onLastResponseFilterChange?: (lastResponse: string[]) => void; - lastRunOutcomeFilter?: string[]; onLastRunOutcomeFilterChange?: (lastRunOutcome: string[]) => void; - typeFilter?: string[]; - onTypeFilterChange?: (type: string[]) => void; - searchFilter?: string; + onRuleParamFilterChange?: (ruleParams: Record) => void; onSearchFilterChange?: (search: string) => void; - refresh?: Date; - rulesListKey?: string; - visibleColumns?: string[]; + onStatusFilterChange?: (status: RuleStatus[]) => void; + onTypeFilterChange?: (type: string[]) => void; + setHeaderActions?: (components?: React.ReactNode[]) => void; } export const percentileFields = { @@ -142,47 +146,51 @@ const EMPTY_ARRAY: string[] = []; export const RulesList = ({ filteredRuleTypes = EMPTY_ARRAY, - showActionFilter = true, + lastResponseFilter, + lastRunOutcomeFilter, + refresh, ruleDetailsRoute, + ruleParamFilter, + rulesListKey, + searchFilter = '', + showActionFilter = true, showCreateRuleButtonInPrompt = false, + showSearchBar = true, statusFilter, - onStatusFilterChange, - lastResponseFilter, + typeFilter, + visibleColumns, onLastResponseFilterChange, - lastRunOutcomeFilter, onLastRunOutcomeFilterChange, - searchFilter = '', + onRuleParamFilterChange, onSearchFilterChange, - typeFilter, + onStatusFilterChange, onTypeFilterChange, setHeaderActions, - refresh, - rulesListKey, - visibleColumns, }: RulesListProps) => { const history = useHistory(); const { + actionTypeRegistry, + application: { capabilities }, http, + kibanaFeatures, notifications: { toasts }, - application: { capabilities }, ruleTypeRegistry, - actionTypeRegistry, - kibanaFeatures, } = useKibana().services; const canExecuteActions = hasExecuteActionsCapability(capabilities); const [isPerformingAction, setIsPerformingAction] = useState(false); const [page, setPage] = useState({ index: 0, size: DEFAULT_SEARCH_PAGE_SIZE }); const [inputText, setInputText] = useState(searchFilter); - const [filters, setFilters] = useState(() => ({ - searchText: searchFilter || '', - types: typeFilter || [], + const [filters, setFilters] = useState({ actionTypes: [], ruleExecutionStatuses: lastResponseFilter || [], ruleLastRunOutcomes: lastRunOutcomeFilter || [], + ruleParams: ruleParamFilter || {}, ruleStatuses: statusFilter || [], + searchText: searchFilter || '', tags: [], - })); + types: typeFilter || [], + }); const [ruleFlyoutVisible, setRuleFlyoutVisibility] = useState(false); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); @@ -357,6 +365,9 @@ export const RulesList = ({ case 'ruleLastRunOutcomes': onLastRunOutcomeFilterChange?.(value as string[]); break; + case 'ruleParams': + onRuleParamFilterChange?.(value as Record); + break; case 'searchText': onSearchFilterChange?.(value as string); break; @@ -389,6 +400,8 @@ export const RulesList = ({ [setFilters, handleUpdateFiltersEffect] ); + const handleClearRuleParamFilter = () => updateFilters({ filter: 'ruleParams', value: {} }); + useEffect(() => { if (statusFilter) { updateFilters({ filter: 'ruleStatuses', value: statusFilter }); @@ -407,6 +420,12 @@ export const RulesList = ({ } }, [lastRunOutcomeFilter]); + useEffect(() => { + if (ruleParamFilter && !isEqual(ruleParamFilter, filters.ruleParams)) { + updateFilters({ filter: 'ruleParams', value: ruleParamFilter }); + } + }, [ruleParamFilter]); + useEffect(() => { if (typeof searchFilter === 'string') { updateFilters({ filter: 'searchText', value: searchFilter }); @@ -701,6 +720,10 @@ export const RulesList = ({ return ( <> + {showSearchBar && !isEmpty(filters.ruleParams) ? ( + + ) : null} + )} + {showRulesList && ( <> - - + {showSearchBar ? ( + <> + + + + ) : null} + void; +} + +export const RulesListClearRuleFilterBanner = ({ + onClickClearFilter, +}: RulesListClearRuleFilterProps) => { + return ( + <> + +

    + {' '} + {' '} + + + +

    +
    + + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx index 7945f8b328623..a429f3abc06c6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx @@ -6,16 +6,16 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiFilterGroup, - EuiFieldSearch, EuiSpacer, EuiLink, + EuiFieldSearch, } from '@elastic/eui'; import { ActionType, RulesListFilters, UpdateFiltersProps } from '../../../../types'; import { getIsExperimentalFeatureEnabled } from '../../../../common/get_experimental_features'; @@ -29,43 +29,42 @@ import { ActionTypeFilter } from './action_type_filter'; import { RuleTagFilter } from './rule_tag_filter'; import { RuleStatusFilter } from './rule_status_filter'; -const ENTER_KEY = 13; - interface RulesListFiltersBarProps { - inputText: string; - filters: RulesListFilters; - showActionFilter: boolean; - rulesStatusesTotal: Record; - rulesLastRunOutcomesTotal: Record; - tags: string[]; - filterOptions: TypeFilterProps['options']; actionTypes: ActionType[]; + filterOptions: TypeFilterProps['options']; + filters: RulesListFilters; + inputText: string; lastUpdate: string; + rulesLastRunOutcomesTotal: Record; + rulesStatusesTotal: Record; + showActionFilter: boolean; showErrors: boolean; - updateFilters: (updateFiltersProps: UpdateFiltersProps) => void; - setInputText: (text: string) => void; + tags: string[]; onClearSelection: () => void; onRefreshRules: () => void; onToggleRuleErrors: () => void; + setInputText: (text: string) => void; + updateFilters: (updateFiltersProps: UpdateFiltersProps) => void; } +const ENTER_KEY = 13; export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) => { const { - filters, - inputText, - showActionFilter = true, - rulesStatusesTotal, - rulesLastRunOutcomesTotal, - tags, actionTypes, filterOptions, + filters, + inputText, lastUpdate, - showErrors, - updateFilters, - setInputText, onClearSelection, onRefreshRules, onToggleRuleErrors, + rulesLastRunOutcomesTotal, + rulesStatusesTotal, + setInputText, + showActionFilter = true, + showErrors, + tags, + updateFilters, } = props; const isRuleTagFilterEnabled = getIsExperimentalFeatureEnabled('ruleTagFilter'); @@ -136,6 +135,19 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) ...getRuleTagFilter(), ]; + const handleChange = (e: React.ChangeEvent) => { + setInputText(e.target.value); + if (e.target.value === '') { + updateFilters({ filter: 'searchText', value: e.target.value }); + } + }; + + const handleKeyup = (e: React.KeyboardEvent) => { + if (e.keyCode === ENTER_KEY) { + updateFilters({ filter: 'searchText', value: inputText }); + } + }; + return ( <> { - setInputText(e.target.value); - if (e.target.value === '') { - updateFilters({ filter: 'searchText', value: e.target.value }); - } - }} - onKeyUp={(e) => { - if (e.keyCode === ENTER_KEY) { - updateFilters({ filter: 'searchText', value: inputText }); - } - }} placeholder={i18n.translate( 'xpack.triggersActionsUI.sections.rulesList.searchPlaceholderTitle', { defaultMessage: 'Search' } )} + value={inputText} + onChange={handleChange} + onKeyUp={handleKeyup} /> {renderRuleStatusFilter()} diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 8fa34efd2e644..115be4f87df69 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -146,3 +146,5 @@ export const getNotifyWhenOptions = async () => { const { NOTIFY_WHEN_OPTIONS } = await import('./application/sections/rule_form/rule_notify_when'); return NOTIFY_WHEN_OPTIONS; }; + +export { transformRule } from './application/lib/rule_api/common_transformations'; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 83f13dcbe18e5..756ea14488fad 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -254,6 +254,10 @@ export interface ActionTypeModel; customConnectorSelectItem?: CustomConnectorSelectionItem; isExperimental?: boolean; + subtype?: Array<{ id: string; name: string }>; + convertParamsBetweenGroups?: (params: ActionParams) => ActionParams | {}; + hideInUi?: boolean; + modalWidth?: number; } export interface GenericValidationResult { @@ -374,6 +378,11 @@ export interface RuleTypeParamsExpressionProps< unifiedSearch: UnifiedSearchPublicPluginStart; } +export type RuleParamsForRules = Record< + string, + Array<{ label: string; value: string | number | object }> +>; + export interface RuleTypeModel { id: string; description: string; @@ -688,13 +697,14 @@ export interface ConnectorServices { } export interface RulesListFilters { - searchText: string; - types: string[]; actionTypes: string[]; ruleExecutionStatuses: string[]; ruleLastRunOutcomes: string[]; + ruleParams: Record; ruleStatuses: RuleStatus[]; + searchText: string; tags: string[]; + types: string[]; } export type UpdateFiltersProps = @@ -709,6 +719,10 @@ export type UpdateFiltersProps = | { filter: 'types' | 'actionTypes' | 'ruleExecutionStatuses' | 'ruleLastRunOutcomes' | 'tags'; value: string[]; + } + | { + filter: 'ruleParams'; + value: Record; }; export interface RulesPageContainerState { diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index d2ff5b039022e..3222559bf2b9f 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -44,7 +44,6 @@ "@kbn/ui-theme", "@kbn/datemath", "@kbn/core-capabilities-common", - "@kbn/safer-lodash-set", "@kbn/shared-ux-router", "@kbn/alerts-ui-shared", "@kbn/safer-lodash-set", @@ -52,7 +51,7 @@ "@kbn/field-types", "@kbn/ecs", "@kbn/alerts-as-data-utils", - "@kbn/core-ui-settings-common" + "@kbn/core-ui-settings-common", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx index 39ce0108a4d41..89c3407f83a46 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { HttpSetup } from '@kbn/core/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { init as initHttpRequests } from './http_requests'; import { mockContextValue } from './app_context.mock'; @@ -18,9 +19,11 @@ export const WithAppDependencies = setHttpClient(httpSetup); return ( - - - + + + + + ); }; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json_page.helpers.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json_page.helpers.ts index c526ed75beb71..6b70f3bdece88 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json_page.helpers.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_json_page.helpers.ts @@ -81,4 +81,7 @@ export type TestSubjects = | 'simulateResultsFlyoutTitle' | 'simulateWatchButton' | 'tab' - | 'triggeredTimeInput'; + | 'triggeredTimeInput' + | 'simulateResultsTable' + | 'conditionMetStatus' + | 'conditionNotMetStatus'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts index cbda9eb293a70..20f8389c0fec7 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_edit_page.helpers.ts @@ -56,5 +56,6 @@ export type TestSubjects = | 'jsonWatchForm' | 'nameInput' | 'pageTitle' + | 'jsonEditor' | 'thresholdWatchForm' | 'watchTimeFieldSelect'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json_page.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json_page.test.ts index 402b52bf89670..f79a72b456afb 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json_page.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json_page.test.ts @@ -274,6 +274,100 @@ describe(' create route', () => { expect(find('simulateResultsFlyoutTitle').text()).toEqual('Simulation results'); }); }); + + describe('results flyout', () => { + const actionModes = ['simulate', 'force_simulate', 'execute', 'force_execute', 'skip']; + const actionModeStatusesConditionMet = [ + 'simulated', + 'simulated', + 'executed', + 'executed', + 'throttled', + ]; + const actionModeStatusesConditionNotMet = [ + 'not simulated', + 'not simulated', + 'not executed', + 'not executed', + 'throttled', + ]; + const conditionMetStatuses = [true, false]; + const ACTION_NAME = 'my-logging-action'; + const ACTION_TYPE = 'logging'; + const ACTION_STATE = 'OK'; + + actionModes.forEach((actionMode, i) => { + conditionMetStatuses.forEach((conditionMet) => { + describe('for ' + actionMode + ' action mode', () => { + describe( + conditionMet ? 'when the condition is met' : 'when the condition is not met', + () => { + beforeEach(async () => { + const { actions, component, form } = testBed; + form.setInputValue('actionModesSelect', actionMode); + + httpRequestsMockHelpers.setLoadExecutionResultResponse({ + watchHistoryItem: { + details: { + result: { + condition: { + met: conditionMet, + }, + actions: + (conditionMet && [ + { + id: ACTION_NAME, + type: ACTION_TYPE, + status: conditionMet && actionModeStatusesConditionMet[i], + }, + ]) || + [], + }, + }, + watchStatus: { + actionStatuses: [ + { + id: ACTION_NAME, + state: ACTION_STATE, + }, + ], + }, + }, + }); + + await act(async () => { + actions.clickSimulateButton(); + }); + component.update(); + }); + + test('should set the correct condition met status', () => { + const { exists } = testBed; + expect(exists('conditionMetStatus')).toBe(conditionMet); + expect(exists('conditionNotMetStatus')).toBe(!conditionMet); + }); + + test('should set the correct values in the table', () => { + const { table } = testBed; + const { tableCellsValues } = table.getMetaData('simulateResultsTable'); + const row = tableCellsValues[0]; + expect(row).toEqual([ + ACTION_NAME, + ACTION_TYPE, + actionMode, + ACTION_STATE, + '', + conditionMet + ? actionModeStatusesConditionMet[i] + : actionModeStatusesConditionNotMet[i], + ]); + }); + } + ); + }); + }); + }); + }); }); }); }); diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.tsx similarity index 88% rename from x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.ts rename to x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.tsx index 0da6fa68bd856..ba38fbd5b3cc7 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit_page.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { act } from 'react-dom/test-utils'; import { getWatch } from '../../__fixtures__'; @@ -16,6 +17,23 @@ import { API_BASE_PATH } from '../../common/constants'; const { setup } = pageHelpers.watchEditPage; +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + // Mocking CodeEditor, which uses React Monaco under the hood + CodeEditor: (props: any) => ( + { + props.onChange(e.jsonContent); + }} + /> + ), + }; +}); + describe('', () => { const { httpSetup, httpRequestsMockHelpers } = setupEnvironment(); let testBed: WatchEditTestBed; @@ -43,14 +61,14 @@ describe('', () => { }); test('should populate the correct values', () => { - const { find, exists, component } = testBed; + const { find, exists } = testBed; const { watch } = WATCH; - const codeEditor = component.find('EuiCodeEditor').at(1); + const jsonEditorValue = testBed.find('jsonEditor').props()['data-currentvalue']; expect(exists('jsonWatchForm')).toBe(true); expect(find('nameInput').props().value).toBe(watch.name); expect(find('idInput').props().value).toBe(watch.id); - expect(JSON.parse(codeEditor.props().value as string)).toEqual(defaultWatch); + expect(JSON.parse(jsonEditorValue)).toEqual(defaultWatch); // ID should not be editable expect(find('idInput').props().readOnly).toEqual(true); diff --git a/x-pack/plugins/watcher/public/application/index.tsx b/x-pack/plugins/watcher/public/application/index.tsx index dc12832825b81..8c2edb133660d 100644 --- a/x-pack/plugins/watcher/public/application/index.tsx +++ b/x-pack/plugins/watcher/public/application/index.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { Observable } from 'rxjs'; import { CoreTheme } from '@kbn/core/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaThemeProvider } from './shared_imports'; import { App, AppDeps } from './app'; @@ -27,9 +28,11 @@ export const renderApp = (bootDeps: BootDeps) => { render( - - - + + + + + , element ); diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx index 95db0ab535f8e..ef71362bf22a1 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_form.tsx @@ -20,18 +20,17 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { XJsonMode } from '@kbn/ace'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { XJson } from '../../../../shared_imports'; import { serializeJsonWatch } from '../../../../../../common/lib/serialization'; import { ErrableFormRow, SectionError, Error as ServerError } from '../../../../components'; -import { XJson, EuiCodeEditor } from '../../../../shared_imports'; import { onWatchSave } from '../../watch_edit_actions'; import { WatchContext } from '../../watch_context'; import { goToWatchList } from '../../../../lib/navigation'; import { RequestFlyout } from '../request_flyout'; import { useAppContext } from '../../../../app_context'; -const xJsonMode = new XJsonMode(); const { useXJsonMode } = XJson; export const JsonWatchEditForm = () => { @@ -168,18 +167,22 @@ export const JsonWatchEditForm = () => { fullWidth errors={jsonErrors} > - { if (validationError) { setValidationError(null); diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx index 9e2fa3f35b50f..bc6ecfcbc16cc 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/json_watch_edit_simulate.tsx @@ -24,9 +24,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { XJsonMode } from '@kbn/ace'; - -const xJsonMode = new XJsonMode(); +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { WatchHistoryItem } from '../../../../models/watch_history_item'; @@ -43,7 +41,7 @@ import { SimulateWatchResultsFlyout } from './simulate_watch_results_flyout'; import { getTimeUnitLabel } from '../../../../lib/get_time_unit_label'; import { useAppContext } from '../../../../app_context'; -import { XJson, EuiCodeEditor } from '../../../../shared_imports'; +import { XJson } from '../../../../shared_imports'; const { useXJsonMode } = XJson; @@ -369,18 +367,22 @@ export const JsonWatchEditSimulate = ({ fullWidth errors={executeWatchErrors} > - { setXJson(xjson); setExecuteDetails( diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/simulate_watch_results_flyout.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/simulate_watch_results_flyout.tsx index 37aedcb0da08f..e48202e80c304 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/simulate_watch_results_flyout.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/json_watch_edit/simulate_watch_results_flyout.tsx @@ -14,6 +14,7 @@ import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, + EuiIcon, EuiSpacer, EuiText, EuiTitle, @@ -23,7 +24,7 @@ import { ExecutedWatchDetails, ExecutedWatchResults, } from '../../../../../../common/types/watch_types'; -import { ActionStateBadge, WatchStateBadge, SectionError } from '../../../../components'; +import { ActionStateBadge, SectionError } from '../../../../components'; import { getTypeFromAction } from '../../watch_edit_actions'; import { WatchContext } from '../../watch_context'; @@ -42,6 +43,36 @@ export const SimulateWatchResultsFlyout = ({ const { actionModes } = executeDetails; + const conditionNotMetActionStatus = (mode: string) => { + switch (mode) { + case 'simulate': + case 'force_simulate': + return i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.statusColumnValue.notSimulated', + { + defaultMessage: 'not simulated', + } + ); + case 'execute': + case 'force_execute': + return i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.statusColumnValue.notExecuted', + { + defaultMessage: 'not executed', + } + ); + case 'skip': + return i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.statusColumnValue.throttled', + { + defaultMessage: 'throttled', + } + ); + default: + return ''; + } + }; + const getTableData = () => { const actions = watch.watch && watch.watch.actions; if (executeResults && actions) { @@ -49,12 +80,18 @@ export const SimulateWatchResultsFlyout = ({ executeResults.watchStatus && executeResults.watchStatus.actionStatuses; return Object.keys(actions).map((actionKey) => { const actionStatus = actionStatuses.find((status) => status.id === actionKey); + const isConditionMet = executeResults.details?.result?.condition.met; return { actionId: actionKey, actionType: getTypeFromAction(actions[actionKey]), actionMode: actionModes[actionKey], actionState: actionStatus && actionStatus.state, actionReason: actionStatus && actionStatus.lastExecutionReason, + actionStatus: + (isConditionMet && + executeResults.details.result.actions.find((action: any) => action.id === actionKey) + .status) || + conditionNotMetActionStatus(actionModes[actionKey]), }; }); } @@ -116,6 +153,15 @@ export const SimulateWatchResultsFlyout = ({ } ), }, + { + field: 'actionStatus', + name: i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.statusColumnLabel', + { + defaultMessage: 'Status', + } + ), + }, ]; const flyoutTitle = ( @@ -156,10 +202,25 @@ export const SimulateWatchResultsFlyout = ({ return null; } - const { - watchStatus: { state }, - details, - } = executeResults; + const { details } = executeResults; + + const conditionMetStatus = (details?.result?.condition.met && ( + <> + {' '} + + + )) || ( + <> + {' '} + + + ); return ( {flyoutTitle} - + {conditionMetStatus} @@ -189,7 +250,11 @@ export const SimulateWatchResultsFlyout = ({ - + )} diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx index bbe5449fa8732..e7da2c3073870 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit_page/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx @@ -18,7 +18,8 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiCodeEditor } from '../../../../../shared_imports'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; + import { ErrableFormRow } from '../../../../../components/form_errors'; import { WebhookAction } from '../../../../../../../common/types/action_types'; @@ -242,19 +243,22 @@ export const WebhookActionFields: React.FunctionComponent = ({ fullWidth errors={errors} > - { editAction({ key: 'body', value: json }); }} diff --git a/x-pack/plugins/watcher/public/application/shared_imports.ts b/x-pack/plugins/watcher/public/application/shared_imports.ts index 971182a22a7a7..2396c7783aeb1 100644 --- a/x-pack/plugins/watcher/public/application/shared_imports.ts +++ b/x-pack/plugins/watcher/public/application/shared_imports.ts @@ -11,12 +11,6 @@ export type { UseRequestConfig, } from '@kbn/es-ui-shared-plugin/public'; -export { - sendRequest, - useRequest, - XJson, - PageError, - EuiCodeEditor, -} from '@kbn/es-ui-shared-plugin/public'; +export { sendRequest, useRequest, XJson, PageError } from '@kbn/es-ui-shared-plugin/public'; export { KibanaThemeProvider, useExecutionContext } from '@kbn/kibana-react-plugin/public'; diff --git a/x-pack/plugins/watcher/tsconfig.json b/x-pack/plugins/watcher/tsconfig.json index 0762abab662de..cd4305fe86779 100644 --- a/x-pack/plugins/watcher/tsconfig.json +++ b/x-pack/plugins/watcher/tsconfig.json @@ -31,7 +31,6 @@ "@kbn/datemath", "@kbn/field-formats-plugin", "@kbn/i18n-react", - "@kbn/ace", "@kbn/shared-ux-router", "@kbn/license-management-plugin", "@kbn/share-plugin", diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 4e8f8c45abb5b..d418b268f69ce 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -45,6 +45,7 @@ const enabledActionTypes = [ '.jira', '.resilient', '.slack', + '.slack_api', '.tines', '.webhook', '.xmatters', @@ -174,6 +175,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) 'localhost', 'some.non.existent.com', 'smtp.live.com', + 'slack.com', ])}`, `--xpack.actions.enableFooterInEmail=${enableFooterInEmail}`, '--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', diff --git a/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts b/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts index 19d62bdd0682a..a4ab6442df1fd 100644 --- a/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts @@ -5,6 +5,7 @@ * 2.0. */ import type { Client } from '@elastic/elasticsearch'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SerializedConcreteTaskInstance } from '@kbn/task-manager-plugin/server/task'; export interface TaskManagerDoc { @@ -138,7 +139,7 @@ export class TaskManagerUtils { async waitForActionTaskParamsToBeCleanedUp(createdAtFilter: Date): Promise { return await this.retry.try(async () => { const searchResult = await this.es.search({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { bool: { diff --git a/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/slack_simulation.ts b/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/slack_simulation.ts index f1a67c568b67f..eee078591a3a7 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/slack_simulation.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/slack_simulation.ts @@ -35,7 +35,7 @@ export async function initPlugin() { } // store a message that was posted to be remembered - const match = text.match(/^message (.*)$/); + const match = text.match(/^message ([\S\s]*)$/); if (match) { messages.push(match[1]); response.statusCode = 200; diff --git a/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/webhook_simulation.ts b/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/webhook_simulation.ts index f3f0aa8f6469b..baa6ee80a8e53 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/webhook_simulation.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/webhook_simulation.ts @@ -79,7 +79,7 @@ function createServerCallback() { } // store a payload that was posted to be remembered - const match = data.match(/^payload (.*)$/); + const match = data.match(/^payload ([\S\s]*)$/); if (match) { payloads.push(match[1]); response.statusCode = 200; @@ -89,6 +89,8 @@ function createServerCallback() { response.statusCode = 400; response.end(`unexpected body ${data}`); + // eslint-disable-next-line no-console + console.log(`webhook simulator received unexpected body: ${data}`); return; }); } else { diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts index c2f6b5ca17d21..6d716b5d3c235 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts @@ -47,6 +47,7 @@ export const DeepContextVariables = { arrayI: [44, 45], nullJ: null, undefinedK: undefined, + dateL: '2023-04-20T04:13:17.858Z', }; function getAlwaysFiringAlertType() { @@ -195,6 +196,9 @@ function getCumulativeFiringAlertType() { }, }; }, + validate: { + params: schema.any(), + }, }; return result; } @@ -531,6 +535,9 @@ function getPatternFiringAlertType() { }, }; }, + validate: { + params: paramsSchema, + }, }; return result; } @@ -572,6 +579,9 @@ function getPatternSuccessOrFailureAlertType() { }, }; }, + validate: { + params: paramsSchema, + }, }; return result; } @@ -646,6 +656,9 @@ function getPatternFiringAutoRecoverFalseAlertType() { }, }; }, + validate: { + params: paramsSchema, + }, }; return result; } @@ -692,6 +705,9 @@ function getLongRunningPatternRuleType(cancelAlertsOnRuleTimeout: boolean = true } return { state: {} }; }, + validate: { + params: paramsSchema, + }, }; return result; } @@ -752,6 +768,9 @@ function getCancellableRuleType() { return { state: {} }; }, + validate: { + params: paramsSchema, + }, }; return result; } @@ -851,6 +870,9 @@ export function defineAlertTypes( async executor() { return { state: {} }; }, + validate: { + params: schema.any(), + }, }; const goldNoopAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.gold.noop', @@ -863,6 +885,9 @@ export function defineAlertTypes( async executor() { return { state: {} }; }, + validate: { + params: schema.any(), + }, }; const onlyContextVariablesAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.onlyContextVariables', @@ -878,6 +903,9 @@ export function defineAlertTypes( async executor() { return { state: {} }; }, + validate: { + params: schema.any(), + }, }; const onlyStateVariablesAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.onlyStateVariables', @@ -893,6 +921,9 @@ export function defineAlertTypes( async executor() { return { state: {} }; }, + validate: { + params: schema.any(), + }, }; const throwAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.throw', @@ -910,6 +941,9 @@ export function defineAlertTypes( async executor() { throw new Error('this alert is intended to fail'); }, + validate: { + params: schema.any(), + }, }; function getLongRunningRuleType() { const paramsSchema = schema.object({ @@ -935,6 +969,9 @@ export function defineAlertTypes( await new Promise((resolve) => setTimeout(resolve, params.delay ?? 5000)); return { state: {} }; }, + validate: { + params: schema.any(), + }, }; return result; } @@ -953,7 +990,11 @@ export function defineAlertTypes( return { state: {} }; }, producer: 'alertsFixture', + validate: { + params: schema.any(), + }, }; + const multipleSearchesRuleType: RuleType< { numSearches: number; delay: string }, {}, @@ -1006,6 +1047,9 @@ export function defineAlertTypes( return { state: {} }; }, + validate: { + params: schema.object({ numSearches: schema.number(), delay: schema.string() }), + }, }; alerting.registerType(getAlwaysFiringAlertType()); diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts_restricted/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts_restricted/server/alert_types.ts index b4ac8a9fb259a..fd4dabf9c8cab 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts_restricted/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts_restricted/server/alert_types.ts @@ -7,6 +7,7 @@ import { CoreSetup } from '@kbn/core/server'; import { RuleType } from '@kbn/alerting-plugin/server'; +import { schema } from '@kbn/config-schema'; import { FixtureStartDeps, FixtureSetupDeps } from './plugin'; export function defineAlertTypes( @@ -25,6 +26,9 @@ export function defineAlertTypes( async executor() { return { state: {} }; }, + validate: { + params: schema.any(), + }, }; const noopUnrestrictedAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.unrestricted-noop', @@ -37,6 +41,9 @@ export function defineAlertTypes( async executor() { return { state: {} }; }, + validate: { + params: schema.any(), + }, }; alerting.registerType(noopRestrictedAlertType); alerting.registerType(noopUnrestrictedAlertType); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts index f0ba9dc451937..7ad1a54de2485 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts @@ -78,6 +78,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo expect(stableBody).to.eql({ name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts new file mode 100644 index 0000000000000..12c17d2a7a4f9 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts @@ -0,0 +1,76 @@ +/* + * 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 '../../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function slackTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Slack API action', () => { + it('should return 200 when creating a slack action successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A slack api action', + connector_type_id: '.slack_api', + secrets: { + token: 'some token', + }, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_deprecated: false, + is_missing_secrets: false, + name: 'A slack api action', + connector_type_id: '.slack_api', + config: {}, + }); + + expect(typeof createdAction.id).to.be('string'); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + is_preconfigured: false, + is_deprecated: false, + is_missing_secrets: false, + name: 'A slack api action', + connector_type_id: '.slack_api', + config: {}, + }); + }); + + it('should respond with a 400 Bad Request when creating a slack action with no token', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A slack api action', + connector_type_id: '.slack_api', + secrets: {}, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [token]: expected value of type [string] but got [undefined]', + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts similarity index 99% rename from x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts index 305afa0fdcaf9..d9c32ccd643b0 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts @@ -18,7 +18,7 @@ export default function slackTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const configService = getService('config'); - describe('slack action', () => { + describe('Slack webhook action', () => { let simulatedActionId = ''; let slackSimulatorURL: string = ''; let slackServer: http.Server; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts index d32fcb86224f2..dce40323e9e1c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { v4 as uuidv4 } from 'uuid'; import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../../scenarios'; import { checkAAD, getUrlPrefix, ObjectRemover } from '../../../../common/lib'; @@ -257,6 +258,65 @@ export default function createActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it('should handle create action request appropriately with a predefined id', async () => { + const predefinedId = uuidv4(); + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector/${predefinedId}`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "test.index-record" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + objectRemover.add(space.id, response.body.id, 'action', 'actions'); + expect(response.body).to.eql({ + id: predefinedId, + is_preconfigured: false, + is_deprecated: false, + is_missing_secrets: false, + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + }); + expect(typeof response.body.id).to.be('string'); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: space.id, + type: 'action', + id: response.body.id, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index 66f83daff0429..05bd4da72c19c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -32,7 +32,8 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide loadTestFile(require.resolve('./connector_types/opsgenie')); loadTestFile(require.resolve('./connector_types/pagerduty')); loadTestFile(require.resolve('./connector_types/server_log')); - loadTestFile(require.resolve('./connector_types/slack')); + loadTestFile(require.resolve('./connector_types/slack_webhook')); + loadTestFile(require.resolve('./connector_types/slack_api')); loadTestFile(require.resolve('./connector_types/webhook')); loadTestFile(require.resolve('./connector_types/xmatters')); loadTestFile(require.resolve('./connector_types/tines')); @@ -48,6 +49,6 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide /** * Sub action framework */ - loadTestFile(require.resolve('./sub_action_framework')); + // loadTestFile(require.resolve('./sub_action_framework')); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts index 7b5f41f4448d2..410fee01c71f5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts @@ -1244,8 +1244,7 @@ instanceStateValue: true throttle: null, summary: true, alertsFilter: { - timeframe: null, - query: { kql: 'kibana.alert.rule.name:abc' }, + query: { kql: 'kibana.alert.rule.name:abc', filters: [] }, }, }); @@ -1307,8 +1306,7 @@ instanceStateValue: true throttle: null, summary: true, alertsFilter: { - timeframe: null, - query: { kql: 'kibana.alert.instance.id:1' }, + query: { kql: 'kibana.alert.instance.id:1', filters: [] }, }, }); @@ -1383,7 +1381,6 @@ instanceStateValue: true timezone: 'UTC', hours: { start, end }, }, - query: null, }, }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts index 240ca53cb091e..a08a7a7c62149 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/health.ts @@ -86,10 +86,6 @@ export default function createFindTests({ getService }: FtrProviderContext) { expect(health.alerting_framework_health.decryption_health.status).to.eql('ok'); expect(health.alerting_framework_health.execution_health.status).to.eql('ok'); expect(health.alerting_framework_health.read_health.status).to.eql('ok'); - // Legacy: pre-v8.0 typo - expect(health.alerting_framework_heath.decryption_health.status).to.eql('ok'); - expect(health.alerting_framework_heath.execution_health.status).to.eql('ok'); - expect(health.alerting_framework_heath.read_health.status).to.eql('ok'); } }); @@ -140,11 +136,6 @@ export default function createFindTests({ getService }: FtrProviderContext) { expect(health.alerting_framework_health.execution_health.timestamp).to.eql( ruleInErrorStatus.execution_status.last_execution_date ); - // Legacy: pre-v8.0 typo - expect(health.alerting_framework_heath.execution_health.status).to.eql('warn'); - expect(health.alerting_framework_heath.execution_health.timestamp).to.eql( - ruleInErrorStatus.execution_status.last_execution_date - ); } }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/active_maintenance_windows.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/active_maintenance_windows.ts new file mode 100644 index 0000000000000..abd837485c7ec --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/active_maintenance_windows.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import expect from '@kbn/expect'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function activeMaintenanceWindowTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('activeMaintenanceWindow', () => { + const objectRemover = new ObjectRemover(supertest); + const createParams = { + title: 'test-maintenance-window', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + freq: 2, // weekly + }, + }; + after(() => objectRemover.removeAll()); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + afterEach(() => objectRemover.removeAll()); + it('should handle update maintenance window request appropriately', async () => { + // Create 2 active and 1 inactive maintenance window + const { body: createdMaintenanceWindow1 } = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send(createParams); + + const { body: createdMaintenanceWindow2 } = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send(createParams); + + const { body: createdMaintenanceWindow3 } = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + ...createParams, + r_rule: { + ...createParams.r_rule, + dtstart: moment.utc().add(1, 'day').toISOString(), + }, + }); + + objectRemover.add( + space.id, + createdMaintenanceWindow1.id, + 'rules/maintenance_window', + 'alerting', + true + ); + objectRemover.add( + space.id, + createdMaintenanceWindow2.id, + 'rules/maintenance_window', + 'alerting', + true + ); + objectRemover.add( + space.id, + createdMaintenanceWindow3.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window/_active`) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send({}); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'space_1_all_with_restricted_fixture at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: 'Forbidden', + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.body.length).to.eql(2); + expect(response.statusCode).to.eql(200); + expect(response.body[0].title).to.eql('test-maintenance-window'); + expect(response.body[0].duration).to.eql(3600000); + expect(response.body[0].r_rule.dtstart).to.eql(createParams.r_rule.dtstart); + expect(response.body[0].events.length).to.be.greaterThan(0); + expect(response.body[0].status).to.eql('running'); + + const ids = response.body.map( + (maintenanceWindow: { id: string }) => maintenanceWindow.id + ); + expect(ids.sort()).to.eql( + [createdMaintenanceWindow1.id, createdMaintenanceWindow2.id].sort() + ); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + + it('should return an empty array if there are no active maintenance windows', async () => { + const { body: createdMaintenanceWindow } = await supertest + .post(`${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + ...createParams, + r_rule: { + ...createParams.r_rule, + dtstart: moment.utc().add(1, 'day').toISOString(), + }, + }); + + objectRemover.add( + 'space1', + createdMaintenanceWindow.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + const response = await supertest + .get(`${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window/_active`) + .set('kbn-xsrf', 'foo') + .send({}) + .expect(200); + + expect(response.body).to.eql([]); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/index.ts index 70a90a4f63c7c..a848a14d3e1b7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/index.ts @@ -27,6 +27,7 @@ export default function maintenanceWindowTests({ loadTestFile, getService }: Ftr loadTestFile(require.resolve('./archive_maintenance_window')); loadTestFile(require.resolve('./finish_maintenance_window')); loadTestFile(require.resolve('./find_maintenance_windows')); + loadTestFile(require.resolve('./active_maintenance_windows')); }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts index 2fa8fa83a5532..40550997eed06 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts @@ -5,6 +5,7 @@ * 2.0. */ +import moment from 'moment'; import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, ObjectRemover } from '../../../../common/lib'; @@ -88,5 +89,54 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider }); }); } + + it('should update RRule correctly when removing fields', async () => { + const { body: createdMaintenanceWindow } = await supertest + .post(`${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + ...createParams, + r_rule: { + ...createParams.r_rule, + count: 1, + until: moment.utc().add(1, 'week').toISOString(), + }, + }) + .expect(200); + + objectRemover.add( + 'space1', + createdMaintenanceWindow.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + const updatedRRule = { + ...createParams.r_rule, + count: 2, + }; + + await supertest + .post( + `${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window/${ + createdMaintenanceWindow.id + }` + ) + .set('kbn-xsrf', 'foo') + .send({ + ...createParams, + r_rule: updatedRRule, + }) + .expect(200); + + const response = await supertest + .get(`${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window/_find`) + .set('kbn-xsrf', 'foo') + .send({}); + + expect(response.body.data[0].id).to.eql(createdMaintenanceWindow.id); + expect(response.body.data[0].r_rule).to.eql(updatedRRule); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/action_task_params/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/action_task_params/migrations.ts index 8a6532302a2e1..c7c9611b21312 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/action_task_params/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/action_task_params/migrations.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import { SavedObject, SavedObjectReference } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { ActionTaskParams } from '@kbn/actions-plugin/server/types'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; @@ -28,7 +29,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { // Inspect migration of non-preconfigured connector ID const response = await es.get>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'action_task_params:b9af6280-0052-11ec-917b-f7aa317691ed', }, { meta: true } @@ -54,7 +55,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { // Inspect migration of preconfigured connector ID const preconfiguredConnectorResponse = await es.get>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'action_task_params:0205a520-0054-11ec-917b-f7aa317691ed', }, { meta: true } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts index 830f5e6f8d96d..f0578f6dbd7ce 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts @@ -34,6 +34,7 @@ export default function createRegisteredConnectorTypeTests({ getService }: FtrPr '.swimlane', '.server-log', '.slack', + '.slack_api', '.webhook', '.cases-webhook', '.xmatters', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts index 3f540927a9be7..7c49eedd3b85e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { SavedObject } from '@kbn/core/server'; import { RawRule } from '@kbn/alerting-plugin/server/types'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Spaces } from '../../../scenarios'; import { checkAAD, @@ -213,7 +214,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const esResponse = await es.get>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: `alert:${response.body.id}`, }, { meta: true } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts index 7d832ccb450b4..4e3a3fb70a455 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts @@ -5,6 +5,7 @@ * 2.0. */ +import moment from 'moment'; import expect from '@kbn/expect'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; @@ -1180,6 +1181,133 @@ export default function eventLogTests({ getService }: FtrProviderContext) { } } }); + + it('should generate expected events affected by active maintenance windows', async () => { + // Create 2 active maintenance windows + const { body: window1 } = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window-1', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: moment.utc().toISOString(), + tzid: 'UTC', + freq: 0, // yearly + count: 1, + }, + }) + .expect(200); + objectRemover.add(space.id, window1.id, 'rules/maintenance_window', 'alerting', true); + + const { body: window2 } = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window-2', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: moment.utc().toISOString(), + tzid: 'UTC', + freq: 0, // yearly + count: 1, + }, + }) + .expect(200); + objectRemover.add(space.id, window2.id, 'rules/maintenance_window', 'alerting', true); + + // Create 1 inactive maintenance window + const { body: window3 } = await supertest + .post(`${getUrlPrefix(space.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window-3', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: moment.utc().add(1, 'day').toISOString(), + tzid: 'UTC', + freq: 0, // yearly + count: 1, + }, + }) + .expect(200); + objectRemover.add(space.id, window3.id, 'rules/maintenance_window', 'alerting', true); + + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + // pattern of when the alert should fire + const pattern = { + instance: [false, true, true], + }; + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiring', + schedule: { interval: '1s' }, + throttle: null, + params: { + pattern, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }) + ); + + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(space.id, alertId, 'rule', 'alerting'); + + // get the events we're expecting + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: space.id, + type: 'alert', + id: alertId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute-start', { gte: 4 }], + ['execute', { gte: 4 }], + ['new-instance', { equal: 1 }], + ['active-instance', { gte: 1 }], + ['recovered-instance', { equal: 1 }], + ]), + }); + }); + + const actionsToCheck = [ + 'new-instance', + 'active-instance', + 'recovered-instance', + 'execute', + ]; + + events.forEach((event) => { + if (actionsToCheck.includes(event?.event?.action || '')) { + const alertMaintenanceWindowIds = + event?.kibana?.alert?.maintenance_window_ids?.sort(); + expect(alertMaintenanceWindowIds).eql([window1.id, window2.id].sort()); + } + }); + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts index 75fd92ec61eaa..9be9db54904aa 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts @@ -68,6 +68,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo id: createdRule.id, name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', @@ -106,6 +107,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo id: createdRule.id, name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', @@ -268,6 +270,94 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo expect(actualAlerts).to.eql(expectedAlerts); }); + it('handles multi-alert status during maintenance window', async () => { + // pattern of when the rule should fire + const pattern = { + alertA: [true, true, true, true], + alertB: [true, true, false, false], + alertC: [true, true, true, true], + }; + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiring', + params: { pattern }, + schedule: { interval: '1s' }, + }) + ) + .expect(200); + + objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); + + const { body: createdMaintenanceWindow } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + freq: 2, // weekly + }, + }); + + objectRemover.add( + Spaces.space1.id, + createdMaintenanceWindow.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + await alertUtils.muteInstance(createdRule.id, 'alertC'); + await alertUtils.muteInstance(createdRule.id, 'alertD'); + await waitForEvents(createdRule.id, ['new-instance', 'recovered-instance']); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdRule.id}/_alert_summary` + ); + + const actualAlerts = checkAndCleanActualAlerts(response.body.alerts, [ + 'alertA', + 'alertB', + 'alertC', + ]); + + const expectedAlerts = { + alertA: { + status: 'Active', + muted: false, + actionGroupId: 'default', + activeStartDate: actualAlerts.alertA.activeStartDate, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertB: { + status: 'OK', + muted: false, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertC: { + status: 'Active', + muted: true, + actionGroupId: 'default', + activeStartDate: actualAlerts.alertC.activeStartDate, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertD: { + status: 'OK', + muted: true, + flapping: false, + }, + }; + expect(actualAlerts).to.eql(expectedAlerts); + }); + describe('legacy', () => { it('handles multi-alert status', async () => { // pattern of when the alert should fire diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts index 50667e7742374..788fe8babbf29 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts @@ -10,6 +10,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { RawRule, RawRuleAction } from '@kbn/alerting-plugin/server/types'; import { FILEBEAT_7X_INDICATOR_PATH } from '@kbn/alerting-plugin/server/saved_objects/migrations'; import type { SavedObjectReference } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { getUrlPrefix } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -185,7 +186,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { // NOTE: We have to use elasticsearch directly against the ".kibana" index because alerts do not expose the references which we want to test exists const response = await es.get<{ references: [{}] }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:38482620-ef1b-11eb-ad71-7de7959be71c', }, { meta: true } @@ -208,7 +209,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('7.16.0 migrates existing alerts to contain legacyId field', async () => { const searchResult = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { term: { @@ -230,7 +231,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('7.16.0 migrates existing rules so predefined connectors are not stored in references', async () => { const searchResult = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { term: { @@ -273,7 +274,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { // NOTE: We hae to use elastic search directly against the ".kibana" index because alerts do not expose the references which we want to test exists const response = await es.get<{ references: [{}] }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:d7a8c6a1-9394-48df-a634-d5457c35d747', }, { meta: true } @@ -297,7 +298,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }; }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:ece1ece2-9394-48df-a634-d5457c351ece', }, { meta: true } @@ -317,7 +318,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }; }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:fce1ece2-9394-48df-a634-d5457c351fce', }, { meta: true } @@ -337,7 +338,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }; }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:1ce1ece2-9394-48df-a634-d5457c3511ce', }, { meta: true } @@ -351,7 +352,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.0 migrates incorrect action group spellings on the Metrics Inventory Threshold rule type', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:92237b30-4e03-11ec-9ab9-d980518a2d28', }, { meta: true } @@ -365,7 +366,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.0 migrates and disables pre-existing rules', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:38482620-ef1b-11eb-ad71-7de7959be71c', }, { meta: true } @@ -378,7 +379,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.0.1 migrates and adds tags to disabled rules in 8.0', async () => { const responseEnabledBeforeMigration = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:1efdfa40-8ec7-11ec-a700-5524407a7653', }, { meta: true } @@ -386,7 +387,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(responseEnabledBeforeMigration.statusCode).toEqual(200); const responseDisabledBeforeMigration = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:13fdfa40-8ec7-11ec-a700-5524407a7667', }, { meta: true } @@ -407,7 +408,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.2.0 migrates params to mapped_params for specific params properties', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:66560b6f-5ca4-41e2-a1a1-dcfd7117e124', }, { meta: true } @@ -423,7 +424,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.2.0 migrates existing esQuery alerts to contain searchType param', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8', }, { meta: true } @@ -435,7 +436,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.3.0 removes internal tags in Security Solution rule', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:8990af61-c09a-11ec-9164-4bfd6fc32c43', }, { meta: true } @@ -448,7 +449,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.4.1 removes IsSnoozedUntil', async () => { const searchResult = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { term: { @@ -474,7 +475,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }; }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:c8b39c29-d860-43b6-8817-b8058d80ddbc', }, { meta: true } @@ -494,7 +495,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }; }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:62c62b7f-8bf3-4104-a064-6247b7bda44f', }, { meta: true } @@ -514,7 +515,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }; }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:f0d13f4d-35ae-4554-897a-6392e97bb84c', }, { meta: true } @@ -526,7 +527,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.6.0 migrates executionStatus and monitoring', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:8370ffd2-f2db-49dc-9741-92c657189b9b', }, { meta: true } @@ -571,7 +572,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.6 migrates executionStatus warnings and errors', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:c87707ac-7328-47f7-b212-2cb40a4fc9b9', }, { meta: true } @@ -587,7 +588,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.7.0 adds aggType and groupBy to ES query rules', async () => { const response = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { bool: { @@ -629,7 +630,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { const response = await es.get<{ alert: RawRule; references: SavedObjectReference[] }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:8bd01ff0-9d84-11ed-994d-f1971f849da5', }, { meta: true } @@ -642,7 +643,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('8.7 adds uuid to the actions', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:9c003b00-00ee-11ec-b067-2524946ba327', }, { meta: true } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts index 963f856845c10..a075fdaa387a5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations/8_2_0.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import type { RawRule } from '@kbn/alerting-plugin/server/types'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -32,7 +33,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('has snoozeEndTime removed', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:bdfce750-fba0-11ec-9157-2f379249da99', }, { meta: true } @@ -63,7 +64,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('has snoozeEndTime migrated to snoozeSchedule', async () => { const response = await es.get<{ alert: RawRule }>( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: 'alert:402084f0-fbb8-11ec-856c-39466bd4c433', }, { meta: true } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts index 1eb7d99b93bda..764dd728b1347 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts @@ -14,13 +14,16 @@ import http from 'http'; import getPort from 'get-port'; -import { URL, format as formatUrl } from 'url'; import axios from 'axios'; import expect from '@kbn/expect'; import { getWebhookServer, getSlackServer } from '@kbn/actions-simulators-plugin/server/plugin'; import { Spaces } from '../../../scenarios'; -import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; +import { + getUrlPrefix, + getTestRuleData as getCoreTestRuleData, + ObjectRemover, +} from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -32,8 +35,10 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon const objectRemover = new ObjectRemover(supertest); let webhookSimulatorURL: string = ''; let webhookServer: http.Server; + let webhookConnector: any; let slackSimulatorURL: string = ''; let slackServer: http.Server; + let slackConnector: any; before(async () => { let availablePort: number; @@ -42,6 +47,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon availablePort = await getPort({ port: 9000 }); webhookServer.listen(availablePort); webhookSimulatorURL = `http://localhost:${availablePort}`; + webhookConnector = await createWebhookConnector(webhookSimulatorURL); slackServer = await getSlackServer(); availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); @@ -49,6 +55,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon slackServer.listen(availablePort); } slackSimulatorURL = `http://localhost:${availablePort}`; + slackConnector = await createSlackConnector(slackSimulatorURL); }); after(async () => { @@ -57,219 +64,177 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon slackServer.close(); }); - it('should handle escapes in webhook', async () => { - const url = formatUrl(new URL(webhookSimulatorURL), { auth: false }); - const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) - .set('kbn-xsrf', 'test') - .send({ - name: 'testing mustache escapes for webhook', - connector_type_id: '.webhook', - secrets: {}, - config: { - headers: { - 'Content-Type': 'text/plain', - }, - url, + describe('escaping', () => { + it('should handle escapes in webhook', async () => { + // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, + // const EscapableStrings + const template = '{{context.escapableDoubleQuote}} -- {{context.escapableLineFeed}}'; + const rule = await createRule({ + id: webhookConnector.id, + group: 'default', + params: { + body: `payload {{alertId}} - ${template}`, + }, + }); + const body = await retry.try(async () => waitForActionBody(webhookSimulatorURL, rule.id)); + expect(body).to.be(`\\"double quote\\" -- line\\nfeed`); + }); + + it('should handle escapes in slack', async () => { + // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, + // const EscapableStrings + const template = + '{{context.escapableBacktic}} -- {{context.escapableBold}} -- {{context.escapableBackticBold}} -- {{context.escapableHtml}}'; + + const rule = await createRule({ + id: slackConnector.id, + group: 'default', + params: { + message: `message {{alertId}} - ${template}`, }, }); - expect(actionResponse.status).to.eql(200); - const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); - - // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, - // const EscapableStrings - const varsTemplate = '{{context.escapableDoubleQuote}} -- {{context.escapableLineFeed}}'; - const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - name: 'testing variable escapes for webhook', - rule_type_id: 'test.patternFiring', - params: { - pattern: { instance: [true] }, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: { - body: `payload {{alertId}} - ${varsTemplate}`, - }, - }, - ], - }) + const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, rule.id)); + expect(body).to.be("back'tic -- `*bold*` -- `'*bold*'` -- <&>"); + }); + + it('should handle context variable object expansion', async () => { + // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, + // const DeepContextVariables + const template = '{{context.deep}}'; + const rule = await createRule({ + id: slackConnector.id, + group: 'default', + params: { + message: `message {{alertId}} - ${template}`, + }, + }); + const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, rule.id)); + expect(body).to.be( + '{"objectA":{"stringB":"B","arrayC":[{"stringD":"D1","numberE":42},{"stringD":"D2","numberE":43}],"objectF":{"stringG":"G","nullG":null}},"stringH":"H","arrayI":[44,45],"nullJ":null,"dateL":"2023-04-20T04:13:17.858Z"}' ); - expect(alertResponse.status).to.eql(200); - const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - - const body = await retry.try(async () => - waitForActionBody(webhookSimulatorURL, createdAlert.id) - ); - expect(body).to.be(`\\"double quote\\" -- line\\nfeed`); - }); - - it('should handle escapes in slack', async () => { - const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) - .set('kbn-xsrf', 'test') - .send({ - name: "testing backtic'd mustache escapes for slack", - connector_type_id: '.slack', - secrets: { - webhookUrl: slackSimulatorURL, + }); + + it('should render kibanaBaseUrl as empty string since not configured', async () => { + const template = 'kibanaBaseUrl: "{{kibanaBaseUrl}}"'; + const rule = await createRule({ + id: slackConnector.id, + group: 'default', + params: { + message: `message {{alertId}} - ${template}`, }, }); - expect(actionResponse.status).to.eql(200); - const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); - // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, - // const EscapableStrings - const varsTemplate = - '{{context.escapableBacktic}} -- {{context.escapableBold}} -- {{context.escapableBackticBold}} -- {{context.escapableHtml}}'; + const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, rule.id)); + expect(body).to.be('kibanaBaseUrl: ""'); + }); - const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - name: 'testing variable escapes for slack', - rule_type_id: 'test.patternFiring', - params: { - pattern: { instance: [true] }, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: { - message: `message {{alertId}} - ${varsTemplate}`, - }, - }, - ], - }) - ); - expect(alertResponse.status).to.eql(200); - const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - - const body = await retry.try(async () => - waitForActionBody(slackSimulatorURL, createdAlert.id) - ); - expect(body).to.be("back'tic -- `*bold*` -- `'*bold*'` -- <&>"); - }); - - it('should handle context variable object expansion', async () => { - const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) - .set('kbn-xsrf', 'test') - .send({ - name: 'testing context variable expansion', - connector_type_id: '.slack', - secrets: { - webhookUrl: slackSimulatorURL, + it('should render action variables in rule action', async () => { + const rule = await createRule({ + id: webhookConnector.id, + group: 'default', + params: { + body: `payload {{rule.id}} - old id variable: {{alertId}}, new id variable: {{rule.id}}, old name variable: {{alertName}}, new name variable: {{rule.name}}`, }, }); - expect(actionResponse.status).to.eql(200); - const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); - - // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, - // const DeepContextVariables - const varsTemplate = '{{context.deep}}'; - const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - name: 'testing context variable expansion', - rule_type_id: 'test.patternFiring', - params: { - pattern: { instance: [true, true] }, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: { - message: `message {{alertId}} - ${varsTemplate}`, - }, - }, - ], - }) + const body = await retry.try(async () => waitForActionBody(webhookSimulatorURL, rule.id)); + expect(body).to.be( + `old id variable: ${rule.id}, new id variable: ${rule.id}, old name variable: ${rule.name}, new name variable: ${rule.name}` ); - expect(alertResponse.status).to.eql(200); - const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - - const body = await retry.try(async () => - waitForActionBody(slackSimulatorURL, createdAlert.id) - ); - expect(body).to.be( - '{"objectA":{"stringB":"B","arrayC":[{"stringD":"D1","numberE":42},{"stringD":"D2","numberE":43}],"objectF":{"stringG":"G","nullG":null}},"stringH":"H","arrayI":[44,45],"nullJ":null}' - ); + }); }); - it('should render kibanaBaseUrl as empty string since not configured', async () => { - const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) - .set('kbn-xsrf', 'test') - .send({ - name: 'testing context variable expansion', - connector_type_id: '.slack', - secrets: { - webhookUrl: slackSimulatorURL, + describe('lambdas', () => { + it('should handle ParseHjson', async () => { + const template = `{{#ParseHjson}} { + ruleId: {{rule.id}} + ruleName: {{rule.name}} + } {{/ParseHjson}}`; + const rule = await createRule({ + id: webhookConnector.id, + group: 'default', + params: { + body: `payload {{alertId}} - ${template}`, }, }); - expect(actionResponse.status).to.eql(200); - const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); - - const varsTemplate = 'kibanaBaseUrl: "{{kibanaBaseUrl}}"'; + const body = await retry.try(async () => waitForActionBody(webhookSimulatorURL, rule.id)); + expect(body).to.be(`{"ruleId":"${rule.id}","ruleName":"testing mustache templates"}`); + }); + + it('should handle asJSON', async () => { + // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, + // const DeepContextVariables + const template = `{{#context.deep.objectA}} + {{{arrayC}}} {{{arrayC.asJSON}}} + {{/context.deep.objectA}} + `; + const rule = await createRule({ + id: webhookConnector.id, + group: 'default', + params: { + body: `payload {{alertId}} - ${template}`, + }, + }); + const body = await retry.try(async () => waitForActionBody(webhookSimulatorURL, rule.id)); + const expected1 = `{"stringD":"D1","numberE":42},{"stringD":"D2","numberE":43}`; + const expected2 = `[{"stringD":"D1","numberE":42},{"stringD":"D2","numberE":43}]`; + expect(body.trim()).to.be(`${expected1} ${expected2}`); + }); + + it('should handle EvalMath', async () => { + // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, + // const DeepContextVariables + const template = `{{#context.deep}}avg({{arrayI.0}}, {{arrayI.1}})/100 => {{#EvalMath}} + round((arrayI[0] + arrayI[1]) / 2 / 100, 2) + {{/EvalMath}}{{/context.deep}}`; + const rule = await createRule({ + id: slackConnector.id, + group: 'default', + params: { + message: `message {{alertId}} - ${template}`, + }, + }); + const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, rule.id)); + expect(body).to.be(`avg(44, 45)/100 => 0.45`); + }); + + it('should handle FormatDate', async () => { + // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, + // const DeepContextVariables + const template = `{{#context.deep}}{{#FormatDate}} + {{{dateL}}} ; America/New_York; dddd MMM Do YYYY HH:mm:ss + {{/FormatDate}}{{/context.deep}}`; + const rule = await createRule({ + id: slackConnector.id, + group: 'default', + params: { + message: `message {{alertId}} - ${template}`, + }, + }); + const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, rule.id)); + expect(body.trim()).to.be(`Thursday Apr 20th 2023 00:13:17`); + }); + }); - const alertResponse = await supertest + async function createRule(action: any) { + const ruleResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - name: 'testing context variable kibanaBaseUrl', - rule_type_id: 'test.patternFiring', - params: { - pattern: { instance: [true, true] }, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: { - message: `message {{alertId}} - ${varsTemplate}`, - }, - }, - ], - }) - ); - expect(alertResponse.status).to.eql(200); - const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - - const body = await retry.try(async () => - waitForActionBody(slackSimulatorURL, createdAlert.id) - ); - expect(body).to.be('kibanaBaseUrl: ""'); - }); + .send(getTestRuleData({ actions: [action] })); + expect(ruleResponse.status).to.eql(200); + const rule = ruleResponse.body; + objectRemover.add(Spaces.space1.id, rule.id, 'rule', 'alerting'); + + return rule; + } - it('should render action variables in rule action', async () => { - const url = formatUrl(new URL(webhookSimulatorURL), { auth: false }); - const actionResponse = await supertest + async function createWebhookConnector(url: string) { + const createResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ - name: 'testing action variable rendering', + name: 'testing mustache for webhook', connector_type_id: '.webhook', secrets: {}, config: { @@ -279,42 +244,30 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon url, }, }); - expect(actionResponse.status).to.eql(200); - const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); + expect(createResponse.status).to.eql(200); + const connector = createResponse.body; + objectRemover.add(Spaces.space1.id, connector.id, 'connector', 'actions'); - const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) - .set('kbn-xsrf', 'foo') - .send( - getTestRuleData({ - name: 'testing variable escapes for webhook', - rule_type_id: 'test.patternFiring', - params: { - pattern: { instance: [true] }, - }, - actions: [ - { - id: createdAction.id, - group: 'default', - params: { - body: `payload {{rule.id}} - old id variable: {{alertId}}, new id variable: {{rule.id}}, old name variable: {{alertName}}, new name variable: {{rule.name}}`, - }, - }, - ], - }) - ); - expect(alertResponse.status).to.eql(200); - const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); - - const body = await retry.try(async () => - waitForActionBody(webhookSimulatorURL, createdAlert.id) - ); - expect(body).to.be( - `old id variable: ${createdAlert.id}, new id variable: ${createdAlert.id}, old name variable: ${createdAlert.name}, new name variable: ${createdAlert.name}` - ); - }); + return connector; + } + + async function createSlackConnector(url: string) { + const createResponse = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'test') + .send({ + name: 'testing mustache for slack', + connector_type_id: '.slack', + secrets: { + webhookUrl: url, + }, + }); + expect(createResponse.status).to.eql(200); + const connector = createResponse.body; + objectRemover.add(Spaces.space1.id, connector.id, 'connector', 'actions'); + + return connector; + } }); async function waitForActionBody(url: string, id: string): Promise { @@ -322,7 +275,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon expect(response.status).to.eql(200); for (const datum of response.data) { - const match = datum.match(/^(.*) - (.*)$/); + const match = datum.match(/^(.*) - ([\S\s]*)$/); if (match == null) continue; if (match[1] === id) return match[2]; @@ -331,3 +284,15 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon throw new Error(`no action body posted yet for id ${id}`); } } + +function getTestRuleData(overrides: any) { + const defaults = { + name: 'testing mustache templates', + rule_type_id: 'test.patternFiring', + params: { + pattern: { instance: [true] }, + }, + }; + + return getCoreTestRuleData({ ...overrides, ...defaults }); +} diff --git a/x-pack/test/api_integration/apis/maps/maps_telemetry.ts b/x-pack/test/api_integration/apis/maps/maps_telemetry.ts index c953aff7000c5..2a055ce007fee 100644 --- a/x-pack/test/api_integration/apis/maps/maps_telemetry.ts +++ b/x-pack/test/api_integration/apis/maps/maps_telemetry.ts @@ -30,8 +30,8 @@ export default function ({ getService }: FtrProviderContext) { return fieldStat.name === 'geo_point'; } ); - expect(geoPointFieldStats.count).to.be(39); - expect(geoPointFieldStats.index_count).to.be(10); + expect(geoPointFieldStats.count).to.be(31); + expect(geoPointFieldStats.index_count).to.be(9); const geoShapeFieldStats = apiResponse.cluster_stats.indices.mappings.field_types.find( (fieldStat: estypes.ClusterStatsFieldTypes) => { diff --git a/x-pack/test/api_integration/apis/metrics_ui/index.js b/x-pack/test/api_integration/apis/metrics_ui/index.js index 150a123121051..8a4b7d3398d1c 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/index.js +++ b/x-pack/test/api_integration/apis/metrics_ui/index.js @@ -22,6 +22,7 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./metrics_process_list')); loadTestFile(require.resolve('./metrics_process_list_chart')); loadTestFile(require.resolve('./infra_log_analysis_validation_log_entry_datasets')); + loadTestFile(require.resolve('./infra')); loadTestFile(require.resolve('./inventory_threshold_alert')); }); } diff --git a/x-pack/test/api_integration/apis/metrics_ui/infra.ts b/x-pack/test/api_integration/apis/metrics_ui/infra.ts new file mode 100644 index 0000000000000..5d84ba1798a9a --- /dev/null +++ b/x-pack/test/api_integration/apis/metrics_ui/infra.ts @@ -0,0 +1,269 @@ +/* + * 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 { + GetInfraMetricsRequestBodyPayload, + GetInfraMetricsResponsePayload, +} from '@kbn/infra-plugin/common/http_api/infra'; +import { DATES } from './constants'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const ENDPOINT = '/api/metrics/infra'; + +const normalizeNewLine = (text: string) => { + return text.replaceAll(/(\s{2,}|\\n\\s)/g, ' '); +}; +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + + const basePayload: GetInfraMetricsRequestBodyPayload = { + type: 'host', + limit: 10, + metrics: [ + { + type: 'cpu', + }, + { + type: 'diskLatency', + }, + { + type: 'memory', + }, + { + type: 'memoryTotal', + }, + { + type: 'rx', + }, + { + type: 'tx', + }, + ], + range: { + from: new Date(DATES['8.0.0'].logs_and_metrics.min).toISOString(), + to: new Date(DATES['8.0.0'].logs_and_metrics.max).toISOString(), + }, + query: { bool: { must_not: [], filter: [], should: [], must: [] } }, + sourceId: 'default', + }; + + const makeRequest = async ({ + body, + invalidBody, + expectedHTTPCode, + }: { + body?: GetInfraMetricsRequestBodyPayload; + invalidBody?: any; + expectedHTTPCode: number; + }) => { + return supertest + .post(ENDPOINT) + .set('kbn-xsrf', 'xxx') + .send(body ?? invalidBody) + .expect(expectedHTTPCode); + }; + + describe('Hosts', () => { + before(() => + esArchiver.load('x-pack/test/functional/es_archives/infra/8.0.0/logs_and_metrics') + ); + after(() => + esArchiver.unload('x-pack/test/functional/es_archives/infra/8.0.0/logs_and_metrics') + ); + + describe('fetch hosts', () => { + it('should return metrics for a host', async () => { + const body: GetInfraMetricsRequestBodyPayload = { ...basePayload, limit: 1 }; + const response = await makeRequest({ body, expectedHTTPCode: 200 }); + + expect(response.body.nodes).length(1); + expect(response.body.nodes).eql([ + { + metadata: [ + { name: 'host.os.name', value: 'CentOS Linux' }, + { name: 'cloud.provider', value: 'gcp' }, + { name: 'host.ip', value: null }, + ], + metrics: [ + { name: 'cpu', value: 0.44708333333333333 }, + { name: 'diskLatency', value: null }, + { name: 'memory', value: 0.4563333333333333 }, + { name: 'memoryTotal', value: 15768948736 }, + { name: 'rx', value: null }, + { name: 'tx', value: null }, + ], + name: 'gke-observability-8--observability-8--bc1afd95-f0zc', + }, + ]); + }); + + it('should return all hosts if query params is not sent', async () => { + const body: GetInfraMetricsRequestBodyPayload = { + ...basePayload, + metrics: [ + { + type: 'memory', + }, + ], + query: undefined, + }; + + const response = await makeRequest({ body, expectedHTTPCode: 200 }); + expect(response.body.nodes).eql([ + { + metadata: [ + { name: 'host.os.name', value: 'CentOS Linux' }, + { name: 'cloud.provider', value: 'gcp' }, + { name: 'host.ip', value: null }, + ], + metrics: [{ name: 'memory', value: 0.4563333333333333 }], + name: 'gke-observability-8--observability-8--bc1afd95-f0zc', + }, + { + metadata: [ + { name: 'host.os.name', value: 'CentOS Linux' }, + { name: 'cloud.provider', value: 'gcp' }, + { name: 'host.ip', value: null }, + ], + metrics: [{ name: 'memory', value: 0.32066666666666666 }], + name: 'gke-observability-8--observability-8--bc1afd95-ngmh', + }, + { + metadata: [ + { name: 'host.os.name', value: 'CentOS Linux' }, + { name: 'cloud.provider', value: 'gcp' }, + { name: 'host.ip', value: null }, + ], + metrics: [{ name: 'memory', value: 0.2346666666666667 }], + name: 'gke-observability-8--observability-8--bc1afd95-nhhw', + }, + ]); + }); + + it('should return 3 hosts when filtered by "host.os.name=CentOS Linux"', async () => { + const body: GetInfraMetricsRequestBodyPayload = { + ...basePayload, + metrics: [ + { + type: 'cpu', + }, + ], + query: { bool: { filter: [{ term: { 'host.os.name': 'CentOS Linux' } }] } }, + }; + const response = await makeRequest({ body, expectedHTTPCode: 200 }); + + const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); + expect(names).eql([ + 'gke-observability-8--observability-8--bc1afd95-f0zc', + 'gke-observability-8--observability-8--bc1afd95-ngmh', + 'gke-observability-8--observability-8--bc1afd95-nhhw', + ]); + }); + + it('should return 0 hosts when filtered by "host.os.name=Ubuntu"', async () => { + const body: GetInfraMetricsRequestBodyPayload = { + ...basePayload, + metrics: [ + { + type: 'cpu', + }, + ], + query: { bool: { filter: [{ term: { 'host.os.name': 'Ubuntu' } }] } }, + }; + const response = await makeRequest({ body, expectedHTTPCode: 200 }); + + const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); + expect(names).eql([]); + }); + }); + + it('should return 0 hosts when filtered by not "host.name=gke-observability-8--observability-8--bc1afd95-nhhw"', async () => { + const body: GetInfraMetricsRequestBodyPayload = { + ...basePayload, + metrics: [ + { + type: 'cpu', + }, + ], + query: { + bool: { + must_not: [ + { term: { 'host.name': 'gke-observability-8--observability-8--bc1afd95-nhhw' } }, + ], + }, + }, + }; + const response = await makeRequest({ body, expectedHTTPCode: 200 }); + + const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); + expect(names).eql([ + 'gke-observability-8--observability-8--bc1afd95-f0zc', + 'gke-observability-8--observability-8--bc1afd95-ngmh', + ]); + }); + + describe('endpoint validations', () => { + it('should fail when limit is 0', async () => { + const body: GetInfraMetricsRequestBodyPayload = { ...basePayload, limit: 0 }; + const response = await makeRequest({ body, expectedHTTPCode: 400 }); + + expect(normalizeNewLine(response.body.message)).to.be( + '[request body]: Failed to validate: in limit: 0 does not match expected type InRange in limit: 0 does not match expected type pipe(undefined, BooleanFromString)' + ); + }); + + it('should fail when limit is negative', async () => { + const body: GetInfraMetricsRequestBodyPayload = { ...basePayload, limit: -2 }; + const response = await makeRequest({ body, expectedHTTPCode: 400 }); + + expect(normalizeNewLine(response.body.message)).to.be( + '[request body]: Failed to validate: in limit: -2 does not match expected type InRange in limit: -2 does not match expected type pipe(undefined, BooleanFromString)' + ); + }); + + it('should fail when limit above 500', async () => { + const body: GetInfraMetricsRequestBodyPayload = { ...basePayload, limit: 501 }; + const response = await makeRequest({ body, expectedHTTPCode: 400 }); + + expect(normalizeNewLine(response.body.message)).to.be( + '[request body]: Failed to validate: in limit: 501 does not match expected type InRange in limit: 501 does not match expected type pipe(undefined, BooleanFromString)' + ); + }); + + it('should fail when metric is invalid', async () => { + const invalidBody = { ...basePayload, metrics: [{ type: 'any' }] }; + const response = await makeRequest({ invalidBody, expectedHTTPCode: 400 }); + + expect(normalizeNewLine(response.body.message)).to.be( + '[request body]: Failed to validate: in metrics/0/type: "any" does not match expected type "cpu" | "diskLatency" | "memory" | "memoryTotal" | "rx" | "tx"' + ); + }); + + it('should pass when limit is 1', async () => { + const body: GetInfraMetricsRequestBodyPayload = { ...basePayload, limit: 1 }; + await makeRequest({ body, expectedHTTPCode: 200 }); + }); + + it('should pass when limit is 500', async () => { + const body: GetInfraMetricsRequestBodyPayload = { ...basePayload, limit: 500 }; + await makeRequest({ body, expectedHTTPCode: 200 }); + }); + + it('should fail when range is not informed', async () => { + const invalidBody = { ...basePayload, range: undefined }; + const response = await makeRequest({ invalidBody, expectedHTTPCode: 400 }); + + expect(normalizeNewLine(response.body.message)).to.be( + '[request body]: Failed to validate: in range: undefined does not match expected type { from: Date, to: Date }' + ); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security/index_fields.ts b/x-pack/test/api_integration/apis/security/index_fields.ts index 8fc9c36accd69..c5ca5ec5d6ea5 100644 --- a/x-pack/test/api_integration/apis/security/index_fields.ts +++ b/x-pack/test/api_integration/apis/security/index_fields.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -24,7 +25,7 @@ export default function ({ getService }: FtrProviderContext) { describe('GET /internal/security/fields/{query}', () => { it('should return a list of available index mapping fields', async () => { await supertest - .get('/internal/security/fields/.kibana') + .get(`/internal/security/fields/${ALL_SAVED_OBJECT_INDICES.join(',')}`) .set('kbn-xsrf', 'xxx') .send() .expect(200) diff --git a/x-pack/test/api_integration/apis/security_solution/utils.ts b/x-pack/test/api_integration/apis/security_solution/utils.ts index f5e65c6da3e7c..c4ce2e852c26a 100644 --- a/x-pack/test/api_integration/apis/security_solution/utils.ts +++ b/x-pack/test/api_integration/apis/security_solution/utils.ts @@ -9,6 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { TransportResult } from '@elastic/elasticsearch'; import type { Client } from '@elastic/elasticsearch'; import { JsonObject } from '@kbn/utility-types'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; export async function getSavedObjectFromES( es: Client, @@ -17,7 +18,7 @@ export async function getSavedObjectFromES( ): Promise, unknown>> { return await es.search( { - index: '.kibana', + index: ALL_SAVED_OBJECT_INDICES, body: { query: { bool: { diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_private_location.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_private_location.ts index d680e99d13405..b48cf2c9a56ca 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_monitor_private_location.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_monitor_private_location.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertestAPI.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertestAPI - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); @@ -455,7 +455,7 @@ export default function ({ getService }: FtrProviderContext) { pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default` ); - expect(packagePolicy.package.version).eql('0.11.4'); + expect(packagePolicy.package.version).eql('0.12.0'); await supertestAPI.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); const policyResponseAfterUpgrade = await supertestAPI.get( @@ -465,7 +465,7 @@ export default function ({ getService }: FtrProviderContext) { (pkgPolicy: PackagePolicy) => pkgPolicy.id === monitorId + '-' + testFleetPolicyID + `-default` ); - expect(semver.gte(packagePolicyAfterUpgrade.package.version, '0.11.4')).eql(true); + expect(semver.gte(packagePolicyAfterUpgrade.package.version, '0.12.0')).eql(true); } finally { await supertestAPI .delete(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId) diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts index 6c0dad1d2ce14..7674d2fbc534f 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts @@ -12,6 +12,10 @@ import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters' import { syntheticsMonitorType } from '@kbn/synthetics-plugin/server/legacy_uptime/lib/saved_objects/synthetics_monitor'; import { REQUEST_TOO_LARGE } from '@kbn/synthetics-plugin/server/routes/monitor_cruds/add_monitor_project'; import { PackagePolicy } from '@kbn/fleet-plugin/common'; +import { + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '@kbn/synthetics-plugin/common/constants/monitor_defaults'; import { FtrProviderContext } from '../../ftr_provider_context'; import { getFixtureJson } from '../uptime/rest/helper/get_fixture_json'; import { PrivateLocationTestService } from './services/private_location_test_service'; @@ -70,10 +74,10 @@ export default function ({ getService }: FtrProviderContext) { }; before(async () => { - await supertest.post(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); + await supertest.put(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); @@ -190,11 +194,7 @@ export default function ({ getService }: FtrProviderContext) { 'service.name': '', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], @@ -253,7 +253,11 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(200); - expect(decryptedCreatedMonitor.body.attributes['throttling.is_enabled']).to.eql(false); + expect(decryptedCreatedMonitor.body.attributes.throttling).to.eql({ + value: null, + id: 'no-throttling', + label: 'No throttling', + }); } } finally { await Promise.all([ @@ -317,6 +321,9 @@ export default function ({ getService }: FtrProviderContext) { custom_heartbeat_id: `${journeyId}-${project}-default`, 'check.response.body.negative': [], 'check.response.body.positive': ['Saved', 'saved'], + 'check.response.json': [ + { description: 'check status', expression: 'foo.bar == "myValue"' }, + ], 'check.response.headers': {}, 'check.request.body': { type: 'text', @@ -353,8 +360,10 @@ export default function ({ getService }: FtrProviderContext) { username: '', password: '', proxy_url: '', + proxy_headers: {}, 'response.include_body': 'always', 'response.include_headers': false, + 'response.include_body_max_bytes': '900', revision: 1, schedule: { number: '60', @@ -374,6 +383,9 @@ export default function ({ getService }: FtrProviderContext) { 'url.port': null, id: `${journeyId}-${project}-default`, hash: 'ekrjelkjrelkjre', + mode: 'any', + ipv6: true, + ipv4: true, }); } } finally { @@ -482,6 +494,9 @@ export default function ({ getService }: FtrProviderContext) { urls: '', id: `${journeyId}-${project}-default`, hash: 'ekrjelkjrelkjre', + mode: 'any', + ipv6: true, + ipv4: true, }); } } finally { @@ -586,6 +601,9 @@ export default function ({ getService }: FtrProviderContext) { : `${parseInt(monitor.wait?.slice(0, -1) || '1', 10) * 60}`, id: `${journeyId}-${project}-default`, hash: 'ekrjelkjrelkjre', + mode: 'any', + ipv4: true, + ipv6: true, }); } } finally { @@ -1780,6 +1798,7 @@ export default function ({ getService }: FtrProviderContext) { positive: ['Saved', 'saved'], }, status: [200], + json: [{ description: 'check status', expression: 'foo.bar == "myValue"' }], }, enabled: false, hash: 'ekrjelkjrelkjre', @@ -1788,6 +1807,7 @@ export default function ({ getService }: FtrProviderContext) { name: 'My Monitor 3', response: { include_body: 'always', + include_body_max_bytes: 900, }, 'response.include_headers': false, schedule: 60, @@ -1881,6 +1901,12 @@ export default function ({ getService }: FtrProviderContext) { positive: ['Saved', 'saved'], }, status: [200], + json: [ + { + description: 'check status', + expression: 'foo.bar == "myValue"', + }, + ], }, enabled: false, hash: 'ekrjelkjrelkjre', @@ -1889,6 +1915,7 @@ export default function ({ getService }: FtrProviderContext) { name: 'My Monitor 3', response: { include_body: 'always', + include_body_max_bytes: 900, }, 'response.include_headers': false, schedule: 60, @@ -1942,6 +1969,12 @@ export default function ({ getService }: FtrProviderContext) { positive: ['Saved', 'saved'], }, status: [200], + json: [ + { + description: 'check status', + expression: 'foo.bar == "myValue"', + }, + ], }, enabled: false, hash: 'ekrjelkjrelkjre', @@ -1950,6 +1983,7 @@ export default function ({ getService }: FtrProviderContext) { name: 'My Monitor 3', response: { include_body: 'always', + include_body_max_bytes: 900, }, 'response.include_headers': false, schedule: 60, @@ -2004,6 +2038,12 @@ export default function ({ getService }: FtrProviderContext) { positive: ['Saved', 'saved'], }, status: [200], + json: [ + { + description: 'check status', + expression: 'foo.bar == "myValue"', + }, + ], }, enabled: false, hash: 'ekrjelkjrelkjre', @@ -2012,6 +2052,7 @@ export default function ({ getService }: FtrProviderContext) { name: 'My Monitor 3', response: { include_body: 'always', + include_body_max_bytes: 900, }, 'response.include_headers': false, schedule: 60, diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts index 03477c85c3a06..0e2cec6c3a5c3 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts @@ -16,6 +16,10 @@ import { API_URLS } from '@kbn/synthetics-plugin/common/constants'; import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters'; import { syntheticsMonitorType } from '@kbn/synthetics-plugin/server/legacy_uptime/lib/saved_objects/synthetics_monitor'; import { PackagePolicy } from '@kbn/fleet-plugin/common'; +import { + PROFILE_VALUES_ENUM, + PROFILES_MAP, +} from '@kbn/synthetics-plugin/common/constants/monitor_defaults'; import { FtrProviderContext } from '../../ftr_provider_context'; import { getFixtureJson } from '../uptime/rest/helper/get_fixture_json'; import { PrivateLocationTestService } from './services/private_location_test_service'; @@ -82,7 +86,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); @@ -175,11 +179,7 @@ export default function ({ getService }: FtrProviderContext) { 'service.name': '', synthetics_args: [], tags: [], - 'throttling.config': '5d/3u/20l', - 'throttling.download_speed': '5', - 'throttling.is_enabled': true, - 'throttling.latency': '20', - 'throttling.upload_speed': '3', + throttling: PROFILES_MAP[PROFILE_VALUES_ENUM.DEFAULT], 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], @@ -256,6 +256,9 @@ export default function ({ getService }: FtrProviderContext) { custom_heartbeat_id: `${journeyId}-test-suite-default`, 'check.response.body.negative': [], 'check.response.body.positive': ['Saved', 'saved'], + 'check.response.json': [ + { description: 'check status', expression: 'foo.bar == "myValue"' }, + ], 'check.response.headers': {}, 'check.request.body': { type: 'text', @@ -292,8 +295,10 @@ export default function ({ getService }: FtrProviderContext) { username: '', password: '', proxy_url: '', + proxy_headers: {}, 'response.include_body': 'always', 'response.include_headers': false, + 'response.include_body_max_bytes': '900', revision: 1, schedule: { number: '60', @@ -313,6 +318,9 @@ export default function ({ getService }: FtrProviderContext) { 'url.port': null, id: `${journeyId}-test-suite-default`, hash: 'ekrjelkjrelkjre', + ipv6: true, + ipv4: true, + mode: 'any', }); } } finally { @@ -418,6 +426,9 @@ export default function ({ getService }: FtrProviderContext) { urls: '', id: `${journeyId}-test-suite-default`, hash: 'ekrjelkjrelkjre', + ipv6: true, + ipv4: true, + mode: 'any', }); } } finally { @@ -520,6 +531,9 @@ export default function ({ getService }: FtrProviderContext) { : `${parseInt(monitor.wait?.slice(0, -1) || '1', 10) * 60}`, id: `${journeyId}-test-suite-default`, hash: 'ekrjelkjrelkjre', + ipv6: true, + ipv4: true, + mode: 'any', }); } } finally { @@ -1802,11 +1816,13 @@ export default function ({ getService }: FtrProviderContext) { timeout: { value: '80s', type: 'text' }, max_redirects: { value: '0', type: 'integer' }, proxy_url: { value: '', type: 'text' }, + proxy_headers: { value: null, type: 'yaml' }, tags: { value: '["tag2","tag2"]', type: 'yaml' }, username: { value: '', type: 'text' }, password: { value: '', type: 'password' }, 'response.include_headers': { value: false, type: 'bool' }, 'response.include_body': { value: 'always', type: 'text' }, + 'response.include_body_max_bytes': { value: '900', type: 'text' }, 'check.request.method': { value: 'POST', type: 'text' }, 'check.request.headers': { value: '{"Content-Type":"application/x-www-form-urlencoded"}', @@ -1817,6 +1833,10 @@ export default function ({ getService }: FtrProviderContext) { 'check.response.headers': { value: null, type: 'yaml' }, 'check.response.body.positive': { value: '["Saved","saved"]', type: 'yaml' }, 'check.response.body.negative': { value: null, type: 'yaml' }, + 'check.response.json': { + value: '[{"description":"check status","expression":"foo.bar == \\"myValue\\""}]', + type: 'yaml', + }, 'ssl.certificate_authorities': { value: null, type: 'yaml' }, 'ssl.certificate': { value: null, type: 'yaml' }, 'ssl.key': { value: null, type: 'yaml' }, @@ -1842,6 +1862,9 @@ export default function ({ getService }: FtrProviderContext) { type: 'text', value: 'test-suite', }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text', value: 'any' }, }, id: `synthetics/http-http-${id}-${testPolicyId}`, compiled_stream: { @@ -1866,6 +1889,15 @@ export default function ({ getService }: FtrProviderContext) { 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], 'run_from.geo.name': 'Test private location 0', 'run_from.id': 'Test private location 0', + 'check.response.json': [ + { + description: 'check status', + expression: 'foo.bar == "myValue"', + }, + ], + ipv4: true, + ipv6: true, + mode: 'any', processors: [ { add_fields: { diff --git a/x-pack/test/api_integration/apis/synthetics/config.ts b/x-pack/test/api_integration/apis/synthetics/config.ts index 5f335f116fefe..c2e5b5dec912c 100644 --- a/x-pack/test/api_integration/apis/synthetics/config.ts +++ b/x-pack/test/api_integration/apis/synthetics/config.ts @@ -12,6 +12,16 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseIntegrationTestsConfig.getAll(), + kbnTestServer: { + ...baseIntegrationTestsConfig.get('kbnTestServer'), + serverArgs: [ + ...baseIntegrationTestsConfig.get('kbnTestServer.serverArgs'), + '--xpack.uptime.service.password=test', + '--xpack.uptime.service.username=localKibanaIntegrationTestsUser', + '--xpack.uptime.service.devUrl=mockDevUrl', + '--xpack.uptime.service.manifestUrl=mockDevUrl', + ], + }, testFiles: [require.resolve('.')], }; } diff --git a/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts b/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts index 87d033be74743..9ab6063775844 100644 --- a/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts +++ b/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts @@ -41,7 +41,7 @@ export default function ({ getService }: FtrProviderContext) { _httpMonitorJson = getFixtureJson('http_monitor'); await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); diff --git a/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts index 8ba507beb7e4c..13bcab980d248 100644 --- a/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts +++ b/x-pack/test/api_integration/apis/synthetics/delete_monitor_project.ts @@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); diff --git a/x-pack/test/api_integration/apis/synthetics/edit_monitor.ts b/x-pack/test/api_integration/apis/synthetics/edit_monitor.ts index f20a2cdf61a45..bf4447a1b5969 100644 --- a/x-pack/test/api_integration/apis/synthetics/edit_monitor.ts +++ b/x-pack/test/api_integration/apis/synthetics/edit_monitor.ts @@ -43,7 +43,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { _httpMonitorJson = getFixtureJson('http_monitor'); await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); - await supertest.post(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); + await supertest.put(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); const testPolicyName = 'Fleet test server policy' + Date.now(); const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName); diff --git a/x-pack/test/api_integration/apis/synthetics/get_monitor.ts b/x-pack/test/api_integration/apis/synthetics/get_monitor.ts index 5394ca64545e6..00772c5550ac1 100644 --- a/x-pack/test/api_integration/apis/synthetics/get_monitor.ts +++ b/x-pack/test/api_integration/apis/synthetics/get_monitor.ts @@ -32,7 +32,7 @@ export default function ({ getService }: FtrProviderContext) { }; before(async () => { - await supertest.post(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); + await supertest.put(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); _monitors = [ getFixtureJson('icmp_monitor'), diff --git a/x-pack/test/api_integration/apis/synthetics/get_monitor_overview.ts b/x-pack/test/api_integration/apis/synthetics/get_monitor_overview.ts index 25aad0704cddd..625dbdac61608 100644 --- a/x-pack/test/api_integration/apis/synthetics/get_monitor_overview.ts +++ b/x-pack/test/api_integration/apis/synthetics/get_monitor_overview.ts @@ -55,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) { }; before(async () => { - await supertest.post(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); + await supertest.put(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true').expect(200); await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME }); await security.role.create(roleName, { kibana: [ diff --git a/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts index d24a3775bbf44..beefd6c561829 100644 --- a/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts +++ b/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts @@ -46,7 +46,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertest - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts index 0cebf231cf787..5eec726cdb328 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts @@ -21,7 +21,7 @@ export const getTestBrowserSyntheticsPolicy = ({ version: 'WzEzNzYsMV0=', name: 'Test HTTP Monitor 03-Test private location 0-default', namespace: 'testnamespace', - package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.4' }, + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.12.0' }, enabled: true, policy_id: 'fe621d20-7b01-11ed-803f-475d82e1f9ca', inputs: [ @@ -44,11 +44,13 @@ export const getTestBrowserSyntheticsPolicy = ({ timeout: { type: 'text' }, max_redirects: { type: 'integer' }, proxy_url: { type: 'text' }, + proxy_headers: { type: 'yaml' }, tags: { type: 'yaml' }, username: { type: 'text' }, password: { type: 'password' }, 'response.include_headers': { type: 'bool' }, 'response.include_body': { type: 'text' }, + 'response.include_body_max_bytes': { type: 'text' }, 'check.request.method': { type: 'text' }, 'check.request.headers': { type: 'yaml' }, 'check.request.body': { type: 'yaml' }, @@ -56,6 +58,7 @@ export const getTestBrowserSyntheticsPolicy = ({ 'check.response.headers': { type: 'yaml' }, 'check.response.body.positive': { type: 'yaml' }, 'check.response.body.negative': { type: 'yaml' }, + 'check.response.json': { type: 'yaml' }, 'ssl.certificate_authorities': { type: 'yaml' }, 'ssl.certificate': { type: 'yaml' }, 'ssl.key': { type: 'yaml' }, @@ -69,6 +72,9 @@ export const getTestBrowserSyntheticsPolicy = ({ origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: 'synthetics/http-http-abf904a4-cb9a-4b29-8c11-4d183cca289b-fe621d20-7b01-11ed-803f-475d82e1f9ca-default', }, @@ -109,6 +115,9 @@ export const getTestBrowserSyntheticsPolicy = ({ origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: 'synthetics/tcp-tcp-abf904a4-cb9a-4b29-8c11-4d183cca289b-fe621d20-7b01-11ed-803f-475d82e1f9ca-default', }, @@ -140,6 +149,9 @@ export const getTestBrowserSyntheticsPolicy = ({ origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: 'synthetics/icmp-icmp-abf904a4-cb9a-4b29-8c11-4d183cca289b-fe621d20-7b01-11ed-803f-475d82e1f9ca-default', }, @@ -185,7 +197,10 @@ export const getTestBrowserSyntheticsPolicy = ({ screenshots: { value: 'on', type: 'text' }, synthetics_args: { value: null, type: 'text' }, ignore_https_errors: { value: false, type: 'bool' }, - 'throttling.config': { value: '5d/3u/20l', type: 'text' }, + 'throttling.config': { + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), + type: 'text', + }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: null, type: 'text' }, 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, @@ -218,7 +233,7 @@ export const getTestBrowserSyntheticsPolicy = ({ enabled: true, schedule: '@every 3m', timeout: '16s', - throttling: '5d/3u/20l', + throttling: { download: 5, upload: 3, latency: 20 }, tags: ['cookie-test', 'browser'], 'source.inline.script': 'step("Visit /users api route", async () => {\\n const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\n expect(response.status()).toEqual(200);\\n});', @@ -246,10 +261,7 @@ export const getTestBrowserSyntheticsPolicy = ({ }, id: 'synthetics/browser-browser.network-abf904a4-cb9a-4b29-8c11-4d183cca289b-fe621d20-7b01-11ed-803f-475d82e1f9ca-default', compiled_stream: { - processors: [ - { add_observer_metadata: { geo: { name: 'Fleet managed' } } }, - { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, - ], + processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }], }, }, { @@ -261,10 +273,7 @@ export const getTestBrowserSyntheticsPolicy = ({ }, id: 'synthetics/browser-browser.screenshot-abf904a4-cb9a-4b29-8c11-4d183cca289b-fe621d20-7b01-11ed-803f-475d82e1f9ca-default', compiled_stream: { - processors: [ - { add_observer_metadata: { geo: { name: 'Fleet managed' } } }, - { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, - ], + processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }], }, }, ], diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_policy.ts index 82e19948779bb..b0962e4d285e6 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_policy.ts @@ -21,7 +21,7 @@ export const getTestSyntheticsPolicy = ( version: 'WzE2MjYsMV0=', name: 'test-monitor-name-Test private location 0-default', namespace: namespace || 'testnamespace', - package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.4' }, + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.12.0' }, enabled: true, policy_id: '5347cd10-0368-11ed-8df7-a7424c6f5167', inputs: [ @@ -55,11 +55,13 @@ export const getTestSyntheticsPolicy = ( timeout: { value: '3ms', type: 'text' }, max_redirects: { value: '3', type: 'integer' }, proxy_url: { value: proxyUrl ?? 'http://proxy.com', type: 'text' }, + proxy_headers: { value: null, type: 'yaml' }, tags: { value: '["tag1","tag2"]', type: 'yaml' }, username: { value: 'test-username', type: 'text' }, password: { value: 'test', type: 'password' }, 'response.include_headers': { value: true, type: 'bool' }, 'response.include_body': { value: 'never', type: 'text' }, + 'response.include_body_max_bytes': { value: '1024', type: 'text' }, 'check.request.method': { value: '', type: 'text' }, 'check.request.headers': { value: '{"sampleHeader":"sampleHeaderValue"}', @@ -70,6 +72,7 @@ export const getTestSyntheticsPolicy = ( 'check.response.headers': { value: null, type: 'yaml' }, 'check.response.body.positive': { value: null, type: 'yaml' }, 'check.response.body.negative': { value: null, type: 'yaml' }, + 'check.response.json': { value: null, type: 'yaml' }, 'ssl.certificate_authorities': { value: isTLSEnabled ? '"t.string"' : null, type: 'yaml', @@ -89,6 +92,9 @@ export const getTestSyntheticsPolicy = ( origin: { value: 'ui', type: 'text' }, 'monitor.project.id': { type: 'text', value: null }, 'monitor.project.name': { type: 'text', value: null }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text', value: 'any' }, }, id: 'synthetics/http-http-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', compiled_stream: { @@ -116,6 +122,9 @@ export const getTestSyntheticsPolicy = ( 'check.request.headers': { sampleHeader: 'sampleHeaderValue' }, 'check.request.body': 'testValue', 'check.response.status': ['200', '201'], + ipv4: true, + ipv6: true, + mode: 'any', ...(isTLSEnabled ? { 'ssl.certificate': 't.string', @@ -179,6 +188,9 @@ export const getTestSyntheticsPolicy = ( origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: 'synthetics/tcp-tcp-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', }, @@ -213,6 +225,9 @@ export const getTestSyntheticsPolicy = ( origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: 'synthetics/icmp-icmp-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', }, @@ -299,10 +314,7 @@ export const getTestSyntheticsPolicy = ( }, id: 'synthetics/browser-browser.network-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', compiled_stream: { - processors: [ - { add_observer_metadata: { geo: { name: 'Fleet managed' } } }, - { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, - ], + processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }], }, }, { @@ -318,10 +330,7 @@ export const getTestSyntheticsPolicy = ( }, id: 'synthetics/browser-browser.screenshot-2bfd7da0-22ed-11ed-8c6b-09a2d21dfbc3-27337270-22ed-11ed-8c6b-09a2d21dfbc3-default', compiled_stream: { - processors: [ - { add_observer_metadata: { geo: { name: 'Fleet managed' } } }, - { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, - ], + processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }], }, }, ], diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts index cd5ea22451b92..bc015cb4bd77c 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts @@ -34,7 +34,7 @@ export const getTestProjectSyntheticsPolicy = ( version: 'WzEzMDksMV0=', name: `4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-Test private location 0`, namespace: 'default', - package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.11.4' }, + package: { name: 'synthetics', title: 'Elastic Synthetics', version: '0.12.0' }, enabled: true, policy_id: '46034710-0ba6-11ed-ba04-5f123b9faa8b', inputs: [ @@ -60,11 +60,13 @@ export const getTestProjectSyntheticsPolicy = ( timeout: { type: 'text' }, max_redirects: { type: 'integer' }, proxy_url: { type: 'text' }, + proxy_headers: { type: 'yaml' }, tags: { type: 'yaml' }, username: { type: 'text' }, password: { type: 'password' }, 'response.include_headers': { type: 'bool' }, 'response.include_body': { type: 'text' }, + 'response.include_body_max_bytes': { type: 'text' }, 'check.request.method': { type: 'text' }, 'check.request.headers': { type: 'yaml' }, 'check.request.body': { type: 'yaml' }, @@ -72,6 +74,7 @@ export const getTestProjectSyntheticsPolicy = ( 'check.response.headers': { type: 'yaml' }, 'check.response.body.positive': { type: 'yaml' }, 'check.response.body.negative': { type: 'yaml' }, + 'check.response.json': { type: 'yaml' }, 'ssl.certificate_authorities': { type: 'yaml' }, 'ssl.certificate': { type: 'yaml' }, 'ssl.key': { type: 'yaml' }, @@ -85,6 +88,9 @@ export const getTestProjectSyntheticsPolicy = ( origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: `synthetics/http-http-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`, }, @@ -128,6 +134,9 @@ export const getTestProjectSyntheticsPolicy = ( origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: `synthetics/tcp-tcp-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`, }, @@ -162,6 +171,9 @@ export const getTestProjectSyntheticsPolicy = ( origin: { type: 'text' }, 'monitor.project.id': { type: 'text' }, 'monitor.project.name': { type: 'text' }, + ipv4: { type: 'bool', value: true }, + ipv6: { type: 'bool', value: true }, + mode: { type: 'text' }, }, id: `synthetics/icmp-icmp-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`, }, @@ -213,7 +225,10 @@ export const getTestProjectSyntheticsPolicy = ( screenshots: { value: 'on', type: 'text' }, synthetics_args: { value: null, type: 'text' }, ignore_https_errors: { value: false, type: 'bool' }, - 'throttling.config': { value: '5d/3u/20l', type: 'text' }, + 'throttling.config': { + value: JSON.stringify({ download: 5, upload: 3, latency: 20 }), + type: 'text', + }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: '"check if title is present"', type: 'text' }, 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, @@ -246,7 +261,7 @@ export const getTestProjectSyntheticsPolicy = ( 'run_from.geo.name': locationName, 'run_from.id': locationName, timeout: null, - throttling: '5d/3u/20l', + throttling: { download: 5, upload: 3, latency: 20 }, 'source.project.content': 'UEsDBBQACAAIAON5qVQAAAAAAAAAAAAAAAAfAAAAZXhhbXBsZXMvdG9kb3MvYmFzaWMuam91cm5leS50c22Q0WrDMAxF3/sVF7MHB0LMXlc6RvcN+wDPVWNviW0sdUsp/fe5SSiD7UFCWFfHujIGlpnkybwxFTZfoY/E3hsaLEtwhs9RPNWKDU12zAOxkXRIbN4tB9d9pFOJdO6EN2HMqQguWN9asFBuQVMmJ7jiWNII9fIXrbabdUYr58l9IhwhQQZCYORCTFFUC31Btj21NRc7Mq4Nds+4bDD/pNVgT9F52Jyr2Fa+g75LAPttg8yErk+S9ELpTmVotlVwnfNCuh2lepl3+JflUmSBJ3uggt1v9INW/lHNLKze9dJe1J3QJK8pSvWkm6aTtCet5puq+x63+AFQSwcIAPQ3VfcAAACcAQAAUEsBAi0DFAAIAAgA43mpVAD0N1X3AAAAnAEAAB8AAAAAAAAAAAAgAKSBAAAAAGV4YW1wbGVzL3RvZG9zL2Jhc2ljLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBNAAAARAEAAAAA', playwright_options: { headless: true, chromiumSandbox: false }, @@ -284,10 +299,7 @@ export const getTestProjectSyntheticsPolicy = ( }, id: `synthetics/browser-browser.network-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`, compiled_stream: { - processors: [ - { add_observer_metadata: { geo: { name: 'Fleet managed' } } }, - { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, - ], + processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }], }, }, { @@ -303,10 +315,7 @@ export const getTestProjectSyntheticsPolicy = ( }, id: `synthetics/browser-browser.screenshot-4b6abc6c-118b-4d93-a489-1135500d09f1-${projectId}-default-d70a46e0-22ea-11ed-8c6b-09a2d21dfbc3`, compiled_stream: { - processors: [ - { add_observer_metadata: { geo: { name: 'Fleet managed' } } }, - { add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }, - ], + processors: [{ add_fields: { target: '', fields: { 'monitor.fleet_managed': true } } }], }, }, ], diff --git a/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts b/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts index acb6b4250628d..f5ff56d2ee506 100644 --- a/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts +++ b/x-pack/test/api_integration/apis/synthetics/sync_global_params.ts @@ -44,7 +44,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await supertestAPI.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200); await supertestAPI - .post('/api/fleet/epm/packages/synthetics/0.11.4') + .post('/api/fleet/epm/packages/synthetics/0.12.0') .set('kbn-xsrf', 'true') .send({ force: true }) .expect(200); diff --git a/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts b/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts index dac32af21106a..bf68da4c148f5 100644 --- a/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts +++ b/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts @@ -6,24 +6,57 @@ */ import { API_URLS } from '@kbn/synthetics-plugin/common/constants'; +import { + syntheticsApiKeyID, + syntheticsApiKeyObjectType, +} from '@kbn/synthetics-plugin/server/legacy_uptime/lib/saved_objects/service_api_key'; import { serviceApiKeyPrivileges } from '@kbn/synthetics-plugin/server/synthetics_service/get_api_key'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { - describe('/internal/uptime/service/enablement', () => { + const correctPrivileges = { + applications: [], + cluster: ['monitor', 'read_ilm', 'read_pipeline'], + indices: [ + { + allow_restricted_indices: false, + names: ['synthetics-*'], + privileges: ['view_index_metadata', 'create_doc', 'auto_configure', 'read'], + }, + ], + metadata: {}, + run_as: [], + transient_metadata: { + enabled: true, + }, + }; + + describe('SyntheticsEnablement', () => { const supertestWithAuth = getService('supertest'); const supertest = getService('supertestWithoutAuth'); const security = getService('security'); const kibanaServer = getService('kibanaServer'); - before(async () => { - await supertestWithAuth.delete(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true'); - }); + const esSupertest = getService('esSupertest'); + + const getApiKeys = async () => { + const { body } = await esSupertest.get(`/_security/api_key`).query({ with_limited_by: true }); + const apiKeys = body.api_keys || []; + return apiKeys.filter( + (apiKey: any) => apiKey.name.includes('synthetics-api-key') && apiKey.invalidated === false + ); + }; - describe('[GET] - /internal/uptime/service/enablement', () => { + describe('[PUT] /internal/uptime/service/enablement', () => { + beforeEach(async () => { + const apiKeys = await getApiKeys(); + if (apiKeys.length) { + await supertestWithAuth.delete(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true'); + } + }); ['manage_security', 'manage_api_key', 'manage_own_api_key'].forEach((privilege) => { - it(`returns response for an admin with privilege ${privilege}`, async () => { + it(`returns response when user can manage api keys`, async () => { const username = 'admin'; const roleName = `synthetics_admin-${privilege}`; const password = `${username}-password`; @@ -38,7 +71,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], elasticsearch: { - cluster: [privilege, ...serviceApiKeyPrivileges.cluster], + cluster: [privilege], indices: serviceApiKeyPrivileges.indices, }, }); @@ -50,7 +83,7 @@ export default function ({ getService }: FtrProviderContext) { }); const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); @@ -58,7 +91,7 @@ export default function ({ getService }: FtrProviderContext) { expect(apiResponse.body).eql({ areApiKeysEnabled: true, canManageApiKeys: true, - canEnable: true, + canEnable: false, isEnabled: false, isValidApiKey: false, }); @@ -69,9 +102,9 @@ export default function ({ getService }: FtrProviderContext) { }); }); - it('returns response for an uptime all user without admin privileges', async () => { - const username = 'uptime'; - const roleName = 'uptime_user'; + it(`returns response for an admin with privilege`, async () => { + const username = 'admin'; + const roleName = `synthetics_admin`; const password = `${username}-password`; try { await security.role.create(roleName, { @@ -83,7 +116,10 @@ export default function ({ getService }: FtrProviderContext) { spaces: ['*'], }, ], - elasticsearch: {}, + elasticsearch: { + cluster: serviceApiKeyPrivileges.cluster, + indices: serviceApiKeyPrivileges.indices, + }, }); await security.user.create(username, { @@ -93,7 +129,7 @@ export default function ({ getService }: FtrProviderContext) { }); const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); @@ -101,19 +137,20 @@ export default function ({ getService }: FtrProviderContext) { expect(apiResponse.body).eql({ areApiKeysEnabled: true, canManageApiKeys: false, - canEnable: false, - isEnabled: false, - isValidApiKey: false, + canEnable: true, + isEnabled: true, + isValidApiKey: true, }); + const validApiKeys = await getApiKeys(); + expect(validApiKeys.length).eql(1); + expect(validApiKeys[0].role_descriptors.synthetics_writer).eql(correctPrivileges); } finally { - await security.role.delete(roleName); await security.user.delete(username); + await security.role.delete(roleName); } }); - }); - describe('[POST] - /internal/uptime/service/enablement', () => { - it('with an admin', async () => { + it(`does not create excess api keys`, async () => { const username = 'admin'; const roleName = `synthetics_admin`; const password = `${username}-password`; @@ -128,7 +165,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], elasticsearch: { - cluster: ['manage_security', ...serviceApiKeyPrivileges.cluster], + cluster: serviceApiKeyPrivileges.cluster, indices: serviceApiKeyPrivileges.indices, }, }); @@ -139,38 +176,213 @@ export default function ({ getService }: FtrProviderContext) { full_name: 'a kibana user', }); - await supertest - .post(API_URLS.SYNTHETICS_ENABLEMENT) + const apiResponse = await supertest + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); + + expect(apiResponse.body).eql({ + areApiKeysEnabled: true, + canManageApiKeys: false, + canEnable: true, + isEnabled: true, + isValidApiKey: true, + }); + + const validApiKeys = await getApiKeys(); + expect(validApiKeys.length).eql(1); + expect(validApiKeys[0].role_descriptors.synthetics_writer).eql(correctPrivileges); + + // call api a second time + const apiResponse2 = await supertest + .put(API_URLS.SYNTHETICS_ENABLEMENT) + .auth(username, password) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(apiResponse2.body).eql({ + areApiKeysEnabled: true, + canManageApiKeys: false, + canEnable: true, + isEnabled: true, + isValidApiKey: true, + }); + + const validApiKeys2 = await getApiKeys(); + expect(validApiKeys2.length).eql(1); + expect(validApiKeys2[0].role_descriptors.synthetics_writer).eql(correctPrivileges); + } finally { + await security.user.delete(username); + await security.role.delete(roleName); + } + }); + + it(`auto re-enables the api key when created with invalid permissions and invalidates old api key`, async () => { + const username = 'admin'; + const roleName = `synthetics_admin`; + const password = `${username}-password`; + try { + // create api key with incorrect permissions + const apiKeyResult = await esSupertest + .post(`/_security/api_key`) + .send({ + name: 'synthetics-api-key', + expiration: '1d', + role_descriptors: { + 'role-a': { + cluster: serviceApiKeyPrivileges.cluster, + indices: [ + { + names: ['synthetics-*'], + privileges: ['view_index_metadata', 'create_doc', 'auto_configure'], + }, + ], + }, + }, + }) + .expect(200); + kibanaServer.savedObjects.create({ + id: syntheticsApiKeyID, + type: syntheticsApiKeyObjectType, + overwrite: true, + attributes: { + id: apiKeyResult.body.id, + name: 'synthetics-api-key (required for monitor management)', + apiKey: apiKeyResult.body.api_key, + }, + }); + + const validApiKeys = await getApiKeys(); + expect(validApiKeys.length).eql(1); + expect(validApiKeys[0].role_descriptors.synthetics_writer).not.eql(correctPrivileges); + + await security.role.create(roleName, { + kibana: [ + { + feature: { + uptime: ['all'], + }, + spaces: ['*'], + }, + ], + elasticsearch: { + cluster: serviceApiKeyPrivileges.cluster, + indices: serviceApiKeyPrivileges.indices, + }, + }); + + await security.user.create(username, { + password, + roles: [roleName], + full_name: 'a kibana user', + }); + const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); expect(apiResponse.body).eql({ areApiKeysEnabled: true, - canManageApiKeys: true, + canManageApiKeys: false, canEnable: true, isEnabled: true, isValidApiKey: true, }); + + const validApiKeys2 = await getApiKeys(); + expect(validApiKeys2.length).eql(1); + expect(validApiKeys2[0].role_descriptors.synthetics_writer).eql(correctPrivileges); } finally { - await supertest - .delete(API_URLS.SYNTHETICS_ENABLEMENT) + await security.user.delete(username); + await security.role.delete(roleName); + } + }); + + it(`auto re-enables api key when invalidated`, async () => { + const username = 'admin'; + const roleName = `synthetics_admin`; + const password = `${username}-password`; + try { + await security.role.create(roleName, { + kibana: [ + { + feature: { + uptime: ['all'], + }, + spaces: ['*'], + }, + ], + elasticsearch: { + cluster: serviceApiKeyPrivileges.cluster, + indices: serviceApiKeyPrivileges.indices, + }, + }); + + await security.user.create(username, { + password, + roles: [roleName], + full_name: 'a kibana user', + }); + + const apiResponse = await supertest + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); + + expect(apiResponse.body).eql({ + areApiKeysEnabled: true, + canManageApiKeys: false, + canEnable: true, + isEnabled: true, + isValidApiKey: true, + }); + + const validApiKeys = await getApiKeys(); + expect(validApiKeys.length).eql(1); + expect(validApiKeys[0].role_descriptors.synthetics_writer).eql(correctPrivileges); + + // delete api key + await esSupertest + .delete(`/_security/api_key`) + .send({ + ids: [validApiKeys[0].id], + }) + .expect(200); + + const validApiKeysAferDeletion = await getApiKeys(); + expect(validApiKeysAferDeletion.length).eql(0); + + // call api a second time + const apiResponse2 = await supertest + .put(API_URLS.SYNTHETICS_ENABLEMENT) + .auth(username, password) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(apiResponse2.body).eql({ + areApiKeysEnabled: true, + canManageApiKeys: false, + canEnable: true, + isEnabled: true, + isValidApiKey: true, + }); + + const validApiKeys2 = await getApiKeys(); + expect(validApiKeys2.length).eql(1); + expect(validApiKeys2[0].role_descriptors.synthetics_writer).eql(correctPrivileges); + } finally { await security.user.delete(username); await security.role.delete(roleName); } }); - it('with an uptime user', async () => { + it('returns response for an uptime all user without admin privileges', async () => { const username = 'uptime'; - const roleName = `uptime_user`; + const roleName = 'uptime_user'; const password = `${username}-password`; try { await security.role.create(roleName, { @@ -191,16 +403,12 @@ export default function ({ getService }: FtrProviderContext) { full_name: 'a kibana user', }); - await supertest - .post(API_URLS.SYNTHETICS_ENABLEMENT) - .auth(username, password) - .set('kbn-xsrf', 'true') - .expect(403); const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); + expect(apiResponse.body).eql({ areApiKeysEnabled: true, canManageApiKeys: false, @@ -209,13 +417,19 @@ export default function ({ getService }: FtrProviderContext) { isValidApiKey: false, }); } finally { - await security.user.delete(username); await security.role.delete(roleName); + await security.user.delete(username); } }); }); - describe('[DELETE] - /internal/uptime/service/enablement', () => { + describe('[DELETE] /internal/uptime/service/enablement', () => { + beforeEach(async () => { + const apiKeys = await getApiKeys(); + if (apiKeys.length) { + await supertestWithAuth.delete(API_URLS.SYNTHETICS_ENABLEMENT).set('kbn-xsrf', 'true'); + } + }); it('with an admin', async () => { const username = 'admin'; const roleName = `synthetics_admin`; @@ -231,7 +445,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], elasticsearch: { - cluster: ['manage_security', ...serviceApiKeyPrivileges.cluster], + cluster: serviceApiKeyPrivileges.cluster, indices: serviceApiKeyPrivileges.indices, }, }); @@ -243,27 +457,28 @@ export default function ({ getService }: FtrProviderContext) { }); await supertest - .post(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); - await supertest + const delResponse = await supertest .delete(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); + expect(delResponse.body).eql({}); const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); expect(apiResponse.body).eql({ areApiKeysEnabled: true, - canManageApiKeys: true, + canManageApiKeys: false, canEnable: true, - isEnabled: false, - isValidApiKey: false, + isEnabled: true, + isValidApiKey: true, }); } finally { await security.user.delete(username); @@ -295,7 +510,7 @@ export default function ({ getService }: FtrProviderContext) { }); await supertestWithAuth - .post(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .set('kbn-xsrf', 'true') .expect(200); await supertest @@ -304,7 +519,7 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(403); const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); @@ -343,7 +558,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], elasticsearch: { - cluster: ['manage_security', ...serviceApiKeyPrivileges.cluster], + cluster: serviceApiKeyPrivileges.cluster, indices: serviceApiKeyPrivileges.indices, }, }); @@ -354,9 +569,23 @@ export default function ({ getService }: FtrProviderContext) { full_name: 'a kibana user', }); - // can disable synthetics in default space when enabled in a non default space + // can enable synthetics in default space when enabled in a non default space + const apiResponseGet = await supertest + .put(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_ENABLEMENT}`) + .auth(username, password) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(apiResponseGet.body).eql({ + areApiKeysEnabled: true, + canManageApiKeys: false, + canEnable: true, + isEnabled: true, + isValidApiKey: true, + }); + await supertest - .post(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_ENABLEMENT}`) + .put(`/s/${SPACE_ID}${API_URLS.SYNTHETICS_ENABLEMENT}`) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); @@ -366,22 +595,22 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(200); const apiResponse = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); expect(apiResponse.body).eql({ areApiKeysEnabled: true, - canManageApiKeys: true, + canManageApiKeys: false, canEnable: true, - isEnabled: false, - isValidApiKey: false, + isEnabled: true, + isValidApiKey: true, }); // can disable synthetics in non default space when enabled in default space await supertest - .post(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); @@ -391,21 +620,22 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'true') .expect(200); const apiResponse2 = await supertest - .get(API_URLS.SYNTHETICS_ENABLEMENT) + .put(API_URLS.SYNTHETICS_ENABLEMENT) .auth(username, password) .set('kbn-xsrf', 'true') .expect(200); expect(apiResponse2.body).eql({ areApiKeysEnabled: true, - canManageApiKeys: true, + canManageApiKeys: false, canEnable: true, - isEnabled: false, - isValidApiKey: false, + isEnabled: true, + isValidApiKey: true, }); } finally { await security.user.delete(username); await security.role.delete(roleName); + await kibanaServer.spaces.delete(SPACE_ID); } }); }); diff --git a/x-pack/test/api_integration/apis/transform/index.ts b/x-pack/test/api_integration/apis/transform/index.ts index c665665b527d3..ad44dc1249e8e 100644 --- a/x-pack/test/api_integration/apis/transform/index.ts +++ b/x-pack/test/api_integration/apis/transform/index.ts @@ -29,6 +29,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { }); loadTestFile(require.resolve('./delete_transforms')); + loadTestFile(require.resolve('./reauthorize_transforms')); loadTestFile(require.resolve('./reset_transforms')); loadTestFile(require.resolve('./start_transforms')); loadTestFile(require.resolve('./stop_transforms')); diff --git a/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts b/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts new file mode 100644 index 0000000000000..274c1b42a4d07 --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/reauthorize_transforms.ts @@ -0,0 +1,274 @@ +/* + * 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 { ReauthorizeTransformsRequestSchema } from '@kbn/transform-plugin/common/api_schemas/reauthorize_transforms'; +import expect from '@kbn/expect'; +import { TRANSFORM_STATE } from '@kbn/transform-plugin/common/constants'; +import type { SecurityCreateApiKeyResponse } from '@elastic/elasticsearch/lib/api/types'; +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +import { asyncForEach, generateDestIndex, generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + // If transform was created with sufficient -> should still authorize and start + // If transform was created with insufficient -> should still authorize and start + + function getTransformIdByUser(username: USER) { + return `transform-by-${username}`; + } + + function generateHeaders(apiKey: SecurityCreateApiKeyResponse) { + return { + ...COMMON_REQUEST_HEADERS, + 'es-secondary-authorization': `ApiKey ${apiKey.encoded}`, + }; + } + + async function createTransform(transformId: string, headers: object) { + const config = generateTransformConfig(transformId, true); + await transform.api.createTransform(transformId, config, { + headers, + deferValidation: true, + }); + } + + async function cleanUpTransform(transformId: string) { + const destinationIndex = generateDestIndex(transformId); + + await transform.api.stopTransform(transformId); + await transform.api.cleanTransformIndices(); + await transform.api.deleteIndices(destinationIndex); + } + + describe('/api/transform/reauthorize_transforms', function () { + const apiKeysForTransformUsers = new Map(); + + async function expectUnauthorizedTransform(transformId: string, createdByUser: USER) { + const user = createdByUser; + const { body: getTransformBody } = await transform.api.getTransform(transformId); + const transformInfo = getTransformBody.transforms[0]; + const expectedApiKeyId = apiKeysForTransformUsers?.get(user)?.id; + + expect(typeof transformInfo.authorization.api_key).to.be('object'); + expect(transformInfo.authorization.api_key.id).to.eql( + expectedApiKeyId, + `Expected authorization api_key for ${transformId} to be ${expectedApiKeyId} (got ${JSON.stringify( + transformInfo.authorization.api_key + )})` + ); + + const stats = await transform.api.getTransformStats(transformId); + expect(stats.state).to.eql( + TRANSFORM_STATE.STOPPED, + `Expected transform state of ${transformId} to be '${TRANSFORM_STATE.STOPPED}' (got ${stats.state})` + ); + expect(stats.health.status).to.eql( + 'red', + `Expected transform health status of ${transformId} to be 'red' (got ${stats.health.status})` + ); + expect(stats.health.issues![0].type).to.eql( + 'privileges_check_failed', + `Expected transform health issue of ${transformId} to be 'privileges_check_failed' (got ${stats.health.status})` + ); + } + + async function expectAuthorizedTransform(transformId: string, createdByUser: USER) { + const { body: getTransformBody } = await transform.api.getTransform(transformId); + const transformInfo = getTransformBody.transforms[0]; + + const expectedApiKeyId = apiKeysForTransformUsers?.get(createdByUser)?.id; + expect(transformInfo.authorization.api_key.id).to.not.eql( + expectedApiKeyId, + `Expected authorization api_key for ${transformId} to not be ${expectedApiKeyId} (got ${JSON.stringify( + transformInfo.authorization.api_key + )})` + ); + const stats = await transform.api.getTransformStats(transformId); + expect(stats.health.status).to.eql( + 'green', + `Expected transform health status of ${transformId} to be 'green' (got ${stats.health.status})` + ); + } + + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + + const apiKeyForTransformUsers = + await transform.securityCommon.createApiKeyForTransformUsers(); + + apiKeyForTransformUsers.forEach(({ user, apiKey }) => + apiKeysForTransformUsers.set(user.name as USER, apiKey) + ); + }); + + after(async () => { + await transform.securityCommon.clearAllTransformApiKeys(); + }); + + describe('single transform reauthorize_transforms', function () { + const transformCreatedByViewerId = getTransformIdByUser(USER.TRANSFORM_VIEWER); + + beforeEach(async () => { + await createTransform( + transformCreatedByViewerId, + generateHeaders(apiKeysForTransformUsers.get(USER.TRANSFORM_VIEWER)!) + ); + }); + afterEach(async () => { + await cleanUpTransform(transformCreatedByViewerId); + }); + + it('should not reauthorize transform created by transform_viewer for transform_unauthorized', async () => { + const reqBody: ReauthorizeTransformsRequestSchema = [{ id: transformCreatedByViewerId }]; + const { body, status } = await supertest + .post(`/api/transform/reauthorize_transforms`) + .auth( + USER.TRANSFORM_UNAUTHORIZED, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_UNAUTHORIZED) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody); + transform.api.assertResponseStatusCode(200, status, body); + expect(body[transformCreatedByViewerId].success).to.eql( + false, + `Expected ${transformCreatedByViewerId} not to be authorized` + ); + expect(typeof body[transformCreatedByViewerId].error).to.be('object'); + + await expectUnauthorizedTransform(transformCreatedByViewerId, USER.TRANSFORM_VIEWER); + }); + + it('should not reauthorize transform created by transform_viewer for transform_viewer', async () => { + const reqBody: ReauthorizeTransformsRequestSchema = [{ id: transformCreatedByViewerId }]; + const { body, status } = await supertest + .post(`/api/transform/reauthorize_transforms`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody); + transform.api.assertResponseStatusCode(200, status, body); + expect(body[transformCreatedByViewerId].success).to.eql( + false, + `Expected ${transformCreatedByViewerId} not to be rauthorized` + ); + expect(typeof body[transformCreatedByViewerId].error).to.be('object'); + + await expectUnauthorizedTransform(transformCreatedByViewerId, USER.TRANSFORM_VIEWER); + }); + + it('should reauthorize transform created by transform_viewer with new api key of poweruser and start the transform', async () => { + const reqBody: ReauthorizeTransformsRequestSchema = [{ id: transformCreatedByViewerId }]; + const { body, status } = await supertest + .post(`/api/transform/reauthorize_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody); + transform.api.assertResponseStatusCode(200, status, body); + expect(body[transformCreatedByViewerId].success).to.eql( + true, + `Expected ${transformCreatedByViewerId} to be reauthorized` + ); + expect(typeof body[transformCreatedByViewerId].error).to.eql( + 'undefined', + `Expected ${transformCreatedByViewerId} to be reauthorized without error` + ); + await transform.api.waitForTransformState( + transformCreatedByViewerId, + TRANSFORM_STATE.STARTED + ); + + await expectAuthorizedTransform(transformCreatedByViewerId, USER.TRANSFORM_VIEWER); + }); + }); + + describe('single transform reauthorize_transforms with invalid transformId', function () { + it('should return 200 with error in response if invalid transformId', async () => { + const reqBody: ReauthorizeTransformsRequestSchema = [{ id: 'invalid_transform_id' }]; + const { body, status } = await supertest + .post(`/api/transform/reauthorize_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody); + transform.api.assertResponseStatusCode(200, status, body); + + expect(body.invalid_transform_id.success).to.eql(false); + expect(body.invalid_transform_id).to.have.property('error'); + }); + }); + + describe('bulk reauthorize_transforms', function () { + const reqBody: ReauthorizeTransformsRequestSchema = [ + USER.TRANSFORM_VIEWER, + USER.TRANSFORM_POWERUSER, + ].map((user) => ({ id: getTransformIdByUser(user) })); + const destinationIndices = reqBody.map((d) => generateDestIndex(d.id)); + + beforeEach(async () => { + await Promise.all( + [USER.TRANSFORM_VIEWER, USER.TRANSFORM_POWERUSER].map((user) => + createTransform( + getTransformIdByUser(user), + generateHeaders(apiKeysForTransformUsers.get(user)!) + ) + ) + ); + }); + + afterEach(async () => { + await asyncForEach(reqBody, async ({ id }: { id: string }, idx: number) => { + await transform.api.stopTransform(id); + }); + await transform.api.cleanTransformIndices(); + await asyncForEach(destinationIndices, async (destinationIndex: string) => { + await transform.api.deleteIndices(destinationIndex); + }); + }); + + it('should reauthorize multiple transforms for transform_poweruser, even if one of the transformIds is invalid', async () => { + const invalidTransformId = 'invalid_transform_id'; + + const { body, status } = await supertest + .post(`/api/transform/reauthorize_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send([...reqBody, { id: invalidTransformId }]); + transform.api.assertResponseStatusCode(200, status, body); + + await expectAuthorizedTransform( + getTransformIdByUser(USER.TRANSFORM_VIEWER), + USER.TRANSFORM_VIEWER + ); + await expectAuthorizedTransform( + getTransformIdByUser(USER.TRANSFORM_POWERUSER), + USER.TRANSFORM_POWERUSER + ); + + expect(body[invalidTransformId].success).to.eql(false); + expect(body[invalidTransformId]).to.have.property('error'); + }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json index 1a12efba9dc1c..44b1677de8a3a 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json @@ -31,11 +31,15 @@ "filter_journeys.match": "", "filter_journeys.tags": [], "ignore_https_errors": false, - "throttling.is_enabled": true, - "throttling.download_speed": "5", - "throttling.upload_speed": "3", - "throttling.latency": "20", - "throttling.config": "5d/3u/20l", + "throttling": { + "value": { + "download": "5", + "latency": "20", + "upload": "3" + }, + "id": "default", + "label": "Default" + }, "locations": [], "name": "Test HTTP Monitor 03", "namespace": "testnamespace", diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json index 091f18e6afe57..b2961eb0660d6 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/http_monitor.json @@ -25,9 +25,12 @@ "urls": "https://nextjs-test-synthetics.vercel.app/api/users", "url.port": null, "proxy_url": "http://proxy.com", + "proxy_headers": {}, "check.response.body.negative": [], "check.response.body.positive": [], + "check.response.json": [], "response.include_body": "never", + "response.include_body_max_bytes": "1024", "check.request.headers": { "sampleHeader": "sampleHeaderValue" }, @@ -81,5 +84,8 @@ "form_monitor_type": "http", "journey_id": "", "id": "", - "hash": "" + "hash": "", + "mode": "any", + "ipv4": true, + "ipv6": true } diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json index ab172c3fe8ca5..ef94fcc067a98 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/icmp_monitor.json @@ -26,5 +26,8 @@ "origin": "ui", "form_monitor_type": "icmp", "id": "", - "hash": "" + "hash": "", + "mode": "any", + "ipv4": true, + "ipv6": true } diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json index c8c2c27ce0f17..dd6d6eefecfcd 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/project_http_monitor.json @@ -56,7 +56,8 @@ } }, "response": { - "include_body": "always" + "include_body": "always", + "include_body_max_bytes": 900 }, "tags": "tag2,tag2", "response.include_headers": false, @@ -69,7 +70,8 @@ "Saved", "saved" ] - } + }, + "json": [{"description":"check status","expression":"foo.bar == \"myValue\""}] }, "hash": "ekrjelkjrelkjre", "ssl.verification_mode": "strict" diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json index c3664c5646b16..a0390c0bc173e 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json @@ -34,5 +34,8 @@ "origin": "ui", "form_monitor_type": "tcp", "id": "", - "hash": "" + "hash": "", + "mode": "any", + "ipv4": true, + "ipv6": true } diff --git a/x-pack/test/apm_api_integration/cloud/config.ts b/x-pack/test/apm_api_integration/cloud/config.ts new file mode 100644 index 0000000000000..06421969888b5 --- /dev/null +++ b/x-pack/test/apm_api_integration/cloud/config.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. + */ + +import { configs } from '../configs'; + +export default configs.cloud; diff --git a/x-pack/test/apm_api_integration/common/config.ts b/x-pack/test/apm_api_integration/common/config.ts index b1991a837335c..27c615cf4dbe4 100644 --- a/x-pack/test/apm_api_integration/common/config.ts +++ b/x-pack/test/apm_api_integration/common/config.ts @@ -56,7 +56,8 @@ type ApmApiClientKey = | 'noMlAccessUser' | 'manageOwnAgentKeysUser' | 'createAndAllAgentKeysUser' - | 'monitorClusterAndIndicesUser'; + | 'monitorClusterAndIndicesUser' + | 'manageServiceAccount'; export type ApmApiClient = Record>>; @@ -146,6 +147,10 @@ export function createTestConfig( kibanaServer, username: ApmUsername.apmMonitorClusterAndIndices, }), + manageServiceAccount: await getApmApiClient({ + kibanaServer, + username: ApmUsername.apmManageServiceAccount, + }), }; }, ml: MachineLearningAPIProvider, diff --git a/x-pack/test/apm_api_integration/configs/index.ts b/x-pack/test/apm_api_integration/configs/index.ts index eed3a3c9f2443..72dbace5e686b 100644 --- a/x-pack/test/apm_api_integration/configs/index.ts +++ b/x-pack/test/apm_api_integration/configs/index.ts @@ -37,6 +37,14 @@ const apmFtrConfigs = { 'logging.loggers': [apmDebugLogger], }, }, + cloud: { + license: 'basic' as const, + kibanaConfig: { + 'xpack.apm.agent.migrations.enabled': 'true', + 'xpack.apm.forceSyntheticSource': 'true', + 'logging.loggers': [apmDebugLogger], + }, + }, }; export type APMFtrConfigName = keyof typeof apmFtrConfigs; diff --git a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts index 89dc5e5f41089..f95bb8de59a89 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.spec.ts @@ -83,6 +83,61 @@ export default function ApiTest({ getService }: FtrProviderContext) { ).to.equal(true); }); + it('transaction_error_rate with transaction name', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + transactionName: 'APIRestController#product', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview[0]).to.eql({ + x: 1627974600000, + y: 1, + }); + }); + + it('transaction_error_rate with nonexistent transaction name', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + transactionName: 'foo', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.every( + (item: { x: number; y: number | null }) => item.y === null + ) + ).to.equal(true); + }); + it('error_count (with data)', async () => { const options = getOptions(); options.params.query.transactionType = undefined; @@ -100,6 +155,33 @@ export default function ApiTest({ getService }: FtrProviderContext) { ).to.equal(true); }); + it('error_count with error grouping key', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + errorGroupingKey: 'd16d39e7fa133b8943cea035430a7b4e', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview).to.eql([ + { x: 1627974600000, y: 4 }, + { x: 1627974900000, y: 2 }, + { x: 1627975200000, y: 0 }, + ]); + }); + it('transaction_duration (with data)', async () => { const options = getOptions(); const response = await apmApiClient.readUser({ @@ -115,5 +197,54 @@ export default function ApiTest({ getService }: FtrProviderContext) { ) ).to.equal(true); }); + + it('transaction_duration with transaction name', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + transactionName: 'DispatcherServlet#doGet', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview[0].data[0]).to.eql({ + x: 1627974600000, + y: 18485.85714285714, + }); + }); + + it('transaction_duration with nonexistent transaction name', async () => { + const options = { + params: { + query: { + start, + end, + serviceName: 'opbeans-java', + transactionType: 'request', + transactionName: 'foo', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview).to.eql([]); + }); }); } diff --git a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts index 3edbabcf023f9..64ced57167ada 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts @@ -8,6 +8,7 @@ import { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; import { errorCountMessage } from '@kbn/apm-plugin/common/rules/default_action_message'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -16,7 +17,11 @@ import { fetchServiceInventoryAlertCounts, fetchServiceTabAlertCount, } from './alerting_api_helper'; -import { waitForRuleStatus, waitForDocumentInIndex } from './wait_for_rule_status'; +import { + waitForRuleStatus, + waitForDocumentInIndex, + waitForAlertInIndex, +} from './wait_for_rule_status'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); @@ -32,7 +37,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { let ruleId: string; let actionId: string | undefined; - const INDEX_NAME = 'error-count'; + const APM_ALERTS_INDEX = '.alerts-observability.apm.alerts-default'; + const ALERT_ACTION_INDEX_NAME = 'alert-action-error-count'; + + const errorMessage = '[ResponseError] index_not_found_exception'; + const errorGroupingKey = getErrorGroupingKey(errorMessage); before(async () => { const opbeansJava = apm @@ -50,11 +59,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .timestamp(timestamp) .duration(100) .failure() - .errors( - opbeansJava - .error({ message: '[ResponseError] index_not_found_exception' }) - .timestamp(timestamp + 50) - ), + .errors(opbeansJava.error({ message: errorMessage }).timestamp(timestamp + 50)), opbeansNode .transaction({ transactionName: 'tx-node' }) .timestamp(timestamp) @@ -69,8 +74,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { await synthtraceEsClient.clean(); await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); - await esDeleteAllIndices(INDEX_NAME); - await es.deleteByQuery({ index: '.alerts*', query: { match_all: {} } }); + await esDeleteAllIndices(ALERT_ACTION_INDEX_NAME); + await es.deleteByQuery({ + index: APM_ALERTS_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + }); await es.deleteByQuery({ index: '.kibana-event-log-*', query: { term: { 'kibana.alert.rule.consumer': 'apm' } }, @@ -82,7 +90,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { actionId = await createIndexConnector({ supertest, name: 'Error count API test', - indexName: INDEX_NAME, + indexName: ALERT_ACTION_INDEX_NAME, }); const createdRule = await createApmRule({ supertest, @@ -93,13 +101,25 @@ export default function ApiTest({ getService }: FtrProviderContext) { threshold: 1, windowSize: 1, windowUnit: 'h', + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.name', + 'error.grouping_key', + ], }, actions: [ { group: 'threshold_met', id: actionId, params: { - documents: [{ message: errorCountMessage }], + documents: [ + { + message: `${errorCountMessage} +- Transaction name: {{context.transactionName}} +- Error grouping key: {{context.errorGroupingKey}}`, + }, + ], }, frequency: { notify_when: 'onActionGroupChange', @@ -113,7 +133,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ruleId = createdRule.id; }); - it('checks if alert is active', async () => { + it('checks if rule is active', async () => { const executionStatus = await waitForRuleStatus({ id: ruleId, expectedStatus: 'active', @@ -125,7 +145,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns correct message', async () => { const resp = await waitForDocumentInIndex<{ message: string }>({ es, - indexName: INDEX_NAME, + indexName: ALERT_ACTION_INDEX_NAME, }); expect(resp.hits.hits[0]._source?.message).eql( @@ -134,10 +154,25 @@ export default function ApiTest({ getService }: FtrProviderContext) { - Service name: opbeans-java - Environment: production - Threshold: 1 -- Triggered value: 15 errors over the last 1 hr` +- Triggered value: 15 errors over the last 1 hr +- Transaction name: tx-java +- Error grouping key: ${errorGroupingKey}` ); }); + it('indexes alert document with all group-by fields', async () => { + const resp = await waitForAlertInIndex({ + es, + indexName: APM_ALERTS_INDEX, + ruleId, + }); + + expect(resp.hits.hits[0]._source).property('service.name', 'opbeans-java'); + expect(resp.hits.hits[0]._source).property('service.environment', 'production'); + expect(resp.hits.hits[0]._source).property('transaction.name', 'tx-java'); + expect(resp.hits.hits[0]._source).property('error.grouping_key', errorGroupingKey); + }); + it('shows the correct alert count for each service on service inventory', async () => { const serviceInventoryAlertCounts = await fetchServiceInventoryAlertCounts(apmApiClient); expect(serviceInventoryAlertCounts).to.eql({ diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts new file mode 100644 index 0000000000000..4fa40d566552f --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts @@ -0,0 +1,183 @@ +/* + * 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 { AggregationType, ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createApmRule, + createIndexConnector, + fetchServiceInventoryAlertCounts, + fetchServiceTabAlertCount, +} from './alerting_api_helper'; +import { + waitForRuleStatus, + waitForDocumentInIndex, + waitForAlertInIndex, +} from './wait_for_rule_status'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); + + const supertest = getService('supertest'); + const es = getService('es'); + const apmApiClient = getService('apmApiClient'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + + const synthtraceEsClient = getService('synthtraceEsClient'); + + registry.when('transaction duration alert', { config: 'basic', archives: [] }, () => { + let ruleId: string; + let actionId: string | undefined; + + const APM_ALERTS_INDEX = '.alerts-observability.apm.alerts-default'; + const ALERT_ACTION_INDEX_NAME = 'alert-action-transaction-duration'; + + before(async () => { + const opbeansJava = apm + .service({ name: 'opbeans-java', environment: 'production', agentName: 'java' }) + .instance('instance'); + const opbeansNode = apm + .service({ name: 'opbeans-node', environment: 'production', agentName: 'node' }) + .instance('instance'); + const events = timerange('now-15m', 'now') + .ratePerMinute(1) + .generator((timestamp) => { + return [ + opbeansJava + .transaction({ transactionName: 'tx-java' }) + .timestamp(timestamp) + .duration(5000) + .success(), + opbeansNode + .transaction({ transactionName: 'tx-node' }) + .timestamp(timestamp) + .duration(4000) + .success(), + ]; + }); + await synthtraceEsClient.index(events); + }); + + after(async () => { + await synthtraceEsClient.clean(); + await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); + await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); + await esDeleteAllIndices([ALERT_ACTION_INDEX_NAME]); + await es.deleteByQuery({ + index: APM_ALERTS_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + }); + await es.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': 'apm' } }, + }); + }); + + describe('create alert with transaction.name group by', () => { + before(async () => { + actionId = await createIndexConnector({ + supertest, + name: 'Transation duration API test', + indexName: ALERT_ACTION_INDEX_NAME, + }); + const createdRule = await createApmRule({ + supertest, + ruleTypeId: ApmRuleType.TransactionDuration, + name: 'Apm transaction duration', + params: { + threshold: 3000, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + serviceName: 'opbeans-java', + environment: 'production', + aggregationType: AggregationType.Avg, + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.type', + 'transaction.name', + ], + }, + actions: [ + { + group: 'threshold_met', + id: actionId, + params: { + documents: [{ message: 'Transaction Name: {{context.transactionName}}' }], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + expect(createdRule.id).to.not.eql(undefined); + ruleId = createdRule.id; + }); + + it('checks if rule is active', async () => { + const executionStatus = await waitForRuleStatus({ + id: ruleId, + expectedStatus: 'active', + supertest, + }); + expect(executionStatus.status).to.be('active'); + }); + + it('returns correct message', async () => { + const resp = await waitForDocumentInIndex<{ message: string }>({ + es, + indexName: ALERT_ACTION_INDEX_NAME, + }); + + expect(resp.hits.hits[0]._source?.message).eql(`Transaction Name: tx-java`); + }); + + it('indexes alert document with all group-by fields', async () => { + const resp = await waitForAlertInIndex({ + es, + indexName: APM_ALERTS_INDEX, + ruleId, + }); + + expect(resp.hits.hits[0]._source).property('service.name', 'opbeans-java'); + expect(resp.hits.hits[0]._source).property('service.environment', 'production'); + expect(resp.hits.hits[0]._source).property('transaction.type', 'request'); + expect(resp.hits.hits[0]._source).property('transaction.name', 'tx-java'); + }); + + it('shows the correct alert count for each service on service inventory', async () => { + const serviceInventoryAlertCounts = await fetchServiceInventoryAlertCounts(apmApiClient); + expect(serviceInventoryAlertCounts).to.eql({ + 'opbeans-node': 0, + 'opbeans-java': 1, + }); + }); + + it('shows the correct alert count in opbeans-java service', async () => { + const serviceTabAlertCount = await fetchServiceTabAlertCount({ + apmApiClient, + serviceName: 'opbeans-java', + }); + expect(serviceTabAlertCount).to.be(1); + }); + + it('shows the correct alert count in opbeans-node service', async () => { + const serviceTabAlertCount = await fetchServiceTabAlertCount({ + apmApiClient, + serviceName: 'opbeans-node', + }); + expect(serviceTabAlertCount).to.be(0); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts new file mode 100644 index 0000000000000..d4d6a0cf3585d --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts @@ -0,0 +1,187 @@ +/* + * 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 { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createApmRule, + createIndexConnector, + fetchServiceInventoryAlertCounts, + fetchServiceTabAlertCount, +} from './alerting_api_helper'; +import { + waitForRuleStatus, + waitForDocumentInIndex, + waitForAlertInIndex, +} from './wait_for_rule_status'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); + + const supertest = getService('supertest'); + const es = getService('es'); + const apmApiClient = getService('apmApiClient'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + + const synthtraceEsClient = getService('synthtraceEsClient'); + + registry.when('transaction error rate alert', { config: 'basic', archives: [] }, () => { + let ruleId: string; + let actionId: string | undefined; + + const APM_ALERTS_INDEX = '.alerts-observability.apm.alerts-default'; + const ALERT_ACTION_INDEX_NAME = 'alert-action-transaction-error-rate'; + + before(async () => { + const opbeansJava = apm + .service({ name: 'opbeans-java', environment: 'production', agentName: 'java' }) + .instance('instance'); + const opbeansNode = apm + .service({ name: 'opbeans-node', environment: 'production', agentName: 'node' }) + .instance('instance'); + const events = timerange('now-15m', 'now') + .ratePerMinute(1) + .generator((timestamp) => { + return [ + opbeansJava + .transaction({ transactionName: 'tx-java' }) + .timestamp(timestamp) + .duration(100) + .failure(), + opbeansJava + .transaction({ transactionName: 'tx-java' }) + .timestamp(timestamp) + .duration(200) + .success(), + opbeansNode + .transaction({ transactionName: 'tx-node' }) + .timestamp(timestamp) + .duration(400) + .success(), + ]; + }); + await synthtraceEsClient.index(events); + }); + + after(async () => { + await synthtraceEsClient.clean(); + await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); + await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); + await esDeleteAllIndices([ALERT_ACTION_INDEX_NAME]); + await es.deleteByQuery({ + index: APM_ALERTS_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + }); + await es.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': 'apm' } }, + }); + }); + + describe('create alert with transaction.name group by', () => { + before(async () => { + actionId = await createIndexConnector({ + supertest, + name: 'Transation error rate API test', + indexName: ALERT_ACTION_INDEX_NAME, + }); + const createdRule = await createApmRule({ + supertest, + ruleTypeId: ApmRuleType.TransactionErrorRate, + name: 'Apm error rate duration', + params: { + threshold: 50, + windowSize: 5, + windowUnit: 'm', + transactionType: 'request', + serviceName: 'opbeans-java', + environment: 'production', + groupBy: [ + 'service.name', + 'service.environment', + 'transaction.type', + 'transaction.name', + ], + }, + actions: [ + { + group: 'threshold_met', + id: actionId, + params: { + documents: [{ message: 'Transaction Name: {{context.transactionName}}' }], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + expect(createdRule.id).to.not.eql(undefined); + ruleId = createdRule.id; + }); + + it('checks if rule is active', async () => { + const executionStatus = await waitForRuleStatus({ + id: ruleId, + expectedStatus: 'active', + supertest, + }); + expect(executionStatus.status).to.be('active'); + }); + + it('returns correct message', async () => { + const resp = await waitForDocumentInIndex<{ message: string }>({ + es, + indexName: ALERT_ACTION_INDEX_NAME, + }); + + expect(resp.hits.hits[0]._source?.message).eql(`Transaction Name: tx-java`); + }); + + it('indexes alert document with all group-by fields', async () => { + const resp = await waitForAlertInIndex({ + es, + indexName: APM_ALERTS_INDEX, + ruleId, + }); + + expect(resp.hits.hits[0]._source).property('service.name', 'opbeans-java'); + expect(resp.hits.hits[0]._source).property('service.environment', 'production'); + expect(resp.hits.hits[0]._source).property('transaction.type', 'request'); + expect(resp.hits.hits[0]._source).property('transaction.name', 'tx-java'); + }); + + it('shows the correct alert count for each service on service inventory', async () => { + const serviceInventoryAlertCounts = await fetchServiceInventoryAlertCounts(apmApiClient); + expect(serviceInventoryAlertCounts).to.eql({ + 'opbeans-node': 0, + 'opbeans-java': 1, + }); + }); + + it('shows the correct alert count in opbeans-java service', async () => { + const serviceTabAlertCount = await fetchServiceTabAlertCount({ + apmApiClient, + serviceName: 'opbeans-java', + }); + expect(serviceTabAlertCount).to.be(1); + }); + + it('shows the correct alert count in opbeans-node service', async () => { + const serviceTabAlertCount = await fetchServiceTabAlertCount({ + apmApiClient, + serviceName: 'opbeans-node', + }); + expect(serviceTabAlertCount).to.be(0); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/wait_for_rule_status.ts b/x-pack/test/apm_api_integration/tests/alerts/wait_for_rule_status.ts index 44da2bea36bf2..f02285d5056eb 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/wait_for_rule_status.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/wait_for_rule_status.ts @@ -53,3 +53,33 @@ export async function waitForDocumentInIndex({ { retries: 10 } ); } + +export async function waitForAlertInIndex({ + es, + indexName, + ruleId, +}: { + es: Client; + indexName: string; + ruleId: string; +}): Promise>> { + return pRetry( + async () => { + const response = await es.search({ + index: indexName, + body: { + query: { + term: { + 'kibana.alert.rule.uuid': ruleId, + }, + }, + }, + }); + if (response.hits.hits.length === 0) { + throw new Error('No hits found'); + } + return response; + }, + { retries: 10 } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts b/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts index 0ea2ed4f57493..14d5ba72275ea 100644 --- a/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts @@ -5,14 +5,11 @@ * 2.0. */ import expect from '@kbn/expect'; -import { - APIClientRequestParamsOf, - APIReturnType, -} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; import { timerange } from '@kbn/apm-synthtrace-client'; import { service } from '@kbn/apm-synthtrace-client/src/lib/apm/service'; import { orderBy } from 'lodash'; +import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { config, generateData } from './generate_data'; @@ -31,25 +28,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - async function callErrorGroupSamplesApi( - overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples'>['params'] - > - ) { + async function callErrorGroupSamplesApi({ groupId }: { groupId: string }) { const response = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', params: { path: { serviceName, - groupId: 'foo', - ...overrides?.path, + groupId, }, query: { start: new Date(start).toISOString(), end: new Date(end).toISOString(), environment: 'ENVIRONMENT_ALL', kuery: '', - ...overrides?.query, }, }, }); @@ -78,7 +69,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { - const response = await callErrorGroupSamplesApi(); + const response = await callErrorGroupSamplesApi({ groupId: 'foo' }); expect(response.status).to.be(200); expect(response.body.occurrencesCount).to.be(0); }); @@ -97,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let errorsSamplesResponse: ErrorGroupSamples; before(async () => { const response = await callErrorGroupSamplesApi({ - path: { groupId: '0000000000000000000000000Error 1' }, + groupId: '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03', }); errorsSamplesResponse = response.body; }); @@ -124,7 +115,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let errorSampleDetailsResponse: ErrorSampleDetails; before(async () => { const errorsSamplesResponse = await callErrorGroupSamplesApi({ - path: { groupId: '0000000000000000000000000Error 1' }, + groupId: '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03', }); const errorId = errorsSamplesResponse.body.errorSampleIds[0]; @@ -133,10 +124,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { errorSampleDetailsResponse = response.body; }); - it('displays correct error sample data', () => { + it('displays correct error grouping_key', () => { expect(errorSampleDetailsResponse.error.error.grouping_key).to.equal( - '0000000000000000000000000Error 1' + '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03' ); + }); + + it('displays correct error message', () => { expect(errorSampleDetailsResponse.error.error.exception?.[0].message).to.equal('Error 1'); }); }); @@ -147,6 +141,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const instance = service(serviceName, 'production', 'go').instance('a'); + const errorMessage = 'Error 1'; + const groupId = getErrorGroupingKey(errorMessage); await synthtraceEsClient.index([ timerange(start, end) @@ -160,24 +156,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { .timestamp(timestamp) .sample(false) .errors( - instance.error({ message: `Error 1` }).timestamp(timestamp), - instance.error({ message: `Error 1` }).timestamp(timestamp + 1) + instance.error({ message: errorMessage }).timestamp(timestamp), + instance.error({ message: errorMessage }).timestamp(timestamp + 1) ), instance .transaction('GET /api/foo') .duration(100) .timestamp(timestamp) .sample(true) - .errors(instance.error({ message: `Error 1` }).timestamp(timestamp)), + .errors(instance.error({ message: errorMessage }).timestamp(timestamp)), ]; }), ]); - errorGroupSamplesResponse = ( - await callErrorGroupSamplesApi({ - path: { groupId: '0000000000000000000000000Error 1' }, - }) - ).body; + errorGroupSamplesResponse = (await callErrorGroupSamplesApi({ groupId })).body; }); after(() => synthtraceEsClient.clean()); @@ -187,6 +179,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { // this checks whether the order of indexing is different from the order that is returned // if it is not, scoring/sorting is broken + expect(errorGroupSamplesResponse.errorSampleIds.length).to.be(3); expect(idsOfErrors).to.not.eql(orderBy(idsOfErrors)); }); }); diff --git a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts index 3229eaba383c0..47c48693eaa0e 100644 --- a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/generate_data.ts @@ -59,7 +59,7 @@ export async function generateData({ .transaction({ transactionName: transaction.name }) .errors( serviceGoProdInstance - .error({ message: 'Error 1', type: transaction.name, groupingName: 'Error test' }) + .error({ message: 'Error 1', type: transaction.name }) .timestamp(timestamp) ) .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts index cd01ca49afcc5..d091fb9baf0e6 100644 --- a/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/top_erroneous_transactions/top_erroneous_transactions.spec.ts @@ -26,6 +26,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const serviceName = 'synth-go'; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + const groupId = '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03'; async function callApi( overrides?: RecursivePartial< @@ -82,7 +83,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await callApi({ - path: { groupId: '0000000000000000000000Error test' }, + path: { groupId }, }); erroneousTransactions = response.body; }); @@ -132,7 +133,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const fiveMinutes = 5 * 60 * 1000; const response = await callApi({ - path: { groupId: '0000000000000000000000Error test' }, + path: { groupId }, query: { start: new Date(end - fiveMinutes).toISOString(), end: new Date(end).toISOString(), @@ -182,7 +183,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('when there are no data for the time period', () => { it('returns an empty array', async () => { const response = await callApi({ - path: { groupId: '0000000000000000000000Error test' }, + path: { groupId }, query: { start: '2021-01-03T00:00:00.000Z', end: '2021-01-03T00:15:00.000Z', diff --git a/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts b/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts index 830e5032a8de7..d7ae081a035e8 100644 --- a/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/top_errors_for_transaction/top_errors_main_stats.spec.ts @@ -71,31 +71,34 @@ export default function ApiTest({ getService }: FtrProviderContext) { after(() => synthtraceEsClient.clean()); describe('returns the correct data', () => { + const NUMBER_OF_BUCKETS = 15; let errorGroups: ErrorGroups; before(async () => { const response = await callApi({ query: { transactionName: firstTransactionName } }); errorGroups = response.body.errorGroups; }); - it('returns correct number of errors and error data', () => { - const numberOfBuckets = 15; - + it('returns correct number of errors', () => { expect(errorGroups.length).to.equal(2); + }); - const firstErrorId = `Error 1 transaction ${firstTransactionName}`; + it('error 1 is correct', () => { + const firstErrorId = `b6c1d4d41b0b60b841f40232497344ba36856fcbea0692a4695562ca73e790bd`; const firstError = errorGroups.find((x) => x.groupId === firstErrorId); expect(firstError).to.not.be(undefined); expect(firstError?.groupId).to.be(firstErrorId); - expect(firstError?.name).to.be(firstErrorId); - expect(firstError?.occurrences).to.be(firstTransactionFailureRate * numberOfBuckets); + expect(firstError?.name).to.be(`Error 1 transaction GET /apple 🍎`); + expect(firstError?.occurrences).to.be(firstTransactionFailureRate * NUMBER_OF_BUCKETS); expect(firstError?.lastSeen).to.be(moment(end).startOf('minute').valueOf()); + }); - const secondErrorId = `Error 2 transaction ${firstTransactionName}`; + it('error 2 is correct', () => { + const secondErrorId = `c3f388e4f7276d4fab85aa2fad2d2a42e70637f65cd5ec9f085de28b36e69ba5`; const secondError = errorGroups.find((x) => x.groupId === secondErrorId); expect(secondError).to.not.be(undefined); expect(secondError?.groupId).to.be(secondErrorId); - expect(secondError?.name).to.be(secondErrorId); - expect(secondError?.occurrences).to.be(firstTransactionFailureRate * numberOfBuckets); + expect(secondError?.name).to.be(`Error 2 transaction GET /apple 🍎`); + expect(secondError?.occurrences).to.be(firstTransactionFailureRate * NUMBER_OF_BUCKETS); expect(secondError?.lastSeen).to.be(moment(end).startOf('minute').valueOf()); }); }); diff --git a/x-pack/test/apm_api_integration/tests/fleet/apm_package_policy_setup.ts b/x-pack/test/apm_api_integration/tests/fleet/apm_package_policy_setup.ts index 34c9fabe143eb..aeb2f00fb9360 100644 --- a/x-pack/test/apm_api_integration/tests/fleet/apm_package_policy_setup.ts +++ b/x-pack/test/apm_api_integration/tests/fleet/apm_package_policy_setup.ts @@ -11,7 +11,7 @@ export function setupFleet(bettertest: BetterTest) { return bettertest({ pathname: '/api/fleet/setup', method: 'post' }); } -export async function createAgentPolicy(bettertest: BetterTest) { +export async function createAgentPolicy(bettertest: BetterTest, id?: string) { const agentPolicyResponse = await bettertest<{ item: AgentPolicy }>({ pathname: '/api/fleet/agent_policies', method: 'post', @@ -19,6 +19,7 @@ export async function createAgentPolicy(bettertest: BetterTest) { body: { name: 'test_agent_policy', description: '', + id, namespace: 'default', monitoring_enabled: ['logs', 'metrics'], }, @@ -27,7 +28,11 @@ export async function createAgentPolicy(bettertest: BetterTest) { return agentPolicyResponse.body.item.id; } -export async function createPackagePolicy(bettertest: BetterTest, agentPolicyId: string) { +export async function createPackagePolicy( + bettertest: BetterTest, + agentPolicyId: string, + id?: string +) { // Get version of available APM package const apmPackageResponse = await bettertest<{ item: any }>({ pathname: `/api/fleet/epm/packages/apm`, @@ -43,6 +48,7 @@ export async function createPackagePolicy(bettertest: BetterTest, agentPolicyId: description: '', namespace: 'default', policy_id: agentPolicyId, + id, enabled: true, inputs: [{ type: 'apm', policy_template: 'apmserver', enabled: true, streams: [], vars: {} }], package: { name: 'apm', title: 'Elastic APM', version: apmPackageVersion }, diff --git a/x-pack/test/apm_api_integration/tests/fleet/migration_check.spec.ts b/x-pack/test/apm_api_integration/tests/fleet/migration_check.spec.ts new file mode 100644 index 0000000000000..1f89f65c536b9 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/fleet/migration_check.spec.ts @@ -0,0 +1,172 @@ +/* + * 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 '../../common/ftr_provider_context'; +import { + createAgentPolicy, + createPackagePolicy, + deleteAgentPolicy, + deletePackagePolicy, + setupFleet, +} from './apm_package_policy_setup'; +import { getBettertest } from '../../common/bettertest'; + +export default function ApiTest(ftrProviderContext: FtrProviderContext) { + const { getService } = ftrProviderContext; + const registry = getService('registry'); + const supertest = getService('supertest'); + const bettertest = getBettertest(supertest); + const apmApiClient = getService('apmApiClient'); + + registry.when('Fleet migration check - basic', { config: 'basic', archives: [] }, () => { + before(async () => { + await setupFleet(bettertest); + }); + + describe('cloud_apm_migration_enabled', () => { + it('should be false when when config not set', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('cloud_apm_migration_enabled', false); + }); + }); + }); + + registry.when('Fleet migration check - cloud', { config: 'cloud', archives: [] }, () => { + before(async () => { + await setupFleet(bettertest); + }); + + describe('migration check properties', () => { + it('should contain all expected properties', async () => { + const { status, body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(status).to.equal(200); + expect(body).to.have.property('has_cloud_agent_policy'); + expect(body).to.have.property('has_cloud_apm_package_policy'); + expect(body).to.have.property('cloud_apm_migration_enabled'); + expect(body).to.have.property('has_required_role'); + expect(body).to.have.property('has_apm_integrations'); + expect(body).to.have.property('latest_apm_package_version'); + }); + }); + + describe('cloud_apm_migration_enabled', () => { + it('should be true when when config is set', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('cloud_apm_migration_enabled', true); + }); + }); + + describe('has_cloud_agent_policy', () => { + it('should be false when cloud agent policy does not exist', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_cloud_agent_policy', false); + }); + describe('with Cloud agent policy', () => { + before(async () => { + await createAgentPolicy(bettertest, 'policy-elastic-agent-on-cloud'); + }); + after(async () => { + await deleteAgentPolicy(bettertest, 'policy-elastic-agent-on-cloud'); + }); + it('should be true when cloud agent policy exists', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_cloud_agent_policy', true); + }); + }); + }); + + describe('has_cloud_apm_package_policy', () => { + before(async () => { + await createAgentPolicy(bettertest, 'policy-elastic-agent-on-cloud'); + }); + after(async () => { + await deleteAgentPolicy(bettertest, 'policy-elastic-agent-on-cloud'); + }); + it('should be false when the Cloud APM package policy does not exist', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_cloud_apm_package_policy', false); + expect(body).to.not.have.property('cloud_apm_package_policy'); + expect(body).to.have.property('has_apm_integrations', false); + }); + describe('with Cloud APM package policy', () => { + before(async () => { + await createPackagePolicy(bettertest, 'policy-elastic-agent-on-cloud', 'apm'); + }); + after(async () => { + await deletePackagePolicy(bettertest, 'apm'); + }); + it('should be true when the Cloud APM package policy exists', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_cloud_apm_package_policy', true); + expect(body).to.have.property('cloud_apm_package_policy'); + expect(body).to.have.property('has_apm_integrations', true); + }); + }); + }); + + describe('has_apm_integrations', () => { + before(async () => { + await createAgentPolicy(bettertest, 'test-agent-policy'); + }); + after(async () => { + await deleteAgentPolicy(bettertest, 'test-agent-policy'); + }); + it('should be false when no APM package policies exist', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_apm_integrations', false); + expect(body).to.have.property('has_cloud_apm_package_policy', false); + }); + describe('with custom APM package policy', () => { + before(async () => { + await createPackagePolicy(bettertest, 'test-agent-policy', 'test-apm-package-policy'); + }); + after(async () => { + await deletePackagePolicy(bettertest, 'test-apm-package-policy'); + }); + it('should be true when any APM package policy exists', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_apm_integrations', true); + expect(body).to.have.property('has_cloud_apm_package_policy', false); + }); + }); + }); + + describe('has_required_role', () => { + it('should be true when user is superuser', async () => { + const { body } = await bettertest({ + pathname: '/internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_required_role', true); + }); + it('should be false when user is not superuser', async () => { + const { body } = await apmApiClient.manageServiceAccount({ + endpoint: 'GET /internal/apm/fleet/migration_check', + }); + expect(body).to.have.property('has_required_role', false); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts b/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts index c2e536f0ce930..91a8aac9bc3d3 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts @@ -7,6 +7,8 @@ import { apm, timerange } from '@kbn/apm-synthtrace-client'; import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +export const SERVICE_VERSIONS = ['2.3', '1.2', '1.1']; + export async function generateMobileData({ start, end, @@ -22,7 +24,7 @@ export async function generateMobileData({ environment: 'production', agentName: 'android/java', }) - .mobileDevice({ serviceVersion: '2.3' }) + .mobileDevice({ serviceVersion: SERVICE_VERSIONS[0] }) .deviceInfo({ manufacturer: 'Samsung', modelIdentifier: 'SM-G973F', @@ -52,7 +54,7 @@ export async function generateMobileData({ environment: 'production', agentName: 'android/java', }) - .mobileDevice({ serviceVersion: '1.2' }) + .mobileDevice({ serviceVersion: SERVICE_VERSIONS[1] }) .deviceInfo({ manufacturer: 'Samsung', modelIdentifier: 'SM-G930F', @@ -89,7 +91,7 @@ export async function generateMobileData({ environment: 'production', agentName: 'android/java', }) - .mobileDevice({ serviceVersion: '1.1' }) + .mobileDevice({ serviceVersion: SERVICE_VERSIONS[2] }) .deviceInfo({ manufacturer: 'Huawei', modelIdentifier: 'HUAWEI P2-0000', @@ -118,6 +120,96 @@ export async function generateMobileData({ carrierMCC: '440', }); + const pixel7 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Google', + modelIdentifier: 'Pixel 7', + modelName: 'Pixel 7', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + const pixel7Pro = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Google', + modelIdentifier: 'Pixel 7 Pro', + modelName: 'Pixel 7 Pro', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + + const pixel8 = apm + .mobileApp({ + name: 'synth-android', + environment: 'production', + agentName: 'android/java', + }) + .mobileDevice({ serviceVersion: '2.3' }) + .deviceInfo({ + manufacturer: 'Google', + modelIdentifier: 'Pixel 8', + modelName: 'Pixel 8', + }) + .osInfo({ + osType: 'android', + osVersion: '10', + osFull: 'Android 10, API level 29, BUILD A022MUBU2AUD1', + runtimeVersion: '2.1.0', + }) + .setGeoInfo({ + clientIp: '223.72.43.22', + cityName: 'Beijing', + continentName: 'Asia', + countryIsoCode: 'CN', + countryName: 'China', + regionIsoCode: 'CN-BJ', + regionName: 'Beijing', + location: { coordinates: [116.3861, 39.9143], type: 'Point' }, + }) + .setNetworkConnection({ type: 'wifi' }); + return await synthtraceEsClient.index([ timerange(start, end) .interval('5m') @@ -126,9 +218,13 @@ export async function generateMobileData({ galaxy10.startNewSession(); galaxy7.startNewSession(); huaweiP2.startNewSession(); + pixel7.startNewSession(); + pixel7Pro.startNewSession(); + pixel8.startNewSession(); return [ galaxy10 .transaction('Start View - View Appearing', 'Android Activity') + .errors(galaxy10.crash({ message: 'error' }).timestamp(timestamp)) .timestamp(timestamp) .duration(500) .success() @@ -172,6 +268,7 @@ export async function generateMobileData({ ), huaweiP2 .transaction('Start View - View Appearing', 'huaweiP2 Activity') + .errors(huaweiP2.crash({ message: 'error' }).timestamp(timestamp)) .timestamp(timestamp) .duration(20) .success() @@ -199,6 +296,7 @@ export async function generateMobileData({ ), galaxy7 .transaction('Start View - View Appearing', 'Android Activity') + .errors(galaxy7.crash({ message: 'error' }).timestamp(timestamp)) .timestamp(timestamp) .duration(20) .success() @@ -224,6 +322,87 @@ export async function generateMobileData({ .success() .timestamp(timestamp + 400) ), + pixel7 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + pixel7 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + pixel7 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + pixel8 + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + pixel8 + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + pixel8 + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), + pixel7Pro + .transaction('Start View - View Appearing', 'Android Activity') + .timestamp(timestamp) + .duration(20) + .success() + .children( + pixel7Pro + .span({ + spanName: 'onCreate', + spanType: 'app', + spanSubtype: 'external', + 'service.target.type': 'http', + 'span.destination.service.resource': 'external', + }) + .duration(50) + .success() + .timestamp(timestamp + 20), + pixel7Pro + .httpSpan({ + spanName: 'GET backend:1234', + httpMethod: 'GET', + httpUrl: 'https://backend:1234/api/start', + }) + .duration(800) + .success() + .timestamp(timestamp + 400) + ), ]; }), ]); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_detailed_statistics_by_field.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_detailed_statistics_by_field.spec.ts new file mode 100644 index 0000000000000..601e3b81e6dad --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_detailed_statistics_by_field.spec.ts @@ -0,0 +1,132 @@ +/* + * 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 { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; +import { isEmpty } from 'lodash'; +import moment from 'moment'; +import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { generateMobileData, SERVICE_VERSIONS } from './generate_mobile_data'; + +type MobileDetailedStatisticsResponse = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics'>; + +export default function ApiTest({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); + const registry = getService('registry'); + const synthtraceEsClient = getService('synthtraceEsClient'); + + const start = new Date('2023-01-01T00:00:00.000Z').getTime(); + const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; + + async function getMobileDetailedStatisticsByField({ + environment = ENVIRONMENT_ALL.value, + kuery = '', + serviceName, + field, + offset, + }: { + environment?: string; + kuery?: string; + serviceName: string; + field: string; + offset?: string; + }) { + return await apmApiClient + .readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics', + params: { + path: { serviceName }, + query: { + environment, + start: moment(end).subtract(7, 'minutes').toISOString(), + end: new Date(end).toISOString(), + offset, + kuery, + field, + fieldValues: JSON.stringify(SERVICE_VERSIONS), + }, + }, + }) + .then(({ body }) => body); + } + + registry.when( + 'Mobile detailed statistics when data is not loaded', + { config: 'basic', archives: [] }, + () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response = await getMobileDetailedStatisticsByField({ + serviceName: 'foo', + field: 'service.version', + }); + expect(response).to.be.eql({ currentPeriod: {}, previousPeriod: {} }); + }); + }); + } + ); + + registry.when( + 'Mobile detailed statistics when data is loaded', + { config: 'basic', archives: [] }, + () => { + before(async () => { + await generateMobileData({ + synthtraceEsClient, + start, + end, + }); + }); + + after(() => synthtraceEsClient.clean()); + + describe('when comparison is disable', () => { + it('returns current period data only', async () => { + const response = await getMobileDetailedStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'service.version', + }); + expect(isEmpty(response.currentPeriod)).to.be.equal(false); + expect(isEmpty(response.previousPeriod)).to.be.equal(true); + }); + }); + + describe('when comparison is enable', () => { + let mobiledetailedStatisticResponse: MobileDetailedStatisticsResponse; + + before(async () => { + mobiledetailedStatisticResponse = await getMobileDetailedStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'service.version', + offset: '8m', + }); + }); + it('returns some data for both periods', async () => { + expect(isEmpty(mobiledetailedStatisticResponse.currentPeriod)).to.be.equal(false); + expect(isEmpty(mobiledetailedStatisticResponse.previousPeriod)).to.be.equal(false); + }); + + it('returns same number of buckets for both periods', () => { + const currentPeriod = mobiledetailedStatisticResponse.currentPeriod[SERVICE_VERSIONS[0]]; + const previousPeriod = + mobiledetailedStatisticResponse.previousPeriod[SERVICE_VERSIONS[0]]; + + [ + [currentPeriod.latency, previousPeriod.latency], + [currentPeriod.throughput, previousPeriod.throughput], + ].forEach(([currentTimeseries, previousTimeseries]) => { + expect(currentTimeseries.length).to.equal(previousTimeseries.length); + }); + }); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts index 587f91ccbe6b8..a8b973f6d5660 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts @@ -92,7 +92,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { response.body.currentPeriod.timeseries.some((item) => item.y === 0 && item.x) ).to.eql(true); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(4); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(7); expect(response.body.previousPeriod.timeseries).to.eql([]); }); }); @@ -125,7 +125,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expect(ntcCell.status).to.be(200); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(2); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(5); expect(ntcCell.body.currentPeriod.timeseries[0].y).to.eql(2); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts index 5cbffe4ab8c00..171b43b1f1a0d 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts @@ -122,7 +122,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { kuery: `service.version:"2.3"`, }); - expect(response.currentPeriod.mostSessions.value).to.eql(3); + expect(response.currentPeriod.mostSessions.value).to.eql(12); expect(response.currentPeriod.mostRequests.value).to.eql(0); }); @@ -132,7 +132,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { kuery: `service.version:"2.3" and service.environment: "production"`, }); - expect(response.currentPeriod.mostSessions.value).to.eql(3); + expect(response.currentPeriod.mostSessions.value).to.eql(12); expect(response.currentPeriod.mostRequests.value).to.eql(0); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_main_statistics_by_field.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_main_statistics_by_field.spec.ts new file mode 100644 index 0000000000000..a58f6e58b99e6 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_main_statistics_by_field.spec.ts @@ -0,0 +1,143 @@ +/* + * 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 { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { generateMobileData } from './generate_mobile_data'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); + const registry = getService('registry'); + const synthtraceEsClient = getService('synthtraceEsClient'); + + const start = new Date('2023-01-01T00:00:00.000Z').getTime(); + const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; + + async function getMobileMainStatisticsByField({ + environment = ENVIRONMENT_ALL.value, + kuery = '', + serviceName, + field, + }: { + environment?: string; + kuery?: string; + serviceName: string; + field: string; + }) { + return await apmApiClient + .readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/main_statistics', + params: { + path: { serviceName }, + query: { + environment, + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + kuery, + field, + }, + }, + }) + .then(({ body }) => body); + } + + registry.when( + 'Mobile main statistics when data is not loaded', + { config: 'basic', archives: [] }, + () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'foo', + field: 'service.version', + }); + expect(response.mainStatistics.length).to.be(0); + }); + }); + } + ); + + registry.when('Mobile main statistics', { config: 'basic', archives: [] }, () => { + before(async () => { + await generateMobileData({ + synthtraceEsClient, + start, + end, + }); + }); + + after(() => synthtraceEsClient.clean()); + + describe('when data is loaded', () => { + it('returns the correct data for App version', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'service.version', + }); + const fieldValues = response.mainStatistics.map((item) => item.name); + + expect(fieldValues).to.be.eql(['1.1', '1.2', '2.3']); + + const latencyValues = response.mainStatistics.map((item) => item.latency); + + expect(latencyValues).to.be.eql([172000, 20000, 20000]); + + const throughputValues = response.mainStatistics.map((item) => item.throughput); + expect(throughputValues).to.be.eql([ + 1.0000011111123457, 0.20000022222246913, 0.20000022222246913, + ]); + }); + it('returns the correct data for Os version', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'host.os.version', + }); + + const fieldValues = response.mainStatistics.map((item) => item.name); + + expect(fieldValues).to.be.eql(['10']); + + const latencyValues = response.mainStatistics.map((item) => item.latency); + + expect(latencyValues).to.be.eql([128571.42857142857]); + + const throughputValues = response.mainStatistics.map((item) => item.throughput); + expect(throughputValues).to.be.eql([1.4000015555572838]); + }); + it('returns the correct data for Devices', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'device.model.identifier', + }); + const fieldValues = response.mainStatistics.map((item) => item.name); + + expect(fieldValues).to.be.eql([ + 'HUAWEI P2-0000', + 'SM-G930F', + 'SM-G973F', + 'Pixel 7 Pro', + 'Pixel 8', + 'SM-G930F', + ]); + + const latencyValues = response.mainStatistics.map((item) => item.latency); + + expect(latencyValues).to.be.eql([400000, 20000, 20000, 20000, 20000, 20000]); + + const throughputValues = response.mainStatistics.map((item) => item.throughput); + expect(throughputValues).to.be.eql([ + 0.40000044444493826, 0.20000022222246913, 0.20000022222246913, 0.20000022222246913, + 0.20000022222246913, 0.20000022222246913, + ]); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.ts new file mode 100644 index 0000000000000..65a6e19c0c058 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.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 { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { generateMobileData } from './generate_mobile_data'; + +type MostUsedCharts = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/most_used_charts'>; + +export default function ApiTest({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); + const registry = getService('registry'); + const synthtraceEsClient = getService('synthtraceEsClient'); + + const start = new Date('2023-01-01T00:00:00.000Z').getTime(); + const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; + + async function getMobileMostUsedCharts({ + environment = ENVIRONMENT_ALL.value, + kuery = '', + serviceName, + transactionType = 'mobile', + }: { + environment?: string; + kuery?: string; + serviceName: string; + transactionType?: string; + }) { + return await apmApiClient + .readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/most_used_charts', + params: { + path: { serviceName }, + query: { + environment, + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + kuery, + transactionType, + }, + }, + }) + .then(({ body }) => body); + } + + registry.when( + 'Most used charts when data is not loaded', + { config: 'basic', archives: [] }, + () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response: MostUsedCharts = await getMobileMostUsedCharts({ serviceName: 'foo' }); + expect(response.mostUsedCharts.length).to.eql(4); + expect(response.mostUsedCharts.every((chart) => chart.options.length === 0)).to.eql(true); + }); + }); + } + ); + + registry.when('Mobile stats', { config: 'basic', archives: [] }, () => { + before(async () => { + await generateMobileData({ + synthtraceEsClient, + start, + end, + }); + }); + + after(() => synthtraceEsClient.clean()); + + describe('when data is loaded', () => { + let response: MostUsedCharts; + + before(async () => { + response = await getMobileMostUsedCharts({ + serviceName: 'synth-android', + environment: 'production', + }); + }); + + it('should get the top 5 and the other option only', () => { + const deviceOptions = response.mostUsedCharts.find( + (chart) => chart.key === 'device' + )?.options; + expect(deviceOptions?.length).to.eql(6); + expect(deviceOptions?.find((option) => option.key === 'other')).to.not.be(undefined); + }); + + it('should get network connection type object from span events', () => { + const nctOptions = response.mostUsedCharts.find( + (chart) => chart.key === 'netConnectionType' + )?.options; + expect(nctOptions?.length).to.eql(2); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts index 8e1c2dceb0b5f..6eeb50f295d6a 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts @@ -78,7 +78,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { true ); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); expect(response.body.previousPeriod.timeseries[0].y).to.eql(0); }); @@ -90,7 +90,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { true ); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); expect(response.body.previousPeriod.timeseries).to.eql([]); }); }); @@ -119,7 +119,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { true ); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(3); + expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); expect(response.body.previousPeriod.timeseries).to.eql([]); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts index a6f7d50fc717a..c52c13ea64cae 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts @@ -134,7 +134,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { kuery: `service.version:"2.3" and service.environment: "production"`, }); - expect(response.currentPeriod.sessions.value).to.eql(3); + expect(response.currentPeriod.sessions.value).to.eql(12); expect(response.currentPeriod.requests.value).to.eql(0); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts index ae3201bb79a28..cfe52ce5afe5e 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts +++ b/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts @@ -91,18 +91,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { size: 10, }); expect(response.terms).to.eql([ - { - label: 'SM-G973F', - count: 6, - }, - { - label: 'HUAWEI P2-0000', - count: 3, - }, - { - label: 'SM-G930F', - count: 3, - }, + { label: 'SM-G973F', count: 6 }, + { label: 'HUAWEI P2-0000', count: 3 }, + { label: 'Pixel 7', count: 3 }, + { label: 'Pixel 7 Pro', count: 3 }, + { label: 'Pixel 8', count: 3 }, + { label: 'SM-G930F', count: 3 }, ]); }); @@ -116,7 +110,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.terms).to.eql([ { label: '2.3', - count: 6, + count: 15, }, { label: '1.1', @@ -139,7 +133,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.terms).to.eql([ { label: '2.3', - count: 6, + count: 15, }, ]); }); diff --git a/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts b/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts index d61ce2cdc975f..499e6dec40087 100644 --- a/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts @@ -32,7 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { name: 'Latency threshold | synth-go', params: { serviceName: 'synth-go', - transactionType: '', + transactionType: undefined, windowSize: 99, windowUnit: 'y', threshold: 100, diff --git a/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts b/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts index 432ece830716c..3dc5a77fc0957 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts @@ -48,7 +48,7 @@ export default function ServiceAlerts({ getService }: FtrProviderContext) { name: `Latency threshold | ${goService}`, params: { serviceName: goService, - transactionType: '', + transactionType: undefined, windowSize: 99, windowUnit: 'y', threshold: 100, diff --git a/x-pack/test/cases_api_integration/common/lib/alerts.ts b/x-pack/test/cases_api_integration/common/lib/alerts.ts index 418c9f3c81bda..898eb1d8af981 100644 --- a/x-pack/test/cases_api_integration/common/lib/alerts.ts +++ b/x-pack/test/cases_api_integration/common/lib/alerts.ts @@ -31,12 +31,13 @@ import { postCaseReq } from './mock'; export const createSecuritySolutionAlerts = async ( supertest: SuperTest.SuperTest, - log: ToolingLog + log: ToolingLog, + numberOfSignals: number = 1 ): Promise> => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, log, rule); await waitForRuleSuccess({ supertest, log, id }); - await waitForSignalsToBePresent(supertest, log, 1, [id]); + await waitForSignalsToBePresent(supertest, log, numberOfSignals, [id]); const signals = await getSignalsByIds(supertest, log, [id]); return signals; diff --git a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts index 0a85ed21569ca..f9e8f2658db81 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts @@ -128,7 +128,7 @@ export const createCaseAndBulkCreateAttachments = async ({ export const getAttachments = (numberOfAttachments: number): BulkCreateCommentRequest => { return [...Array(numberOfAttachments)].map((_, index) => { - if (index % 0) { + if (index % 10 === 0) { return { type: CommentType.user, comment: `Test ${index + 1}`, diff --git a/x-pack/test/cases_api_integration/common/lib/api/index.ts b/x-pack/test/cases_api_integration/common/lib/api/index.ts index 3bf0c470b9ba2..88fe7a1482379 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/index.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/index.ts @@ -10,15 +10,20 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { TransportResult } from '@elastic/elasticsearch'; import type { Client } from '@elastic/elasticsearch'; import { GetResponse } from '@elastic/elasticsearch/lib/api/types'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server/src/saved_objects_index_pattern'; import type SuperTest from 'supertest'; import { CASES_INTERNAL_URL, CASES_URL, + CASE_COMMENT_SAVED_OBJECT, + CASE_CONFIGURE_SAVED_OBJECT, CASE_CONFIGURE_URL, CASE_REPORTERS_URL, + CASE_SAVED_OBJECT, CASE_STATUS_URL, CASE_TAGS_URL, + CASE_USER_ACTION_SAVED_OBJECT, } from '@kbn/cases-plugin/common/constants'; import { CasesConfigureResponse, @@ -190,7 +195,7 @@ export const deleteAllCaseItems = async (es: Client) => { export const deleteCasesUserActions = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: 'type:cases-user-actions', wait_for_completion: true, refresh: true, @@ -201,7 +206,7 @@ export const deleteCasesUserActions = async (es: Client): Promise => { export const deleteCasesByESQuery = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: 'type:cases', wait_for_completion: true, refresh: true, @@ -212,7 +217,7 @@ export const deleteCasesByESQuery = async (es: Client): Promise => { export const deleteComments = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: 'type:cases-comments', wait_for_completion: true, refresh: true, @@ -223,7 +228,7 @@ export const deleteComments = async (es: Client): Promise => { export const deleteConfiguration = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: 'type:cases-configure', wait_for_completion: true, refresh: true, @@ -234,7 +239,7 @@ export const deleteConfiguration = async (es: Client): Promise => { export const deleteMappings = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: 'type:cases-connector-mappings', wait_for_completion: true, refresh: true, @@ -289,7 +294,7 @@ export const getConnectorMappingsFromES = async ({ es }: { es: Client }) => { unknown > = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { term: { @@ -319,12 +324,12 @@ export const getConfigureSavedObjectsFromES = async ({ es }: { es: Client }) => unknown > = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { term: { type: { - value: 'cases-configure', + value: CASE_CONFIGURE_SAVED_OBJECT, }, }, }, @@ -337,17 +342,17 @@ export const getConfigureSavedObjectsFromES = async ({ es }: { es: Client }) => }; export const getCaseSavedObjectsFromES = async ({ es }: { es: Client }) => { - const configure: TransportResult< + const cases: TransportResult< estypes.SearchResponse<{ cases: ESCaseAttributes }>, unknown > = await es.search( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { query: { term: { type: { - value: 'cases', + value: CASE_SAVED_OBJECT, }, }, }, @@ -356,7 +361,53 @@ export const getCaseSavedObjectsFromES = async ({ es }: { es: Client }) => { { meta: true } ); - return configure; + return cases; +}; + +export const getCaseCommentSavedObjectsFromES = async ({ es }: { es: Client }) => { + const comments: TransportResult< + estypes.SearchResponse<{ ['cases-comments']: ESCaseAttributes }>, + unknown + > = await es.search( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + body: { + query: { + term: { + type: { + value: CASE_COMMENT_SAVED_OBJECT, + }, + }, + }, + }, + }, + { meta: true } + ); + + return comments; +}; + +export const getCaseUserActionsSavedObjectsFromES = async ({ es }: { es: Client }) => { + const userActions: TransportResult< + estypes.SearchResponse<{ ['cases-user-actions']: ESCaseAttributes }>, + unknown + > = await es.search( + { + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + body: { + query: { + term: { + type: { + value: CASE_USER_ACTION_SAVED_OBJECT, + }, + }, + }, + }, + }, + { meta: true } + ); + + return userActions; }; export const updateCase = async ({ @@ -724,7 +775,7 @@ export const getSOFromKibanaIndex = async ({ }) => { const esResponse = await es.get( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, id: `${soType}:${soId}`, }, { meta: true } diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts index e40323068b6c0..6fc840f873ba1 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/basic/index.ts @@ -39,5 +39,8 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces loadTestFile(require.resolve('../common/migrations')); + + // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces + loadTestFile(require.resolve('../common/kibana_alerting_cases_index')); }); }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index b1df6d69afe3a..5334570600f35 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -244,7 +244,7 @@ export default ({ getService }: FtrProviderContext): void => { beforeEach(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); await createSignalsIndex(supertest, log); - const signals = await createSecuritySolutionAlerts(supertest, log); + const signals = await createSecuritySolutionAlerts(supertest, log, 2); alerts = [signals.hits.hits[0], signals.hits.hits[1]]; }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts index 712ec4722d500..c0e6322f638e5 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/find_cases.ts @@ -9,6 +9,7 @@ import { v1 as uuidv1 } from 'uuid'; import expect from '@kbn/expect'; import { CASES_URL } from '@kbn/cases-plugin/common/constants'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CaseResponse, CaseSeverity, @@ -523,7 +524,7 @@ export default ({ getService }: FtrProviderContext): void => { */ const getAllCasesSortedByCreatedAtAsc = async () => { const cases = await es.search({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, body: { size: 10000, sort: [{ 'cases.created_at': { unmapped_type: 'date', order: 'asc' } }], diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts index e9ca52d903b5b..acc4da548985b 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts @@ -126,7 +126,7 @@ export default ({ getService }: FtrProviderContext): void => { beforeEach(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); await createSignalsIndex(supertest, log); - const signals = await createSecuritySolutionAlerts(supertest, log); + const signals = await createSecuritySolutionAlerts(supertest, log, 2); alerts = [signals.hits.hits[0], signals.hits.hits[1]]; }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comments.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comments.ts index 3557e50464666..281a0996c28d6 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comments.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/comments/delete_comments.ts @@ -128,7 +128,7 @@ export default ({ getService }: FtrProviderContext): void => { beforeEach(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); await createSignalsIndex(supertest, log); - const signals = await createSecuritySolutionAlerts(supertest, log); + const signals = await createSecuritySolutionAlerts(supertest, log, 2); alerts = [signals.hits.hits[0], signals.hits.hits[1]]; }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_create_attachments.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_create_attachments.ts index 6d5f33d7196e1..a26031124ca45 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_create_attachments.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_create_attachments.ts @@ -13,7 +13,6 @@ import { BulkCreateCommentRequest, CaseResponse, CaseStatuses, - CommentRequestAlertType, CommentRequestExternalReferenceSOType, CommentType, } from '@kbn/cases-plugin/common/api'; @@ -70,6 +69,7 @@ import { } from '../../../../common/lib/alerts'; import { User } from '../../../../common/lib/authentication/types'; import { SECURITY_SOLUTION_FILE_KIND } from '../../../../common/lib/constants'; +import { arraysToEqual } from '../../../../common/lib/validation'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -79,25 +79,35 @@ export default ({ getService }: FtrProviderContext): void => { const es = getService('es'); const log = getService('log'); - const validateComments = ( + const validateCommentsIgnoringOrder = ( comments: CaseResponse['comments'], attachments: BulkCreateCommentRequest ) => { - comments?.forEach((attachment, index) => { - const comment = removeServerGeneratedPropertiesFromSavedObject(attachment); + expect(comments?.length).to.eql(attachments.length); - expect(comment).to.eql({ - ...attachments[index], + const commentsWithoutGeneratedProps = []; + const attachmentsWithoutGeneratedProps = []; + + for (const comment of comments!) { + commentsWithoutGeneratedProps.push(removeServerGeneratedPropertiesFromSavedObject(comment)); + } + + for (const attachment of attachments) { + attachmentsWithoutGeneratedProps.push({ + ...attachment, created_by: defaultUser, pushed_at: null, pushed_by: null, updated_by: null, }); - }); + } + + expect(arraysToEqual(commentsWithoutGeneratedProps, attachmentsWithoutGeneratedProps)).to.be( + true + ); }; - // FAILING: https://github.com/elastic/kibana/issues/154859 - describe.skip('bulk_create_attachments', () => { + describe('bulk_create_attachments', () => { afterEach(async () => { await deleteAllCaseItems(es); }); @@ -118,7 +128,7 @@ export default ({ getService }: FtrProviderContext): void => { numberOfAttachments: 1, }); - validateComments(theCase.comments, attachments); + validateCommentsIgnoringOrder(theCase.comments, attachments); }); it('should bulk create multiple attachments', async () => { @@ -129,7 +139,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(theCase.totalComment).to.eql(attachments.length); expect(theCase.updated_by).to.eql(defaultUser); - validateComments(theCase.comments, attachments); + validateCommentsIgnoringOrder(theCase.comments, attachments); }); it('creates the correct user action', async () => { @@ -1292,6 +1302,20 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should create a new attachment without alerts attached to the case', async () => { + const alertCommentWithId3 = { + ...postCommentAlertMultipleIdsReq, + alertId: ['test-id-1', 'test-id-2', 'test-id-3'], + index: ['test-index-1', 'test-index-2', 'test-index-3'], + }; + + const alertCommentOnlyId3 = { + ...postCommentAlertMultipleIdsReq, + alertId: ['test-id-3'], + index: ['test-index-3'], + }; + + const allAttachments = [postCommentAlertMultipleIdsReq, alertCommentOnlyId3]; + const postedCase = await createCase(supertest, postCaseReq); await bulkCreateAttachments({ @@ -1304,52 +1328,71 @@ export default ({ getService }: FtrProviderContext): void => { await bulkCreateAttachments({ supertest, caseId: postedCase.id, - params: [ - { - ...postCommentAlertMultipleIdsReq, - alertId: ['test-id-1', 'test-id-2', 'test-id-3'], - index: ['test-index-1', 'test-index-2', 'test-index-3'], - }, - ], + params: [alertCommentWithId3], expectedHttpCode: 200, }); const attachments = await getAllComments({ supertest, caseId: postedCase.id }); expect(attachments.length).to.eql(2); - const secondAttachment = attachments[1] as CommentRequestAlertType; - - expect(secondAttachment.alertId).to.eql(['test-id-3']); - expect(secondAttachment.index).to.eql(['test-index-3']); + validateCommentsIgnoringOrder(attachments, allAttachments); }); it('should create a new attachment without alerts attached to the case on the same request', async () => { + const alertCommentWithId3 = { + ...postCommentAlertMultipleIdsReq, + alertId: ['test-id-1', 'test-id-2', 'test-id-3'], + index: ['test-index-1', 'test-index-2', 'test-index-3'], + }; + + const alertCommentOnlyId3 = { + ...postCommentAlertMultipleIdsReq, + alertId: ['test-id-3'], + index: ['test-index-3'], + }; + + const allAttachments = [postCommentAlertMultipleIdsReq, alertCommentOnlyId3]; + const postedCase = await createCase(supertest, postCaseReq); await bulkCreateAttachments({ supertest, caseId: postedCase.id, - params: [ - postCommentAlertMultipleIdsReq, - { - ...postCommentAlertMultipleIdsReq, - alertId: ['test-id-1', 'test-id-2', 'test-id-3'], - index: ['test-index-1', 'test-index-2', 'test-index-3'], - }, - ], + params: [postCommentAlertMultipleIdsReq, alertCommentWithId3], expectedHttpCode: 200, }); const attachments = await getAllComments({ supertest, caseId: postedCase.id }); expect(attachments.length).to.eql(2); - const secondAttachment = attachments[1] as CommentRequestAlertType; - - expect(secondAttachment.alertId).to.eql(['test-id-3']); - expect(secondAttachment.index).to.eql(['test-index-3']); + validateCommentsIgnoringOrder(attachments, allAttachments); }); it('does not remove user comments when filtering out duplicate alerts', async () => { + const alertCommentWithId3 = { + ...postCommentAlertMultipleIdsReq, + alertId: ['test-id-1', 'test-id-2', 'test-id-3'], + index: ['test-index-1', 'test-index-2', 'test-index-3'], + }; + + const alertCommentOnlyId3 = { + ...postCommentAlertMultipleIdsReq, + alertId: ['test-id-3'], + index: ['test-index-3'], + }; + + const superComment = { + ...postCommentUserReq, + comment: 'Super comment', + }; + + const allAttachments = [ + postCommentAlertMultipleIdsReq, + alertCommentOnlyId3, + postCommentUserReq, + superComment, + ]; + const postedCase = await createCase(supertest, postCaseReq); await bulkCreateAttachments({ @@ -1362,35 +1405,14 @@ export default ({ getService }: FtrProviderContext): void => { await bulkCreateAttachments({ supertest, caseId: postedCase.id, - params: [ - postCommentUserReq, - { - ...postCommentAlertMultipleIdsReq, - alertId: ['test-id-1', 'test-id-2', 'test-id-3'], - index: ['test-index-1', 'test-index-2', 'test-index-3'], - }, - postCommentUserReq, - ], + params: [superComment, alertCommentWithId3, postCommentUserReq], expectedHttpCode: 200, }); const attachments = await getAllComments({ supertest, caseId: postedCase.id }); expect(attachments.length).to.eql(4); - const firstAlert = attachments[0] as CommentRequestAlertType; - const firstUserComment = attachments[1] as CommentRequestAlertType; - const secondAlert = attachments[2] as CommentRequestAlertType; - const secondUserComment = attachments[3] as CommentRequestAlertType; - - expect(firstUserComment.type).to.eql('user'); - expect(secondUserComment.type).to.eql('user'); - expect(firstAlert.type).to.eql('alert'); - expect(secondAlert.type).to.eql('alert'); - - expect(firstAlert.alertId).to.eql(['test-id-1', 'test-id-2']); - expect(firstAlert.index).to.eql(['test-index', 'test-index-2']); - expect(secondAlert.alertId).to.eql(['test-id-3']); - expect(secondAlert.index).to.eql(['test-index-3']); + validateCommentsIgnoringOrder(attachments, allAttachments); }); }); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_delete_file_attachments.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_delete_file_attachments.ts index 2a65a0a2fbdc3..bd2a4b8f85456 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_delete_file_attachments.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/internal/bulk_delete_file_attachments.ts @@ -121,15 +121,6 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('fails to delete a file when the file does not exist', async () => { - await bulkDeleteFileAttachments({ - supertest, - caseId: postedCase.id, - fileIds: ['abc'], - expectedHttpCode: 404, - }); - }); - it('returns a 400 when the fileIds is an empty array', async () => { await bulkDeleteFileAttachments({ supertest, @@ -254,6 +245,17 @@ export default ({ getService }: FtrProviderContext): void => { await deleteAllCaseItems(es); }); + it('returns a 204 when the file does not exist', async () => { + const postedCase = await createCase(supertest, getPostCaseRequest()); + + await bulkDeleteFileAttachments({ + supertest, + caseId: postedCase.id, + fileIds: ['abc'], + expectedHttpCode: 204, + }); + }); + it('deletes a file when the owner is not formatted as an array of strings', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); @@ -412,6 +414,37 @@ export default ({ getService }: FtrProviderContext): void => { await deleteAllCaseItems(es); }); + it('deletes the attachment even when the file does not exist', async () => { + const postedCase = await createCase( + supertest, + getPostCaseRequest({ owner: 'securitySolution' }) + ); + + const caseWithAttachments = await bulkCreateAttachments({ + supertest, + caseId: postedCase.id, + params: [ + getFilesAttachmentReq({ + externalReferenceId: 'abc', + owner: 'securitySolution', + }), + ], + }); + + await bulkDeleteFileAttachments({ + supertest, + caseId: postedCase.id, + fileIds: ['abc'], + }); + + await getComment({ + supertest, + caseId: postedCase.id, + commentId: caseWithAttachments.comments![0].id, + expectedHttpCode: 404, + }); + }); + it('deletes a single file', async () => { const postedCase = await createCase( supertest, diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/kibana_alerting_cases_index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/kibana_alerting_cases_index.ts new file mode 100644 index 0000000000000..5b14a3a85aea4 --- /dev/null +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/kibana_alerting_cases_index.ts @@ -0,0 +1,204 @@ +/* + * 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 { + MAIN_SAVED_OBJECT_INDEX, + ALERTING_CASES_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; +import { + CASE_COMMENT_SAVED_OBJECT, + CASE_CONFIGURE_SAVED_OBJECT, + CASE_SAVED_OBJECT, + CASE_USER_ACTION_SAVED_OBJECT, +} from '@kbn/cases-plugin/common/constants'; +import { + deleteAllCaseItems, + getCaseCommentSavedObjectsFromES, + getCaseSavedObjectsFromES, + getCaseUserActionsSavedObjectsFromES, + getConfigureSavedObjectsFromES, +} from '../../../common/lib/api'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + describe('Kibana index: Alerting & Cases', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/cases/migrations/8.8.0'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/cases/migrations/8.8.0'); + await deleteAllCaseItems(es); + }); + + it(`migrates the ${CASE_SAVED_OBJECT} SO from the ${MAIN_SAVED_OBJECT_INDEX} index to the ${ALERTING_CASES_SAVED_OBJECT_INDEX} correctly`, async () => { + const res = await getCaseSavedObjectsFromES({ es }); + const cases = res.body.hits.hits; + + expect(cases.length).to.be(1); + expect(cases[0]._source?.cases).to.eql({ + assignees: [], + closed_at: null, + closed_by: null, + connector: { fields: [], name: 'none', type: '.none' }, + created_at: '2023-04-19T08:14:05.032Z', + created_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + description: 'cases in the new index', + duration: null, + external_service: null, + owner: 'cases', + settings: { syncAlerts: false }, + severity: 0, + status: 0, + tags: ['new index', 'test'], + title: 'cases in the new index', + total_alerts: -1, + total_comments: -1, + updated_at: '2023-04-19T08:14:18.693Z', + updated_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + }); + }); + + it(`migrates the ${CASE_COMMENT_SAVED_OBJECT} SO from the ${MAIN_SAVED_OBJECT_INDEX} index to the ${ALERTING_CASES_SAVED_OBJECT_INDEX} correctly`, async () => { + const res = await getCaseCommentSavedObjectsFromES({ es }); + const comments = res.body.hits.hits; + + expect(comments.length).to.be(1); + expect(comments[0]._source?.['cases-comments']).to.eql({ + comment: 'This is amazing!', + created_at: '2023-04-19T08:14:11.290Z', + created_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + owner: 'cases', + pushed_at: null, + pushed_by: null, + type: 'user', + updated_at: null, + updated_by: null, + }); + }); + + it(`migrates the ${CASE_USER_ACTION_SAVED_OBJECT} SO from the ${MAIN_SAVED_OBJECT_INDEX} index to the ${ALERTING_CASES_SAVED_OBJECT_INDEX} correctly`, async () => { + const res = await getCaseUserActionsSavedObjectsFromES({ es }); + const userActions = res.body.hits.hits.map( + (userAction) => userAction._source?.['cases-user-actions'] + ); + + expect(userActions.length).to.be(3); + expect(userActions).to.eql([ + { + action: 'create', + created_at: '2023-04-19T08:14:05.052Z', + created_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + owner: 'cases', + payload: { + assignees: [], + connector: { + fields: null, + name: 'none', + type: '.none', + }, + description: 'cases in the new index', + owner: 'cases', + settings: { + syncAlerts: false, + }, + severity: 'low', + status: 'open', + tags: [], + title: 'cases in the new index', + }, + type: 'create_case', + }, + { + action: 'create', + created_at: '2023-04-19T08:14:11.318Z', + created_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + owner: 'cases', + payload: { + comment: { + comment: 'This is amazing!', + owner: 'cases', + type: 'user', + }, + }, + type: 'comment', + }, + { + action: 'add', + created_at: '2023-04-19T08:14:18.719Z', + created_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + owner: 'cases', + payload: { + tags: ['new index', 'test'], + }, + type: 'tags', + }, + ]); + }); + + it(`migrates the ${CASE_CONFIGURE_SAVED_OBJECT} SO from the ${MAIN_SAVED_OBJECT_INDEX} index to the ${ALERTING_CASES_SAVED_OBJECT_INDEX} correctly`, async () => { + const res = await getConfigureSavedObjectsFromES({ es }); + const configure = res.body.hits.hits; + + expect(configure.length).to.be(1); + expect(configure[0]._source?.['cases-configure']).to.eql({ + closure_type: 'close-by-user', + connector: { fields: [], name: 'none', type: '.none' }, + created_at: '2023-04-19T08:14:42.212Z', + created_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + owner: 'cases', + updated_at: '2023-04-19T08:14:44.202Z', + updated_by: { + email: null, + full_name: null, + profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + username: 'elastic', + }, + }); + }); + }); +}; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts index 9cf0aaa18be46..9e68a8379b702 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/index.ts @@ -49,5 +49,8 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces loadTestFile(require.resolve('../common/migrations')); + + // NOTE: These need to be at the end because they could delete the .kibana index and inadvertently remove the users and spaces + loadTestFile(require.resolve('../common/kibana_alerting_cases_index')); }); }; diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts new file mode 100644 index 0000000000000..635cf88965e5f --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts @@ -0,0 +1,134 @@ +/* + * 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 type { FtrProviderContext } from '../ftr_provider_context'; + +// Defined in CSP plugin +const LATEST_FINDINGS_INDEX = 'logs-cloud_security_posture.findings_latest-default'; + +export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + const retry = getService('retry'); + const es = getService('es'); + const supertest = getService('supertest'); + const log = getService('log'); + + /** + * required before indexing findings + */ + const waitForPluginInitialized = (): Promise => + retry.try(async () => { + log.debug('Check CSP plugin is initialized'); + const response = await supertest + .get('/internal/cloud_security_posture/status?check=init') + .expect(200); + expect(response.body).to.eql({ isPluginInitialized: true }); + log.debug('CSP plugin is initialized'); + }); + + const index = { + remove: () => es.indices.delete({ index: LATEST_FINDINGS_INDEX, ignore_unavailable: true }), + add: async (findingsMock: T[]) => { + await Promise.all( + findingsMock.map((finding) => + es.index({ + index: LATEST_FINDINGS_INDEX, + body: finding, + }) + ) + ); + }, + }; + + const dashboard = { + getDashboardPageHeader: () => testSubjects.find('cloud-posture-dashboard-page-header'), + + getDashboardTabs: async () => { + const dashboardPageHeader = await dashboard.getDashboardPageHeader(); + return await dashboardPageHeader.findByClassName('euiTabs'); + }, + + getCloudTab: async () => { + const tabs = await dashboard.getDashboardTabs(); + return await tabs.findByXpath(`//span[text()="Cloud"]`); + }, + + getKubernetesTab: async () => { + const tabs = await dashboard.getDashboardTabs(); + return await tabs.findByXpath(`//span[text()="Kubernetes"]`); + }, + + clickTab: async (tab: 'Cloud' | 'Kubernetes') => { + if (tab === 'Cloud') { + const cloudTab = await dashboard.getCloudTab(); + await cloudTab.click(); + } + if (tab === 'Kubernetes') { + const k8sTab = await dashboard.getKubernetesTab(); + await k8sTab.click(); + } + }, + + getIntegrationDashboardContainer: () => testSubjects.find('dashboard-container'), + + // Cloud Dashboard + + getCloudDashboard: async () => { + await dashboard.clickTab('Cloud'); + return await testSubjects.find('cloud-dashboard-container'); + }, + + getCloudSummarySection: async () => { + await dashboard.getCloudDashboard(); + return await testSubjects.find('dashboard-summary-section'); + }, + + getCloudComplianceScore: async () => { + await dashboard.getCloudSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + + // Kubernetes Dashboard + + getKubernetesDashboard: async () => { + await dashboard.clickTab('Kubernetes'); + return await testSubjects.find('kubernetes-dashboard-container'); + }, + + getKubernetesSummarySection: async () => { + await dashboard.getKubernetesDashboard(); + return await testSubjects.find('dashboard-summary-section'); + }, + + getKubernetesComplianceScore: async () => { + await dashboard.getKubernetesSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + + getKubernetesComplianceScore2: async () => { + // await dashboard.getKubernetesSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + }; + + const navigateToComplianceDashboardPage = async () => { + await PageObjects.common.navigateToUrl( + 'securitySolution', // Defined in Security Solution plugin + 'cloud_security_posture/dashboard', + { shouldUseHashForSubUrl: false } + ); + }; + + return { + waitForPluginInitialized, + navigateToComplianceDashboardPage, + dashboard, + index, + }; +} diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts index e5738873edc51..26aacd8cca997 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts @@ -7,8 +7,10 @@ import { pageObjects as xpackFunctionalPageObjects } from '../../functional/page_objects'; import { FindingsPageProvider } from './findings_page'; +import { CspDashboardPageProvider } from './csp_dashboard_page'; export const pageObjects = { ...xpackFunctionalPageObjects, findings: FindingsPageProvider, + cloudPostureDashboard: CspDashboardPageProvider, }; diff --git a/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts b/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts new file mode 100644 index 0000000000000..cb4635899f5c3 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.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 expect from '@kbn/expect'; +import Chance from 'chance'; +import type { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const retry = getService('retry'); + const pageObjects = getPageObjects(['common', 'cloudPostureDashboard']); + const chance = new Chance(); + + const data = [ + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `kubelet`, sub_type: 'lower case sub type' }, + result: { evaluation: 'failed' }, + rule: { + name: 'Upper case rule name', + section: 'Upper case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + }, + }, + cluster_id: 'Upper case cluster id', + }, + ]; + + describe('Cloud Posture Dashboard Page', () => { + let cspDashboard: typeof pageObjects.cloudPostureDashboard; + let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard; + + before(async () => { + cspDashboard = pageObjects.cloudPostureDashboard; + dashboard = pageObjects.cloudPostureDashboard.dashboard; + await cspDashboard.waitForPluginInitialized(); + + await cspDashboard.index.add(data); + await cspDashboard.navigateToComplianceDashboardPage(); + await retry.waitFor( + 'Cloud posture integration dashboard to be displayed', + async () => !!dashboard.getIntegrationDashboardContainer() + ); + }); + + after(async () => { + await cspDashboard.index.remove(); + }); + + describe('Kubernetes Dashboard', () => { + it('displays accurate summary compliance score', async () => { + const scoreElement = await dashboard.getKubernetesComplianceScore(); + + expect((await scoreElement.getVisibleText()) === '0%').to.be(true); + }); + }); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/pages/index.ts b/x-pack/test/cloud_security_posture_functional/pages/index.ts index 80e96b8b17ce9..7566afda0501a 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/index.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/index.ts @@ -11,5 +11,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Cloud Security Posture', function () { loadTestFile(require.resolve('./findings')); + loadTestFile(require.resolve('./compliance_dashboard')); }); } diff --git a/x-pack/test/common/lib/test_data_loader.ts b/x-pack/test/common/lib/test_data_loader.ts index b379d4b61e3ba..d8f7453c89ddb 100644 --- a/x-pack/test/common/lib/test_data_loader.ts +++ b/x-pack/test/common/lib/test_data_loader.ts @@ -6,6 +6,7 @@ */ import { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import Fs from 'fs/promises'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -176,7 +177,7 @@ export function getTestDataLoader({ getService }: Pick { await es.deleteByQuery({ - index: '.kibana', + index: ALL_SAVED_OBJECT_INDICES, wait_for_completion: true, body: { // @ts-expect-error diff --git a/x-pack/test/defend_workflows_cypress/endpoint_config.ts b/x-pack/test/defend_workflows_cypress/endpoint_config.ts index f1ea9a9c81a12..e6191cea7074b 100644 --- a/x-pack/test/defend_workflows_cypress/endpoint_config.ts +++ b/x-pack/test/defend_workflows_cypress/endpoint_config.ts @@ -8,6 +8,7 @@ import { getLocalhostRealIp } from '@kbn/security-solution-plugin/scripts/endpoint/common/localhost_services'; import { FtrConfigProviderContext } from '@kbn/test'; +import { ExperimentalFeatures } from '@kbn/security-solution-plugin/common/experimental_features'; import { DefendWorkflowsCypressEndpointTestRunner } from './runner'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { @@ -15,6 +16,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const config = defendWorkflowsCypressConfig.getAll(); const hostIp = getLocalhostRealIp(); + const enabledFeatureFlags: Array = ['responseActionExecuteEnabled']; + return { ...config, kbnTestServer: { @@ -27,6 +30,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { )}`, // set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts '--xpack.securitySolution.packagerTaskInterval=5s', + `--xpack.securitySolution.enableExperimental=${JSON.stringify(enabledFeatureFlags)}`, ], }, testRunner: DefendWorkflowsCypressEndpointTestRunner, diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts index 62fa4d3786db6..2c6ae644d911c 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts @@ -99,7 +99,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'query', threat: [], - throttle: 'no_actions', exceptions_list: [], version: 1, revision: 0, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts index 4346087684fd4..e6adaa069b497 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts @@ -7,7 +7,12 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { ROLES } from '@kbn/security-solution-plugin/common/test'; @@ -32,8 +37,15 @@ import { waitForSignalsToBePresent, getThresholdRuleForSignalTesting, waitForRulePartialFailure, + createRule, } from '../../utils'; import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -200,7 +212,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'query', threat: [], - throttle: 'no_actions', exceptions_list: [], version: 1, }; @@ -566,5 +577,139 @@ export default ({ getService }: FtrProviderContext) => { expect(rule?.execution_summary?.last_execution.status).to.eql('partial failure'); }); }); + + describe('per-action frequencies', () => { + const createSingleRule = async (rule: RuleCreateProps) => { + const createdRule = await createRule(supertest, log, rule); + createdRule.actions = removeUUIDFromActions(createdRule.actions); + return createdRule; + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithFrequencies; + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts index 6d0e79975bfd6..63d7e18367dc6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts @@ -10,7 +10,11 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_CREATE, DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; +import { RuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -27,6 +31,12 @@ import { removeServerGeneratedPropertiesIncludingRuleId, waitForRuleSuccess, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -276,6 +286,141 @@ export default ({ getService }: FtrProviderContext): void => { }, ]); }); + + describe('per-action frequencies', () => { + const bulkCreateSingleRule = async (rule: RuleCreateProps) => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_BULK_CREATE) + .set('kbn-xsrf', 'true') + .send([rule]) + .expect(200); + + const createdRule = body[0]; + createdRule.actions = removeUUIDFromActions(createdRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(createdRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(createdRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithFrequencies; + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(createdRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts index a394bef16cd22..e029c13aff8e5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts @@ -176,6 +176,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts index fa83ceb9a19b1..8e0ad07a782c3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts @@ -309,6 +309,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -357,6 +358,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); expect(body[1].actions).to.eql([ @@ -368,6 +370,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts index c9cb430e144ba..72fb3631ac0c3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts @@ -8,6 +8,7 @@ import expect from 'expect'; import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { RuleResponse } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { binaryToString, @@ -208,10 +209,17 @@ export default ({ getService }: FtrProviderContext): void => { const outputRule1: ReturnType = { ...getSimpleRuleOutput('rule-1'), actions: [ - { ...action1, uuid: firstRule.actions[0].uuid }, - { ...action2, uuid: firstRule.actions[1].uuid }, + { + ...action1, + uuid: firstRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + ...action2, + uuid: firstRule.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, ], - throttle: 'rule', }; expect(firstRule).toEqual(outputRule1); }); @@ -258,13 +266,23 @@ export default ({ getService }: FtrProviderContext): void => { const outputRule1: ReturnType = { ...getSimpleRuleOutput('rule-2'), - actions: [{ ...action, uuid: firstRule.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: firstRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; const outputRule2: ReturnType = { ...getSimpleRuleOutput('rule-1'), - actions: [{ ...action, uuid: secondRule.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: secondRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; expect(firstRule).toEqual(outputRule1); expect(secondRule).toEqual(outputRule2); @@ -437,9 +455,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const firstRule = removeServerGeneratedProperties(firstRuleParsed); @@ -514,6 +532,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -523,9 +542,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const firstRule = removeServerGeneratedProperties(firstRuleParsed); @@ -631,6 +650,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -640,9 +660,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const outputRule2: ReturnType = { @@ -656,6 +676,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -665,9 +686,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const secondRuleParsed = JSON.parse(body.toString().split(/\n/)[1]); @@ -682,7 +703,8 @@ export default ({ getService }: FtrProviderContext): void => { }); }; -function expectToMatchRuleSchema(obj: unknown): void { +function expectToMatchRuleSchema(obj: RuleResponse): void { + expect(obj.throttle).toBeUndefined(); expect(obj).toEqual({ id: expect.any(String), rule_id: expect.any(String), @@ -718,7 +740,6 @@ function expectToMatchRuleSchema(obj: unknown): void { language: expect.any(String), index: expect.arrayContaining([]), query: expect.any(String), - throttle: expect.any(String), actions: expect.arrayContaining([]), }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts index 7cd260697be72..348400580edd2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts @@ -126,8 +126,13 @@ export default ({ getService }: FtrProviderContext): void => { const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: body.data[0].actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: body.data[0].actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; body.data = [removeServerGeneratedProperties(body.data[0])]; @@ -171,8 +176,13 @@ export default ({ getService }: FtrProviderContext): void => { const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: body.data[0].actions[0].uuid }], - throttle: '1h', // <-- throttle makes this a scheduled action + actions: [ + { + ...action, + uuid: body.data[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ], }; body.data = [removeServerGeneratedProperties(body.data[0])]; @@ -239,9 +249,9 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: hookAction.actionTypeId, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; body.data = [removeServerGeneratedProperties(body.data[0])]; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/fleet_integration.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/fleet_integration.ts index 2aaa0778ccf5e..e48530ad16513 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/fleet_integration.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/fleet_integration.ts @@ -5,6 +5,7 @@ * 2.0. */ import expect from 'expect'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { deleteAllRules, @@ -46,6 +47,20 @@ export default ({ getService }: FtrProviderContext): void => { overrideExistingPackage: true, }); + // Before we proceed, we need to refresh saved object indices. This comment will explain why. + // At the previous step we installed the Fleet package with prebuilt detection rules. + // Prebuilt rules are assets that Fleet indexes as saved objects of a certain type. + // Fleet does this via a savedObjectsClient.import() call with explicit `refresh: false`. + // So, despite of the fact that the endpoint waits until the prebuilt rule assets will be + // successfully indexed, it doesn't wait until they become "visible" for subsequent read + // operations. Which is what we do next: we read these SOs in getPrebuiltRulesAndTimelinesStatus(). + // Now, the time left until the next refresh can be anything from 0 to the default value, and + // it depends on the time when savedObjectsClient.import() call happens relative to the time of + // the next refresh. Also, probably the refresh time can be delayed when ES is under load? + // Anyway, here we have a race condition between a write and subsequent read operation, and to + // fix it deterministically we have to refresh saved object indices and wait until it's done. + await es.indices.refresh({ index: ALL_SAVED_OBJECT_INDICES }); + // Verify that status is updated after package installation const statusAfterPackageInstallation = await getPrebuiltRulesAndTimelinesStatus(supertest); expect(statusAfterPackageInstallation.rules_installed).toBe(0); @@ -57,6 +72,15 @@ export default ({ getService }: FtrProviderContext): void => { expect(response.rules_installed).toBe(statusAfterPackageInstallation.rules_not_installed); expect(response.rules_updated).toBe(0); + // Similar to the previous refresh, we need to do it again between the two operations: + // - previous write operation: install prebuilt rules and timelines + // - subsequent read operation: get prebuilt rules and timelines status + // You may ask why? I'm not sure, probably because the write operation can install the Fleet + // package under certain circumstances, and it all works with `refresh: false` again. + // Anyway, there were flaky runs failing specifically at one of the next assertions, + // which means some kind of the same race condition we have here too. + await es.indices.refresh({ index: ALL_SAVED_OBJECT_INDICES }); + // Verify that status is updated after rules installation const statusAfterRuleInstallation = await getPrebuiltRulesAndTimelinesStatus(supertest); expect(statusAfterRuleInstallation.rules_installed).toBe(response.rules_installed); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts index 6ba7871b1dbf5..9fd2542664f3b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts @@ -75,8 +75,8 @@ export default ({ getService }: FtrProviderContext) => { expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); expect(ruleSO?.alert.actions).to.eql([]); - expect(ruleSO?.alert.throttle).to.eql('no_actions'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); }); it('migrates legacy actions for rule with action run on every run', async () => { @@ -122,6 +122,7 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, { actionRef: 'action_1', @@ -133,10 +134,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('rule'); - expect(ruleSO?.alert.notifyWhen).to.eql('onActiveAlert'); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -197,6 +199,7 @@ export default ({ getService }: FtrProviderContext) => { actionRef: 'action_0', group: 'default', uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { actionTypeId: '.slack', @@ -206,10 +209,11 @@ export default ({ getService }: FtrProviderContext) => { actionRef: 'action_1', group: 'default', uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('1h'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -269,10 +273,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('1d'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -327,10 +332,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '7d', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('7d'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts index 04586999aa163..d43ecaff6823e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -32,7 +33,7 @@ export default ({ getService }: FtrProviderContext): void => { references: [{}]; }>( { - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, id: 'siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3', }, { @@ -79,7 +80,7 @@ export default ({ getService }: FtrProviderContext): void => { }; }>( { - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, id: 'siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3', }, { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts index 98b64726bbaca..ae434a50df98f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -24,7 +30,14 @@ import { getSimpleMlRule, createLegacyRuleAction, getLegacyActionSO, + getSimpleRuleWithoutRuleId, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -377,9 +390,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; - outputRule.throttle = '1h'; outputRule.revision = 1; expect(bodyToCompare).to.eql(outputRule); @@ -414,5 +427,168 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('patch per-action frequencies', () => { + const patchSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + const { body: patchedRule } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, throttle, actions }) + .expect(200); + + patchedRule.actions = removeUUIDFromActions(patchedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(patchedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts index 1a26b17bca42e..2d80d17f9100e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts @@ -207,9 +207,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; - outputRule.throttle = '1h'; outputRule.revision = 1; expect(bodyToCompare).to.eql(outputRule); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index c77ac5f142f92..185d820351459 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -9,7 +9,6 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_ACTION, DETECTION_ENGINE_RULES_URL, - NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; import type { RuleResponse } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; @@ -17,7 +16,10 @@ import { BulkActionType, BulkActionEditType, } from '@kbn/security-solution-plugin/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; +import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { binaryToString, createLegacyRuleAction, @@ -35,6 +37,7 @@ import { removeServerGeneratedProperties, waitForRuleSuccess, } from '../../utils'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -168,7 +171,6 @@ export default ({ getService }: FtrProviderContext): void => { const rule = removeServerGeneratedProperties(JSON.parse(ruleJson)); expect(rule).to.eql({ ...getSimpleRuleOutput(), - throttle: 'rule', actions: [ { action_type_id: '.webhook', @@ -178,6 +180,7 @@ export default ({ getService }: FtrProviderContext): void => { body: '{"test":"a default action"}', }, uuid: rule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }); @@ -331,6 +334,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: ruleBody.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); // we want to ensure rule is executing successfully, to prevent any AAD issues related to partial update of rule SO @@ -403,6 +407,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: ruleBody.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -416,15 +421,225 @@ export default ({ getService }: FtrProviderContext): void => { .send({ query: '', action: BulkActionType.duplicate, - duplicate: { include_exceptions: false }, + duplicate: { include_exceptions: false, include_expired_exceptions: false }, + }) + .expect(200); + + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); + + // Check that the duplicated rule is returned with the response + expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + + // Check that the updates have been persisted + const { body: rulesResponse } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}/_find`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(rulesResponse.total).to.eql(2); + }); + + it('should duplicate rules with exceptions - expired exceptions included', async () => { + await deleteAllExceptions(supertest, log); + + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + const { body: exceptionList } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + // create an exception list item + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const ruleId = 'ruleId'; + const ruleToDuplicate = { + ...getSimpleRule(ruleId), + exceptions_list: [ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + ], + }; + const newRule = await createRule(supertest, log, ruleToDuplicate); + + // add an exception item to the rule + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/${newRule.id}/exceptions`) + .set('kbn-xsrf', 'true') + .send({ + items: [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + expire_time: expiredDate, + }, + ], + }) + .expect(200); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkActionType.duplicate, + duplicate: { include_exceptions: true, include_expired_exceptions: true }, + }) + .expect(200); + + const { body: foundItems } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${body.attributes.results.created[0].exceptions_list[1].list_id}` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Item should have been duplicated, even if expired + expect(foundItems.total).to.eql(1); + + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); + + // Check that the duplicated rule is returned with the response + expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + + // Check that the exceptions are duplicated + expect(body.attributes.results.created[0].exceptions_list).to.eql([ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + { + id: body.attributes.results.created[0].exceptions_list[1].id, + list_id: body.attributes.results.created[0].exceptions_list[1].list_id, + namespace_type: 'single', + type: 'rule_default', + }, + ]); + + // Check that the updates have been persisted + const { body: rulesResponse } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}/_find`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(rulesResponse.total).to.eql(2); + }); + + it('should duplicate rules with exceptions - expired exceptions excluded', async () => { + await deleteAllExceptions(supertest, log); + + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + const { body: exceptionList } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + // create an exception list item + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const ruleId = 'ruleId'; + const ruleToDuplicate = { + ...getSimpleRule(ruleId), + exceptions_list: [ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + ], + }; + const newRule = await createRule(supertest, log, ruleToDuplicate); + + // add an exception item to the rule + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/${newRule.id}/exceptions`) + .set('kbn-xsrf', 'true') + .send({ + items: [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + expire_time: expiredDate, + }, + ], + }) + .expect(200); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkActionType.duplicate, + duplicate: { include_exceptions: true, include_expired_exceptions: false }, }) .expect(200); + const { body: foundItems } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${body.attributes.results.created[0].exceptions_list[1].list_id}` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Item should NOT have been duplicated, since it is expired + expect(foundItems.total).to.eql(0); + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); // Check that the duplicated rule is returned with the response expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + // Check that the exceptions are duplicted + expect(body.attributes.results.created[0].exceptions_list).to.eql([ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + { + id: body.attributes.results.created[0].exceptions_list[1].id, + list_id: body.attributes.results.created[0].exceptions_list[1].list_id, + namespace_type: 'single', + type: 'rule_default', + }, + ]); + // Check that the updates have been persisted const { body: rulesResponse } = await supertest .get(`${DETECTION_ENGINE_RULES_URL}/_find`) @@ -462,7 +677,7 @@ export default ({ getService }: FtrProviderContext): void => { .send({ query: '', action: BulkActionType.duplicate, - duplicate: { include_exceptions: false }, + duplicate: { include_exceptions: false, include_expired_exceptions: false }, }) .expect(200); @@ -491,6 +706,7 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, ...(uuid ? { uuid } : {}), + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -1120,6 +1336,7 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: setTagsRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -1404,6 +1621,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1461,6 +1679,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1572,6 +1791,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1624,6 +1844,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1678,12 +1899,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const expectedRuleActions = [ - { ...defaultRuleAction, uuid: body.attributes.results.updated[0].actions[0].uuid }, + { + ...defaultRuleAction, + uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, { ...webHookActionMock, id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1746,12 +1972,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const expectedRuleActions = [ - { ...defaultRuleAction, uuid: body.attributes.results.updated[0].actions[0].uuid }, + { + ...defaultRuleAction, + uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, { ...slackConnectorMockProps, id: slackConnector.id, action_type_id: '.slack', uuid: body.attributes.results.updated[0].actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1800,20 +2031,22 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].actions).to.eql([ - { ...defaultRuleAction, uuid: createdRule.actions[0].uuid }, - ]); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: readRule } = await fetchRule(ruleId).expect(200); expect(readRule.actions).to.eql([ - { ...defaultRuleAction, uuid: createdRule.actions[0].uuid }, + { + ...defaultRuleAction, + uuid: createdRule.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, ]); }); - it('should change throttle if actions list in payload is empty', async () => { + it('should not change throttle if actions list in payload is empty', async () => { // create a new connector const webHookConnector = await createWebHookConnector(); @@ -1849,13 +2082,14 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].throttle).to.be('1h'); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: readRule } = await fetchRule(ruleId).expect(200); - expect(readRule.throttle).to.eql('1h'); + expect(readRule.throttle).to.eql(undefined); + expect(readRule.actions).to.eql(createdRule.actions); }); }); @@ -1903,6 +2137,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: editedRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); // version of prebuilt rule should not change @@ -1917,6 +2152,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: readRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); expect(prebuiltRule.version).to.be(readRule.version); @@ -1993,7 +2229,7 @@ export default ({ getService }: FtrProviderContext): void => { }, ]; casesForEmptyActions.forEach(({ payloadThrottle }) => { - it(`throttle is set to NOTIFICATION_THROTTLE_NO_ACTIONS, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { + it(`should not update throttle, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { const ruleId = 'ruleId'; const createdRule = await createRule(supertest, log, { ...getSimpleRule(ruleId), @@ -2016,34 +2252,32 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].throttle).to.eql( - NOTIFICATION_THROTTLE_NO_ACTIONS - ); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: rule } = await fetchRule(ruleId).expect(200); - expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(rule.throttle).to.eql(undefined); }); }); const casesForNonEmptyActions = [ { payloadThrottle: NOTIFICATION_THROTTLE_RULE, - expectedThrottle: NOTIFICATION_THROTTLE_RULE, + expectedThrottle: undefined, }, { payloadThrottle: '1h', - expectedThrottle: '1h', + expectedThrottle: undefined, }, { payloadThrottle: '1d', - expectedThrottle: '1d', + expectedThrottle: undefined, }, { payloadThrottle: '7d', - expectedThrottle: '7d', + expectedThrottle: undefined, }, ]; [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].forEach( @@ -2081,10 +2315,23 @@ export default ({ getService }: FtrProviderContext): void => { // Check that the updated rule is returned with the response expect(body.attributes.results.updated[0].throttle).to.eql(expectedThrottle); + const expectedActions = body.attributes.results.updated[0].actions.map( + (action: any) => ({ + ...action, + frequency: { + summary: true, + throttle: payloadThrottle !== 'rule' ? payloadThrottle : null, + notifyWhen: + payloadThrottle !== 'rule' ? 'onThrottleInterval' : 'onActiveAlert', + }, + }) + ); + // Check that the updates have been persisted const { body: rule } = await fetchRule(ruleId).expect(200); expect(rule.throttle).to.eql(expectedThrottle); + expect(rule.actions).to.eql(expectedActions); }); }); } @@ -2097,11 +2344,11 @@ export default ({ getService }: FtrProviderContext): void => { const cases = [ { payload: { throttle: '1d' }, - expected: { notifyWhen: 'onThrottleInterval' }, + expected: { notifyWhen: null }, }, { payload: { throttle: NOTIFICATION_THROTTLE_RULE }, - expected: { notifyWhen: 'onActiveAlert' }, + expected: { notifyWhen: null }, }, ]; cases.forEach(({ payload, expected }) => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts index d2162e02d443e..31904342e294f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts @@ -135,8 +135,13 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeServerGeneratedProperties(body); const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: bodyToCompare.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; expect(bodyToCompare).to.eql(ruleWithActions); }); @@ -174,8 +179,13 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeServerGeneratedProperties(body); const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: bodyToCompare.actions[0].uuid }], - throttle: '1h', // <-- throttle makes this a scheduled action + actions: [ + { + ...action, + uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ], }; expect(bodyToCompare).to.eql(ruleWithActions); }); @@ -236,9 +246,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: hookAction.actionTypeId, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; expect(bodyToCompare).to.eql(ruleWithActions); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/resolve_read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/resolve_read_rules.ts index 64b87eea43b38..5839a03cf5c1a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/resolve_read_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/resolve_read_rules.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, deleteAllRules, deleteSignalsIndex } from '../../utils'; @@ -60,7 +61,7 @@ export default ({ getService }: FtrProviderContext) => { // and we won't have a conflict await es.index({ id: 'alert:90e3ca0e-71f7-513a-b60a-ac678efd8887', - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, refresh: true, body: { alert: { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts index 760e64cc6f102..4b218d07b7613 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts @@ -56,7 +56,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('creating a rule', () => { - it('When creating a new action and attaching it to a rule, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating a new action and attaching it to a rule, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -66,13 +66,19 @@ export default ({ getService }: FtrProviderContext) => { const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: null, + notify_when: 'onActiveAlert', + }); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, the rule should have its kibana alerting "mute_all" set to "true" and notify_when set to null', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: NOTIFICATION_THROTTLE_NO_ACTIONS, @@ -81,11 +87,11 @@ export default ({ getService }: FtrProviderContext) => { const { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); - expect(muteAll).to.eql(true); + expect(muteAll).to.eql(false); expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and with actions set, the rule should have its kibana alerting "mute_all" set to "true" and notify_when set to null', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and with actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -101,11 +107,11 @@ export default ({ getService }: FtrProviderContext) => { const { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); - expect(muteAll).to.eql(true); + expect(muteAll).to.eql(false); expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: NOTIFICATION_THROTTLE_RULE, @@ -115,7 +121,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -125,10 +131,10 @@ export default ({ getService }: FtrProviderContext) => { throttle: NOTIFICATION_THROTTLE_RULE, }; const rule = await createRule(supertest, log, ruleWithThrottle); - expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(rule.throttle).to.eql(undefined); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -142,13 +148,19 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: null, + notify_when: 'onActiveAlert', + }); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "1h" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onThrottleInterval"', async () => { + it('When creating throttle with "1h" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: '1h', @@ -158,10 +170,10 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onThrottleInterval'); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "1h" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onThrottleInterval"', async () => { + it('When creating throttle with "1h" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -175,10 +187,16 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onThrottleInterval'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: '1h', + notify_when: 'onThrottleInterval', + }); + expect(notifyWhen).to.eql(null); }); }); @@ -193,7 +211,7 @@ export default ({ getService }: FtrProviderContext) => { const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(readRule.throttle).to.eql(undefined); }); it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, we should return "NOTIFICATION_THROTTLE_NO_ACTIONS" when doing a read', async () => { @@ -203,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -214,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); it('When creating a new action and attaching it to a rule, if we change the alert to a "muteAll" through the kibana alerting API, we should get back "NOTIFICATION_THROTTLE_NO_ACTIONS" ', async () => { @@ -232,7 +250,7 @@ export default ({ getService }: FtrProviderContext) => { .send() .expect(204); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); }); @@ -249,7 +267,7 @@ export default ({ getService }: FtrProviderContext) => { await createRule(supertest, log, ruleWithWebHookAction); ruleWithWebHookAction.name = 'some other name'; const updated = await updateRule(supertest, log, ruleWithWebHookAction); - expect(updated.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(updated.throttle).to.eql(undefined); }); it('will not change the "muteAll" or "notifyWhen" if we update some part of the rule', async () => { @@ -268,7 +286,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${updated.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -284,7 +302,7 @@ export default ({ getService }: FtrProviderContext) => { await createRule(supertest, log, ruleWithWebHookAction); ruleWithWebHookAction.actions = []; const updated = await updateRule(supertest, log, ruleWithWebHookAction); - expect(updated.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(updated.throttle).to.eql(undefined); }); }); @@ -306,7 +324,7 @@ export default ({ getService }: FtrProviderContext) => { .send({ rule_id: rule.rule_id, name: 'some other name' }) .expect(200); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(readRule.throttle).to.eql(undefined); }); it('will not change the "muteAll" or "notifyWhen" if we patch part of the rule', async () => { @@ -329,7 +347,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -350,7 +368,7 @@ export default ({ getService }: FtrProviderContext) => { .send({ rule_id: rule.rule_id, actions: [] }) .expect(200); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index 8396f72a9bb08..82b23e8b381d1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -28,7 +34,14 @@ import { createLegacyRuleAction, getThresholdRuleForSignalTesting, getLegacyActionSO, + getSimpleRuleWithoutRuleId, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -185,8 +198,6 @@ export default ({ getService }: FtrProviderContext) => { outputRule.revision = 1; // Expect an empty array outputRule.actions = []; - // Expect "no_actions" - outputRule.throttle = 'no_actions'; const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body); expect(bodyToCompare).to.eql(outputRule); }); @@ -221,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { id: connector.body.id, action_type_id: connector.body.connector_type_id, params: { - message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, }; // update a simple rule's name @@ -229,7 +240,6 @@ export default ({ getService }: FtrProviderContext) => { updatedRule.rule_id = createRuleBody.rule_id; updatedRule.name = 'some other name'; updatedRule.actions = [action1]; - updatedRule.throttle = '1d'; delete updatedRule.id; const { body } = await supertest @@ -249,13 +259,12 @@ export default ({ getService }: FtrProviderContext) => { group: 'default', id: connector.body.id, params: { - message: - 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions![0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]; - outputRule.throttle = '1d'; expect(bodyToCompare).to.eql(outputRule); @@ -662,6 +671,176 @@ export default ({ getService }: FtrProviderContext) => { expect(outputRule.saved_id).to.be(undefined); }); }); + + describe('per-action frequencies', () => { + const updateSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + // update a simple rule's `throttle` and `actions` + const ruleToUpdate = getSimpleRuleUpdate(); + ruleToUpdate.throttle = throttle; + ruleToUpdate.actions = actions; + ruleToUpdate.id = ruleId; + delete ruleToUpdate.rule_id; + + const { body: updatedRule } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleToUpdate) + .expect(200); + + updatedRule.actions = removeUUIDFromActions(updatedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(updatedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts index 0c8dcacd0ebbf..5da2e8f1c10cd 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts @@ -11,8 +11,12 @@ import { RuleResponse } from '@kbn/security-solution-plugin/common/detection_eng import { DETECTION_ENGINE_RULES_URL, DETECTION_ENGINE_RULES_BULK_UPDATE, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + NOTIFICATION_DEFAULT_FREQUENCY, } from '@kbn/security-solution-plugin/common/constants'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -25,7 +29,16 @@ import { getSimpleRule, createLegacyRuleAction, getLegacyActionSO, + removeServerGeneratedPropertiesIncludingRuleId, + getSimpleRuleWithoutRuleId, + getSimpleRuleOutputWithoutRuleId, } from '../../utils'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -138,7 +151,7 @@ export default ({ getService }: FtrProviderContext) => { id: connector.body.id, action_type_id: connector.body.connector_type_id, params: { - message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, }; const [rule1, rule2] = await Promise.all([ @@ -160,12 +173,10 @@ export default ({ getService }: FtrProviderContext) => { const updatedRule1 = getSimpleRuleUpdate('rule-1'); updatedRule1.name = 'some other name'; updatedRule1.actions = [action1]; - updatedRule1.throttle = '1d'; const updatedRule2 = getSimpleRuleUpdate('rule-2'); updatedRule2.name = 'some other name'; updatedRule2.actions = [action1]; - updatedRule2.throttle = '1d'; // update both rule names const { body }: { body: RuleResponse[] } = await supertest @@ -189,13 +200,12 @@ export default ({ getService }: FtrProviderContext) => { group: 'default', id: connector.body.id, params: { - message: - 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]; - outputRule.throttle = '1d'; expect(bodyToCompare).to.eql(outputRule); }); @@ -247,7 +257,6 @@ export default ({ getService }: FtrProviderContext) => { outputRule.name = 'some other name'; outputRule.revision = 1; outputRule.actions = []; - outputRule.throttle = 'no_actions'; const bodyToCompare = removeServerGeneratedProperties(response); expect(bodyToCompare).to.eql(outputRule); }); @@ -603,5 +612,177 @@ export default ({ getService }: FtrProviderContext) => { ]); }); }); + + describe('bulk per-action frequencies', () => { + const bulkUpdateSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + // update a simple rule's `throttle` and `actions` + const ruleToUpdate = getSimpleRuleUpdate(); + ruleToUpdate.throttle = throttle; + ruleToUpdate.actions = actions; + ruleToUpdate.id = ruleId; + delete ruleToUpdate.rule_id; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) + .set('kbn-xsrf', 'true') + .send([ruleToUpdate]) + .expect(200); + + const updatedRule = body[0]; + updatedRule.actions = removeUUIDFromActions(updatedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(updatedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts index 3eb2db9ff5155..5ba1f091a9fb3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/query.ts @@ -24,7 +24,10 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import { orderBy } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; -import { QueryRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; +import { + QueryRuleCreateProps, + AlertSuppressionMissingFieldsStrategy, +} from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/rule_monitoring'; import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types'; import { @@ -1434,6 +1437,616 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + // when missing_fields_strategy set to "doNotSuppress", each document with missing grouped by field + // will generate a separate alert + describe('unsuppressed alerts', () => { + const { indexListOfDocuments, indexGeneratedDocuments } = dataGeneratorFactory({ + es, + index: 'ecs_compliant', + log, + }); + + before(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/ecs_compliant' + ); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/ecs_compliant' + ); + }); + + it('should create unsuppressed alerts for single host.name field when some of the documents have missing values', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:00:00.000Z'; + const firstDoc = { + id, + '@timestamp': timestamp, + agent: { + name: 'agent-1', + }, + }; + const missingFieldDoc = { + id, + '@timestamp': timestamp, + }; + + await indexListOfDocuments([ + firstDoc, + firstDoc, + missingFieldDoc, + missingFieldDoc, + missingFieldDoc, + ]); + + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name'], + }); + expect(previewAlerts.length).to.eql(4); + // first alert should be suppressed + expect(previewAlerts[0]._source).to.eql({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-1', + }, + ], + [ALERT_SUPPRESSION_START]: timestamp, + [ALERT_SUPPRESSION_END]: timestamp, + [ALERT_ORIGINAL_TIME]: timestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + // alert is not suppressed and do not have suppress properties + expect(previewAlerts[1]._source).to.have.property('id', id); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(2).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).to.have.property('id', id); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + it('should create unsuppressed alerts for single host.name field when all the documents have missing values', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:00:00.000Z'; + const missingFieldDoc = { + id, + '@timestamp': timestamp, + }; + + await indexListOfDocuments([ + missingFieldDoc, + missingFieldDoc, + missingFieldDoc, + missingFieldDoc, + ]); + + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name'], + }); + expect(previewAlerts.length).to.eql(4); + + // alert is not suppressed and do not have suppress properties + expect(previewAlerts[1]._source).to.have.property('id', id); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(previewAlerts[1]._source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(2).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).to.have.property('id', id); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + it('should not create unsuppressed alerts if documents are not missing fields', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:00:00.000Z'; + const firstDoc = { + id, + '@timestamp': timestamp, + agent: { + name: 'agent-1', + }, + }; + + await indexListOfDocuments([firstDoc, firstDoc]); + + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name'], + }); + expect(previewAlerts.length).to.eql(1); + // first alert should be suppressed + expect(previewAlerts[0]._source).to.eql({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-1', + }, + ], + [ALERT_SUPPRESSION_START]: timestamp, + [ALERT_SUPPRESSION_END]: timestamp, + [ALERT_ORIGINAL_TIME]: timestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + }); + + it('should create no more than max_signals unsuppressed alerts', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:00:00.000Z'; + const firstDoc = { id, '@timestamp': timestamp, agent: { name: 'agent-0' } }; + + await indexGeneratedDocuments({ + docsCount: 150, + seed: () => ({ + id, + '@timestamp': timestamp, + }), + }); + + await indexListOfDocuments([firstDoc, firstDoc]); + + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + max_signals: 100, + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 200, + sort: ['agent.name'], + }); + // alerts number should be still at 100 + expect(previewAlerts.length).to.eql(100); + // first alert should be suppressed + expect(previewAlerts[0]._source).to.eql({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-0', + }, + ], + [ALERT_SUPPRESSION_START]: timestamp, + [ALERT_SUPPRESSION_END]: timestamp, + [ALERT_ORIGINAL_TIME]: timestamp, + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(1).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).to.have.property('id', id); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + it('should create unsuppressed alerts for multiple fields when ony some of the documents have missing values', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:00:00.000Z'; + const firstDoc = { + id, + '@timestamp': timestamp, + agent: { name: 'agent-0', version: 'filebeat' }, + }; + const secondDoc = { + id, + '@timestamp': timestamp, + agent: { name: 'agent-0', version: 'auditbeat' }, + }; + const thirdDoc = { + id, + '@timestamp': timestamp, + agent: { name: 'agent-1', version: 'filebeat' }, + }; + const missingFieldDoc1 = { + id, + '@timestamp': timestamp, + agent: { name: 'agent-2' }, + }; + const missingFieldDoc2 = { + id, + '@timestamp': timestamp, + agent: { version: 'filebeat' }, + }; + const missingAllFieldsDoc = { + id, + '@timestamp': timestamp, + }; + + await indexListOfDocuments([ + firstDoc, + secondDoc, + secondDoc, + thirdDoc, + thirdDoc, + thirdDoc, + missingFieldDoc1, + missingFieldDoc2, + missingFieldDoc2, + missingAllFieldsDoc, + missingAllFieldsDoc, + ]); + + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name', 'agent.version'], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name', 'agent.version'], + }); + // total 8 alerts = 3 suppressed alerts + 5 unsuppressed (from docs with at least one missing field) + expect(previewAlerts.length).to.eql(8); + // first 3 alerts should be suppressed + expect(previewAlerts[0]._source).to.eql({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { field: 'agent.name', value: 'agent-0' }, + { field: 'agent.version', value: 'auditbeat' }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + expect(previewAlerts[1]._source).to.eql({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { field: 'agent.name', value: 'agent-0' }, + { field: 'agent.version', value: 'filebeat' }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 0, + }); + + expect(previewAlerts[2]._source).to.eql({ + ...previewAlerts[2]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { field: 'agent.name', value: 'agent-1' }, + { field: 'agent.version', value: 'filebeat' }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(3).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).to.have.property('id', id); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + it('should create unsuppressed alerts for multiple fields when all the documents have missing values', async () => { + const id = uuidv4(); + const timestamp = '2020-10-28T06:00:00.000Z'; + const missingFieldDoc1 = { id, '@timestamp': timestamp, agent: { name: 'agent-2' } }; + const missingFieldDoc2 = { id, '@timestamp': timestamp, agent: { version: 'filebeat' } }; + const missingAllFieldsDoc = { id, '@timestamp': timestamp }; + + await indexListOfDocuments([ + missingFieldDoc1, + missingFieldDoc2, + missingFieldDoc2, + missingAllFieldsDoc, + missingAllFieldsDoc, + missingAllFieldsDoc, + ]); + + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name', 'agent.version'], + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + }; + + const { previewId } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T06:30:00.000Z'), + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name', 'agent.version'], + }); + // total 6 alerts = 6 unsuppressed (from docs with at least one missing field) + expect(previewAlerts.length).to.eql(6); + + // all alerts are not suppressed and do not have suppress properties + previewAlerts.forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).to.have.property('id', id); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + }); + }); + + // following 2 tests created to show the difference in 2 modes, using the same data and using suppression time window + // rule will be executing 2 times and will create alert during both executions, that will be suppressed according to time window + describe('with a suppression time window', async () => { + let id: string; + const timestamp = '2020-10-28T06:00:00.000Z'; + const laterTimestamp = '2020-10-28T07:00:00.000Z'; + beforeEach(async () => { + id = uuidv4(); + // we have 3 kind of documents here: + // 1. agent.name: 2 docs with agent-1 in both rule runs + // 2. agent.name: 2 docs with agent-2 in second rule run only + // 3. agent.name: 3 docs with undefined in both rule runs + // if suppress on missing = true - we'll get 3 suppressed alerts + // if suppress on missing = false - we'll get 2 suppressed alerts and 3 unsuppressed for missing agent.name + const firstDoc = { id, '@timestamp': timestamp, agent: { name: 'agent-1' } }; + const secondDoc = { id, '@timestamp': laterTimestamp, agent: { name: 'agent-1' } }; + const thirdDoc = { id, '@timestamp': laterTimestamp, agent: { name: 'agent-2' } }; + const missingFieldDoc1 = { id, '@timestamp': laterTimestamp }; + const missingFieldDoc2 = { id, '@timestamp': laterTimestamp }; + + await indexListOfDocuments([ + firstDoc, + secondDoc, + thirdDoc, + thirdDoc, + missingFieldDoc1, + missingFieldDoc1, + missingFieldDoc2, + ]); + }); + it('should create suppressed alerts for single host.name when rule configure with suppress', async () => { + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name'], + duration: { + value: 300, + unit: 'm', + }, + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.Suppress, + }, + from: 'now-1h', + interval: '1h', + timestamp_override: 'event.ingested', + }; + // checking first default suppress mode + const { previewId, logs } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).to.eql(3); + + expect(previewAlerts[0]._source).to.eql({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-1', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + expect(previewAlerts[1]._source).to.eql({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-2', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + expect(previewAlerts[2]._source).to.eql({ + ...previewAlerts[2]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: null, + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 2, + }); + + for (const logEntry of logs) { + expect(logEntry.errors.length).to.eql(0); + } + }); + + it('should create unsuppressed alerts for single host.name', async () => { + const rule: QueryRuleCreateProps = { + ...getRuleForSignalTesting(['ecs_compliant']), + query: `id:${id}`, + alert_suppression: { + group_by: ['agent.name'], + duration: { + value: 300, + unit: 'm', + }, + missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.DoNotSuppress, + }, + from: 'now-1h', + interval: '1h', + timestamp_override: 'event.ingested', + }; + const { previewId, logs } = await previewRule({ + supertest, + rule, + timeframeEnd: new Date('2020-10-28T07:30:00.000Z'), + invocationCount: 2, + }); + const previewAlerts = await getPreviewAlerts({ + es, + previewId, + size: 10, + sort: ['agent.name', ALERT_ORIGINAL_TIME], + }); + expect(previewAlerts.length).to.eql(5); + + // first alerts expected to be suppressed + expect(previewAlerts[0]._source).to.eql({ + ...previewAlerts[0]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-1', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + expect(previewAlerts[1]._source).to.eql({ + ...previewAlerts[1]._source, + [ALERT_SUPPRESSION_TERMS]: [ + { + field: 'agent.name', + value: 'agent-2', + }, + ], + [ALERT_SUPPRESSION_DOCS_COUNT]: 1, + }); + + // third alert is not suppressed and do not have suppress properties + expect(previewAlerts[2]._source).to.have.property('id', id); + expect(previewAlerts[2]._source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(previewAlerts[2]._source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(previewAlerts[2]._source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(previewAlerts[2]._source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + + // rest of alerts are not suppressed and do not have suppress properties + previewAlerts.slice(3).forEach((previewAlert) => { + const source = previewAlert._source; + expect(source).to.have.property('id', id); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + expect(source).not.to.have.property(ALERT_SUPPRESSION_END); + expect(source).not.to.have.property(ALERT_SUPPRESSION_TERMS); + expect(source).not.to.have.property(ALERT_SUPPRESSION_DOCS_COUNT); + }); + + for (const logEntry of logs) { + expect(logEntry.errors.length).to.eql(0); + } + }); + }); + }); }); describe('with exceptions', async () => { diff --git a/x-pack/test/detection_engine_api_integration/utils/delete_all_rule_execution_info.ts b/x-pack/test/detection_engine_api_integration/utils/delete_all_rule_execution_info.ts index 3a7e3c91fd402..d1f158506a4f1 100644 --- a/x-pack/test/detection_engine_api_integration/utils/delete_all_rule_execution_info.ts +++ b/x-pack/test/detection_engine_api_integration/utils/delete_all_rule_execution_info.ts @@ -6,11 +6,12 @@ */ import type { Client } from '@elastic/elasticsearch'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { ToolingLog } from '@kbn/tooling-log'; import { countDownES } from './count_down_es'; /** - * Remove all rules execution info saved objects from the .kibana index + * Remove all rules execution info saved objects from the security solution savedObjects index * This will retry 50 times before giving up and hopefully still not interfere with other tests * @param es The ElasticSearch handle * @param log The tooling logger @@ -20,7 +21,7 @@ export const deleteAllRuleExecutionInfo = async (es: Client, log: ToolingLog): P async () => { return es.deleteByQuery( { - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, q: 'type:siem-detection-engine-rule-execution-info', wait_for_completion: true, refresh: true, diff --git a/x-pack/test/detection_engine_api_integration/utils/delete_all_timelines.ts b/x-pack/test/detection_engine_api_integration/utils/delete_all_timelines.ts index 973995fc1b619..291cd269580b0 100644 --- a/x-pack/test/detection_engine_api_integration/utils/delete_all_timelines.ts +++ b/x-pack/test/detection_engine_api_integration/utils/delete_all_timelines.ts @@ -6,14 +6,15 @@ */ import type { Client } from '@elastic/elasticsearch'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; /** - * Remove all timelines from the .kibana index + * Remove all timelines from the security solution savedObjects index * @param es The ElasticSearch handle */ export const deleteAllTimelines = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, q: 'type:siem-ui-timeline', wait_for_completion: true, refresh: true, diff --git a/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts b/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts index 57f20a1c0e645..19e15e9b4679b 100644 --- a/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts +++ b/x-pack/test/detection_engine_api_integration/utils/downgrade_immutable_rule.ts @@ -7,6 +7,7 @@ import type { ToolingLog } from '@kbn/tooling-log'; import type { Client } from '@elastic/elasticsearch'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { countDownES } from './count_down_es'; export const downgradeImmutableRule = async ( @@ -18,7 +19,7 @@ export const downgradeImmutableRule = async ( async () => { return es.updateByQuery( { - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, refresh: true, wait_for_completion: true, body: { diff --git a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts index af66123014383..31b8c4571e2f5 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts @@ -92,7 +92,6 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial = 'http://www.example.com/some-article-about-attack', 'Some plain text string here explaining why this is a valid thing to look out for', ], - throttle: 'no_actions', timeline_id: 'timeline_id', timeline_title: 'timeline_title', updated_by: 'elastic', diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts index 836ad5390250e..c56cb7a98b440 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notification_so.ts @@ -7,6 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectReference } from '@kbn/core/server'; import { LegacyRuleNotificationAlertTypeParams } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_actions_legacy'; @@ -15,13 +16,13 @@ interface LegacyActionNotificationSO extends LegacyRuleNotificationAlertTypePara } /** - * Fetch all legacy action sidecar notification SOs from the .kibana index + * Fetch all legacy action sidecar notification SOs from the alerting savedObjects index * @param es The ElasticSearch service */ export const getLegacyActionNotificationSO = async ( es: Client ): Promise> => es.search({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: 'alert.alertTypeId:siem.notifications', }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts index 2e104a454bf78..3d92779e010d2 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_notifications_so_by_id.ts @@ -7,6 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectReference } from '@kbn/core/server'; import { LegacyRuleNotificationAlertTypeParams } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_actions_legacy'; @@ -15,7 +16,7 @@ interface LegacyActionNotificationSO extends LegacyRuleNotificationAlertTypePara } /** - * Fetch legacy action sidecar notification SOs from the .kibana index + * Fetch legacy action sidecar notification SOs from the alerting savedObjects index * @param es The ElasticSearch service * @param id SO id */ @@ -24,6 +25,6 @@ export const getLegacyActionNotificationSOById = async ( id: string ): Promise> => es.search({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: `alert.alertTypeId:siem.notifications AND alert.params.ruleAlertId:"${id}"`, }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_so.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_so.ts index 57cf8b5efe71a..e714dfcec28cc 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_so.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_action_so.ts @@ -6,6 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SavedObjectReference } from '@kbn/core/server'; import type { LegacyRuleActions } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_actions_legacy'; @@ -14,11 +15,11 @@ interface LegacyActionSO extends LegacyRuleActions { } /** - * Fetch all legacy action sidecar SOs from the .kibana index + * Fetch all legacy action sidecar SOs from the security solution savedObjects index * @param es The ElasticSearch service */ export const getLegacyActionSO = async (es: Client): Promise> => es.search({ - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, q: 'type:siem-detection-engine-rule-actions', }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts index 1be0506359172..9e6b6a31e9786 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_legacy_actions_so_by_id.ts @@ -7,6 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SavedObjectReference } from '@kbn/core/server'; import type { LegacyRuleActions } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_actions_legacy'; @@ -15,7 +16,7 @@ interface LegacyActionSO extends LegacyRuleActions { } /** - * Fetch legacy action sidecar SOs from the .kibana index + * Fetch legacy action sidecar SOs from the security solution savedObjects index * @param es The ElasticSearch service * @param id SO id */ @@ -24,6 +25,6 @@ export const getLegacyActionSOById = async ( id: string ): Promise> => es.search({ - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, q: `type:siem-detection-engine-rule-actions AND _id:"siem-detection-engine-rule-actions:${id}"`, }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts b/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts new file mode 100644 index 0000000000000..519cd9136c604 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts @@ -0,0 +1,96 @@ +/* + * 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 SuperTest from 'supertest'; + +import { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; +import { getSlackAction } from './get_slack_action'; +import { getWebHookAction } from './get_web_hook_action'; + +const createConnector = async ( + supertest: SuperTest.SuperTest, + payload: Record +) => + (await supertest.post('/api/actions/action').set('kbn-xsrf', 'true').send(payload).expect(200)) + .body; +const createWebHookConnector = (supertest: SuperTest.SuperTest) => + createConnector(supertest, getWebHookAction()); +const createSlackConnector = (supertest: SuperTest.SuperTest) => + createConnector(supertest, getSlackAction()); + +export const getActionsWithoutFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + }, + ]; +}; + +export const getActionsWithFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + frequency: { summary: false, throttle: '3d', notifyWhen: 'onThrottleInterval' }, + }, + ]; +}; + +export const getSomeActionsWithFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + frequency: { summary: false, throttle: '3d', notifyWhen: 'onThrottleInterval' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + }, + ]; +}; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts b/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts index 5a46f96cc400a..159d1e3010a2a 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_rule_so_by_id.ts @@ -7,6 +7,7 @@ import type { Client } from '@elastic/elasticsearch'; import { SavedObjectReference } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { Rule } from '@kbn/alerting-plugin/common'; @@ -16,12 +17,12 @@ interface RuleSO { } /** - * Fetch legacy action sidecar SOs from the .kibana index + * Fetch legacy action sidecar SOs from the alerting savedObjects index * @param es The ElasticSearch service * @param id SO id */ export const getRuleSOById = async (es: Client, id: string): Promise> => es.search({ - index: '.kibana', + index: ALERTING_CASES_SAVED_OBJECT_INDEX, q: `type:alert AND _id:"alert:${id}"`, }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts index 9826d9a2b98b4..cdadcce39e0a8 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts @@ -40,7 +40,7 @@ export const getMockSharedResponseSchema = ( tags: [], to: 'now', threat: [], - throttle: 'no_actions', + throttle: undefined, exceptions_list: [], version: 1, revision: 0, diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts index 25776fefd5687..7ecee679e50b3 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { NOTIFICATION_DEFAULT_FREQUENCY } from '@kbn/security-solution-plugin/common/constants'; import { getSimpleRuleOutput } from './get_simple_rule_output'; import { RuleWithoutServerGeneratedProperties } from './remove_server_generated_properties'; @@ -13,7 +14,6 @@ export const getSimpleRuleOutputWithWebHookAction = ( uuid: string ): RuleWithoutServerGeneratedProperties => ({ ...getSimpleRuleOutput(), - throttle: 'rule', actions: [ { action_type_id: '.webhook', @@ -23,6 +23,7 @@ export const getSimpleRuleOutputWithWebHookAction = ( body: '{}', }, uuid, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, }, ], }); diff --git a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts index c6c44fe37c337..0b4bfd9254b15 100644 --- a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts +++ b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/create_prebuilt_rule_saved_objects.ts @@ -12,6 +12,7 @@ import { getPrebuiltRuleWithExceptionsMock, } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules/mocks'; import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; /** * A helper function to create a rule asset saved object @@ -73,7 +74,12 @@ export const createPrebuiltRuleAssetSavedObjects = async ( await es.bulk({ refresh: true, body: rules.flatMap((doc) => [ - { index: { _index: '.kibana', _id: `security-rule:${doc['security-rule'].rule_id}` } }, + { + index: { + _index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + _id: `security-rule:${doc['security-rule'].rule_id}`, + }, + }, doc, ]), }); @@ -97,7 +103,7 @@ export const createHistoricalPrebuiltRuleAssetSavedObjects = async ( body: rules.flatMap((doc) => [ { index: { - _index: '.kibana', + _index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, _id: `security-rule:${doc['security-rule'].rule_id}_${doc['security-rule'].version}`, }, }, diff --git a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/delete_all_prebuilt_rule_assets.ts b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/delete_all_prebuilt_rule_assets.ts index 11f9c75c81cc1..899d5ddd7f83f 100644 --- a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/delete_all_prebuilt_rule_assets.ts +++ b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/delete_all_prebuilt_rule_assets.ts @@ -6,14 +6,15 @@ */ import type { Client } from '@elastic/elasticsearch'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; /** - * Remove all prebuilt rule assets from the .kibana index + * Remove all prebuilt rule assets from the security solution savedObjects index * @param es The ElasticSearch handle */ export const deleteAllPrebuiltRuleAssets = async (es: Client): Promise => { await es.deleteByQuery({ - index: '.kibana', + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, q: 'type:security-rule', wait_for_completion: true, refresh: true, diff --git a/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts b/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts new file mode 100644 index 0000000000000..08d95bc750212 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.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. + */ + +import { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; + +export const removeUUIDFromActions = (actions: RuleActionArray): RuleActionArray => { + return actions.map(({ uuid, ...restOfAction }) => ({ + ...restOfAction, + })); +}; diff --git a/x-pack/test/fleet_api_integration/apis/agents/status.ts b/x-pack/test/fleet_api_integration/apis/agents/status.ts index 4908e1d2e8c6d..ed2c71cb4d8bc 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/status.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/status.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; +import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { AGENTS_INDEX } from '@kbn/fleet-plugin/common'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { testUsers } from '../test_users'; @@ -22,7 +23,7 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/fleet/agents'); await es.create({ id: 'ingest-agent-policies:policy-inactivity-timeout', - index: '.kibana', + index: INGEST_SAVED_OBJECT_INDEX, refresh: 'wait_for', document: { type: 'ingest-agent-policies', @@ -270,7 +271,7 @@ export default function ({ getService }: FtrProviderContext) { policiesToAdd.map((policyId) => es.create({ id: 'ingest-agent-policies:' + policyId, - index: '.kibana', + index: INGEST_SAVED_OBJECT_INDEX, refresh: 'wait_for', document: { type: 'ingest-agent-policies', diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts index fcf7fdaa4ed81..65103398f8ab4 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts @@ -8,6 +8,7 @@ import fs from 'fs'; import path from 'path'; import expect from '@kbn/expect'; +import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; @@ -111,7 +112,7 @@ export default function (providerContext: FtrProviderContext) { await deletePackage(testPkgName, testPkgVersion); const epmPackageRes = await esClient.search({ - index: '.kibana', + index: INGEST_SAVED_OBJECT_INDEX, size: 0, rest_total_hits_as_int: true, query: { @@ -127,7 +128,7 @@ export default function (providerContext: FtrProviderContext) { }, }); const epmPackageAssetsRes = await esClient.search({ - index: '.kibana', + index: INGEST_SAVED_OBJECT_INDEX, size: 0, rest_total_hits_as_int: true, query: { diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_with_signature_verification.ts b/x-pack/test/fleet_api_integration/apis/epm/install_with_signature_verification.ts index 1c1ee8e1ed3b9..3bd2079fc187c 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_with_signature_verification.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_with_signature_verification.ts @@ -6,6 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; +import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Installation } from '@kbn/fleet-plugin/server/types'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; @@ -32,7 +33,7 @@ export default function (providerContext: FtrProviderContext) { const getInstallationSavedObject = async (pkg: string): Promise => { const res: { _source?: { 'epm-packages': Installation } } = await es.transport.request({ method: 'GET', - path: `/.kibana/_doc/epm-packages:${pkg}`, + path: `/${INGEST_SAVED_OBJECT_INDEX}/_doc/epm-packages:${pkg}`, }); return res?._source?.['epm-packages'] as Installation; diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts index ca8ac94656402..d8511e0cb4418 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/create.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/create.ts @@ -6,6 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; +import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Installation } from '@kbn/fleet-plugin/common'; import { v4 as uuidv4 } from 'uuid'; @@ -649,7 +650,7 @@ export default function (providerContext: FtrProviderContext) { const getInstallationSavedObject = async (pkg: string): Promise => { const res: { _source?: { 'epm-packages': Installation } } = await es.transport.request({ method: 'GET', - path: `/.kibana/_doc/epm-packages:${pkg}`, + path: `/${INGEST_SAVED_OBJECT_INDEX}/_doc/epm-packages:${pkg}`, }); return res?._source?.['epm-packages'] as Installation; diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/get.ts b/x-pack/test/fleet_api_integration/apis/package_policy/get.ts index be2c7cc73e5f3..841048cceb0f9 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/get.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/get.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; import { testUsers } from '../test_users'; @@ -337,7 +338,7 @@ export default function (providerContext: FtrProviderContext) { // Delete the agent policy directly from ES to orphan the package policy const esClient = getService('es'); await esClient.delete({ - index: '.kibana', + index: INGEST_SAVED_OBJECT_INDEX, id: `ingest-agent-policies:${agentPolicyId}`, refresh: 'wait_for', }); diff --git a/x-pack/test/functional/apps/aiops/explain_log_rate_spikes.ts b/x-pack/test/functional/apps/aiops/explain_log_rate_spikes.ts index 92320dad62087..7731eea1d9d7a 100644 --- a/x-pack/test/functional/apps/aiops/explain_log_rate_spikes.ts +++ b/x-pack/test/functional/apps/aiops/explain_log_rate_spikes.ts @@ -13,8 +13,8 @@ import type { FtrProviderContext } from '../../ftr_provider_context'; import { isTestDataExpectedWithSampleProbability, type TestData } from './types'; import { explainLogRateSpikesTestData } from './test_data'; -export default function ({ getPageObject, getService }: FtrProviderContext) { - const headerPage = getPageObject('header'); +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'console', 'header', 'home', 'security']); const elasticChart = getService('elasticChart'); const aiops = getService('aiops'); @@ -58,7 +58,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await aiops.explainLogRateSpikesPage.assertSamplingProbabilityMissing(); } - await headerPage.waitUntilLoadingHasFinished(); + await PageObjects.header.waitUntilLoadingHasFinished(); await ml.testExecution.logTestStep( `${testData.suiteTitle} displays elements in the doc count panel correctly` @@ -78,77 +78,78 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await aiops.explainLogRateSpikesPage.clickDocumentCountChart(testData.chartClickCoordinates); await aiops.explainLogRateSpikesPage.assertAnalysisSectionExists(); - await ml.testExecution.logTestStep('displays the no results found prompt'); - await aiops.explainLogRateSpikesPage.assertNoResultsFoundEmptyPromptExists(); + if (testData.brushDeviationTargetTimestamp) { + await ml.testExecution.logTestStep('displays the no results found prompt'); + await aiops.explainLogRateSpikesPage.assertNoResultsFoundEmptyPromptExists(); - await ml.testExecution.logTestStep('adjusts the brushes to get analysis results'); - await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(false); + await ml.testExecution.logTestStep('adjusts the brushes to get analysis results'); + await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(false); - // Get the current width of the deviation brush for later comparison. - const brushSelectionWidthBefore = await aiops.explainLogRateSpikesPage.getBrushSelectionWidth( - 'aiopsBrushDeviation' - ); - - // Get the px values for the timestamp we want to move the brush to. - const { targetPx, intervalPx } = await aiops.explainLogRateSpikesPage.getPxForTimestamp( - testData.brushDeviationTargetTimestamp - ); - - // Adjust the right brush handle - await aiops.explainLogRateSpikesPage.adjustBrushHandler( - 'aiopsBrushDeviation', - 'handle--e', - targetPx + intervalPx * testData.brushIntervalFactor - ); - - // Adjust the left brush handle - await aiops.explainLogRateSpikesPage.adjustBrushHandler( - 'aiopsBrushDeviation', - 'handle--w', - targetPx - intervalPx * (testData.brushIntervalFactor - 1) - ); + // Get the current width of the deviation brush for later comparison. + const brushSelectionWidthBefore = + await aiops.explainLogRateSpikesPage.getBrushSelectionWidth('aiopsBrushDeviation'); - if (testData.brushBaselineTargetTimestamp) { // Get the px values for the timestamp we want to move the brush to. - const { targetPx: targetBaselinePx } = - await aiops.explainLogRateSpikesPage.getPxForTimestamp( - testData.brushBaselineTargetTimestamp - ); + const { targetPx, intervalPx } = await aiops.explainLogRateSpikesPage.getPxForTimestamp( + testData.brushDeviationTargetTimestamp + ); // Adjust the right brush handle await aiops.explainLogRateSpikesPage.adjustBrushHandler( - 'aiopsBrushBaseline', + 'aiopsBrushDeviation', 'handle--e', - targetBaselinePx + intervalPx * testData.brushIntervalFactor + targetPx + intervalPx * testData.brushIntervalFactor ); // Adjust the left brush handle await aiops.explainLogRateSpikesPage.adjustBrushHandler( - 'aiopsBrushBaseline', + 'aiopsBrushDeviation', 'handle--w', - targetBaselinePx - intervalPx * (testData.brushIntervalFactor - 1) + targetPx - intervalPx * (testData.brushIntervalFactor - 1) ); - } - // Get the new brush selection width for later comparison. - const brushSelectionWidthAfter = await aiops.explainLogRateSpikesPage.getBrushSelectionWidth( - 'aiopsBrushDeviation' - ); + if (testData.brushBaselineTargetTimestamp) { + // Get the px values for the timestamp we want to move the brush to. + const { targetPx: targetBaselinePx } = + await aiops.explainLogRateSpikesPage.getPxForTimestamp( + testData.brushBaselineTargetTimestamp + ); + + // Adjust the right brush handle + await aiops.explainLogRateSpikesPage.adjustBrushHandler( + 'aiopsBrushBaseline', + 'handle--e', + targetBaselinePx + intervalPx * testData.brushIntervalFactor + ); - // Assert the adjusted brush: The selection width should have changed and - // we test if the selection is smaller than two bucket intervals. - // Finally, the adjusted brush should trigger - // a warning on the "Rerun analysis" button. - expect(brushSelectionWidthBefore).not.to.be(brushSelectionWidthAfter); - expect(brushSelectionWidthAfter).not.to.be.greaterThan( - intervalPx * 2 * testData.brushIntervalFactor - ); + // Adjust the left brush handle + await aiops.explainLogRateSpikesPage.adjustBrushHandler( + 'aiopsBrushBaseline', + 'handle--w', + targetBaselinePx - intervalPx * (testData.brushIntervalFactor - 1) + ); + } + + // Get the new brush selection width for later comparison. + const brushSelectionWidthAfter = + await aiops.explainLogRateSpikesPage.getBrushSelectionWidth('aiopsBrushDeviation'); + + // Assert the adjusted brush: The selection width should have changed and + // we test if the selection is smaller than two bucket intervals. + // Finally, the adjusted brush should trigger + // a warning on the "Rerun analysis" button. + expect(brushSelectionWidthBefore).not.to.be(brushSelectionWidthAfter); + expect(brushSelectionWidthAfter).not.to.be.greaterThan( + intervalPx * 2 * testData.brushIntervalFactor + ); - await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(true); + await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(true); - await ml.testExecution.logTestStep('rerun the analysis with adjusted settings'); + await ml.testExecution.logTestStep('rerun the analysis with adjusted settings'); + + await aiops.explainLogRateSpikesPage.clickRerunAnalysisButton(true); + } - await aiops.explainLogRateSpikesPage.clickRerunAnalysisButton(true); await aiops.explainLogRateSpikesPage.assertProgressTitle('Progress: 100% — Done.'); // The group switch should be disabled by default @@ -178,14 +179,14 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { ); } - // Assert the field selector that allows to costumize grouping + await ml.testExecution.logTestStep('open the field filter'); await aiops.explainLogRateSpikesPage.assertFieldFilterPopoverButtonExists(false); await aiops.explainLogRateSpikesPage.clickFieldFilterPopoverButton(true); await aiops.explainLogRateSpikesPage.assertFieldSelectorFieldNameList( testData.expected.fieldSelectorPopover ); - // Filter fields + await ml.testExecution.logTestStep('filter fields'); await aiops.explainLogRateSpikesPage.setFieldSelectorSearch(testData.fieldSelectorSearch); await aiops.explainLogRateSpikesPage.assertFieldSelectorFieldNameList([ testData.fieldSelectorSearch, @@ -196,6 +197,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { ); if (testData.fieldSelectorApplyAvailable) { + await ml.testExecution.logTestStep('regroup results'); await aiops.explainLogRateSpikesPage.clickFieldFilterApplyButton(); if (!isTestDataExpectedWithSampleProbability(testData.expected)) { @@ -206,10 +208,33 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { ); } } + + if (testData.action !== undefined) { + await ml.testExecution.logTestStep('check all table row actions are present'); + await aiops.explainLogRateSpikesAnalysisGroupsTable.assertRowActions( + testData.action.tableRowId + ); + + await ml.testExecution.logTestStep('click log pattern analysis action'); + await aiops.explainLogRateSpikesAnalysisGroupsTable.clickRowAction( + testData.action.tableRowId, + testData.action.type + ); + + await ml.testExecution.logTestStep('check log pattern analysis page loaded correctly'); + await aiops.logPatternAnalysisPageProvider.assertLogCategorizationPageExists(); + await aiops.logPatternAnalysisPageProvider.assertTotalDocumentCount( + testData.action.expected.totalDocCount + ); + await aiops.logPatternAnalysisPageProvider.assertQueryInput( + testData.action.expected.queryBar + ); + } }); } - describe('explain log rate spikes', async function () { + // FLAKY: https://github.com/elastic/kibana/issues/155222 + describe.skip('explain log rate spikes', async function () { for (const testData of explainLogRateSpikesTestData) { describe(`with '${testData.sourceIndexOrSavedSearch}'`, function () { before(async () => { @@ -222,13 +247,27 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await ml.testResources.setKibanaTimeZoneToUTC(); - await ml.securityUI.loginAsMlPowerUser(); + if (testData.dataGenerator === 'kibana_sample_data_logs') { + await PageObjects.security.login('elastic', 'changeme', { + expectSuccess: true, + }); + + await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { + useActualUrl: true, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.home.addSampleDataSet('logs'); + await PageObjects.header.waitUntilLoadingHasFinished(); + } else { + await ml.securityUI.loginAsMlPowerUser(); + } }); after(async () => { await elasticChart.setNewChartUiDebugFlag(false); - await ml.testResources.deleteIndexPatternByTitle(testData.sourceIndexOrSavedSearch); - + if (testData.dataGenerator !== 'kibana_sample_data_logs') { + await ml.testResources.deleteIndexPatternByTitle(testData.sourceIndexOrSavedSearch); + } await aiops.explainLogRateSpikesDataGenerator.removeGeneratedData(testData.dataGenerator); }); diff --git a/x-pack/test/functional/apps/aiops/test_data.ts b/x-pack/test/functional/apps/aiops/test_data.ts index b6d3293aeba81..95e21fbfc7800 100644 --- a/x-pack/test/functional/apps/aiops/test_data.ts +++ b/x-pack/test/functional/apps/aiops/test_data.ts @@ -7,6 +7,111 @@ import type { TestData } from './types'; +export const kibanaLogsDataViewTestData: TestData = { + suiteTitle: 'kibana sample data logs', + dataGenerator: 'kibana_sample_data_logs', + isSavedSearch: false, + sourceIndexOrSavedSearch: 'kibana_sample_data_logs', + brushIntervalFactor: 1, + chartClickCoordinates: [235, 0], + fieldSelectorSearch: 'referer', + fieldSelectorApplyAvailable: true, + action: { + type: 'LogPatternAnalysis', + tableRowId: '488337254', + expected: { + queryBar: + 'clientip:30.156.16.164 AND host.keyword:elastic-elastic-elastic.org AND ip:30.156.16.163 AND response.keyword:404 AND machine.os.keyword:win xp AND geo.dest:IN AND geo.srcdest:US\\:IN', + totalDocCount: '100', + }, + }, + expected: { + totalDocCountFormatted: '14,074', + analysisGroupsTable: [ + { + group: + '* clientip: 30.156.16.164* host.keyword: elastic-elastic-elastic.org* ip: 30.156.16.163* referer: http://www.elastic-elastic-elastic.com/success/timothy-l-kopra* response.keyword: 404Showing 5 out of 8 items. 8 items unique to this group.', + docCount: '100', + }, + ], + filteredAnalysisGroupsTable: [ + { + group: + '* clientip: 30.156.16.164* host.keyword: elastic-elastic-elastic.org* ip: 30.156.16.163* response.keyword: 404* machine.os.keyword: win xpShowing 5 out of 7 items. 7 items unique to this group.', + docCount: '100', + }, + ], + analysisTable: [ + { + fieldName: 'clientip', + fieldValue: '30.156.16.164', + logRate: 'Chart type:bar chart', + pValue: '3.10e-13', + impact: 'High', + }, + { + fieldName: 'geo.dest', + fieldValue: 'IN', + logRate: 'Chart type:bar chart', + pValue: '0.000716', + impact: 'Medium', + }, + { + fieldName: 'geo.srcdest', + fieldValue: 'US:IN', + logRate: 'Chart type:bar chart', + pValue: '0.000716', + impact: 'Medium', + }, + { + fieldName: 'host.keyword', + fieldValue: 'elastic-elastic-elastic.org', + logRate: 'Chart type:bar chart', + pValue: '7.14e-9', + impact: 'High', + }, + { + fieldName: 'ip', + fieldValue: '30.156.16.163', + logRate: 'Chart type:bar chart', + pValue: '3.28e-13', + impact: 'High', + }, + { + fieldName: 'machine.os.keyword', + fieldValue: 'win xp', + logRate: 'Chart type:bar chart', + pValue: '0.0000997', + impact: 'Medium', + }, + { + fieldName: 'referer', + fieldValue: 'http://www.elastic-elastic-elastic.com/success/timothy-l-kopra', + logRate: 'Chart type:bar chart', + pValue: '4.74e-13', + impact: 'High', + }, + { + fieldName: 'response.keyword', + fieldValue: '404', + logRate: 'Chart type:bar chart', + pValue: '0.00000604', + impact: 'Medium', + }, + ], + fieldSelectorPopover: [ + 'clientip', + 'geo.dest', + 'geo.srcdest', + 'host.keyword', + 'ip', + 'machine.os.keyword', + 'referer', + 'response.keyword', + ], + }, +}; + export const farequoteDataViewTestData: TestData = { suiteTitle: 'farequote with spike', dataGenerator: 'farequote_with_spike', @@ -122,6 +227,7 @@ export const artificialLogDataViewTestData: TestData = { }; export const explainLogRateSpikesTestData: TestData[] = [ + kibanaLogsDataViewTestData, farequoteDataViewTestData, farequoteDataViewTestDataWithQuery, artificialLogDataViewTestData, diff --git a/x-pack/test/functional/apps/aiops/types.ts b/x-pack/test/functional/apps/aiops/types.ts index 01733a8e1a2af..2093d4d961363 100644 --- a/x-pack/test/functional/apps/aiops/types.ts +++ b/x-pack/test/functional/apps/aiops/types.ts @@ -7,6 +7,15 @@ import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +interface TestDataTableActionLogPatternAnalysis { + type: 'LogPatternAnalysis'; + tableRowId: string; + expected: { + queryBar: string; + totalDocCount: string; + }; +} + interface TestDataExpectedWithSampleProbability { totalDocCountFormatted: string; sampleProbabilityFormatted: string; @@ -40,11 +49,12 @@ export interface TestData { sourceIndexOrSavedSearch: string; rowsPerPage?: 10 | 25 | 50; brushBaselineTargetTimestamp?: number; - brushDeviationTargetTimestamp: number; + brushDeviationTargetTimestamp?: number; brushIntervalFactor: number; chartClickCoordinates: [number, number]; fieldSelectorSearch: string; fieldSelectorApplyAvailable: boolean; query?: string; + action?: TestDataTableActionLogPatternAnalysis; expected: TestDataExpectedWithSampleProbability | TestDataExpectedWithoutSampleProbability; } diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index b22bb934109dc..e9000a9cf3e6d 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -150,7 +150,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Tests - describe('Hosts View', function () { + // Failing: See https://github.com/elastic/kibana/issues/155429 + describe.skip('Hosts View', function () { this.tags('includeFirefox'); before(async () => { @@ -367,9 +368,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { [ { metric: 'hosts', value: '6' }, { metric: 'cpu', value: '0.8%' }, - { metric: 'memory', value: '16.8%' }, - { metric: 'tx', value: '0 bit/s' }, - { metric: 'rx', value: '0 bit/s' }, + { metric: 'memory', value: '16.81%' }, + { metric: 'tx', value: 'N/A' }, + { metric: 'rx', value: 'N/A' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric); @@ -497,9 +498,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { [ { metric: 'hosts', value: '3' }, { metric: 'cpu', value: '0.8%' }, - { metric: 'memory', value: '16.2%' }, - { metric: 'tx', value: '0 bit/s' }, - { metric: 'rx', value: '0 bit/s' }, + { metric: 'memory', value: '16.25%' }, + { metric: 'tx', value: 'N/A' }, + { metric: 'rx', value: 'N/A' }, ].map(async ({ metric, value }) => { const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric); expect(tileValue).to.eql(value); @@ -528,6 +529,89 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); }); + + describe('Pagination and Sorting', () => { + beforeEach(async () => { + await pageObjects.infraHostsView.changePageSize(5); + }); + + it('should show 5 rows on the first page', async () => { + const hostRows = await pageObjects.infraHostsView.getHostsTableData(); + hostRows.forEach((row, position) => { + pageObjects.infraHostsView + .getHostsRowData(row) + .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[position])); + }); + }); + + it('should paginate to the last page', async () => { + await pageObjects.infraHostsView.paginateTo(2); + const hostRows = await pageObjects.infraHostsView.getHostsTableData(); + hostRows.forEach((row) => { + pageObjects.infraHostsView + .getHostsRowData(row) + .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[5])); + }); + }); + + it('should show all hosts on the same page', async () => { + await pageObjects.infraHostsView.changePageSize(10); + const hostRows = await pageObjects.infraHostsView.getHostsTableData(); + hostRows.forEach((row, position) => { + pageObjects.infraHostsView + .getHostsRowData(row) + .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[position])); + }); + }); + + it('should sort by Disk Latency asc', async () => { + await pageObjects.infraHostsView.sortByDiskLatency(); + let hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataFirtPage).to.eql(tableEntries[0]); + + await pageObjects.infraHostsView.paginateTo(2); + hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataLastPage).to.eql(tableEntries[1]); + }); + + it('should sort by Disk Latency desc', async () => { + await pageObjects.infraHostsView.sortByDiskLatency(); + let hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataFirtPage).to.eql(tableEntries[1]); + + await pageObjects.infraHostsView.paginateTo(2); + hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataLastPage).to.eql(tableEntries[0]); + }); + + it('should sort by Title asc', async () => { + await pageObjects.infraHostsView.sortByTitle(); + let hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataFirtPage).to.eql(tableEntries[0]); + + await pageObjects.infraHostsView.paginateTo(2); + hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataLastPage).to.eql(tableEntries[5]); + }); + + it('should sort by Title desc', async () => { + await pageObjects.infraHostsView.sortByTitle(); + let hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataFirtPage).to.eql(tableEntries[5]); + + await pageObjects.infraHostsView.paginateTo(2); + hostRows = await pageObjects.infraHostsView.getHostsTableData(); + const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostDataLastPage).to.eql(tableEntries[0]); + }); + }); }); }); }; diff --git a/x-pack/test/functional/apps/infra/tour.ts b/x-pack/test/functional/apps/infra/tour.ts index 2f9dfa314c168..aea50f27a8482 100644 --- a/x-pack/test/functional/apps/infra/tour.ts +++ b/x-pack/test/functional/apps/infra/tour.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import { observTourStepStorageKey } from '@kbn/observability-plugin/public/components/shared/tour'; +import { API_BASE_PATH } from '@kbn/guided-onboarding-plugin/common'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects, getService }: FtrProviderContext) => { @@ -44,7 +45,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { beforeEach(async () => { // Activate the Observability guide, step 3, in order to trigger the EuiTour await supertest - .put(`/api/guided_onboarding/state`) + .put(`${API_BASE_PATH}/state`) .set('kbn-xsrf', 'true') .send({ status: 'in_progress', diff --git a/x-pack/test/functional/apps/lens/group1/field_formatters.ts b/x-pack/test/functional/apps/lens/group1/field_formatters.ts new file mode 100644 index 0000000000000..855515bd3d2ce --- /dev/null +++ b/x-pack/test/functional/apps/lens/group1/field_formatters.ts @@ -0,0 +1,122 @@ +/* + * 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 { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects([ + 'visualize', + 'lens', + 'header', + 'dashboard', + 'common', + 'settings', + ]); + const retry = getService('retry'); + const fieldEditor = getService('fieldEditor'); + + describe('lens fields formatters tests', () => { + before(async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + await PageObjects.lens.switchToVisualization('lnsDatatable'); + }); + + afterEach(async () => { + await PageObjects.lens.clickField('runtimefield'); + await PageObjects.lens.removeField('runtimefield'); + await fieldEditor.confirmDelete(); + await PageObjects.lens.waitForFieldMissing('runtimefield'); + }); + + it('should display url formatter correctly', async () => { + await retry.try(async () => { + await PageObjects.lens.clickAddField(); + await fieldEditor.setName('runtimefield'); + await fieldEditor.enableValue(); + await fieldEditor.typeScript("emit(doc['geo.dest'].value)"); + await fieldEditor.setFormat(FIELD_FORMAT_IDS.URL); + await fieldEditor.setUrlFieldFormat('https://www.elastic.co?{{value}}'); + await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.searchField('runtime'); + await PageObjects.lens.waitForField('runtimefield'); + await PageObjects.lens.dragFieldToWorkspace('runtimefield'); + }); + await PageObjects.lens.waitForVisualization(); + expect(await PageObjects.lens.getDatatableHeaderText(0)).to.equal( + 'Top 5 values of runtimefield' + ); + expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('https://www.elastic.co?CN'); + }); + + it('should display static lookup formatter correctly', async () => { + await retry.try(async () => { + await PageObjects.lens.clickAddField(); + await fieldEditor.setName('runtimefield'); + await fieldEditor.enableValue(); + await fieldEditor.typeScript("emit(doc['geo.dest'].value)"); + await fieldEditor.setFormat(FIELD_FORMAT_IDS.STATIC_LOOKUP); + await fieldEditor.setStaticLookupFormat('CN', 'China'); + await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + await PageObjects.lens.waitForVisualization(); + expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('China'); + }); + + it('should display color formatter correctly', async () => { + await retry.try(async () => { + await PageObjects.lens.clickAddField(); + await fieldEditor.setName('runtimefield'); + await fieldEditor.enableValue(); + await fieldEditor.typeScript("emit(doc['geo.dest'].value)"); + await fieldEditor.setFormat(FIELD_FORMAT_IDS.COLOR); + await fieldEditor.setColorFormat('CN', '#ffffff', '#ff0000'); + await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + await PageObjects.lens.waitForVisualization(); + const styleObj = await PageObjects.lens.getDatatableCellSpanStyle(0, 0); + expect(styleObj['background-color']).to.be('rgb(255, 0, 0)'); + expect(styleObj.color).to.be('rgb(255, 255, 255)'); + }); + + it('should display string formatter correctly', async () => { + await retry.try(async () => { + await PageObjects.lens.clickAddField(); + await fieldEditor.setName('runtimefield'); + await fieldEditor.enableValue(); + await fieldEditor.typeScript("emit(doc['geo.dest'].value)"); + await fieldEditor.setFormat(FIELD_FORMAT_IDS.STRING); + await fieldEditor.setStringFormat('lower'); + await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + await PageObjects.lens.waitForVisualization(); + expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('cn'); + }); + + it('should display truncate string formatter correctly', async () => { + await retry.try(async () => { + await PageObjects.lens.clickAddField(); + await fieldEditor.setName('runtimefield'); + await fieldEditor.enableValue(); + await fieldEditor.typeScript("emit(doc['links.raw'].value)"); + await fieldEditor.setFormat(FIELD_FORMAT_IDS.TRUNCATE); + await fieldEditor.setTruncateFormatLength('3'); + await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + await PageObjects.lens.waitForVisualization(); + expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('dal...'); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group1/index.ts b/x-pack/test/functional/apps/lens/group1/index.ts index 03f4a4032154e..a129a66c519d9 100644 --- a/x-pack/test/functional/apps/lens/group1/index.ts +++ b/x-pack/test/functional/apps/lens/group1/index.ts @@ -83,6 +83,7 @@ export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext loadTestFile(require.resolve('./text_based_languages')); loadTestFile(require.resolve('./fields_list')); loadTestFile(require.resolve('./layer_actions')); + loadTestFile(require.resolve('./field_formatters')); } }); }; diff --git a/x-pack/test/functional/apps/lens/group1/smokescreen.ts b/x-pack/test/functional/apps/lens/group1/smokescreen.ts index 53a51b89f2bcc..cd277f45fecb5 100644 --- a/x-pack/test/functional/apps/lens/group1/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/group1/smokescreen.ts @@ -148,7 +148,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await listingTable.searchForItemWithName('lnsXYvis'); await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); await PageObjects.lens.goToTimeRange(); - await testSubjects.click('lnsXY_splitDimensionPanel > indexPattern-dimension-remove'); + await PageObjects.lens.removeDimension('lnsXY_splitDimensionPanel'); await PageObjects.lens.switchToVisualization('line'); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', @@ -200,9 +200,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( longLabel ); - expect( - await testSubjects.isDisplayed('lnsXY_yDimensionPanel > indexPattern-dimension-remove') - ).to.equal(true); + expect(await PageObjects.lens.canRemoveDimension('lnsXY_yDimensionPanel')).to.equal(true); await PageObjects.lens.removeDimension('lnsXY_yDimensionPanel'); await testSubjects.missingOrFail('lnsXY_yDimensionPanel > lns-dimensionTrigger'); }); diff --git a/x-pack/test/functional/apps/lens/group3/metric.ts b/x-pack/test/functional/apps/lens/group3/metric.ts index 843422e87c51f..f5ef312c39d2d 100644 --- a/x-pack/test/functional/apps/lens/group3/metric.ts +++ b/x-pack/test/functional/apps/lens/group3/metric.ts @@ -15,6 +15,52 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const inspector = getService('inspector'); + const inspectorTrendlineData = [ + ['2015-09-19 06:00', 'null'], + ['2015-09-19 09:00', 'null'], + ['2015-09-19 12:00', 'null'], + ['2015-09-19 15:00', 'null'], + ['2015-09-19 18:00', 'null'], + ['2015-09-19 21:00', 'null'], + ['2015-09-20 00:00', '6,011.351'], + ['2015-09-20 03:00', '5,849.901'], + ['2015-09-20 06:00', '5,722.622'], + ['2015-09-20 09:00', '5,769.092'], + ['2015-09-20 12:00', '5,740.875'], + ['2015-09-20 15:00', '5,520.429'], + ['2015-09-20 18:00', '5,153.053'], + ['2015-09-20 21:00', '6,656.581'], + ['2015-09-21 00:00', '5,139.357'], + ['2015-09-21 03:00', '5,884.891'], + ['2015-09-21 06:00', '5,683.283'], + ['2015-09-21 09:00', '5,863.661'], + ['2015-09-21 12:00', '5,657.531'], + ['2015-09-21 15:00', '5,841.935'], + ]; + + const inspectorExpectedTrenlineDataWithBreakdown = [ + ['97.220.3.248', '2015-09-19 06:00', 'null'], + ['97.220.3.248', '2015-09-19 09:00', 'null'], + ['97.220.3.248', '2015-09-19 12:00', 'null'], + ['97.220.3.248', '2015-09-19 15:00', 'null'], + ['97.220.3.248', '2015-09-19 18:00', 'null'], + ['97.220.3.248', '2015-09-19 21:00', 'null'], + ['97.220.3.248', '2015-09-20 00:00', 'null'], + ['97.220.3.248', '2015-09-20 03:00', 'null'], + ['97.220.3.248', '2015-09-20 06:00', 'null'], + ['97.220.3.248', '2015-09-20 09:00', 'null'], + ['97.220.3.248', '2015-09-20 12:00', 'null'], + ['97.220.3.248', '2015-09-20 15:00', 'null'], + ['97.220.3.248', '2015-09-20 18:00', 'null'], + ['97.220.3.248', '2015-09-20 21:00', 'null'], + ['97.220.3.248', '2015-09-21 00:00', 'null'], + ['97.220.3.248', '2015-09-21 03:00', 'null'], + ['97.220.3.248', '2015-09-21 06:00', 'null'], + ['97.220.3.248', '2015-09-21 09:00', '19,755'], + ['97.220.3.248', '2015-09-21 12:00', 'null'], + ['97.220.3.248', '2015-09-21 15:00', 'null'], + ]; + const clickMetric = async (title: string) => { const tiles = await PageObjects.lens.getMetricTiles(); for (const tile of tiles) { @@ -47,7 +93,33 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); expect((await PageObjects.lens.getMetricVisualizationData()).length).to.be.equal(1); + }); + + it('should enable trendlines', async () => { + // trendline data without the breakdown + await PageObjects.lens.openDimensionEditor( + 'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger' + ); + await testSubjects.click('lnsMetric_supporting_visualization_trendline'); + await PageObjects.lens.closeDimensionEditor(); + + await inspector.open('lnsApp_inspectButton'); + + const trendLineData = await inspector.getTableDataWithId('inspectorTableChooser1'); + expect(trendLineData).to.eql(inspectorTrendlineData); + await inspector.close(); + await PageObjects.lens.openDimensionEditor( + 'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger' + ); + await testSubjects.click('lnsMetric_supporting_visualization_none'); + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.waitForVisualization('mtrVis'); + }); + + it('should enable metric with breakdown', async () => { + // trendline data without the breakdown await PageObjects.lens.configureDimension({ dimension: 'lnsMetric_breakdownByDimensionPanel > lns-empty-dimension', operation: 'terms', @@ -112,7 +184,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { showingBar: false, }, ]); + }); + it('should enable bar with max dimension', async () => { await PageObjects.lens.openDimensionEditor( 'lnsMetric_maxDimensionPanel > lns-empty-dimension' ); @@ -125,7 +199,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.removeDimension('lnsMetric_maxDimensionPanel'); }); - it('should enable trendlines', async () => { + it('should enable trendlines with breakdown', async () => { await PageObjects.lens.openDimensionEditor( 'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger' ); @@ -139,11 +213,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { (datum) => datum.showingTrendline ) ).to.be(true); + await PageObjects.lens.closeDimensionEditor(); await inspector.open('lnsApp_inspectButton'); - expect(await inspector.getNumberOfTables()).to.equal(2); - + const trendLineData = await inspector.getTableDataWithId('inspectorTableChooser1'); + expect(trendLineData).to.eql(inspectorExpectedTrenlineDataWithBreakdown); await inspector.close(); await PageObjects.lens.openDimensionEditor( diff --git a/x-pack/test/functional/apps/security/doc_level_security_roles.ts b/x-pack/test/functional/apps/security/doc_level_security_roles.ts index 56feb76394b61..b1af9ec3350b7 100644 --- a/x-pack/test/functional/apps/security/doc_level_security_roles.ts +++ b/x-pack/test/functional/apps/security/doc_level_security_roles.ts @@ -19,7 +19,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['security', 'common', 'header', 'discover', 'settings']); const kibanaServer = getService('kibanaServer'); - describe('dls', function () { + // Failing: See https://github.com/elastic/kibana/issues/155447 + describe.skip('dls', function () { before('initialize tests', async () => { await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/security/dlstest'); diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts b/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts index dc86cf7489530..2cccd4525856d 100644 --- a/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts +++ b/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts @@ -125,11 +125,9 @@ export default function ({ getService }: FtrProviderContext) { }, }; } - await transform.api.createTransform( - testData.originalConfig.id, - testData.originalConfig, - testData.expected.healthStatus === TRANSFORM_HEALTH.yellow - ); + await transform.api.createTransform(testData.originalConfig.id, testData.originalConfig, { + deferValidation: testData.expected.healthStatus === TRANSFORM_HEALTH.yellow, + }); } await transform.testResources.setKibanaTimeZoneToUTC(); await transform.securityUI.loginAsTransformPowerUser(); diff --git a/x-pack/test/functional/apps/visualize/telemetry.ts b/x-pack/test/functional/apps/visualize/telemetry.ts index 20620f20e2c97..6bf1ae510a5f4 100644 --- a/x-pack/test/functional/apps/visualize/telemetry.ts +++ b/x-pack/test/functional/apps/visualize/telemetry.ts @@ -32,6 +32,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.load( `x-pack/test/functional/fixtures/kbn_archiver/dashboard/with_by_value_visualizations` ); + await kibanaServer.uiSettings.update({ + 'histogram:maxBars': 100, + 'visualization:visualize:legacyHeatmapChartsLibrary': true, + }); await retry.try(async () => { await PageObjects.common.navigateToApp('dashboard'); @@ -49,6 +53,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload( 'x-pack/test/functional/fixtures/kbn_archiver/dashboard/with_by_value_visualizations' ); + await kibanaServer.uiSettings.update({ + 'histogram:maxBars': 1000, + 'visualization:visualize:legacyHeatmapChartsLibrary': false, + }); await kibanaServer.savedObjects.cleanStandardList(); }); diff --git a/x-pack/test/functional/es_archives/cases/migrations/8.8.0/data.json.gz b/x-pack/test/functional/es_archives/cases/migrations/8.8.0/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..887afd9d4a907a3c5a5d2cb24169a7cb33200c21 GIT binary patch literal 1911509 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 rPDj1nrj>Z)OO%l066TUK^! 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 ^=oJG3fleQP_E zPE@p9CW4R=(U5CmpTj2fpEkyc#H@o;l zDcNNh{C%b-Pa*b1>VY7_RGnie#{-ox?Epz?&Y zUw{p>u?|3 zzjPmoIpb@^!|%sn?vpjc18L5l+U~S@v2fV1fSSX#+p+ue1OGWpVVP`GMPl!+sKk== zPxhMVp3vworvJ^bLC#)olw=^m8gTt&wTAiZwXZq@$GX@P=0G^Acb5&n7+%65?>=P7 zGLvGt=mYrlit5;d!tt+#X5;l)AIwP$cxjEI2=$B)Cs-7^%0Db_EU_rmOUrKiT2%MO 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

    fuf%o+ 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Ȥ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

    #Mmvnt4xhKRhi2#S0;dF9%jB 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

    Z|d93L&MVqmjSbLH{#gZz8sCx_r1DEXOF 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
    d>{yjs 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

    9PAG!m-p^em3b#Kk#glV$cE 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ν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

    &+w6k)H<@QHEoaUq{=GsAq9fREXjrNK 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

    Lj>&? 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 z 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 z%0A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�~{xKz9baqQzJ`0nO$#tB>bBNR&_O@46 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 Pqai zK5rvQPj5)Jxun5C1K?y_f(Kk~;jI^mcjoJO9MwjZ9XrZg50?<@J-GOtAlM$?_hBN( zH~J70973PQ>Y#V2cKAR?hA$9&lK#$@S(<~~Fy zX8rfl5EK#2as`e zAvg|}xt*Hj)B8o`O55b+Q|$ov8mRr*NNj8_fr4`ZJT!rI29CK2-Y+)lzbQ~p2Q;Pp znABD*Ki?F=<(*F!xyV3OM_(@BBG%RD5ln^b;OASg(vykx&XDxXENed_b!G#HUKKyE zfRVq~%;tbn;5GzMUib;I>$R1jN)krAq3Pj*EU566Kq4QSC`!s9ndfzov%4__C>O#5 zFl6+2cBu;-hJ*?BaIxw)SY_5D9VCQ6j1hRwuL)ifYRwHNTQ+?4h}DfcgiQ9{1RA-M zi*Gfm+L7uPNr3>DE&iMWyT=Alxn6-9;cy(tiF}syE|ZJVEg%UW`S@+V{KRVE5eY%A znm3;4p}ya{RA7H60N$`g$7>UYaAQ3Owno#--g60XDiP-%53B zaSf?WJM4-Hce%n93RPNr0stt=?u9;3z6bawW>1C*uUnSOC(Co&T=Cnm6)hW^+Dlp9 zK^WTS6YB`MQFdzuS_95L2#mfTo)YDZL~d$>^68P$ptmjRdmxT(inpEEX1%bWHccX{ z@`bDIUy_^5KXs#Gg)2FAY)XZnrFoajoyBxbF&RnhS3Qt}&Z>FbsjVlOX4G<5DCrc> z>}jVG5=OBVKCQfY+K)u`R^_31CgE)4;OqN6j;Ou{U0?E^vhsXt2e~!8?bME3nHrecZ6IQJ0-*KHkpMkNR|4$9&df1FLUYNj`Qr$o&$Q^`6>9ZV@{* z7jhF2)-an!KZI>P4G&P@aDiNrD1u~yy3och-rh7uJ}0gXyEFLM%1!Ohk`U%)8ry*c z%6P=kd#t@NnmRZ|UXrzTwDhl=h%dSxCXa_RAqvq*Wg``mK3(>uYgO`~AIW*W7%pg zArBcsP6o5Bb^N?T_xTtgXStV`c<~q(3J56*lj#Lktic+w*a6|zfqWfCTa>Md6Kavq zEG zH+`8&N+7RM_MTkpSfpT9V^av*iYY;f0~;x^5r*#Khk*d+28AU5A^S0xVXG{4x+J5k zM3)8zAlj46LE88btWP}>>|Wj-+DhLJm*oR^;FWY#j!uP@$#&c6@dqY(p&E&42jRMy zRoQj*EZnoq!Sw`b{^^sy$?HbLRMS431+Sz?DfVEPyBs;^7j$?)Ob;5RJV-0I34H3i zod}O#b7xC#v~1Mi13qRE;690==*&f#W!ob8`B@z}N_;-3X3#~ka^B>@J@dx8iR4LJ z0>X1!)a9;ZXkh@V*ypuH=n9DM7hQ$rZG=+ak^OAa$W$*ceq*uEsr1w^H5s3lqx_gor zT{^j)0-MrHBZ~6U0S1v)ci~h%95J}LV*-K$nDZ1CAfxKKf?KL$~GKq*QEP0zw z@x{#rgTHdYD?xWa8{cniad$uN@Adb5dW7eUFq6AK;LYP_U#!*y_pQ2Q32F#FY}D^Q z^RXaWZ(hm1+y!+Wk{GGk<~2TyH~4*l@;SFmZBI5Q7@;g|m!v^{N$*5$2JWKIhE?s7 z99=Hw(Y3UoFFax>)5PSbqD+bJCFJ9ih)f@L z_m7)jS#R}Lk6+a0aK$0sbz!Mti|4F(+fMRrz*MHF6JVE-T<4UPX7?p4k~pz#oR*Rz zzv)E>$n1|yLAAr^(E}cVKk*lz?^8jVI4srJ4MC#L_gWH?qTBmi=;z#8G(8jhNLJ1$ zi+Yr}Dua5+XOEo*(i{dvxA$E(79$+N1@J#O82h&T4; zP5Ky-da>S)$$0Jk4cgFy;WHvDhwI7+qU6I=;PW(quQA(+JG=O6htWkJ5A|iw@D>Mc z%1xf22ytAGc3*$-Jx-BJQq;ybZ*YzWeghOEGnC@_q*-6XBkDR|yiu(=gsIr#E?8-= z-T|-Ja*uk0Q+lIcyYUo-qzRn0QLIQX&6lUAV-QGkY1T~c8>}9`)99Q+UeL2`9i4T# z$<+~*!ZpQV9j=b)`VYpF;LAJ*vnfaDCYxLd!qn1hKDcpc{q*Ex;gnvmN#FVMI`g;2 z>Yi!e9$!4DAJWs5^68+SKVuDh)>COsLsaamVaZ2R>CX!2EXBvlbl5^=#&d@2*-a4FZ%R1A`#hGhD}e=RqfmR^u#4WwXR+O!n6qefhBjY?ILi zM0%VLIi#QZKG&C5s3<10;?jzCq$i`E2Pv2wPN~iX51v`?z3di;xr3{m@I)LSF;QGe zovZXJzXD`!%a@xZp>|ReUcJ}vuk4YZx33A$ByOzBn4)Kp-kjs6+eQk6R$gD{o5im@E<@5K)o0T`VM~-(u4PyK`9O7zAefX; zZRI&Ua?wMncpfFSEpP0}@bbrq%}BDd)oQgl^?!1oVqceYxdp{WZTt?ePM+_|EdC1L zL_V#u2_|0cB2f4Ij=_QTZy1O1^7zu+kO2WMz)mRd)sutsIlV@LJ zU#(fn>@++UkyX0StcsAHF30?`kcSa}vZvt$o%Zf(Q$7g^7kfR%9k?!mi`ZEe%SF93 zzi&Yl%K&2r1!vRZwU|h_BJ$Ucd(#op>XL%p#nF}?X9HMTvhR~j$Jsi$1MEGf{1Z2X zH6w|TfQa^h+%!|q$-Q&C+a!!iDCgS_*xl}RpL)@cd`nJ96?fCw4U~*gvv7s!$_2;Q zc%99RG1H@DRanDtNxV-z^-9cR>_fL?TB;P1+aPFp=qtTT0vw}Tu7rcev7WmBEL*19 z>yrwKb}fX zQfj*wd>y;@h0Z%mSYf3#OYe|Xabx;w(Ms;3=c-&i#AX}`2`YQ3GcSVV(PVfi#oQ-j zxNvZm)Rt_gvG3G67K{rIwB`~nm2e?lqo45yeS(d1uiVc@LyxR&>_IuLkAZmM=ce?q z{3;t5(<$VM>R{o=ySM@Do(%0T3fB@N7dY{K^yOST4oWYccDtko1-RVNbk;cFGCsR- zQY~%;VKQE#%T&7zYe4EY$1rW(R)pTVPRdP|lWX43GXE*<+6}UkyaN|v=5V}86->5r z6kd2m{^@PYR5GUVFv=Rhke`%+T}s3>Ic6%DJzGfz@Z+cyt^tDqt;kAQfkiPE@_{Gk zhxWX<2XnK1xnmzfe5ORSYID2vquxs)_AtmeC?@Zko8MN@Lz_LR+9jV_KDvQ^pH_NL zw%Y+Qdq|ex7~*Sf^1|s|IQE@TLNWsGN=wd6o#3Ljl#Z;H?}LXtjZC*r574caXNw+j zAFi0~57}iS)Qsfv0q}3)%hN|GIWTlZxCgCDAV*MV)*ffX1Atr7Pbt2LTs#Vj5nGB~ z+}K^{%FV7!7mQO?^*au|fqAq`+e^wzemiceZFY{f=J)4kHypRF+X&uOTMlO2UmwWy z3;As&(x6Ej;_r0_VG&GKtt^GrgA~Qr-O{UldRJBK*r|SLQ){(^4yT$K3yTO;Xo|QE7>gcgTZRxzANhei}YhHn1%qVk*0)+tyAos01iMEPu6y*u@IUeKb zk>T;Sjxh(@0P(ciA12i1jH?7L3BDYkw%q)wMZDfO4{CRkv_@e){V`YD_^&ed`+Gh< z*Ut2n_83b@jx)v>{Rwp&0fw4rs7tGjraVV`H0Hfn7vo7RU}FAcdNer*VjFU+yvbwK zH6jd)P;YwIMw-AucbkvP8!T^BfH+Z?UN>Yej5QcTP(Kq-G5+;27kDCn)>{Hs8X%fqhCM*U4F}#i7CESErlBBa9 z$*BK;vA=eDIQ**|+}7>=64c^|zw#|)lVW5^kxZ1;kiNIv(M zyd=I>B0#Xh?8fW zH5k&}AM*w#Oj8wOw9Iuqb?I2i5{P=s-g8=fBkdE+vuoZ5rc(t1Ptn7+#6ohDTi=SO zq#_vFgxSkdQ3^eWVm{dXZsMRE-(9Y(pK@xS;`QF-kLU3;mHKA9Zb$Ilgjb8HWX9AX znoeqKI{Ca~9S$+gsTMIht7`s(Zq+)KGC6r*ug^B~68$#9OpDgA zfks;|LA~r4s4RD0dIWO2TJ}w;Gsa&F*yge7c>?n=*zi^VM{D{ISYy0pZ0*OrkjcYa z4kYY#MAl{QopbJ#Lzwf_6;hxj9LATujY$4{eq~v46-7B;ExX92K3-_p1Rfvm zFp!9ItqmV^jmy+}A0_9oizv7$1$5undCgKK5Y7=3r zu+DFe}{m@4?!%(YZh4^SYh-V*wbq zJ3Qevf>)98_BW`6l?2ZB9Ucl1M&2P`YjzeTyIb~=$yr$0T`&XJCx_JyYRF%_E_oZe zAk-whebHDUIKTO1q)RSOJu^r_!W(fdXoRzA^bW|v6rx>5Vt-VA-Y}ZH1sL7!9;Wv_ z2Ve57nU&G0S-+l?v3hfDX$nz!4Q8LNlHxp2RC(v7p7=fZsGItp&K^OqJd={ez-w@ta2~K?#R)u$}=-ccNlXL z&G89=^=Q&iKz3#_0#l3e9``OlmjYL1Zsyf-%e?YIsE$K67CTkIw$LkeCkkfGWx*Qq zP1Z9n*v}?n1)JsxyOzs%W!m0Q0hrzeCun#}{QinwfdKA&f|RwwX8BOg$=X|_HVdH| zLK*v=c(XWg+FTRdJvBW{m7fgBK>6rWh&LC8INMbfz6#bSP&yHlR=jfU&q@!NnX&$Q zQ1;U$tD)mWGEVBF5x`}Z3qDuUfemCfH6X$reGml{wro$&clZP_FKwQytcneW^_6;* zy@yZ2Lx-Zd^s_uCMifaPGpE#z?d{*D^$vR^6xbcWEv`~G;np$;*7~Z4^IPMH%zO` zdS}Z=&8{awZu<)s#~X2PB&(=jgHR?ZdgP|sLIJeUP~vSpmb>K(y5TG3FIDl#bR183 zkF%wjsC35|-jKr&`z(0z1JR4Peg+n5jinxR95v|tw7#sBAVK{72}-sYI-2hj~|!a?ssFsl|FBFeFycgyHrz^Fpv~m16uN(^56;e*_Xg z9@8pxC(Ko#mUNgip})wgEho7Tb=gmppoQ)*pTiEEuA5DNeGZr5SoN9fpnQz2**`vO z{2G;^9vj#k=3B4x@pcVhvtDyuP7z4(WJA*F*Syx$rT6CRi8rLWIA|jgsyqkNM(wg= zWssX;HkE?6TUpIJRSV3h-|oPRvHy8W@A{iH8sC4u;)RxiKQcLf{lKsee^!JKCrj4S zyG#8-VUoE8)iPcpG*!cVcp&Oaf|t>}>C_c$bLQzs|HZ!=&m?i0SH(X$Q(gsl>yF{hGt*n~ZLkQZvh~D*1y`PZ4?P6e!w`vvkz1e;f>l9*kdxVE}k1e zGAv1SN$gDj7;wq!gG%3(@F6^XZ_*-h+byK&11riU)s`-|hiEQ)qSA#G%LIPH7-oz- z>{6VI&Y=+IAy%QT?Shh!sAG-k4nCx$R(HqQyWS1JohH=XeAcp`&#jmhH!|htg+h) zhvQaSW6T_o@wzdcD!E8mJ{TuqDT6QS>?1bo_vdQca)&nylr4$6$_A6)jRM! z$C1oMyoL+jNN%*fHeqdB({aBSlI|*oL3tZ?wbOmBz`YXzED-EIO9y@19oGT0+64rz zZ-=4!M|K`F%}TKw5ZI2xCwv234Mgp}>J#xNzredh%F>G;hJg)f_@bj$JK;CEqoTZ? zm)A#Op7aGtu(cv-$+ zr@m5{M_&tc2{#*YNS_jDbJ6Bpg-hUF&dcLV^RBg-;#{bK>fp44Nx0+jpkV9(WfJ9! zxyVIUUYPL|#lmJ^CjWu1XMc|qzajXb4r?7D;4&?(TrF0_$D2LfYeL*TV4g)*B@fijw@k|M#kWVRu<0YWxxlaT&KqkzmW)XD@%QSeoY?o~oAQ`%6fgn< zheJf6j|j}r93BL}y4KJ;6!U`_yTbfgwp#MI7sboEv<;TsNPZ5}hs2S<-C*D)B`~AR zLd%}f4CqKW*3lA&e1R#zpcVMTL5DHpUvdCrXr*av#Y25bx??VUwtHfuT9Tp^G9wDa zn>z92>#F`87b&rY0f5kokuDrEpLv~l3mzm+zL%n3c|;%653!9Kbhq(E^Cogh&WQxB zTR&SPP{pR9&3E#}ALOevX83?-hzGS-?y%&2!+Tq>NBV@mjH;-qp8q53TN76(yq}0+ zX_NFVl~~?)-&hU@`=MlH+n1&4W+%nXwE>FN@V-$%Ui4JjarD#uoHwc0oPp<8&Y z^3*gJyUPSrW;U=N7wO*ISIim-U$*e7#GZ0Yhs@}XvMNGb({z4b9F4IVHpUvcSO^=A zL^GEQF@{}i1xao3Wlme@*mR$L4oN2QHuVE|^O?bHpZd90;_?Bj9_KYwHgI zwB>Ac_qILqD>AU34W(p+nTG%zzhVRVOdN{#HX+`K%3&nXIG-WV!_l<8wvu5P-Dj`w z(k=J36z#KQp?sIb{>}#hzpAP-8r#V@F0eO_7c-aPf#zx(K{?RUz&xgSVtSi1eR8+U z0UZnNUBFPKa)1V*3qeY|13!`6S?Aki9umcZ?UnOOgCfJsnOkGM zTIRs{+??a~vW=D7)B2pCQ`j1g7sBc!)|wO39Ahb00(ukd)oucAU67#Q>%Y{y@pHWh zcY&VOb8;W;&Mz63nXmAi#HGcI)5jERDU=9`AA7!nl3wN5Nf6x~l{IbA!HW_`7+oh8 znTM&;@KhbA;rmHZUG1}7*V^!0%kyV2STRoyH$Kjri@7~0PItJ!K6cMcq|>&s&D+zq z_HNn54Wc^`F?XQ5W-CQ{FtW~+FB&$$6Z&qF;P%5Oc>j~Xy+7Cq^HKwG&f7myp0RBE z9P%_cbTTycD)a*YmPTHu3m2`n$_tE9abm-n=lyPKdBr^lP106sJ{n4UK8++^d3T!Y zdn}goW1ZIZGxda9i%CCzXtxJID0q;c5Y`%`e)}4@)}de0c~f$RYqcA9Bx9?I`~C6R zG$(yhNfW#9;SWu;7Cye}O9Y$#D%1tPbHkBjV_7z6&ibKxlkRYC;^n%nOuASXo{TpE zC|6F=bD7h~2Fql@SskaRROu&>pJ>QcNq9 zvGUdDreG!beRHeLm1S$p`19WG9^q3T-`EI)mnt~V0S>;Xmxd+ar#jFDBndr;8so)M z5!-8ACqvwcig%3p^8>{ay8B2Er8q&)5c<#Y8V#|PGLWwrJVZ=(d*4bdrTfuKF#^q68dosm*8uHLicFfpbw(P^WV@5_v<0U?c4}5HV-E>OReL!cP{v( zrl`0kbd%jw-`~{;jv!s(Obi)ZWkg<2`PF{I18#O(hJ2BPCMha!6_kak$}) z{`zVh`(Ww*7Ic!7mpG^E*odqjK_T{)A16E#o={dE?mFi{J%vi#>{0K}&2qaYrl6?z zeIN?IL_X6xL_BvNqYjV7NVfzCiD@jxrrS=4)q+DjFrXb5!$qyehY@ z0PWx(ME!A4%R-LE<=o0U$T@2Tg;LVX7jwJu0KWHz0jT`7i$RUOcce@eue2SnjxWj7 z<$Q#h9n^o|bTtVhl!Y%Z(?D@;no2L%@+P6R$67he*pRcV$`S7?0TVJ#mMTUokKv(( z!)-5wew&~Bsa+3;psz}iHD3n4SuhzlkSpRNE6=$i5#$j&h=zf%3Fqn=r%>eSefQ-`0xjJmwrw)MPijwpq1c)^I_o>sC7K!rQ zuQPJEWLkc z8>KFn0(yMpw)f#>>3F8{D}}liN&t9%a*irf+Wh zOiqWQSb0I`!(}uceiuVn#-f|odIIjz%`OGu2DnJd2CKl#M$J(+g8F`6Fn&($70$0Q z%dRrDcYKe~=pSbJF-(DR$APFoH-3geXYU?_#E_TY(fF96v5*o522$={Xb)i9*z;Ld ze#S|bBuu?mefv%+8nv+v+W@??uq}}JY$fjPcRseHm+f9F9%9GuirEIe6EiIiN>6uJI}6Oi1upMlTK`ahZ`QC7^WjuT)K+IZhvyISVJ!Zo$oRzI7Bj(mowM$sZX4e~$53@@IL? zwuwh&wR%0XQBceu;e&|b++!Sqw&rDF>|MVO2V9M$S|nxM6JzUxVUSz{zCLV7rsH_A z7Wi-ZNsol~tBZQ5f9y`ZWQcdgFAT1owNBzt%Vf!oyIPz_paQ&2!h#iyO*6Z5E7r&l zZ?VTR0zSj7)`NU6@^z|<4eki?|ev+ctB4NF_iO2Wwe$O+!v&<}&0!xzK$?X#) ztRP_ibq*4ajQ*YIX+bF0v}#x?GK&*2!8z&Ki0(y z;-ug*j2@x{;dd!p3mziIi-a797>4QBk#4+!yfnP6$ zykGaDbLc7Hjk{YCobc;ChHambL57j8K6-n?$)rEnwgH>kHz9Jk#OzqUvQHt~fd)4vszBBSOi#m}15 z+FsgCcPt>@XL{c+*EH}2|C>Q=DCf_{hrPC=Izz=m*#v*@;y~Se2f^|Um@ivJ_keSF z&X`zH$Ezkxg7_UQ(DQDN+ML#IF273oCScMHe{0cW!c>iVM4oNH_H?`T=iA$U$Fvp} ziwFPLo|Yr<91ah>bboOPT0gtCpWM!vcdUqpQw%ve3Sl<0N!Fk%Py7VT@vk4YO87<-l2V=kpK%N` zvRQ8UTl-SkyywWf2)?376>zNbdS&~EqQ@MoLgYi80Y_^JHXW^@6liy`zs6t*;{A|G zS~0B2=EZ^84hi_at7D!dh4o4rsvV&)inl|rgAdh^dC`9+RYWW&if-0{zVE0HByl+R zmErhUVQC-k>pJVb-H+%s2V%kSbAY(c_aClEbmu7;d$ZEq?P9w!62X`q)|K$Fl@{RK zUP<1Jjer?oQhl`&TFC$1hDX}4*k$rbj zZ+*U6bx&@Snxh3p+V2HhB}V=mWP9{fXGD5#Y=HfkDWb}`FZ~^Tg!#2Jb2*4JoeU0a zSBLk%!y|3j>DV4NdhKR;a;@dajRSHWDP9-Td`!9+1e$&mezMS6HD~GWoFgR5smHnN zB0IV~v$dHFrQi|?9W7Zv&9|@U2+}9iL_~;Y9db(O<+y96ftioXy~iKx`v~C2LJcSE zk?g~_FV3Ii#POLy)A_P&-hyh&PbB4xk?TpI9YYd;4_RngPE$BJQJrNWzO|XTjxg4w zKonYs)o6L<=w|_d%Rnn6N-&e(!#$(4(I7@R#pXPZFq=viOEn1ufTK4{k0f~jRc!=dbG9p zQTo-!NPJZ$=APxB4~$X%f@tNT*FUA0Y=z*+($qR)+i+e-(Hg_06eVm%v#jKXDnXhf zpnlO}Hc`~uAoa0_c8?@EL1wgjPO z743@@3|fh2HYYZGiR7jvmRTdpk;8E&ah^}eC{DUWx$Gg?au0Tr%_eyWU{+^*fKi-? z*bmWQyy6f&hDRu1jHQKG-kn(a{d$#?c@(VJnGAP;p%&m*^m9Pol?h^@j%X2i<%MH; zD!=Ene|yUW7ZTUmw6!l`l5Izv)|SKC&W|aoo#5DVVs~4Zy_*xmaT8@uLeQ>cldC@TAy1a_bsBS&k%0l3G1kU9jv-w*%}>%gS$@A2AoIrcbgi zcVNvDSRUMmky2wR!PL}UAVT$E-DV>9j|+ZGoS0#rh`}Y2=WJVqls0(dPhiVzF0nB= zTBy($HqbB4`V=yATRDVFdIh8$x01a{ZaF5L^nz?Y+9g+>6DLr7^FfxUG;_hHc=LDS zUWyTlHufifd88eO9tl*-m*B@wOPH(VDRP`8w9OL5dlHQ~#W6Y*EN5wO%lU-k0z$HT zQuSYWOpGBg*qvNv6fj=Q5tMZ3N=-9g=9arC=G)x%7s{fKkQ9;@TXM{&uR*G}4XLcm zC6yykTd3F=G<%Xf))qN}Sc5Sj$-X+`Z9HkabDtPg;t?r3)k(T5+f3o<&Px5fWJ(_9_U%dj*ZF^rP#UeD7%eli zOA-e8e3FODtzhzX@CGi*0>22P*vUu32wvEnT*BgZ2`UsO`{3=Rt0JL#^P|3E2raF< zo-coM!tGBVf?);G+(UoP2i)HGD8OE1;)bQSk-e;aNDWOdf_>_&?#_rHjbIR>*YYM(W5nQRDTNzXljWfs3ito1VA!vRuR46(Y+{LlP;D5 zM)=j&9}%WqyOd)lj5(!}=;%Ijd-b7ImTRq{i?9S!v(BU#Ibpf7Op*X+>^PRXP8?$4 z@_10QfG-0NZi71&75oYD|@YM&;91hjv-JZ3}#j~EJeyt~3#;eI) zy)RG3Qv4`CbK1MoymW8CnJ^r4OJCP@-_@D=#L#BvB)1G0lP@FN2O4;XNcV2vKt` ziE9#C1chDAW-$;QcYBEL1gEeFfq%e0O41WOlbCQ@wEM|`J5#&o+YjaesdQk z#Bh||H_@7mo`z5LX#75BdlQ>N`c2}BBpbraJ^k=*)IU^amM$7n^y{pDz{1w?jp>Es zIlNv^pVdPhJ9AXU|IXUd8{BNYk0+ufFLqC7#<|AK@u|&!e2gv8=eQ@btI`ADJD`v~L%*{(iA5&e}F}3g{0)8gvIMD7wQcu>7tS}Oun|Z6#enN;OXm)0mqj@KSPgnZby_}||z#a*1BhnrGX*QN+V-idj z3;1itGc(f#@tS^sH4eu1Ws#9AawxFaXNMC~G~~0s{^qUtXX-mNXZ_Nfo&Y_&u`<+y zH+P}#%dHoiS^w?RG!IK(V;6bw@<_C;g-?SJ;Sp^;7kg$hj`cyVT--)Pi})*eF_S*q zkP1ZK28MiYzV6}ee_yKEY|3OZht3#Z`_T5hbs*xcDm6=$>(((|kp;Og@LQ~jtb(x9 zD}Ycg-pKD;ITe?Z<#%H*@AQ21Zc%HWtp{IpBoyz$`uWVir~fz?u3M_V`~Cc^Tn~7+ zN7%N-@#q!)qkTuNXZ)1&;;G-YEYA&tZjGudengk=-f{iqOEiKozz$vs7CuC_-bPF5OKD} zdo;0=&HofHkp{R4DZwB+DvE@ZW?f**g&@7!Q}56mwv7!QpZ2+z9XT5_>0mk5R?J+T59xt zf&>uszA)py)$WV714~!R@)wDmI2<_)UoUd9OM7q@LoxfsEbaxL-@vk-M6O}E);!1? zK@^1vbB3{%m1__;KTQ#@u-4#$ zF-P!lXxqwvh%jWy^I^;o+0(82_AqIU?}4V+;80sm#bZgwXvhp%UrP3@62W3Gbpave zv0{hMAOp3P(GTKYZ#4_YXAE-4ahTIK<;=`b2Ko0sr}*-9*h0=p4oPax{ed`9N*?31 z<-(2xNlN^JLns(+>yEwD^%>gAOI<1cH!L+<%G}*R7__s~<<(ckpp*xt@soSU=*~9II=@&YI32sQRvlu2D@r~=GPV1DjJ+$r*jjWsaZ1h_ z9B!11)!o~f1BJ*axk#IU>ip>~FU}H5;q&l(ep?;n$qu{j_AptBX(BfwAlj9!*BolR zrSX0#jge`RVf&hsB_^Vy){0Q+(t~(W;QBaOhnV-`Z}v)ZkRH8<>t6jZK0Qv)^ErQi z#$Z6=zA{p_JX^sN^XZ^H1uN+nx#m>6$cw|EX&f?5{UsKUft#zlkSv#qFkGT)yqw`C zP*HSkcy`CwX!zZKH9>gXi&@Hra$kGl@-g2@@Pt^w>(61``gn=YKXn&w;}Us2G@Bxg z@6`!t@1AVr-X$V}SyoE4Jgrt~jktIG0OJ_4T~Ae9Z@8weqlNYX2)iB5X$Yme!12pt zTk_={kyr&Kj<)3{THZSP*(uls?sZ|C#UBucy)Sx9{t8*HkVcX1^axWjbz~(12gCmm z*xJD(Ig=cm+$LJH#@w^2iml&rXyW&gF)NK5PI?u$;GwXaQ(?@TReRVn8gvHNvP&Ab8m8Xm*L}9 zuRS+`7hr!@#c70FZnHjuW*UG@?odEhuLX$^Xa}eCnpIN9xb1l-{oXk zJLG>)nGKqm0$7ca)gH)L7Y%K^w1OrD)2*a^FQ8#VaJ?lB}!=$sUcguf@TPe^qQR$YQm*e+H-Y zna;R+V<)6GqNxOotVqq@)H)tVcB(zYnSbygNZ)70(;%O z62_TQ)a)x{*c+4|vHmxP_Q*y#Z=fMMeLilp=Pd4Qo67w2b7G`q-3ES#zr}=CKL2xm<5g4E)y@bNta*l_>a@qakF4!s zyjSMiOze5KzWC)QZ1)3Ci{=QFhfQ$a!X3sZ?94)h4Wg4MtAg#_+V%NxRJ;w%J~GwA zDXY~+uRh$Z`7I)q6krR>SISebNG~*O0+I= z^Rec$aC$dWetyC*knXg#tqKP&1ec_xgb5Yrq3tidOw(JsZ-X*H9e6|kcb-kCy~YiZ zysyNpaPT5t5f#M=2&1ayprvFP?_(cGt`)r#Tf^=?2wdQfThx`_vIL0Q#C7>57Z=6y znlo>um7ars;<*VftQWxA8*)waDzgrPB)WOOOqIo=U*nOx@EN_M?&#nui!bARbK-hx z@BWMlikcKor*OO2EfsVW3nqxnnRWHN->W#M7IlU{);4Z|C>ezi*XGHI-aZy`Ir+kA z1ReK#1;l~X-fPx4?I|;suZrdylbdMll^MZa^%2NxrKs3T5==XSk8;f?ECQ3>I;|&` zn45h1Mw4_Jls>;qLrvL3SjN55T`985i56eTZS}UqA$+tcX^PSnaL!8x9>p~#_43rV zCOz8=jU6R{{b%-m!uS^KEWE3jt3$ZTv|Kz`9!xbd_*&%_=9?t9s-TR7$TYLJ=F-D} z4J-g;S>z=M_dSrBg?@5(%Q{+`wGR3e`8$Ixx#y$oiUtQ|ZZRC~NCWK*Yq#9Ngo%4e zugMEq5A>eDU?PD)%fk@B*T&69FmB%jfFFy%A|N~=o2Q6kTI$~C#Z!9*l{w($)gyh+ zXN``Q{&>q7&YR!x;*?9)p6$@aM%O!Na&-R{)F|x-T)g=x4occg?5>671>c_-HtJCM zo?xjMUz?SBr4&y?l5Z>tVB%V~K@UL;Ny}Lq+1R0oj0a*DfS&@Ocv)L>zY$e)jLG&G%Pv z49({?Ues>q%r`nHo&VO`mnqfQ?_*;FH+I^;zPOt`&KcHj)oGWGG#hH$v7E@%Mo95E zzv8O9u*E*yI`%BZ%au_`drIGo?Fe&3x!x z83S7rpUcWywpgvKJZK#M4ZUwYyr6s_3R0^NSbi~R#6|lVVdA_M6ge|m-V|6(vqo39 zZmkVE?jea^*VA<+Pq0J5doyDBaV@45_$Qv33HtAG2oZ?hJ;Q%v&j{JFUZ1tYCuC1A z6?_lTQN8}Lsjn%;VqAWIOsZO)48I*$zCTCNj$5^&D8m%p^{WW^$CGdW?H>e?uPuQD zRoXXc6B{w%&;aAP!%u+W!3~0=G9O7VNXliG$8RHX`Y20?N5meZ}U6Bn3VGRBs9k>9LXFB2Kd8iEi`;R_s$lY&!g_Fk=@j3`;Ewe2AAkad)E*=LFMsk>jjc zHtg6~Ya+>=X==}+9H;~S09Q*5F&rc)FNSU^$if>Eu~1oqBrWpWqCM2gwZ&pFt}KvG z5ah1w?bs%V$gJeNQ3^Z}WLoR}eZ5}I!xDpaBuDN` z_160O6Fwcz%kTnfE{02G9V;g*HJ-l*)zkMNBkgL7y@z#p7mwz92oEY}i=SB8K5VaL z?T1F*1VT?tpB7^wkzAm1NT59IqrKje@8%`t3J$QJ!2_*hYFllP)& zG3FFWij8H`v-0(P)_#2KU8RwuS5OVx>{7(Nfb)O*Rls;rxB-Kb_)(ao%|s;e?`;w) z%OnrJhCa)@Ague+myH3+HL|!jheYu{_l4T**yXqm@=x~h{P~J@6IuAT@fui)BJe@C zB2+a6RUhYAUYSv4w8~!wD%5Ki`PSKZ)$+ZmeCNlV+{n1?`_OrVP#i8#Et^140hB|3 z*5`*aWJB4D&7-5H+v)7(lGmD-J|_Kf73;55{q3`X<|%~6A-``!z=;+w8v;Rpj{P(l zxpyEFTM~SVBYax@_%jAa_DxYIKv1M4Tu&|ccf^Gmz4B@A1GWo)?C~-+@sx#)oGko2{U@?MsI z&dDYdD`LOCH)5ml&}l_ZdZ^zb>>(Cmm-oW&jgO^zd560iz!<@1G{Zh{-_OJ8yUlN& zIq=E6`QyR0UZ1z5d_9Mi7R0yXBF&@qIrc4-gjBNOl$T$be>@!rZ3&6A#S7x^z|In> z^RJRAcQG+$h2643mrDPOKHA~QQ-0MJ=aeV$aVgCVw4Qra&g@(K4pkeo&rk6;U!Z!o zMyYE*n^6%^rRjo4jLAX?L9u`7oi_=ds~@0ULJJ7AX!``0ed?3BX{-6N_30xY%Iftx z9e(Ch?~XiY+!c;cw(60U)S$ITd=Qt_(GjqH5r&sOw1|@ihUl=P7JA8*SN87=$rOM8 z*7Ko37A1=t5c&``_{)}g`SWHY3Bc#ol4DmO4TO58%?}QrZs;1s(0Gz#^>!J7XbqtQ zk%2#QlAP7dhoo=BRp*S2;d(=M0}Cyu$CqthF@R!_)WR#mIlbP7#T)~O3n&eOI5zX7 z0z?;m59>c*i(B#ftuU?B)+AASL-H(?JVz-Hazq1I8P z5gYMrE~WEt&Ol?)*KzLXZdWjuSy-+Ek1sON-QON4v2xjVmqP7MF=%dCv13NQPRiROEvo;J_8n8uyh>ViAG%VaRaEfJG4n zgaf(T(?!7tkIjVOE69y;I8Y`!SLdf^r!?D#l#wFt8$a_{8%eaj2)e--YhanXp!xO6 zGh~}V+w)2`b`gqWL=3STBZpmI>@5Q`cg{QE(fCXZElc!g^0&^(KOYPgK$!pTb3n64 z%BKN}WmXDL=~s zseijF2-t74Q~X|#bE8=_<@MfwGQQ9Ubzb0kDv2)SfAbp=MS^EHoD9;y$Ff98+*C=0 zlW$ag=S;4-w%;L_W_*aIzwKnblw0CeKH^g@=xj$CV4ygUE7GSVEIMJ`wl*wsGr6Ym>1AAb8+jnQufS$`@S!Je=n_HrMQQzbK|p)+Oo+%yB$bq${jA%&#F+x z{lUzQI~Q0O3o{JP3~-it9@DFZ!vN+5lY_)Iel8d7=Xu)-Q8aK(Iej3|JPFtwqdd{86%Bdqs;&hiQ&@C>fb;XlSv%c|2`^)po&&ygE z&I@|1h7VzIk$EXDlVFKn-p=C^H^DH!_GM-F>I-!t>DhQSVqRw;PWS~PRjIMpbpx7W zRtvFH8Zfc-a&E9x;Cv-Xp9bb(M`@O zy#|b>=L?J#ukiqLytm%SuzlRLMFSxHQr+Hw+{o$A@NlAvCKO`Oyu=MpPNTBeVwOdD z`z-w1T#K7bu_1Tz(pFB9L03l4rB{Awc^b11?5djS_{aLS9**Y(c2h2qx^0?ij;#rw zsrF@>ZOU}sF(Pa6{|@yqa|lHvi<)9$Y19vcPvz60W&J)5gI5%ASgrGEM;dX%CS3AS zmoB`1z-r|gY}j>76|w#)wC-Wl-k~hkhShReFeaYg-v8WC%mbq<)BFErbG!Z2?gdUv zaDHQ#>dy{~;%LoD77=Bk*Z;SQL)$IT$`CH)Z5CnoQ(=kyF}|EqZ6b_k}M1N8$`yjx@Wj) zHU`bkNzzSb8T1Q?rgG{%AQ3JICAXe9DgRlW5kqtnmJpumvzEwm+>2i4gdIwz0 zD0V(*Ev>)9L21g@#3D%2)aq1cyWX$L(K}SWvdt=-%oq2o zKie;QE!19{@$Vf|+Q&7L0PN4>{@WxgZV-})zX)9n@yoRU%P>-W<@Y)7lz8_6J2bu_ zJnns#X+gS(%|V#DQ)knvOC~|*n%hghdrZj<20xiQ;Wu`7uIW7xu{mfnx!h%@$+&6j z#K7ob9&AwLz0T%2JY{cw#jc?rQ^NRbN9@A}sN_EJb9cY+*1wniH;b5+!K{ca?zVlp zy2fE;ElB&&eZ|qe{QLb^A4L5$SnzyiU>f+0NW|*<*_8BwV!d#Q)4Pp8mTvsQZt^MI z1P#ZMsw*sx^QR$A6^WzhfB838#G0sMTT$k8CHbNba=lpZVuLR7Kx?naXN;?y{ub}* zT32~Vo;0(Qt50wMZE|HNczjVO36&eOe8%tw`e*Km=SsNzJ45IAxxaPsOm?vHo&2p@ z*|)$vj%&!6mTrpI#oGc`KmEpKXvD}{kL|5Lf`yy+Z}*IE{8`tB;$853a*?8zLAY;t zSXL2OV(C0dJGbwO=^Up>8hp#BOF!t-pRuI97YL*i?e4p%qhRr!K*KKWmLLyTXBeuf zp|^w`=FVQ6&?iCUsxlVsjU4jaYNbbpck(jQ3@g(C9DmQwg@03-=O=^@zt~*o|8I`O zCU=oi8OB%q2%zTSFL!`(Q`IR{YFRS%8O29IK0A3;sCBozDRq+!P>TIdDd7fo1crp3fA;J4f4>wuLPGS3v15y` zNM2fpD;}A`d3XdphqlTXw(_fTHVi6P&>`cd#0uY_7G(){yQ@|1Pt3jY*!V)4E%`ho zYB0|d^f^m~UT%J^CNN#tSV7iIg<4nZ1f6cSJY0djT82AErqjc6<)ztn4?8`At?6ov zlB#USU;UX9HjE9-l0h@eroy$k&KxBVRsMzZ8Jz8>b07SU?VjvwwLX96tDQIVZA$wr z0PwH`JWzVrFL&E#MM3cVI49^8eqTp+uZTWaMS|!16WPfnV>I*0?I_`{SMUCO%PoF!UAPACF=DzR_ zLs2ipW|6LVzhSL(z!l+eNn0{3-1-D;0Qcn}l&|^K$%TSQ!5rbHZzrQd?EJ1dX;6fP z6BVoCLHf6c3EEKLi;Mq-K{TNl2i=4C9hm%Ax;SxlV4N4d_`HCyTir z4-B{H0u`92%|Aq?62h}LkwK5ict21-2@Jdc`aN?Lg1n>Ibrf7qk`h9yfSu}VpdbF` z80SvO*@l!Eg4GKz7t2`RSrhrejFMq{?m!d>S!k-3j9e3DK?``*lOZ*-BuVRD12j)p ztvE^FJLx++^kD1RLkgcW(P}|$MAJgr^0zu;w5A`z9+uc$9R41yK$apXAG5@Zl{|n6 z#C>-N)%k68E{z5bD|}p2k9HXuXCb3j7B+p^7D6uPl+Wvv{I>JgZJ(+(AN#LXrI({& zCdb#(2m!zUzc`bH?uBm(JTd3RKmm)Uzh!sV3g>QodlJTtYF5aC4~bq+b7x0h!fC); zV836v%PW_82;nLqqE0V`MR!u;mq#=sX=3C4rM0Yj0iR2z0h!xwcPrM}-sBcn0z$e~ zwzj=cCyBjyr!s0KafHC9U1j^Z$!^h6+AIr9E-%x|AkEkq^ryVZ$B$i?B-N7%a6?%H3dVd2v5rqi4K!lc`v^blbK z9b}AcdHZon|HQ^?won5NBO`JbMK`m?E+E-?FXj@*IopYmOi`6W0<-{?8I@0ISN$kc zdMPxQrCQ5$UiR9_4da$2RM^=9s-<**k_U$i9T)D2-3+NvKJI42Vv+6_d*?~>X$Ve_ zjZFP!Zcf8JkS*nI!Q1X#15O!p?ixyI%^4*4dsrtOrvMP9is$E`%dbF`SSa1qOyFtw+>;N#%sc3c@WJl!5BHy0 zzI|PaHkl#P&a+G09AaB#1b>e0ju9h6!!Grb#o9^-lk?g?VF!oSnV?GCPNh z9Uey*xRPjbL`2xn)}Tqg>^^^R{XtvwKz~&#<`yqmij_La&oWE4P^euX&1}%Nf77lPHibQt{^~!67&IOu;VLZWP9{bOb2jB zEn+%fnnN~uo^oH4cy7F*ze8L0VD()P62<8!iXrP_p-HiM%;R>QvL$f@;0cTi!Q)Kf zs?C0{ZUKxUJw?dY(5?TL-APqXip8h+rMois`KXQMMETcJyQd*Cbtadgi4-|i5EX$% zXBSOil|d3N6#4=Q*cWF8VP$_WNR}|lxsP$ z+@<2=V%}GyVTiIS#|x_j+`N>OZn3qTc&oRh#vYfFVQd(3^L-7G&Q(%($bPL*VK}gBXR&im!1!#eMa$Or^c-*M z;n;bQ?LY5(>;Rclrj^N)i+Mhu^3s0_Sr?wPl=P*M>AktUa@J3j>9O?o2(pg#LS?Il z`EMZ$YaH39a__cibt`N1Ar_;A4x4LPp`+;G2DRC!1JvkGSdX2GZ^5dAHMRS+?D-PZpV-rB*A1ZB_&lRQYTIg_d8e+#I)M7I5(naZ+ zLyy%15{i^`Nj-P(r-gQN0ITl@K@ws`6WYJ`84AiH^}cmWGPP|2^N^QuSsk;>wt%-P zAbi21vxG7qJ{>$gNcog**5~i>PA58_PHMA=D~$+O*|60&UXKm|Q^>csb>B-_iV4iu zzvqiRmp&NLk}Us=w+8#{a%mELnk9)|PA~JaEbm7Dv7!fFT>0We1U`ic%)ahT`QYe( z%Oo3UW)RMKooK9+HoxRkXZ6==UAELm{BgX2HS4)QW}R`ag-AYDgfozO9fpm@BU&>? zTZ;6=CE~E!7Zl0z+goz);AMY{xghTk8uP`08X8t;vw>1w zdN3mAvS7-p5)WDYB5Wu4!V3Uk-slaTr=>F5zvbN|HRMh@#6#9Qo~=wfxP+=CIHGy~ z5?bon6QS(Gm6s=wVfKQzs{+rX_j(u#Fv^tkr1z7$^w*p=sON5;bT(ltXdiBJyP@00 zX`8Q3;ZM~PN!_Ee&p!V=86sU#-*JasqKm9EOxI+%ua5J|BdW}gEWoenaTM*vK23{s ze5P@<<`MI?{Dh;IOc1!kpx-`BwqC$HkjA?}6WIEDA84wk^zbvX3f#K9@^X18g;bCE zW4C18+Y<85xle3m_91uivy74whl}HHDkbwFUOg>Fliz9>Be!zjU>Jaul4?>V#_i={MMAX*$*%nCm@lN;^qNhCA7# zpf6Z?FnI~G5U*^L8WfGt%@#B~U-yY|FOCUJeq0pN;j^-E13 zjBLAq@jS}_5jqgzgM6O|^~sm=*=u}kzphrfj?*yflQ2fp92o=zCg&y8^C~ z9aqc6>^UW7uXm5yE1n2;-5`+tU90Q2HJpbkpMqT+uh*+`I3C_nc`FJc(T_h8k9{2)Th)0(!|fD-Fq#Z!D6~GBh+LJJ7gz9Qvq^49jZ1G!ZZGGylwulfohM zy__8m3g>#=36^|K{H5+f>o-^bO>Vu2tUja(H^E1?lV>H9 z9p41_tp))p;e?0?XCacRU*eS0-OKM}Sm6P*JO8Aa*%|S7>QP{Y$XQ-NE4v}~vH$#A zdA18aU`oQdOprtfO#7AHHA%{+R(I0F9>AY@Dt2kP6!N;bfRBar?s?17qhcwVkrwyc zR6%~Fpd`aP3q(mTIW|rb*SpM>#y~^n^E`*N zBveop%K5Utp)WdCzXe;Po~g#)Jm;34-g4zkP8q6)r@g)1zx_a;rsDQEIHh+@F80s= zVeh?qW=D=~!T0$U7w45nm)@Ph1%|d*MN=e;{*B%gy(b6r--oq#OKM3+{q1hG8W)54 zfX42kXEI{NT9J{NF*wf8ZKPiwaDt8qz}h90Hto@FCH$EE6H#_m;R;N@4J7?0I;*!U z(r;%k>5w$*Yv8l~0xk1~70a=Ort5 z@SPg0njBimOmE&y>7<1^w2|qV^R`g}d^ez%z)7f*4lrG8bZ!GALtzEkFK`9G2nUeo zP9Cqh%3a~U3Oq^j05pO@?~;w1g%&~_y2GMGD)}ndYm=JsGI`qUJV)bk5qP*d(yn^z z5GPVT3CpCRs9r<|V0sdlaVN|KJ9fQ|pn4ff60oX1?DKQvh(Mn`=SeN@+5x0Hb;|Gt z;-?|3uUJBxtWGZnWAy!T^UWyL%x4$V1A5|SVGwCu;n70fsOQ7xwzF78&6E=Ez>Olo zJT11#nc)WaN{WsXK#6ofi} zs#dFb3l+{3{ipG8a%$*nR5aF+)G_|+JjXQdPlE@#&CkyyQ(j@ijGBNd#aoi}ZPVM3 z2gWUwwyjlp(8DHA0SEkPf;7CYAcPRML9F*4?h%zgM0kf?-aE#BP%Hjx|LfAx}w)CxcoZmN|!C|nA6{XPsGNtgxPJ_#74wB=EBFja}lqxU>%*H+lgv^VOgt9VAY z+|PG3dVy_ejX4jzcuK3Kc6(W(ESpO>*Cyafu#0a}1>defH)cgv*FA17hr-&Ch68m` z*=jK>fFEpV*e`mAFrfRfRWG5);)V~jgr~Q5gPstwNBiNr@Zk}o zG}epqYC~)7CJ3qJd%~;+-&UD@vB3I*xIjkNc>+j7c0`C47IJSqIkTwIqj&b}zM3R; z`Av&HdU=O?4II`s>tH%qEm1Fag*xUD29v<^%sq;q?fk&nuwsfzA#Rb25pdQEa@V*p z)aHdd@}1XB<|7QshZSahxU|w-4UuGCywI7VXOE}IVWrAvOdt(1UPwr6O@w6=XIYO2 zN(j~@TsyQrT0pB5C+;s>cdDAjS6r0!q(ffipJ2U4Q8H^yV7xY*qm?&xL565_j~hfry* zco2L~wls7Ua^xdQO*%;eC?&t!=Iy~TRSy8_RZh>GYlI`NwA?u zqj^Gv&hjsN?^Af6iRLR+tdR9z=~ z=5%Am6M-}L(y_k45r39D(7oyO1cvfvW|Lt*DBQyq$tB`#bQl)(SL{->-gvzqas{%2 zWvT0f$SZO2Y>*rmCGpw~UF#HC3AjM}0>5Yr8VBtHqZiI(S(Zq|XW{HDQh4BbK>N^M zb_hI@ot8~ahLhZ$sEBs30feGU03Z*89*?qL9=H|SffJxD#ow9CT#sfCW zvd);Y)v#LS>{#sQ5{=3n@3Ln4JmShNn3NNnO!Fh)Yr8R z+;nx)ee?>Wwouc@-9xdHk01&p*X)J_+H{%h-VbV;DWBkkW+sME&nIwfG=LBUfv=F> zrEpZ52h18CefQ=^hZ67f3BDt=XZC$LPP6Cn{QOMFNSy@RQQ>xDg{tx2;pu8<~NxbbQ`8IzY*aM-pmEX zSFJzG5!$N;Q3a=k+gZ}314?tk|i}P@)Gq}@}vk1 zyPjNc*H6w(&}rs2;AAGA=6w}=P`ROTHNepf5X084of;|Y{UX>|jGt}$e1c_hG;^0l zYEnYQB|%UtNs^4l?HB}R5p?cPF2gXgyrQaao*DVfg!^+|Cy?e`EOOd9B4v6%Jy9R# z#iWA=msZJ;wN?xONl{&@j=HUIBe^uFlHP6{Cx{XwS-WG!k{Vp;3Wg&2#q1L#o6D@q zcCB|c&s)gM2A4F`Ez>+{cBdV6oHF^TxT{T9g7vMwD%# zfOl6zkkDID(8yXvhb6_n%|YuRvjJqPB%$Y~B0(F#)O?UIp;Rx5nJ<8x=Q%)63fpOr z#vl}JE1nWB(dI%zqB+@3w=i{sx}shUPd+6DSlOAmi!@^&C>Iqx>QPyEq$u&3%LdTi zoD_3s9w(H(!zk9@+?$0AbeL0pfyLmUhBOOlF#xVN9mEk5UEQUkKMQ8ZeS(3T-%WkS zl613U85EJk^du~xK}GsK&*jmzAugfon$lR}&&7#Wx0uH82Tqb}E$;@EVrD>@m>Q4t z#f`!3j!@hnxOv^(^tngfcxe@@f1?tS6eKZJ`VDm^P?$960jwuslWqeU+y<-0gPL2~ z$Fbfc7~rKO@*q0I3A;6`1qwi0uIK{}a`#4g=w+55N9ZfZf#0zPP@>lbAyM?H zYlX)kdysJk<*h1OL!Zh0Q_gAcMyYz)8S6n!@D=skl$2Z$A`(aqfJkl-Fc5D_yW*c* ze{kw+s>UR^hVM6o6E%TNz^Ts%lJzf20p5|X>9PZ(VGUTL(;lYXlYjF|gzJTJPHir8wkD@iTtFl2aQsk1kBSZIoHLryhPwI_HD&@^!%u8U!n3ojQtGv0W z+ssxuq8FdJxevSwaBe5vL?c@;1RFLRLHFr{sWk?VD)K9E+x#$FP9GqHXG2pbaoHh7 z;W&O|CScx(LhCDN?GbiIvhi{XU_n%D=K*+uO(4Dd!3xkl%kyoMDVLmUEDUXj8dzJ< z-)^F=mN3;H6c(EAP?|x>Q?zlZhJ`>DfL}6p+GCzHAp{g)IqrMBd!R>!g9IG&qey zq0LUwMP`zLLW_z_K(hA8jf+C7stxrwIEsexoF}m3kpzZZoN9-J0^H2j`WP6<4(#bo z0EDzT$E3@&@lxxWGDK8)IJYy-9)1W{jGt!foQ67pQv*M>`mHA_+BoYKmZS}IMhSq% zwsM^K4yJurS3Ibcuui<`8m56X%d-Lnmi>5Fpnw-cpobUp>m$&isylT7Nb{hEmU#Cx zVZUn}*?!t?*3@_pbt)4Ytx_Qgg#%AsMmg3VQBh59yLD?1mN5^^ZLeKoiS~d1=rJvW zIps3KwK9(IptV>xke-4sA`G`E&vDt>OH-o1W4EHULRZ$cOHBYCsfMw$!T_Vt1-zIB z$?0~Bh;pD)z+0b{&FL9BZFEN5>BY7vB)g=lt~?eXM;9-+u9U49ve7~*G zB6=4&7yvUv%N-pln7D8y%`Y``@vTS#mt`EPGpS^I#UFm(O}7oWjb#KUZXVO4&I0Xz zyZ%jySxk@Vd7>WJATQ~ti8iiiX9=hQ9LUl_@*MDV!iNIjJ>w80S;1VT0~x3mYGT^$ zvp#Aj_$%D*mD&LoNF_ia)6YU8L#UwVr5n1AL7)s_=`KlJewh&+B70r`%yc%FR+*a8 zcqL zz{Bd#XOY7Sq0nlS7-#BISx%{GuX|p+2(5$$^nmGQ8=dXnV7!dS3x(q88oP{T6y$3umR2$%|)R%~&Bih>U&XLE; z44lBTf!exSZtyd@!pdI1h{bP_SkO^7`|^Db zp`KhdvwUgTdVK8j768}_C&dSz-gQ8=5~{gwW4tIgp|BFi#h#AuE1_ESR-b@uy{K14 z`3;F{ZwC+6v5qymnbeFadH`QQpug=_nMBQm&PwoH#sZI$KsvBgxw*`7?%*A@H>x+D1C%3Yr=jp5ELMyRr1@ zS32f0P=*1T7BPM{08&+6gy~jWNCVlo&sZvhVSe_Uxee*a%0qh`sM9AFpPDBGW%*=& z_U==+ar`kY_cQGN_I$DEnQ?^oSm+VrBxO$@%iY+HQvU1trD`-Bngvq5L?s@FBSc)H zoZHU8TnkK=&|_kBl>LiOf`!kUV1+^O#1_Daxf-?JFI{_6;p~3s8s3^!=YE3l3F3 zkDH`84}&6K9Xe@OEKEz0q7;Fnne-oRloWzYK5i8#?T;$tE85$rn58s@B`yia;z>5b zMdJ|{Cvi!+;lnFt*Q$5vZ+HbcQMr5~YP7u@!^&Sp%$-+YOAiNGyXpn2K;ArF09&fB zd!e(c`=)rof16y!-TMdTs@uK%l(E08ZAx-el}%vT?9gQTzkVC$r*V}xhHX?3K@~%51kcMNRIl~NCmYTYxJ~uPjU$|KJ)ZvWT_4)b`E4Tlxoqf6j`+jE7w8M9-cbvZ-I1E{OhFr6&0YeAh>goc& zIbwXtz3vPKRY&>`7TAS!moog~+5FS~Hg3ZTjoZ)c{9C&|g5WM*I$V_^7-`@UA;>U? zCXgW9H9cjS04UpYXtR!m48DWK8sliRVkXu_{EL$khbD;7COy=1oGkOy9JKBiGbh%! z0Y|WepkaVV(X#6LQ-Surgd11*`7_*Dff?2Y2V&)cYiEpVMa?gu5{C(&%p%}*U8Zw8 zvl`Ohw)LS_XS(0KV=B7u^C0EeB}{bD$ar)6OwW2lX@9tQoQF7B<8dIK(u1m}WIEz3 z0XFz&kuJU35$A=ew;OFogWk||bmpg(;ZugrVpY!qirN-_(Y+ef>PIO*@J3I}Rpl;_ zDFpjXDUvUI1DWp8x%%dp-^a=@U6${{ z*82J<{7Y=J=l!DUVdilPPzhlCh{5s=twOu6cqs6wNThK*jYV@ND_A5BwXge7_n$t3 zJwF{Ee6my+!lUXKiv@WtcK}FBHw$gTFEwQ1utDEG9O$X9UN3QbbdTKDbH5UnyJc8H zf5FA=p&ISqW2<>l*9E@gC`->i9Wkdjrcp6OEh05Eu-z2`HFNNuJfySZ$vs=v7{c|6 zp)4I@ENp%Dl%73sM0#G7;U4FkUcXB4S(_(FKn6u%cZOGHhaou{U1m`L$(d*9Okea% z2U4moB=^j{Y+}hSVh*Zqr-McYR=}RUksW$IiydOCE>p7VR26i6CL6E+)z`msSM}y| z4orWRTm`A#U3KxsPXQH8L|AK0s<2a-)dtk{2d<<)$DzBh3x1Id=CR!CA(}(5C(W+& zNSdeOUxT@sg0=cEgjsrpR;#a`5Kw58K=}Z*H!$_g6=ke)bA!XBJkl`FvIMx34EQVb z!A9CUh@r5wiI~-ZdH>FcQ5{gT{7jrUZDra(6Iwu|dpEO6aFhyFTRSXc{RS-`r+uIDr-XbiR!jCknr0a;g?pANfQkq3idhkK zFJ9;*OE>H$LCNd&WGzf$PJl08MlNSemtGtvHAPmzM81kofu*TwjNSLo`Pp}zPMNV| z0Ww`blS1XXz06rRxi;kz?@PquAsyV3WFGr$ZWwCJkvHlWKflI<>9^jyOA1TJI5j*{ zCgUCe@d`lc@ko`cNzH^RH{chwSeia4tKD~2U+0nA1Sa$<`cZk_e%2*0=IJX+7!TgV z*&9!Cja-^U(Dc-7Kn*b4zC`bZ(4e~k9B)A{27}(h9$K4fmga>Tf7RMHOAmSIQ0NJS z(*5ZgucPeWrELhq2|PaA)QXO4&ucnMr%jJ7Ta5?JgL0I_4S@_cG*sk;iWLT`2`f*k$kYc`f0AJxaSp@m8{4FLQ=VY?Dy0 zZCitO+f>}86J3M?3u6sW80tw9Xzfy9bHSq6;4Wy#WSQ(XTDU&AhB<@toCOl(b$+{? z_*l~|%ak?@k}~|rp0-B^fmG`Jr{4V@>y2w-)drY8ct|{6ztcOVzg?XwHsEs{C|I~E zS25zftvPwhpbPKS8c4Qq*%tQeV#K?JQ(C@zW0KHsDN80-m&RxufM?o_b?JK&{h61+ zP;F?DyJk!j9hwPTRMdOh&x(d6Pq-Ak%Z~n>ix%!JUdY;+Iro&56W{=YNcA!Pb-@d-(H9sqLJk0Ac>Hu}Af`?PtX%^7~DDV6p+ zUG5bBfr%w+)_au z2SJ|H#V9<9F2WLLU|CDAiHmD&lc7ND7rCP^V)B2lx4-43_GV}i4D`JOj*Y*~f&Cq~ z_-vbvyDHDwL2>v0cK=_uaf}$21ZaDORMbfB4?b1cn`?GiCT|QFi~r5De4Qivk@sxe zAw#l#$*ITQ(kKk%;O4cM)oc3#Yr5nKjR8L0C|N$9&nTvQLDX6znuWa z3TYF@GuF}gd!FoIwrI)p5?$)g+;nG2R^IkY?eu+HrE~N!5Z-ZO;I1ydyHH z<;NU~wG3KYq^0booy+_lTUeLkR!tqHBBtIUON)(XYFF-FO_c>%KfP1FPY#~lf9r8B zK4ka4IwUD-DU*>xTl7ZNCb5)be~o_sEw;J@VL>k$BBGileBtLTGa4L!jP_+0`7hco z$FVsZ_Dcreu#y>v;aVV!+Se#XjIq|H+iwxtag6&3=+`w$HMG)p5Vv?i^#> zp6oYF9M#%EE>zp#v1_+K2op$rwNNfTFW--ty`h^42T+_t$7e>m230_I#&^#v`GwtJx5TLCP1qxzala~m{aa#K9fl_$BgL`!Hi4J@c+Tp- z>t6p5FJlWg)$qU!=1cqGRF>^OYnTi7UyeI0b7!iFXVfp+`ugV$iHj)XgK5=Y#xt%5 zs~g}%7m+8v&G%FPs+|09vF%{!=|xlmvgylT_QUJ-F2aT+y_cH8mp*G6t9nu(xaAMq z3meMqJgx(30r0y158)fJ>>P_nro?VWjoM6Nt0Sa}=})_Yn%mWO4@&7o{Vjj@BmEPT z)lVsqPUjhBu>8fj^`}WuZAs$Jw$28wwGWEmtIJh#z2#^a$ocl%q0dkHZWJ3hoj7%t zmqG!67qKkPlc0+1?R{lS*S5;nDK41rXGlH+HdvM8|qny z6C|zk#xPRUKTJWiuLCK+91~Qy2*`yE%kjK_i49VB|8U)62S*P3a}7Js?%M_3#J`RM z|NZy71s8GeKl8MIC+^G_7`n--T&O1W6>q)N)UDtcSrfUpZi~o2d8L} zr`F9Eafj2H@b_SaR~}I|__N}-QnV$H&w|h5))ZdEUH!}tm2A;kA1wRf;gr;xgrQMw z>;J^Vc?K}$OTQnWtm6QmCQ9pG#2rh&$M1>9?IG4G4Tkptls(D#QA}j+|p6O`;Qxx}>V}9$?-fAhXQN(Aq z%idt)Ak&xowe3|C^)uUKf3iQb5&!1nh$wuZ5m|2B))A-Sq~I(BW5rf27 z1lHO;dF#(U%c!Z;n9Z}rQIuJc0{fQ912-V4dIsOt#5}K`FX^uGxUeKjrcevmVipc9 ziGS!)fnhx8Uj$pPU;Rd_yI!B4%1YG5)#4ZU=%4vIfARJ7Gd@XKQ5W$as|!B;SBk;xUwsFLk(JNfzR)gwnS;9Jt6Dd}q4O~+ z9;D{=sPkE~>@kg=@rU3TbaCt$D(S3GUDb`~=%mZ-X@SiO-dz?)_prnGLo#40Xrjxn z-((IZ%6FXrV(YVl7z@{*8+rZgdEv=k&qhmYP1zX<+$h3(PJvVzRLX+y>jdfOp)J1j zIrbekATdW(A``(_o=LDv1QNIa^_TJVmgkc^mUNMf6XHhbN^sQb`F{vE4=m|@d!p-> zM=Pbu+CA?*3=sZ^91%~Z;1%h^+gCABX5CznYPY~s^Q+jTFq{*m;33ft^a$tjsI#8d z={k-GdxkwK-NQfgbBV{368@>?mwLA0ep9G>(x3Jt4w z1`HjG1m0W6A20Dc4nS+-`w?JBU=D~4rJtb0Vwm3YH1x>>I6un+tdsSP2ecfugEN1D z1&bZ}&A<*x+ORhv)j={O8{X@}`ZcybZocMGpmYK-$$utV0xQJ{8pE;Xl6Yqi^m$%* zetl0C==9%}C;i-~C;CB&V@!MMEshKO@_xjltbfnF{sBLFtZQl3f8@pw#FzIrXve*@ zzwT!;T(-S^r3yWX;i6{iMJaTrS7l>xS%KQR=7n=AL?E z{fFd+qT=Et{|39`2f9eb6tR5v)qg@X6Wsn7&Kpb<>IOvA5Gz4hgkp5tS2|>4a(DRj zU$T8*v3=ZoG>!ied0TpTUj*I%PSC$-f92Eu!Y+NO-EaA1^KQ3_9b5>RbrB5MUaQ>S zypD(e4cPuIZd}?-Usa?;HLP_Aw6DYlbSd3mdHbWs(_4Di8_3M1`niwTsh(FDexU3E zcy~ekrp41l>KXJ%lj3Juya)74i+7h6*ZzqX-@tj_yHBAFrZCF`0u_L8KI^qU$wkd} z@ypntGV+KoSxeES=lJ6E+6ZiiQ)Y<6M{KwYWyoQXFGOQj$UB+EcW`2aD*WWpM_rEbmH-uZA zzVBbgxDFD(VPZ_U-UFhQ^EG|L1pN=egf|WP_Y=H)f0Ay|Ej-&XYZV~JLE7PtHZiE@ z`zKv}V=zirc{S6|c45Jp{qEObL6N$_o_+kC8@yHH(&P{7Z~<>mz!hshyY_dg@!z=u zm}hcpu}4&bk2dm9E1QI`ZCNoqHyqn7v3K2{+MPcEKpV>|cIyo4RdZ_cQT^g9&5Z*RpMFU&J%HqSOd? zro8z>^UIjs*FV}v5j1m#+xC|}E54xZ(w!i;%|;S>M_AZ$Mfet;{@O8Pzmad`wI}!4 zN|NGj@sVJ=`E*2qCG~jeuX|EcU3)Bs0T{mWT_M9$usx-_vFZ+Sg#Sqy_>ovyVs6n6#R87!y1YY z9medjCOa%QzN#gAev)TyCrQMloJVN&ueGM`|NgqXd1%9$514}aQYU>Q>N0ak##c2q z+=r+;o&FeLlS!>tCd1HLd^tw*U&Vh|pH*Z*wfpK3Uj3b@gZ{+7L44eXTZ$=fXz#@O zNp~235p@*2qkBVn*j@O>yPt{JZy&zi|HQlR3=YkdzAALLR-_heYvL1{^cO9!x3BM7 z{``xUUo-?956mHk4^f}g^{2JpehEKS<~}}#pAmKK#6l_$$&08H!9~<7{8>O0fnM<; zjJr+0y$SJ_ZbUrU{VDZ8VYbt=RYW=7iLA2&lJAfG)v9Uxn~(b#23^g{rZCm`;3~A0 z;Z6TTzTpNyD=A~mm-Ux&?tIq$s=L_xGM33Z1Ex4PdtL-S?@4lkU_q4rf|3TEqieSvbr{H{5LpTCo(I65412m4~b?&rU;W-eTA zl;6MXANH}^V5HuqTZKQak9df}2MM4iT0Q&ImbJz|ziD=lW-q?JinR>n*hBw|&lX!2 zG(MKKQOaHV{Ck}p$>7MdZn|9xXfZt71@XPiL5Z?OHc^)(z_=l_y}@b5`4rZmd`|Am?G0nBti4s`TFi>O@nqND13e0s- zf3EXL1;At*8F(t$>rs14l4UyS=Clh)Teg9dnq|jqAT&K~CZl&cy@RzT>=Ah32Q~uC zQGmNQC^YPsb}=9Qu&ktr)*yjHUv=wbRU+Eq9lnTTaY~3 zCfRoUvmR-W2YQqd8?gf@J(x>`jNjHKjNMrun#(pkDMDLuw1~b9*Lp{2d=DXD2>y3D zE*WP$^S#-+Z1t;ndSgB{K*(+Sk$i*#O7wgq;t?3jiDy24LM>YF*J3mr_aFbq|M=P8 z|MQ=J^YcH#X-elm(P@H5DMtJ$hBM?(7={pk3R9f^69nTaiogjOW%z&m*Z=sx|K{B5 z?Efns({N6cYu|q$5PY40{RxIF82c}n`Y#y%p9BsC|M8!He^&YJ_+N47f4IVY^y%js zNjQhgSpVYzYgjLTIpV*{Cd^ZCT617;>i;term)$H|Y`{D%Hx^?j?(au7es4 zja|oVcb>_=ZRhWh%H093WODtc^>L$3!HccZ&v{l1U*}b+1(lkmLf8Kv*SWiDeXbw$ z(QTve4ZcQ=ecfM!;)WiB>)8AGd9HgNqvjDIbFH}a=DXSz8{YK2#P;htXCgh;5V7Y3 zso%*hr|;GM^NiOu#3e?8>-`}5-kOK+;%9Udq2QIznq}vTW$YUFE|n9L_3!TvaQXlG zUq50?F&#!QL_mLn;|Ti`M#sdTXavFkM5ZJ{$21(HWc+Jv3H{g-LjBJR627pdAG-7J zV@rLT|Jja8hkcqGB;WE+;TWU6>_V_F$7F*K@sLK2xwH(!p+l?Qzxk%8y-euNj1v|U zR}eKA_FH{z%1F~?IFuqK0S?kyp5=I5uzcJr zwJIz5kYy~-cD6AXF_1JtV0VQNOT*CKg0KzPy)Fnu;dkZXkfV2Lq4pF-NWEETl;bNO)%i!vyWsi61RGv0R(hKWjm(0Q6Sd!(EpY z?tvv3P>{akP#W@ZPb~c?r!DPdNW1%o>)<0w>jXr*(`N~d_Y2c!9f!2QgK5PAj+L~G zGEzbSCfybwn$ZKGbA92y@cn_pz&TMeB;PixBnlQ8)Xg_Td~67C_(YEeSJn2#>ZW+?K9EqNfDAzu1=C%Xejyefdo{4v0S`YHF_F;xNV3>;5Bl3_ng}~N;Ll9{b z!crIl0zDh9bqkqSQUT`&|DfuTZ09f=n{Wof*E5ltoDS3R+mnIL)%cXF)^!@vpaooy zGV1R27Q(`R`xdT4XD z`i&AWAMi8*j$v`vJsXU`c|jsl){rLZz}88uR4xdljWT*gtSB&%?MzmJ7+=;7=`Kn} z^a31KFf@cj!l(_Ge!BDs3P#Ciq@&~Y(t<%}*1Q4;8O&2h`oYrDiGynxA&ntD14tHV^Z%C5{#IfbYHNrW z9$)4zW81=H>%gk(A-@`frSJt)%~SQ6^VR)Hdz2%Sikxfr21o1BODZGFg5E#l_Vx zSm7g3>jB6eh0RUoc5c`0xlkx5n3t9ACZ*=tjRAC)O>$7-kR7Db--Dk%JP;2`p$i?Z7mpwV&30tTkpr23xBC?71$PH zzt^@hxdJe+@3pP(#NW8o_^Gy)V=99EUO&s2;>tX}*U!4o>CWnZe-G6iGSCY8**Pw2 z9lx%z7;VmZrA`gNre38$FUsCkElt>0MVh;ykp(@H&1lUpH(* zU9&%=(}fu~g^(j|tM%*6;O!oSUX>D>9fW4nDX(=cTG4EFO&XN!UgmYvz1d-)2( z&5mKe+jUJ7RDI~oORJZq8*R4#%k7{jmhQ~x>lK|=RL2eVB1qJFKEPK5DkuoFFCL#Y zh(nq<1h{QhP4aNIa24_7n;)kNc)1t9_V}B}Flon_dweM+;CQAAwgM~WJqWvMqdpU?9~k>; zx*EQ|kF!NFVE1t)8nevVmDjpOH|7i7 zq@w?qx;N``97nPSe-Ix)f(wX8?UY23ltgiR;|59G#1&kA{XkLKRW;Ss?J?g}=A6lM zJR^^1gogl_*}d1yV5sUFGuyMbjE}2!c>X8j?LVDshgY0;E<1a9{CM}|dgfOr*=f`0 z`J=5}S8ij!YufQXXUM`Ivo55DVbt|MXH)-d3%(2A1Vm2`EFtOl&2-sUQGf!ZZd;mI zQo(4Z_RN?3Z!pRH$al*Va0ztFl^U#+U;cY?CZng1UqbU>y8ZHdima6ssBe< z_8Ydb;oL=>qhvTY_DpkUu?9YV2p4Yp#KYJTvcIp?aP^iZL(jT$!npNX&ku*Gchtb*D|HT?cv47(}+ zF?Ott2t1vsk?yGrf$SI2*3Jk=N?2OHU~0Z+(f4O%8teb$Ui||n z>fn6B<2K9lj*-BxF6V^%`_u$@>!ajP*MQxpvV2Cs{=X_Sa>vzDpyN~^G_f!88)78v*bM)BWj4(v78Oyu zze)7%&;4_4IXnS%SVYyRIq0I*>?W_G7wK$rBLp+I>o54KUh+RUcJc&h!%vKNr87Tj z*&lvQi1sT1ce?)J*J!2QbnPzAoK6uqeVFd9_>7at+v`sEC_ZX1i5x)=#*b^Lta ze!TCbY=(K9Kouv<0qO0^ap&hs;&=b&T=#$TzpT2FeJF|{en!>BVNNd0m;am}|8Hj( zb6z&t#~=Cc(O4g!C+nOAug{M>%4CFHJ*<9;0ngpqUWM`_de?h%{LyaDL=DAg`xo-o zuTCd!{agKm$tz4}EFOQF_pn~BE86#u&!#!cuIS!PQt}G_N56{04EjH#t^S*xTy&Hg z-nk(JAuWmDo&9tKIKcSM@d9`2KP2Zlewn>7zE;cy%{6#j;JSZ-;Q3#WK`Bw;<(LqgLdjYKU{Cs zc+Fk5X{$%>s*hro=Z?Co*ZD3`cc*=|JB|C(p6*ZA(`irE=WDP&koD#kOq+OpzeSAV zZ0oR+^PTackCu-k>kdhBvib9yl2#`&5zks9Z(iaR(u=% z>iPb2b0b&UKPu;&3_Jd8vwNTIe|5R;erNX6T6k1tgwyrHOBMWeuk?J`WGnLL_A-a4D5vDM`r=!Su>I<9dZvN`?e#PG`fjXzgM?OU@Nu9~E=`+$$cht1Wd0Kx~&UR!5AUbz5;x=i3 z&%))-x9VXUz;=COk$9{82YJ9h#i?bdBY8bk2Y$Z%jH8?STLZfb#qme{VNodB2)f&# z(TY1KvTsKEpUfrvZ#>W>=YXTR2CV&Z-)w(g*XsSSJYla=cNx-h$BDn@z5Uty?wo{& z^Zczmi2KchEW6*iKR;a)=OmZFd&bygr1Cr4fw}u<)c)gn)n99Ul^;)>5r{@8E@9MZ z=iqSg#1x$n+D1TEuY4wN$9uB)+i}#2c?DwYzN6Gay0=O%f7G$CM7l*=sgC|f9qYN8 z`0iW#k_VcBa(SgQ4GpyZg1-OFy=ApKUY3hD#n^S`fo{vybN%zZr+i~_6Zco;Bi3?j zDQ$%I@zj5>j?t?U{3$iGe~mlZdm#Gsn{ynB@zLhDoy7n6d%eTDT|X?&lkyfl+lgH@mn&R>UvVQrq@-EYldZ^m!k!GHe7 zeLNSMC(<>GEuXXq8PQ)?--3&y2jb4;1Q^_O+VvgI@r$Yy%raI_c@82yT;uwqS^P`S zjvg*lt!DJ|m%oZ{I9^mu2~GQNskPDC-I0V<`p!5oHD-ZM_y~r-_rRo^{8wr%82``( z($aINn;TTe1x$)6t^myO9>WZp@Q zEW{%c%u`us(6C={zaXhuZL_QW=j5i{`vk^Y?g;J?~&dY08;f0V7yKL2^n^JmxOQlkq!`s;Ggg3T~E_|5r^ zPGi+(;Qf5peYbyweVzy3cSyuCG|1tv>S?)IF!0VM6kBKZHv>1p3}!iyoa@3*v<5KT zZDsu~&03!x#_1P+Z-K5!zyh6ypK5HuTlO@~_HVxZ8drbRw13sNV+jPNs-YDKG<0PD zQY8n_?RPdrC-9;58?zyHN38&Mbc!j$hiz95iJ*A=UsTt+O&_bF5h~)bN8kJ5J_Gqb zBe(glxTIyjK)%kezcFhhA9q!8%SZmlyPMi({e(Yr&Fh)zfqmQ^J$H`pYlKY!mKS< z9{F`v$A9C`>Tdss|5<|S0a3N5M;p+<;;w(-bF4!r^EZlFednw1B{Jo|fg!tbhqv+S z-JY_pT}S!yn2_6#Xe0m0{Y1vqqJ7VQU~4vI?5PK~BI^BBy)9Q-<|o-(vgBg?oX1_S zJ}e&PYE$T&>yH>QKku-3KY9ne&FuJrWfS5*6JP)Dd3$#;2+w5)HDtq!a1$9}obGTCjPl<^%QEwWBl_)BARRies<0XLBS-RWlOFE?r4s{!51@sob*Q(Js|bz6GE z<(Bc?>2uLzec5rdNpz_NREn6MUdU`ouNbb>%UY7Wu04F;X(+GXdYJZHl@~+`89UAQ zomM5iRMOA6;>~I^gTBuPw+Mp1OXE0a1e`WfQ8NObCEuBSBl|$be{iq<2`5V5n>T5C zz0OC}1QQvf$vV098}{T0_u`+g0gcl#Ta18Tefub_e%7ho`XoEYUr&1eFsrgy6;#h_ zzan6=J;yeWQ-pIj zB&Q=HK^tS^g#Va7`RCd)oTG1|4SW-AOsScB&kNBBfo()BaLqHcUAg4%9Gm#oou{3J zEN?Yj{_tyABI?5YI??~`*RSuq#$_jS^bH_*r<*D=&RS~6i%m2>7N)@Ukp?EX+&{;Iw`y`jYuJ=po zcE8c%lU=V;lYHl;`ajROs#e$SZ_FAr*530zxaFh0ZGWt5SyM$WeJP259d8}>r=Wtr z_uf$(d1CyIf9#!H-Qg(C{=U)cdm40t+WvLDvZQflj`As-p?G;|eh?+-)M`XNq&cuP znSG9U_2Rr=x$ge?Z6;j-m;58AJ#wlg9#xl2;B*^lK>7oIa)0CQ@pgJ-{=w9c`S;qF z^$sL1p0tIdG;i9ruuZSif45X`wZq@>_mt4f@%gilKKzY6_lMo~=UUi)G{Q5n zWO91bVOv(=TK<1qr~W^vg;!QF%HH{vrsn#`SuEez4ZeN9$l|JLF#7}NJwJbT&o6Id z=M-Fj@BKmNyZ_H|_wyZ4|G#=mt-tQBT3s~|)h9!)`+2a_6G>2A?MQ8bJ+A}1UNe0z zt&x2($SHIWDfEVlY1KpSehgXwvQYOI@ z{hK!9w#6T{v*{TzKHjMK{C(2|I@ZDdjkic|hqX+3I8*)B<+Dgj^9B`>7cQyL#^%U0 z()zZF!|k7>DHnb#J6~;lmNT(=+zi|5LQU`VKfkY$PYzIk184SJv5e$DmT7w(Fr>Dy|JZa@AFZ(q;;L5R5T3h({f zHRWufEbQt^W-YW89}6(sfBOA-@tjlawf|m!ZHZHJO&-5z`^PwZS*>+pD^&4EvsUA3 zErlx*f^gf@d%#f_^@F&G!0ynknB}lRaw}(=&@UF{FaLW@BtEZB%x+!LzOJDm-Jitf zAJ$+AX7W07^QB*SsZ`)8U?-p}c~MloI@jb8TSMmtw;dPU+t&0JSC2n$a;YCeby_mCQRv{tXf-G5mdzscq9T-741A`ryUSye4q96|Wwy@*Q51_w`}7^a^)N{5qr%4r4a zMnb$w@&9_)ErVFft)5|mfs}{8v#fVLevZ5*62DY^tsFt*W@G&?d82bMkaKM|Wq083 z|J;+q@7q;7-sC2WhauaDeCGl}dyis00J+uHa_SY?6b-@Z-^?>SZ1`0@J+wivuW_aVR=^oZr*>x5LV?@*9?32Rk8w}-Y1RKb{jf_Lp6mJ( zxVU;E>P$mNfj715?*jmTuVqx?Jz?=$D8)AQxT@gT|M-{MTsB>4|Gg8aj$QscJ(S=6 zjLj?i8JqS-_V&=Z;}!d4v{V?NNR<=$p1%fXtz9GYNWnt9V*+k zS`OcLO=bIk&^I@($T)oPjQ;I~wdtMu-s$b^j6%NY-!^dpJS5U%V|-rLp*^R0D`6p_ zkf&U;I!Vq79`4h(&V7F-+ePe6k;gR=+SQ}DaKEFYo*k#Sso6CgP%nhqb`$h@hSK+5 zmAj#$eJ$xKrvEN)wV>V-&$_}`3PpG;uv6NnQaZcCiAw{@4=2k1UFvik~zl9K;V z`Pf?UQ%Z$(=>OEybliulhtvq*H2!$^>bN>Pnuu=%C3D|Ey$Ns?BZZ&?jgZ6XGHUmw zcVis3P}qvL4sU^k)GBBiLEUMUqOTw`yD4o-U4lJmHRmG05gtw*b_`1;#i;=6)6H*n zZhgi4+fg6278LZ*;qM^nf74S+&pGFv^SGK;^N%s@#X_1BH#!eOgMqe9y0f&)!wOV$ z6RJ?`=yLdXytK50ci_Q%s#?q(JUy5FrAC7PPdOnqTF|tC&7v*{Gq_xof71`|$RACS zA6c$Sho61p{5=;(-CS=2i&gy_5TLr{$cfxTf})Db=P2jju|82Ip@^}^B_e?9Q$R~2 zOgV{e>MRmI6}i)0TC|^YX(py7sZfJtdDszBP}Q`uskDsM9-~dYDv~&ky|sa-aa?w= ze}YKZ=_N6}GEp*P;!xP9bnH|~IU_C%IzTy9TUw;Pxt!cNg%pY+au?aReIS~?9oh0Gph;Mz zGG8xU?6(Gdk6bw+UAU5Q8KMbkso<&9!NKT7tpwJPjUi7}z=3pf*@13kla(A_Daq)dW zX5@mcYdl22^EzKNt0gURY7M{#h9lGGvBZK5d)tYH26)Uw+#K}W>Gqae0l?sPkixep zs8JKSV@tZJ6uCIHf&z=OroDvkbJ10Fn3p4-1n*8Kx^ICt(cVci+Mvi&>KrD@l9HXW zEE9gI^dgA18AK51cgc?lt?V!ilYj@J2Zy6$6e?fZIO``>7$NPPhM3rWRtC{x>|z)~ z%N1LyTB-a=5m+#K*FKIyZ(+*;!@ZAz7Oh01z&oxG8^_^=|jBAUi4o9P!(WzKOkswgo6tTcB*Tk{S(%FUg7keSxRw1iGIR~>ELDJD@62zca%vhtuR zYYR5&u2#-3nc-98-){|BlT*;YlBeW#D-?U5NL;l!6#3nJLV-q?zG~*sroAB9qw6$b z1NP4mV=&4Bci-3O`W`j{Zd-X@+ML)DFB^`;Qe55XxtR_kR|k~RsuaRuhaOvgU-Z=K zqM5IP0QEO@bd^e|tSqzRG?>Z9ZVkb@XrYB)C9wtFk%X_l8T%SV zGKgkg1XDn?>!mk0r2Z&-jFvTQ-h zg+wu@mRtAk11a^8h}bvcD?;le0JS1qhH~Ty^-2s>nRb|q3yYNEo_Wu;L?G1>i;*=6 z$k5^>PczU{mlqP$U{B0^E?IPFxQuR8s)#%iaEXleY{6Bh*&wn~19qfoL>6b#OhKen z79$Fh9@G@BYjgmudhD)R#svt@V#hB7hGir|s_jko!#Jv)YF=o|IAx4sNZJ%F2_=Ar zHe$e+N1XAC#mRumrLktB4Fd^*tdsuJWyBbSY%61`dT<$3H*De0~AS0Oj&-tY~Tbfcdt!wBBCblv?0}izFIG6#GJ6gk^xuryMJh@Jg`(| ztlma^=L$q$^j0x;r{-&s2hrT44O`EQ833AW@P&9qkvI1~+hL9{^22vcHD%KB%%=^b%;Oy%g9nQlR%V;Y$t>X^G@9ezSl) z$KgA!K(};Y2;*2q@_S}(`kaN_C?-A728ZcHGvt{~)K#9RB#!;y&SS<8FCdr`x*!B$ z*%PbRvx$A_hDuIdYG=L_vEzU5ConPJmrKO#+DUpv+h-aN-nRuP{52)S(oYuj1WSz+ z1$2HS5vZkjOXrM7+oBpJ_DX{Rk#fd$e(SAsUy+#MUPBrC_*#_{F3x_gg>1K z#>2<8ghUsT+tTz1sT{XYL(38j8ViQapYn_Y0NdXCNjh1b@A2)U-f{g2OB9ye#nJDA z#Xn&(od$(EAs~o!2MD=3bs58}n7XE2^nSpquOF3s4nqcA$a~+HNCM*dRadeQTYcin z0}vhx3wYFwFvK?0JkKNXL&0}(NQ;LeSoeEMVFko401!v-DeEf_2ib~g5%`&81z0pp zta@?5S+p1@;GP{HnUQEagI|@vsM|nd(BZ%b?jz)0X}1} zq))mmo4UP94vU|vot(5?kc&$BFzC9k(s@T~5B-tfzbqjUl9uuBkl#&%vWxza`|n`R z;T$;=Ulrakh-K;HX8&#{&?s%pdr+SOeiYzC+bC92qz^`UTB01b^1G(77Q=xFGBgDz zcEU0j*hTeXt{O3O<^_-jVxP98{nQIxIG!mL{<;J&E-<*s@B8-wZ+WG1aM4s*(nfui zVC_bfUPMDgFV(tTcj@=d-I-Lorf#M|h$2K(9d|>lm6ylD zy@d^Uyi0s9Wt$w@L+Iw%p)rmuNh_D6MsB$xB0xi;+=nE~QwC~)!Cmj`1^5&vb z7(s6dk5beIVdHa>fupN3yd=>slmYsp&{p$GVJ1}hj%!nWNqn)E8<595T!;>G^Sne|UO zM$Cw3pmh&|w)+vj&*R~_k9H#oER(v&J?Tjv8c$ohwI*?{e^foS5`_H|21#nv2_$9nq;XSaD{Q`@`)GkHp8V zD8+aeyR`GlDj1|F!kGe;Xr2`mZH>0nTRCVQMLawWaPOB}WyPWHNf(j?2K#T*asut_ zuYGv&9kJf~=gZDKm+B+=R%3U@Uf;CFgJUX7yvP~i=Gg6uJBTnNxfQ1XfuWXWZ552= zKo2>%wJGD248uw@uFBKCawET#&HZOE>G}_e1+|)MpGSCXMBd-7WAOpLoek zb>Lgp!bFe$b{6o8HLcY{1P3v;r6m|V^}NR2>M(U}Eeo)u>UJWk40mmqc#EQ?^7tBu zfdZs_aEGIV5tb9tva4a2U>|E4F=w2O0OgCiH;#DBF70FM+*1$t`QsmT5pj`)eH<}f z#<;)GwwKB;!No?0*1$qzcLBT*U3cVtJlj$CPWNle1FG)P&BhQuJ*m7IZV1Si1w1v$ zJA1dQ#$0sH?iZ65||?IbvmGkHg_G?cZXVFEeyxR z_<&cjthW(UD7PmT`$hm_KZvJfxT5GW5&!DoN{NVa>Yo99fY&W*1rEzaIt-4+MDs-j zzAbWa9=O)(Yx*$xSOF4nm_|u=y=CO)lL4mEU3?8ZxI@XSBli%39RSXXJIv2-AA?Lj_hB8c*E^r_S`5j@+fgJY)-|yf8jgmL<@yl}-|0;Vl`XUDi7};WoT$ z9>(}?zzYa%EKZW(8nRliUmrU;($($xomF5%Lpj2T5BXLERKbh=x7F;0xQPFAdFfRV+}NPqea zS2`%XjrSLzi!0nA2=;|{$ukZ3VwZU~^U21#uh74WC*!H=I4 za0dKdrg8*sZ^qk z_n{LdE+8g=))7eMhL@ zRo+c*jY@Ws6L;-Pu@1p$!c}1u_@$zxqg`EUVk#E6us+sx8X^JUxY;7Tl9RQXZ2N;I zcuA#z6m0vQNbcv6zp_)SAi(?~iWcuWl-x)v5yX*aFgfuMMIseM#x_PQu1rWPJ+wtw zNt{dR0&KcyPd;S9tI2y#<9*DgzNAv?Wuh@eo<(?`Z6yo*LWXo96PXW* zjOkLah|&$0TLfPckYAt(F7lG#S=t<|?Yd*zA()rATi=Dt@f`0@vLE2k7^c;(U5N_w z{uE&G&G>H9Iyh5BN{7SYupnIDF;>FHwilTZyjoIl0?ypCDp(*l?)8N3UUq-AjuUMrGQq-v zgV0qs5j{dKYhLKA5-RCxWCuVv_#jW`vd8b+S3A(yrdKy6Djw&)7Ki*rEm~G4p#W-vQV? zXxi@vz>}NNK#pe1$Q1udlR*sA2r!&9{*twAd+^$zc~84I z+!j4sgudY>paj<}C5PkQ*d?!52;9Hlp}7rp7A>pRXw$^2jm~a>)lS|ry!<^$`nDqh z5Y5$;DvJS-CZpDR0QJGNq{f1>b_#*b1vL}OtJL%SD(ty*Zd1mZuY(YIVpJfY%3fCn zm8%!TiM9^XGhC=yC!)xdW)h2>B)+3YmRd!r{P*sJ&R`>Ze)E{PS>TKZl!2-?3>9uy z7<#2Z8Kq^s9u>=#hs$!t&UO%o{RQ7|_{CrAPF-3Uc}=|}DG&86b^Crnr({FLsICK& z8q@8XN2@K@_h}qIoGpqp<0Whvb?XS`QlqdGy1#X!y|oHu?syQLB_-Wc;?QBA@~bF> zC;9OZ7?pu-+AH#-59humbzXRIy@-DGXowd{99t+b7&c_3dym1xVcIF=tJK@DEeLrE z16oFol!Cd6h~$*O!m*>9*hpDK^N*#B8>a9?$KwFU?f<6V5s9YW5ClOK_Zxc6g4)dN zx#*Awo&I|D#jqzrQF&1oisdH=y9qW;I!NZ78`6xBJ*6hcPZ>JNaIiD5nIjF>3ydFL z<}F?4^gXFt1|1(&0oP^r%=|5hO@58}uvGV06yx*e@fiDBX^+Y)dv*FPer+OKyz5U@ zN1>Q*ZDtRa8;9hA)mhK`Vr^Az3SSKKS4&t;5-bq0v?5sss0*lPk&2yTQOyN!_+r~# zPlJy<^^=GY1m*j&KzqHoL&eU)TUVL;D3>%R)S^;!1i-K}goInhxx$-RFm8LOTl|u*yj*SMYs%5RGStgM+b>DP%}eyH;^i7|gLw!Mrj^o%LxkWSgO+OwJGuZ~ znKQ0RcwRbJI2?=C4jd;Tgt)cSGx^G!tuiIu%acbzYn8c3IW?c#5NvTLs-$_j;vl+f zWmF|j38ScOH0u&C76MT7M9n1$m-uUtnjIrhClO)UK4qTa9dYNvJqVAkklvsOeOX6t zn|Pp71Ox{5C$@skwV4DHwwb~Cpwi+s(cqG#eaulV^0`ZJ^aT*m$nFw^OAgulzM{ju zulnB7mkn+?v8X8My% zdlK!ZXZkQGq$qPZfw6A+9ir4LFySsouPR$iT0t%DdsH);Rxtuse(qRcsEDtKOVe+| z3X zOblFe3|8;;k_ovh$+9Fh9<0R?L`1~=m9f8aM@pSo zs`ak7=mAE+(Z@vh>w;s(v;y4mnPu&B6nn>-s1!39&^@`4>D=v(PAM;VwyMS{f$3+h z-P)*vuc2Q6N~I3>7rMkUkI_5nmlNb-j+lhwMy_i#O`Eo^YJJ6SR7A69tt&+Wk z637Hfgd3H2n9)lL_~BIJI^HDcGJ&+tvIRo)?)_VvulKqqt*W3C zStzAzPH0skZxaf*O38aJmlv@z?MIw4n__{%?IRK9)iuK}MM@wD@yr;@ziwbgH!Z!8syCo*mh_&mtC~+cdr-u08jta2V1fE|LV!QPsW7Nj(aI}basV( zhsCCH1S6r%n}c*v`j->!E;ylzSeA#jV=j5{R!>cHG3_<=S%m zOYUKs3Ylk@CwAgN_>Z3@S~u~<05a?KNjfJz3weXmz}eeFyZ3@=Y$T?Kh`tf>wAGse zz7s{w#ruKlvwdkD#G${zA)2llZ8q)0;E2?b1QyV+IBDNJ+WWSEV4$NKT6L3K+BzO-j)H$fD`-~Iu z+i3EU%mmm&OO!-tEw8)@RZLoF5#3T#B|6&-^_q2)1BB)_!w*&1RqPNHn(lZ#RVc}f zAw6tz)Fe}7g-K{LyEmBBQdt!2hw@=5ft?C{1;s-HLAspoL(&8aZwWL=#rs}&FW{3* z%*F~F66{toHC`3}wV_7JC?#|eE)U!P-d#FVr?@66|_WZ@9BEX$>5|o1sT)>Ew&_v&Qfs;e@ z3A9omO1}t$b$AbQ1u+gik0;+;VK6y|s>VgcMA|lFI+Sv1ieQp@J96RLGl~Z(^ zA;og^3>2tTTZ|mwP&_8!MY|d?UAyx|g7&@meU9(aA6hvRj>&p08L?tre4^Gdv{doHMT(hyTR!)+ z0_dtyWy;n96nm^SNE4^Fk49k^8opvx@GF{yQ>Ps47M$O`x=VVobzf&rkuG#tWSEG= ze8pK&p&fV+5D&M|-4Xm0qwdXOx)P|#Y>NC|CK}obG}JS*;v|VPL#tb~eU}<` z93(X`8GqqUYHw+temn-Tb(FHm-Xk6u8+QwCx)GR_w8zR>euOnJ~8q; zW-&}da8VyYoJXC=xFud8tQ{0{)cHbvN z5h4a}+}3I1#9C9>W&U1QULPX~xE^zsiy;=7j1Km~G>%S2)|j0ba5vkY?yl3;k5^j6 zM2qh9Eq@kEo<94|cMS7vCVOboi#;;}7@=7DB83&c^rR6ZQeRSj8!;o`vYrG66<$#G z1R+5#xK^z)G7ILoc9}KBbhcW%hY1K)FKfj;t&(Hf6W%jMG$MRtXAE3rPC1w>Rj&yv zrX``9wRiZEMy7)x^&DU&;Un^2@U!$ob#OYUB`=`H*SldUy^%m!RUgMmiufgrxar{E z!)-I7E7|D2d^q1QNX*rLuUjap5GraKSP8wC)Dydm@hBJgUZcKS%7^`sf&*`WD1v=G zI8k*_GnzhReW(NFOQIej#icx{cd!PW!SDfKF(2Wf<(?`++bx*#}PIJ9PD4lt=` zrJLHha?R`k9noGGB91302V1YNBNf!^B$`63n{Y-^5xH2{o0Hh0L$vcmM%rHrRB=%B zX=MvJxJrCaQCoFXwJ2zhk@^;u*l00uNnrN9+Uv$SHK6m5N<}#6 ziieY>9(oh0+MK$!TYMf>)Z^x$Lbm7wz>kPK22-E1GmrB$4#=lr0`*xk;J<)RN)i3u83C+ zrwsfeSY1vMs(R}DklV(4U+JyrIy9t?w-+7K5_FV6qu0v^=uouEs0qMRO*+Uc93MvG zIwI^$HUqRgN!YJ1o6nSvAP)NW@?v3c`_b@6!a&sO9J>>A+}kMyN%h`D1Wzltta3%L zfa-i8=i$hfp&;&=PjnFLtf=#6y##L2F~6h&y}z7#a6Lsywd@C8c4BZH@v=#Byx zx0R6`)CdE`3w6O1-h^QcJSRil6_?b=j)J+&59?a4*p4-WWBH&5|Ck~3U%mARZ>>m} zvNR3j`AYatIs`(+c~8u1^F1&WfeYJD;Gl%X%z4vtISkr2 zf4si<=Gmf+4Lm& zbeTyEn}~m#l8HUbFJFNptJwdbc$~-4y`Pc3xE{v3Xj z`3#g3?I?zPTS%Gd@qsvZ`+<1QU7` z_vZLOo6jpE&+7C@mC8b1Uw_={U*DAF>1xRhWAWT97NYuG^9P(2SgRYzc_mf1f0 zT+RnJa`9&{R~ZbjIseuKn-UvX*hdZ%IqExbh<<9C!rT$zTy0L)V}H9}Hz07vt-7r& z@g0wJV8sQly(vU%;36>Q1lAvzDYVDs3n zq&7wgILW~QZQzYB@-*087>(5)i`ec9GjP7jqGP5B-ZA^}e@f?CXp847;}w z{*Z#Bx2T*o6&dHlOUpf{bNej0%AB+Map)1jp&>hti?3cPD&3=qdhw5 zM{B|Cp`ekA38SfO-BMDHyU8ssGEzdIKrYP=0udj z=_`gWUo|BwW~%)K3ji_sTQkJ8WiZQ}vjWCCQq7P|M&t-?)**(PPUzK(G%|QzA_-S6 z`aRwyY^CJ$G;L&Lh%*E%y7QNYlPQGo&68@vB(L2U0~z z$kdElX{4pin;!o>BQS1Em6X?Yrl*;GegVZeNz6XoiwxX&G`;~CI z|J%2lG=Rgxy@jaSevq2NUewEkA}Liv`>a)?wp4ggFR7AV#r|VW`H_wOaaG5mY9QJ% zxF#3M1G!P}>CG9PO)OTeG30xvO;c<~&G(c>xQtP6CWw1B?Yr>{Vcw(BP>3kh#05AK z3Uw!~NF}V?s--6q)q_bmK14qu#9BCy^qu+doEJ~_dhcHL%j!M(xPh>hOg7vk1SK2x zplC;k8vu$NYQ65zWcj%_#As1h75u`7b?YJRzA@-7ZKi|W0yNFwLhL|cy%aKK0QJj3 zPOL$6RJpa9N=@X3`4DX>ebU{9XWFpu`}`s8Wgr4cs0u|T+hYk>YtrKD6Lhv%QqVbC zvIVQ>Ro0~N5h1z30anL-sD)0-uTY|g3r+x^~%*PNI;$&$J zDI3`D`v(VQPoaE-5RrE8Q~Z`DjDqi%wWxgLWC zNAkFu2696v#4;E|t~|!kIWd{j*az{H$EOXLWveF?zYyp zP@(G>*5h_0tRdDku7XTxuUnS%l~oI@3$WkmdIV~>DYe^8=bnb9DN(nDeMv9tiZQ;B z#G!RsT%5;PukoiP*M!_b@V=^0?2IGb7!YZ$#M^a+u%*6xu>*Mni; zrA3s2s`S9TOUcs;7m1r>TR8s4gO5{IJ>gOi_++u*$>NvE$gh?@l1^JoOkeXfpU*P( zxFaj_JdUptr>q!o&k>H{!fkf^ywsz+^b6`A9r04u`~-9L6S2Aeu~i&{b;|u&BFPvK z1~%npv$my#w5!dH|G-}}&fiF8x~0FhLjk#CfN&aTEjl^X^(uc1<@#%`eOG$va zfGg84pgZ@2J)~5y>*|?x7?A-5hTo8>rc0Q%A$k0SWUUZ)UokZD-{W}sx)Ttm08&DC z^?N=GTAwP|Z*y7<+TQ{n6eX6DZqOKNl%q?UbcWVuXLn5LNQ17;ns8~;qm|p&e(WEE zG$jCoFJ9htZwt+6Kk9y-GZl&3e)7#O0F%qA@$u~VbIC44S>k~7ZWm#y#Vw`9O4775 zK--gj@prQ|x58-gK>)|gkJnu#)|w@<(Rfhhh|SH6C;k9Z>vs+hl}Fd-Fu2-N)E8Vs zu1_lwVTzjYTD1>SjolCMpB-$W;C)Fv<_N<^^JD5iTp^@Qr;b=n%TYwr^>2B;pN~*z z(pxEtLRH(kFe%{&p}s4+-*Ts-%6)*cay|&jSq-JA(@D>3dlqoIRi^gFh+jTj`hY_* z3lSq(DYk)8!g8|9EG6!*D=Gb6k1?GuVfT6PX^Rwa{ch%j~we%G@hS|(Ie39QulxzMf6z#EBDxn2412QrGBz7t=VJ!V%2hl;(k3C=Z;{o^dml@F_upE0fpvziWd#-y%V^9^Qws z(Y?cA`!;@7SYCFPC(!t5VfS_eCxSjSgd`mez`gy;F{#*66Pt!nf8%VXu2a2ho^X{L z%|AddftBODnHYb=d~GzGB5o9?>0>@066fD6WnXE6+5Q~#jIUB4JH2rA|5xVcSQ+nQS_G+B1YCbl2ZvVXaqp^Dd zYq1zc5Rs%ztjlj8YkiIKyDJEA1w^pW}h4J zO59}mzFG!pX=SI+!g3s^)M?Zt8yS#QOso?WVAD|C8NI8DwV9E}iVT;#{V9cJ15%FD z$};8V(c)jjJR@){Zj_%q_s(p1;&kgMpYyrlu2<+DH@!EBEFufdG_dOM^*voVvrR>i z(sLoRcV8o%M4G-W7W`LR&-A}o(;stg3^plH@4hA~Vzikzud);ke{;UY3+ zunp|uM2X%(m80PgcB(Io6+7Y1YR_L5OZyGkN{5Y~IHRJ4xN;7tP&FW4oL4YbnzmTd z9vago;MFZ~U?nGq(SUEingraZwJPpofNnh}G@*~``h4}-fvmcUVkNDmv8`UGZP{Pl z!;yWa{b5bg(Rs%gvl5TPbsq&OdaU=<``06_S_&s&eR|CM@cM?YE7AbTL!EOrpwl%` zR_l-R1gOqAtTG%IDt1PT<>52XNQs$M!FeikH6@Esn?1F?k zqT9o7nxl_MQV}e}*n{3z;ad(w3>osZk8-SodGUkY7)#`4x+7KAu~0T&HMk5SVJUp~ zvC^k$z0R6an<=bl>cS}H5;+Ut)+cW7M`(~=25^ff+aN?B3qi!Xwsd@CyPs9nU^W+N z?^{$UmC!ZKG?$>u>A_i(+6l3O8bh_7ntJGlu&>^@#Ja zc)=x=mk6fl$K0r`_v*mwf9vM{Mb59n=iqHNPxliPr_`rUkQ!_MwAj-IqvC=%Cr+4b z^3=%tv-}*#!4W?d*B?%8su>I57(CA;it830?_<5&F5eB4w|1#L)1c=SD6;P4GUX{w z#~QDD`3SteF_9g7vS~=B*$pJoN*dptvJ@A`z!nYDom#_`Pui1D&ndGM#d+>jRi(aR za$XkC9Ijz3m#RJ&ww;>E_a+_gyX|dm|JK%+o1>aR%9>^x)&dsNPE-d4|5*djWQY#2 zbA=xW!#Z3`!ufTRhF+g2V*_e`OpWj4YVGFgZl15}5tK_5G==-!iG4%O>W%WyFh`S3 zH1V@e#-byK(dsTOE&@3zrK9zlxcVN+lHJ^6p0k<^q#L_Wvk)) zsdBo1uUR5qPWyYkGn?Sqy65YN#RUIPZ^4XcAisw?&~otCj6zwR{Si4LRQpcCc$`F- zg)9{dq{zD~1raYXFwv}wEdJIr9^4?0s6$1#_L^SzUBeH!hVeWk*Iyk_2qjpK~Zs z_ffDmD*LfDO%W@P`j#`sAqN+Ncv&o#<(6f^*7LbAttm58vsYVvTY9m28M*YZpawt< z1DrqP5)=Mwsi}%%1i42Q_+MmC1keYNYf7#tLwX$zZxR{mgxp^*XZxirO&_kJE_RBn zSOtL7S@gSSYQ^~C*h9g6t@atn+x`3!Ahfr8JU`>Cu#E>y z$ADAjPlC|dz=3)>R?8E7v1!;kc{|IBa?zV$%-A?PW~v@s+<_IaS*_jtL&0D;w=9>> zFs;4yX_aZyD;|K`sdOvVcuE9b#PWs^Eto85_V5O~ZauA~l;$+f*W?IWq&jr@(@jB?AR$^{>Sm#R2m#Tus5q3$mV(6s7lQWrh| zD{o}*jnNW9mHeHr(caV4e4PaAYMy7AhOGOHP<0Beb{P0^o^J_h&cdv4Xpvi*o=QEn zbIz~<|FGFRUSL~%VNa?vu;Ga9ik&o}F)$765imKW#jki{UriR3zQflV**J(j?k_TI zBsBV42P9opM+H1|2@xTJ5eVf(eo_mk;&?zd`2p)_yK6|;?4XzGRQBNDP<#OH$ p z1+wDkss`xe`bu26T_@ugtB`)5gHpK=e$oetj{o+CIUV-*>&AXN5azi zrQ-69WiHV?-KAIf2IwNFY}{ufnZeRjb%9kkTWU{zaGc%S$2g#1o?&~8y%9#c{1=J< zGcjE~Q3Na#Q^&;sid;gcZ8c?IMFoH#^y~SNJdB|+iCKTBK&4!qX?K*zQ6rI zcA~U29$}uwK_P}K2$T+?wFSxza}-PMeyWOohQ@~|KvW#=sGU-&9Mi(#koHQW$S`Ro zMfG|hP74^-*$z~(T z)=c(CO`y&k1Mz!Hj@OR>-_xVMfT^`>O(JAo-$edkAd90q2II^s@pg{jE|bOhm&5RN zH?wg4CLyToj!=<*51N`8mG|#h6!P)n1PIhuKL#8!#nE&@^*$(`*bD;GL$TMCbdrY2 zwl9EHZ{mo(j3Kxx)43LAqX3;4W*T=v=)?10W!N3PiKZXtD7)C`+|lf32_-e?(d8%d zsiCanp5fiSn(b&JV4={N>Q6RK594{O_|Z^Dx{Xa4f@HGMqRKq^QuV4$Q;S^<)_pl9 zVQG??o<#wE-AmEMw&W;=c~?pf)V#Wn%wixutr~16IisE0f!i7aQ}f#(e8E_=28c$D z(~?@TVZAsW^B>=w60Az(%DJS6jxGn|gkS~QKf zRQYJ#<*JXl2f7Lh|BsGm0#Cek~}k_|gdZ zUU6Z%d(LK2rz7``OW&89RW5XA64`@*}j9?BGdN!SU*oSYbSRz#e<{t(={-gVV zU+3q5ci!iXQ;e~Tc4^j+<^H*c?XQ>C^ZnRgUr~z0yZwIdpS~CBj;&pbn#~W(cej`p(P7S{w4B=j220j{%<6tE zT70%7@%?!3`Ox%DB{UTk)Y~Z?whXJ)|0eEL{NH<7Z{(GLUi`+kak){My}B_0Ecmvp zP18XR4-mvx2=03B8?ye3(i%iRdBZD76W(El{5>q1w#cXUf%u1h96S(}3Ils+c|n|2 zC7Uk9>0c(~{X;7lh-3-B+V$5Bg;S+*Q;ijTn4;B{5w-fehq))4oWAhDuk{I z{eZUj3%{%&HlP)0oZnG>@vsh!m+Ci_R=h!O&MkOCna3J|)eWtXOYXX3934vwG#*Ze z>XYt-b(O)B?N5i>W^;S!JDV3g=65~%yZ7t6&tt{|Rp#?heN=^cPB)3Y?Hl>gRHr4% z==HaiwnB~f^8OU-G$hHr33kH~=^UPWuBKz^pt5%K9}ff|YYKqu~;5Qbgs%426dT^e}AN z7QECC;I;E$pwVYSDCd8@TcQ;D<} zdC>2D{8P_aA}3B$(npqmFcfSfS$}n0Z(qJUFZC5&7uaqUeP)Vq$^hAZDxpktv-uF7 zDklMqS1ex~Ofnp%^a>jqf`bo*&{;Rx<333;z-<}kFbyJ|Owv8V;pCo8oxS}y;3 zA?BN}CT$LmefjLUm1rHGj#1G{&%8Y*9r0>#I%uM9aVy+;3nJ;#r(=5r^LcaJ++1-K z_N}JycMK<2FKDJ2I=i z64<(urD_`%ren)oNTOJt#@Dvp$~mgLp!v#IslJ za|wHk6i8-S8I;+YdMpM&r<(dsr2XwBt8T$om

    $&$S-U%45ZeZK+;0t!E>@qxSI! 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 literal 0 HcmV?d00001 diff --git a/x-pack/test/functional/es_archives/cases/migrations/8.8.0/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/8.8.0/mappings.json new file mode 100644 index 0000000000000..cf252ec426fb5 --- /dev/null +++ b/x-pack/test/functional/es_archives/cases/migrations/8.8.0/mappings.json @@ -0,0 +1,3049 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + "is_hidden": true + }, + ".kibana_8.8.0": { + "is_hidden": true + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "0be88ebcc8560a075b6898236a202eb1", + "action_task_params": "3d1b76c39bfb2cc8296b024d73854724", + "alert": "96a5a144778243a9f4fece0e71c2197f", + "api_key_pending_invalidation": "16f515278a295f6245149ad7c5ddedb7", + "apm-indices": "3d1b76c39bfb2cc8296b024d73854724", + "apm-server-schema": "b1d71908f324c17bf744ac72af5038fb", + "apm-service-group": "2af509c6506f29a858e5a0950577d9fa", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "cases": "ee88bba6d1a7ae7157452b72e598b85b", + "cases-comments": "93535d41ca0279a4a2e5d08acd3f28e3", + "cases-configure": "c124bd0be4c139d0f0f91fb9eeca8e37", + "cases-connector-mappings": "a98c33813f364f0b068e8c592ac6ef6d", + "cases-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "cases-user-actions": "07a6651cf37853dd5d64bfb2c796e102", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "config-global": "c63748b75f39d0c54de12d12c1ccbc20", + "connector_token": "740b3fd18387d4097dca8d177e6a35c6", + "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", + "created_at": "00da57df13e94e9d98437d13ace4bfe0", + "csp-rule-template": "8b03466c92eddc1458758ca6827eb540", + "dashboard": "bc881edf161013a17165a244e0812e9f", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "endpoint:user-artifact-manifest": "a0d7b04ad405eed54d76e279c3727862", + "enterprise_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "epm-packages": "c6404322e681df9db37c9359c9d5a6cd", + "epm-packages-assets": "44621b2f6052ef966da47b7c3a00f33b", + "event_loop_delays_daily": "5df7e292ddd5028e07c1482e130e6654", + "exception-list": "8a1defe5981db16792cb9a772e84bb9a", + "exception-list-agnostic": "8a1defe5981db16792cb9a772e84bb9a", + "file": "c38faa34f21af9cef4301a687de5dce0", + "file-upload-usage-collection-telemetry": "a34fbb8e3263d105044869264860c697", + "fileShare": "aa8f7ac2ddf8ab1a91bd34e347046caa", + "fleet-fleet-server-host": "434168167d1a1fd82fe92584ab9845c2", + "fleet-message-signing-keys": "3d1b76c39bfb2cc8296b024d73854724", + "fleet-preconfiguration-deletion-record": "4c36f199189a367e43541f236141204c", + "fleet-proxy": "05b7a22977de25ce67a77e44dd8e6c33", + "graph-workspace": "27a94b2edcb0610c6aea54a7c56d7752", + "guided-onboarding-guide-state": "a3db59c45a3fd2730816d4f53c35c7d9", + "guided-onboarding-plugin-state": "3d1b76c39bfb2cc8296b024d73854724", + "index-pattern": "83c02d842fe2a94d14dfa13f7dcd6e87", + "infrastructure-monitoring-log-view": "c50526fc6040c5355ed027d34d05b35c", + "infrastructure-ui-source": "3d1b76c39bfb2cc8296b024d73854724", + "ingest-agent-policies": "ea5e6042413da5798f7f2bae9ae0aa7e", + "ingest-download-sources": "5d602f6594f3b589dc9a8e3a44031522", + "ingest-outputs": "e2347e19e5c2a7448529d77abbf27f30", + "ingest-package-policies": "20e952d74d979ef91a4918dde7617eb3", + "ingest_manager_settings": "a9f356eec1c40d030f433bbf47c2b479", + "inventory-view": "3d1b76c39bfb2cc8296b024d73854724", + "kql-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "legacy-url-alias": "0750774cf16475f88f2361e99cc5c8f0", + "lens": "b0da10d5ab9ebd81d61700737ddc76c9", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "maintenance-window": "a58ac2ef53ff5103710093e669dcc1d8", + "map": "9134b47593116d7953f6adba096fc463", + "metrics-explorer-view": "3d1b76c39bfb2cc8296b024d73854724", + "ml-job": "3bb64c31915acf93fc724af137a0891b", + "ml-module": "46ef4f0d6682636f0fff9799d6a2d7ac", + "ml-trained-model": "d2f03c1a5dd038fa58af14a56944312b", + "monitoring-telemetry": "2669d5ec15e82391cf58df4294ee9c68", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "osquery-manager-usage-metric": "4dc4f647d27247c002f56f22742175fe", + "osquery-pack": "ac387f64e316364439c98079ae519a7a", + "osquery-pack-asset": "5e1c0d70ab14c3c801b6e9a2d390ad7c", + "osquery-saved-query": "e1f9820518bd51cacb73124ad6552adc", + "query": "df07b1a361c32daf4e6842c1d5521dbe", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "rules-settings": "001f60645e96c71520214b57f3ea7590", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "df07b1a361c32daf4e6842c1d5521dbe", + "search-session": "fea3612a90b81672991617646f229a61", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "security-rule": "9d9d11b97e3aaa87fbaefbace2b5c25f", + "security-solution-signals-migration": "72761fd374ca11122ac8025a92b84fca", + "siem-detection-engine-rule-actions": "f5c218f837bac10ab2c3980555176cf9", + "siem-ui-timeline": "0f4cc81427182c41cebd7d9c640ec253", + "siem-ui-timeline-note": "28393dfdeb4e4413393eb5f7ec8c5436", + "siem-ui-timeline-pinned-event": "293fce142548281599060e07ad2c9ddb", + "slo": "71d78aec1e4a92e097d427141199506a", + "space": "aed174cfc1cecf5740ff4a872a5c9a7b", + "spaces-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "synthetics-monitor": "b57abe4845799dc48f948c2b3ff19818", + "synthetics-param": "3d1b76c39bfb2cc8296b024d73854724", + "synthetics-privates-locations": "3d1b76c39bfb2cc8296b024d73854724", + "tag": "83d55da58f6530f7055415717ec06474", + "telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "type": "2f4316de49999235636386fe51dc06c1", + "typeMigrationVersion": "539e3ecebb3abc1133618094cc3b7ae7", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-ml-upgrade-operation": "3caf305ad2da94d80d49453b0970156d", + "upgrade-assistant-reindex-operation": "6d1e2aca91767634e1829c30f20f6b16", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "uptime-synthetics-api-key": "c3178f0fde61e18d3530ba9a70bc278a", + "url": "a37dbae7645ad5811045f4dd3dc1c0a8", + "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", + "visualization": "4891c012863513388881fc109fec4809", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "dynamic": "false", + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "action_task_params": { + "dynamic": "false", + "type": "object" + }, + "alert": { + "dynamic": "false", + "properties": { + "actions": { + "dynamic": "false", + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "executionStatus": { + "properties": { + "error": { + "properties": { + "message": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + } + } + }, + "lastDuration": { + "type": "long" + }, + "lastExecutionDate": { + "type": "date" + }, + "numberOfTriggeredActions": { + "type": "long" + }, + "status": { + "type": "keyword" + }, + "warning": { + "properties": { + "message": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + } + } + } + } + }, + "lastRun": { + "properties": { + "alertsCount": { + "properties": { + "active": { + "type": "float" + }, + "ignored": { + "type": "float" + }, + "new": { + "type": "float" + }, + "recovered": { + "type": "float" + } + } + }, + "outcome": { + "type": "keyword" + }, + "outcomeOrder": { + "type": "float" + } + } + }, + "legacyId": { + "type": "keyword" + }, + "mapped_params": { + "properties": { + "risk_score": { + "type": "float" + }, + "severity": { + "type": "keyword" + } + } + }, + "monitoring": { + "properties": { + "run": { + "properties": { + "calculated_metrics": { + "properties": { + "p50": { + "type": "long" + }, + "p95": { + "type": "long" + }, + "p99": { + "type": "long" + }, + "success_ratio": { + "type": "float" + } + } + }, + "last_run": { + "properties": { + "metrics": { + "properties": { + "duration": { + "type": "long" + }, + "gap_duration_s": { + "type": "float" + }, + "total_alerts_created": { + "type": "float" + }, + "total_alerts_detected": { + "type": "float" + }, + "total_indexing_duration_ms": { + "type": "long" + }, + "total_search_duration_ms": { + "type": "long" + } + } + }, + "timestamp": { + "type": "date" + } + } + } + } + } + } + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "normalizer": "lowercase", + "type": "keyword" + } + }, + "type": "text" + }, + "notifyWhen": { + "type": "keyword" + }, + "params": { + "ignore_above": 4096, + "type": "flattened" + }, + "revision": { + "type": "long" + }, + "running": { + "type": "boolean" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "snoozeSchedule": { + "properties": { + "duration": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "skipRecurrences": { + "format": "strict_date_time", + "type": "date" + } + }, + "type": "nested" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedAt": { + "type": "date" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "api_key_pending_invalidation": { + "properties": { + "apiKeyId": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + } + } + }, + "apm-indices": { + "dynamic": "false", + "type": "object" + }, + "apm-server-schema": { + "properties": { + "schemaJson": { + "index": false, + "type": "text" + } + } + }, + "apm-service-group": { + "properties": { + "color": { + "type": "text" + }, + "description": { + "type": "text" + }, + "groupName": { + "type": "keyword" + }, + "kuery": { + "type": "text" + } + } + }, + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "app_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_daily": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad-template": { + "dynamic": "false", + "properties": { + "help": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "dynamic": "false", + "properties": { + "assignees": { + "properties": { + "uid": { + "type": "keyword" + } + } + }, + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "profile_uid": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector": { + "properties": { + "fields": { + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "profile_uid": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "duration": { + "type": "unsigned_long" + }, + "external_service": { + "properties": { + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "profile_uid": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "owner": { + "type": "keyword" + }, + "settings": { + "properties": { + "syncAlerts": { + "type": "boolean" + } + } + }, + "severity": { + "type": "short" + }, + "status": { + "type": "short" + }, + "tags": { + "type": "keyword" + }, + "title": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "total_alerts": { + "type": "integer" + }, + "total_comments": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "profile_uid": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "dynamic": "false", + "properties": { + "actions": { + "properties": { + "type": { + "type": "keyword" + } + } + }, + "alertId": { + "type": "keyword" + }, + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "username": { + "type": "keyword" + } + } + }, + "externalReferenceAttachmentTypeId": { + "type": "keyword" + }, + "owner": { + "type": "keyword" + }, + "persistableStateAttachmentTypeId": { + "type": "keyword" + }, + "pushed_at": { + "type": "date" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "cases-configure": { + "dynamic": "false", + "properties": { + "closure_type": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "owner": { + "type": "keyword" + } + } + }, + "cases-connector-mappings": { + "dynamic": "false", + "properties": { + "owner": { + "type": "keyword" + } + } + }, + "cases-telemetry": { + "dynamic": "false", + "type": "object" + }, + "cases-user-actions": { + "dynamic": "false", + "properties": { + "action": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "username": { + "type": "keyword" + } + } + }, + "owner": { + "type": "keyword" + }, + "payload": { + "dynamic": "false", + "properties": { + "assignees": { + "properties": { + "uid": { + "type": "keyword" + } + } + }, + "comment": { + "properties": { + "externalReferenceAttachmentTypeId": { + "type": "keyword" + }, + "persistableStateAttachmentTypeId": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "connector": { + "properties": { + "type": { + "type": "keyword" + } + } + } + } + }, + "type": { + "type": "keyword" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "config-global": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "connector_token": { + "dynamic": "false", + "properties": { + "connectorId": { + "type": "keyword" + }, + "tokenType": { + "type": "keyword" + } + } + }, + "core-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "coreMigrationVersion": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "csp-rule-template": { + "dynamic": "false", + "properties": { + "metadata": { + "properties": { + "benchmark": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + } + } + }, + "dashboard": { + "properties": { + "controlGroupInput": { + "properties": { + "chainingSystem": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "controlStyle": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "ignoreParentSettingsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + } + } + }, + "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" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "index": false, + "type": "keyword" + }, + "created": { + "index": false, + "type": "date" + }, + "decodedSha256": { + "index": false, + "type": "keyword" + }, + "decodedSize": { + "index": false, + "type": "long" + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "index": false, + "type": "long" + }, + "encryptionAlgorithm": { + "index": false, + "type": "keyword" + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "artifacts": { + "properties": { + "artifactId": { + "index": false, + "type": "keyword" + }, + "policyId": { + "index": false, + "type": "keyword" + } + }, + "type": "nested" + }, + "created": { + "index": false, + "type": "date" + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "index": false, + "type": "keyword" + } + } + }, + "enterprise_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "dynamic": "false", + "type": "object" + }, + "experimental_data_stream_features": { + "properties": { + "data_stream": { + "type": "keyword" + }, + "features": { + "dynamic": "false", + "properties": { + "synthetic_source": { + "type": "boolean" + }, + "tsdb": { + "type": "boolean" + } + }, + "type": "nested" + } + }, + "type": "nested" + }, + "install_format_schema_version": { + "type": "version" + }, + "install_source": { + "type": "keyword" + }, + "install_started_at": { + "type": "date" + }, + "install_status": { + "type": "keyword" + }, + "install_version": { + "type": "keyword" + }, + "installed_es": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + }, + "type": "nested" + }, + "installed_kibana": { + "dynamic": "false", + "type": "object" + }, + "installed_kibana_space_id": { + "type": "keyword" + }, + "internal": { + "type": "boolean" + }, + "keep_policies_up_to_date": { + "index": false, + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "package_assets": { + "dynamic": "false", + "type": "object" + }, + "verification_key_id": { + "type": "keyword" + }, + "verification_status": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "epm-packages-assets": { + "properties": { + "asset_path": { + "type": "keyword" + }, + "data_base64": { + "type": "binary" + }, + "data_utf8": { + "index": false, + "type": "text" + }, + "install_source": { + "type": "keyword" + }, + "media_type": { + "type": "keyword" + }, + "package_name": { + "type": "keyword" + }, + "package_version": { + "type": "keyword" + } + } + }, + "event_loop_delays_daily": { + "dynamic": "false", + "properties": { + "lastUpdatedAt": { + "type": "date" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "expire_time": { + "type": "date" + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "expire_time": { + "type": "date" + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "file": { + "dynamic": "false", + "properties": { + "FileKind": { + "type": "keyword" + }, + "Meta": { + "type": "flattened" + }, + "Status": { + "type": "keyword" + }, + "Updated": { + "type": "date" + }, + "created": { + "type": "date" + }, + "extension": { + "type": "keyword" + }, + "mime_type": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "size": { + "type": "long" + }, + "user": { + "type": "flattened" + } + } + }, + "file-upload-usage-collection-telemetry": { + "properties": { + "file_upload": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "fileShare": { + "dynamic": "false", + "properties": { + "created": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "token": { + "type": "keyword" + }, + "valid_until": { + "type": "long" + } + } + }, + "fleet-fleet-server-host": { + "properties": { + "host_urls": { + "index": false, + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "proxy_id": { + "type": "keyword" + } + } + }, + "fleet-message-signing-keys": { + "dynamic": "false", + "type": "object" + }, + "fleet-preconfiguration-deletion-record": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "fleet-proxy": { + "properties": { + "certificate": { + "index": false, + "type": "keyword" + }, + "certificate_authorities": { + "index": false, + "type": "keyword" + }, + "certificate_key": { + "index": false, + "type": "keyword" + }, + "is_preconfigured": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "proxy_headers": { + "index": false, + "type": "text" + }, + "url": { + "index": false, + "type": "keyword" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "legacyIndexPatternRef": { + "index": false, + "type": "text" + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "guided-onboarding-guide-state": { + "dynamic": "false", + "properties": { + "guideId": { + "type": "keyword" + }, + "isActive": { + "type": "boolean" + } + } + }, + "guided-onboarding-plugin-state": { + "dynamic": "false", + "type": "object" + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-monitoring-log-view": { + "dynamic": "false", + "properties": { + "name": { + "type": "text" + } + } + }, + "infrastructure-ui-source": { + "dynamic": "false", + "type": "object" + }, + "ingest-agent-policies": { + "properties": { + "agent_features": { + "properties": { + "enabled": { + "type": "boolean" + }, + "name": { + "type": "keyword" + } + } + }, + "data_output_id": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "download_source_id": { + "type": "keyword" + }, + "fleet_server_host_id": { + "type": "keyword" + }, + "inactivity_timeout": { + "type": "integer" + }, + "is_default": { + "type": "boolean" + }, + "is_default_fleet_server": { + "type": "boolean" + }, + "is_managed": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "keyword" + }, + "monitoring_enabled": { + "index": false, + "type": "keyword" + }, + "monitoring_output_id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "schema_version": { + "type": "version" + }, + "status": { + "type": "keyword" + }, + "unenroll_timeout": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-download-sources": { + "properties": { + "host": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "source_id": { + "index": false, + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "index": false, + "type": "keyword" + }, + "ca_trusted_fingerprint": { + "index": false, + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "config_yaml": { + "type": "text" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "is_default_monitoring": { + "type": "boolean" + }, + "is_preconfigured": { + "index": false, + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "output_id": { + "index": false, + "type": "keyword" + }, + "proxy_id": { + "type": "keyword" + }, + "shipper": { + "dynamic": "false", + "type": "object" + }, + "ssl": { + "type": "binary" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "elasticsearch": { + "dynamic": "false", + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "dynamic": "false", + "type": "object" + }, + "is_managed": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + } + }, + "ingest_manager_settings": { + "properties": { + "fleet_server_hosts": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "index": false, + "type": "boolean" + }, + "prerelease_integrations_enabled": { + "type": "boolean" + } + } + }, + "inventory-view": { + "dynamic": "false", + "type": "object" + }, + "kql-telemetry": { + "dynamic": "false", + "type": "object" + }, + "legacy-url-alias": { + "dynamic": "false", + "properties": { + "disabled": { + "type": "boolean" + }, + "resolveCounter": { + "type": "long" + }, + "sourceId": { + "type": "keyword" + }, + "targetId": { + "type": "keyword" + }, + "targetNamespace": { + "type": "keyword" + }, + "targetType": { + "type": "keyword" + } + } + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "state": { + "dynamic": "false", + "type": "object" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "maintenance-window": { + "dynamic": "false", + "properties": { + "enabled": { + "type": "boolean" + }, + "events": { + "format": "epoch_millis||strict_date_optional_time", + "type": "date_range" + } + } + }, + "map": { + "properties": { + "bounds": { + "dynamic": "false", + "type": "object" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "metrics-explorer-view": { + "dynamic": "false", + "type": "object" + }, + "ml-job": { + "properties": { + "datafeed_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "job_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "ml-module": { + "dynamic": "false", + "properties": { + "datafeeds": { + "type": "object" + }, + "defaultIndexPattern": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "description": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "jobs": { + "type": "object" + }, + "logo": { + "type": "object" + }, + "query": { + "type": "object" + }, + "title": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "type": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-trained-model": { + "properties": { + "job": { + "properties": { + "create_time": { + "type": "date" + }, + "job_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "model_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "monitoring-telemetry": { + "properties": { + "reportedClusterUuids": { + "type": "keyword" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "type": "keyword" + }, + "osquery-manager-usage-metric": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + }, + "osquery-pack": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "type": "text" + }, + "queries": { + "dynamic": "false", + "properties": { + "ecs_mapping": { + "dynamic": "false", + "type": "object" + }, + "id": { + "type": "keyword" + }, + "interval": { + "type": "text" + }, + "platform": { + "type": "keyword" + }, + "query": { + "type": "text" + }, + "version": { + "type": "keyword" + } + } + }, + "shards": { + "dynamic": "false", + "type": "object" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "long" + } + } + }, + "osquery-pack-asset": { + "dynamic": "false", + "properties": { + "description": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queries": { + "dynamic": "false", + "properties": { + "ecs_mapping": { + "dynamic": "false", + "type": "object" + }, + "id": { + "type": "keyword" + }, + "interval": { + "type": "text" + }, + "platform": { + "type": "keyword" + }, + "query": { + "type": "text" + }, + "version": { + "type": "keyword" + } + } + }, + "shards": { + "dynamic": "false", + "type": "object" + }, + "version": { + "type": "long" + } + } + }, + "osquery-saved-query": { + "dynamic": "false", + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "text" + }, + "description": { + "type": "text" + }, + "ecs_mapping": { + "dynamic": "false", + "type": "object" + }, + "id": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "platform": { + "type": "keyword" + }, + "query": { + "type": "text" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "text" + }, + "version": { + "type": "keyword" + } + } + }, + "query": { + "dynamic": "false", + "properties": { + "description": { + "type": "text" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "rules-settings": { + "dynamic": "false", + "properties": { + "flapping": { + "type": "object" + } + } + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "dynamic": "false", + "properties": { + "description": { + "type": "text" + }, + "title": { + "type": "text" + } + } + }, + "search-session": { + "dynamic": "false", + "properties": { + "created": { + "type": "date" + }, + "realmName": { + "type": "keyword" + }, + "realmType": { + "type": "keyword" + }, + "sessionId": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "security-rule": { + "dynamic": "false", + "properties": { + "rule_id": { + "type": "keyword" + }, + "version": { + "type": "long" + } + } + }, + "security-solution-signals-migration": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "createdBy": { + "index": false, + "type": "text" + }, + "destinationIndex": { + "index": false, + "type": "keyword" + }, + "error": { + "index": false, + "type": "text" + }, + "sourceIndex": { + "type": "keyword" + }, + "status": { + "index": false, + "type": "keyword" + }, + "taskId": { + "index": false, + "type": "keyword" + }, + "updated": { + "index": false, + "type": "date" + }, + "updatedBy": { + "index": false, + "type": "text" + }, + "version": { + "type": "long" + } + } + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "dynamic": "false", + "type": "object" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eqlOptions": { + "properties": { + "eventCategoryField": { + "type": "text" + }, + "query": { + "type": "text" + }, + "size": { + "type": "text" + }, + "tiebreakerField": { + "type": "text" + }, + "timestampField": { + "type": "text" + } + } + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "indexNames": { + "type": "text" + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "sort": { + "dynamic": "false", + "properties": { + "columnId": { + "type": "keyword" + }, + "columnType": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "slo": { + "dynamic": "false", + "properties": { + "budgetingMethod": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "indicator": { + "properties": { + "params": { + "type": "flattened" + }, + "type": { + "type": "keyword" + } + } + }, + "name": { + "type": "text" + }, + "tags": { + "type": "keyword" + } + } + }, + "space": { + "dynamic": "false", + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "spaces-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "synthetics-monitor": { + "dynamic": "false", + "properties": { + "alert": { + "properties": { + "status": { + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + }, + "custom_heartbeat_id": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "hash": { + "type": "keyword" + }, + "hosts": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "id": { + "type": "keyword" + }, + "journey_id": { + "type": "keyword" + }, + "locations": { + "properties": { + "id": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 256, + "type": "keyword" + }, + "label": { + "type": "text" + } + } + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 256, + "normalizer": "lowercase", + "type": "keyword" + } + }, + "type": "text" + }, + "origin": { + "type": "keyword" + }, + "project_id": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + }, + "schedule": { + "properties": { + "number": { + "type": "integer" + } + } + }, + "tags": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + }, + "type": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "urls": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "synthetics-param": { + "dynamic": "false", + "type": "object" + }, + "synthetics-privates-locations": { + "dynamic": "false", + "type": "object" + }, + "tag": { + "properties": { + "color": { + "type": "text" + }, + "description": { + "type": "text" + }, + "name": { + "type": "text" + } + } + }, + "telemetry": { + "dynamic": "false", + "type": "object" + }, + "type": { + "type": "keyword" + }, + "typeMigrationVersion": { + "type": "version" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-ml-upgrade-operation": { + "dynamic": "false", + "properties": { + "snapshotId": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "upgrade-assistant-reindex-operation": { + "dynamic": "false", + "properties": { + "indexName": { + "type": "keyword" + }, + "status": { + "type": "integer" + } + } + }, + "uptime-dynamic-settings": { + "dynamic": "false", + "type": "object" + }, + "uptime-synthetics-api-key": { + "dynamic": "false", + "properties": { + "apiKey": { + "type": "binary" + } + } + }, + "url": { + "dynamic": "false", + "properties": { + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "slug": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "usage-counters": { + "dynamic": "false", + "properties": { + "domainId": { + "type": "keyword" + } + } + }, + "visualization": { + "dynamic": "false", + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "type": "object" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "workplace_search_telemetry": { + "dynamic": "false", + "type": "object" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "hidden": "true", + "mapping": { + "total_fields": { + "limit": "1500" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "priority": "10", + "refresh_interval": "1s" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/pre_calculated_histogram/data.json b/x-pack/test/functional/es_archives/pre_calculated_histogram/data.json index 121a4036aaacd..cf696a0691c4b 100644 --- a/x-pack/test/functional/es_archives/pre_calculated_histogram/data.json +++ b/x-pack/test/functional/es_archives/pre_calculated_histogram/data.json @@ -2,7 +2,7 @@ "type": "doc", "value": { "id": "index-pattern:histogram-test", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "title": "histogram-test" diff --git a/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json b/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json index c9dfdf1c4e913..75e5447906c70 100644 --- a/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json +++ b/x-pack/test/functional/es_archives/security_solution/ecs_compliant/mappings.json @@ -15,6 +15,9 @@ "name": { "type": "keyword" }, + "version": { + "type": "keyword" + }, "type": { "type": "long" } diff --git a/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json b/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json index 8dbc0fbe883e8..1663b2e9c6bdf 100644 --- a/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json +++ b/x-pack/test/functional/es_archives/security_solution/legacy_actions/data.json @@ -1,7 +1,7 @@ { "type": "doc", "value": { - "index" : ".kibana", + "index" : ".kibana_alerting_cases", "id" : "action:c95cb100-b075-11ec-bb3f-1f063f8e06cf", "source" : { "action" : { @@ -23,9 +23,7 @@ "namespaces" : [ "default" ], - "migrationVersion" : { - "action" : "8.0.0" - }, + "typeMigrationVersion": "8.0.0", "coreMigrationVersion" : "8.1.2", "updated_at" : "2022-03-30T22:07:27.777Z" } @@ -35,26 +33,24 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "action:207fa0e0-c04e-11ec-8a52-4fb92379525a", - "source" : { - "action" : { - "actionTypeId" : ".slack", - "name" : "Slack connector", - "isMissingSecrets" : false, - "config" : { }, - "secrets" : "WTpvAWxAnR1yHdFqQrmGGeOmqGzeIDQnjGexz7F3JdFVEH0CJkPbiyEk9SiiBr1OPZ3s0LWqphjNREjyxSTCTuYTKaEIQ1+mJFQzjIulekMZklQdon7kNh4hOVHW832IX1Lpx9f5cgAPQst2bqJCehz+3BeZOh17Hgrvb/0zzKRCR4cvlhYbOUNUCNR/d6YoEniwUK8pO9xcel9hle7XY6MNfBGiMX8T41O87lvgp9xDjch3LpwqzDjUIFs=" + "index": ".kibana_alerting_cases", + "id": "action:207fa0e0-c04e-11ec-8a52-4fb92379525a", + "source": { + "action": { + "actionTypeId": ".slack", + "name": "Slack connector", + "isMissingSecrets": false, + "config": {}, + "secrets": "WTpvAWxAnR1yHdFqQrmGGeOmqGzeIDQnjGexz7F3JdFVEH0CJkPbiyEk9SiiBr1OPZ3s0LWqphjNREjyxSTCTuYTKaEIQ1+mJFQzjIulekMZklQdon7kNh4hOVHW832IX1Lpx9f5cgAPQst2bqJCehz+3BeZOh17Hgrvb/0zzKRCR4cvlhYbOUNUCNR/d6YoEniwUK8pO9xcel9hle7XY6MNfBGiMX8T41O87lvgp9xDjch3LpwqzDjUIFs=" }, - "type" : "action", - "references" : [ ], - "namespaces" : [ + "type": "action", + "references": [], + "namespaces": [ "default" ], - "migrationVersion" : { - "action" : "8.0.0" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:07:27.777Z" + "typeMigrationVersion": "8.0.0", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:07:27.777Z" } } } @@ -62,45 +58,45 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:9095ee90-b075-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ no actions", - "tags" : [ + "index": ".kibana_alerting_cases", + "id": "alert:9095ee90-b075-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ no actions", + "tags": [ "__internal_rule_id:2297be91-894c-4831-830f-b424a0ec84f0", "__internal_immutable:false", "auto_disabled_8.0" ], - "alertTypeId" : "siem.queryRule", - "consumer" : "siem", + "alertTypeId": "siem.queryRule", + "consumer": "siem", "revision": 0, - "params" : { - "author" : [ ], - "description" : "a", - "ruleId" : "2297be91-894c-4831-830f-b424a0ec84f0", - "falsePositives" : [ ], - "from" : "now-360s", - "immutable" : false, - "license" : "", - "outputIndex" : "", - "meta" : { - "from" : "1m", - "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + "params": { + "author": [], + "description": "a", + "ruleId": "2297be91-894c-4831-830f-b424a0ec84f0", + "falsePositives": [], + "from": "now-360s", + "immutable": false, + "license": "", + "outputIndex": "", + "meta": { + "from": "1m", + "kibana_siem_app_url": "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" }, - "maxSignals" : 100, - "riskScore" : 21, - "riskScoreMapping" : [ ], - "severity" : "low", - "severityMapping" : [ ], - "threat" : [ ], - "to" : "now", - "references" : [ ], - "version" : 1, - "exceptionsList" : [ ], - "type" : "query", - "language" : "kuery", - "index" : [ + "maxSignals": 100, + "riskScore": 21, + "riskScoreMapping": [], + "severity": "low", + "severityMapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptionsList": [], + "type": "query", + "language": "kuery", + "index": [ "apm-*-transaction*", "traces-apm*", "auditbeat-*", @@ -110,46 +106,44 @@ "packetbeat-*", "winlogbeat-*" ], - "query" : "*:*", - "filters" : [ ] + "query": "*:*", + "filters": [] }, - "schedule" : { - "interval" : "5m" + "schedule": { + "interval": "5m" }, - "enabled" : true, - "actions" : [ ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:05:53.511Z", - "updatedAt" : "2022-03-30T22:05:53.511Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-03-31T19:53:37.507Z", - "error" : null, - "lastDuration" : 2377 + "enabled": true, + "actions": [], + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:05:53.511Z", + "updatedAt": "2022-03-30T22:05:53.511Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-03-31T19:53:37.507Z", + "error": null, + "lastDuration": 2377 }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "9095ee90-b075-11ec-bb3f-1f063f8e06cf" + "scheduledTaskId": null, + "legacyId": "9095ee90-b075-11ec-bb3f-1f063f8e06cf" }, - "type" : "alert", - "references" : [ ], - "namespaces" : [ + "type": "alert", + "references": [], + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-31T19:53:39.885Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-31T19:53:39.885Z" } } } @@ -157,45 +151,45 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:dc6595f0-b075-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ action - every rule run", - "tags" : [ + "index": ".kibana_alerting_cases", + "id": "alert:dc6595f0-b075-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ action - every rule run", + "tags": [ "__internal_rule_id:72a0d429-363b-4f70-905e-c6019a224d40", "__internal_immutable:false", "auto_disabled_8.0" ], - "alertTypeId" : "siem.queryRule", - "consumer" : "siem", + "alertTypeId": "siem.queryRule", + "consumer": "siem", "revision": 0, - "params" : { - "author" : [ ], - "description" : "a", - "ruleId" : "72a0d429-363b-4f70-905e-c6019a224d40", - "falsePositives" : [ ], - "from" : "now-360s", - "immutable" : false, - "license" : "", - "outputIndex" : "", - "meta" : { - "from" : "1m", - "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + "params": { + "author": [], + "description": "a", + "ruleId": "72a0d429-363b-4f70-905e-c6019a224d40", + "falsePositives": [], + "from": "now-360s", + "immutable": false, + "license": "", + "outputIndex": "", + "meta": { + "from": "1m", + "kibana_siem_app_url": "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" }, - "maxSignals" : 100, - "riskScore" : 21, - "riskScoreMapping" : [ ], - "severity" : "low", - "severityMapping" : [ ], - "threat" : [ ], - "to" : "now", - "references" : [ ], - "version" : 1, - "exceptionsList" : [ ], - "type" : "query", - "language" : "kuery", - "index" : [ + "maxSignals": 100, + "riskScore": 21, + "riskScoreMapping": [], + "severity": "low", + "severityMapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptionsList": [], + "type": "query", + "language": "kuery", + "index": [ "apm-*-transaction*", "traces-apm*", "auditbeat-*", @@ -205,111 +199,109 @@ "packetbeat-*", "winlogbeat-*" ], - "query" : "*:* ", - "filters" : [ ] + "query": "*:* ", + "filters": [] }, - "schedule" : { - "interval" : "5m" + "schedule": { + "interval": "5m" }, - "enabled" : true, - "actions" : [ + "enabled": true, + "actions": [ { - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Test Actions" + "subject": "Test Actions" }, - "actionTypeId" : ".email", - "actionRef" : "action_0" + "actionTypeId": ".email", + "actionRef": "action_0" } ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:08:00.172Z", - "updatedAt" : "2022-03-30T22:08:00.172Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-03-31T19:55:43.502Z", - "error" : null, - "lastDuration" : 2700 + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:08:00.172Z", + "updatedAt": "2022-03-30T22:08:00.172Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-03-31T19:55:43.502Z", + "error": null, + "lastDuration": 2700 }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "dc6595f0-b075-11ec-bb3f-1f063f8e06cf" + "scheduledTaskId": null, + "legacyId": "dc6595f0-b075-11ec-bb3f-1f063f8e06cf" }, - "type" : "alert", - "references" : [ + "type": "alert", + "references": [ { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "name" : "action_0", - "type" : "action" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name": "action_0", + "type": "action" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-31T19:55:46.202Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-31T19:55:46.202Z" } } } { "type": "doc", - "value": { - "index" : ".kibana", - "id" : "alert:064e3160-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ action - every hour", - "tags" : [ + "value": { + "index": ".kibana_alerting_cases", + "id": "alert:064e3160-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ action - every hour", + "tags": [ "__internal_rule_id:4c056b05-75ac-4209-be32-82100f771eb4", "__internal_immutable:false", "auto_disabled_8.0" ], - "alertTypeId" : "siem.queryRule", - "consumer" : "siem", + "alertTypeId": "siem.queryRule", + "consumer": "siem", "revision": 0, - "params" : { - "author" : [ ], - "description" : "a", - "ruleId" : "4c056b05-75ac-4209-be32-82100f771eb4", - "falsePositives" : [ ], - "from" : "now-360s", - "immutable" : false, - "license" : "", - "outputIndex" : "", - "meta" : { - "from" : "1m", - "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + "params": { + "author": [], + "description": "a", + "ruleId": "4c056b05-75ac-4209-be32-82100f771eb4", + "falsePositives": [], + "from": "now-360s", + "immutable": false, + "license": "", + "outputIndex": "", + "meta": { + "from": "1m", + "kibana_siem_app_url": "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" }, - "maxSignals" : 100, - "riskScore" : 21, - "riskScoreMapping" : [ ], - "severity" : "low", - "severityMapping" : [ ], - "threat" : [ ], - "to" : "now", - "references" : [ ], - "version" : 1, - "exceptionsList" : [ ], - "type" : "query", - "language" : "kuery", - "index" : [ + "maxSignals": 100, + "riskScore": 21, + "riskScoreMapping": [], + "severity": "low", + "severityMapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptionsList": [], + "type": "query", + "language": "kuery", + "index": [ "apm-*-transaction*", "traces-apm*", "auditbeat-*", @@ -319,46 +311,44 @@ "packetbeat-*", "winlogbeat-*" ], - "query" : "*:*", - "filters" : [ ] + "query": "*:*", + "filters": [] }, - "schedule" : { - "interval" : "5m" + "schedule": { + "interval": "5m" }, - "enabled" : true, - "actions" : [ ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:09:10.261Z", - "updatedAt" : "2022-03-30T22:09:10.261Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-03-31T19:56:43.499Z", - "error" : null, - "lastDuration" : 2815 + "enabled": true, + "actions": [], + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:09:10.261Z", + "updatedAt": "2022-03-30T22:09:10.261Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-03-31T19:56:43.499Z", + "error": null, + "lastDuration": 2815 }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "064e3160-b076-11ec-bb3f-1f063f8e06cf" + "scheduledTaskId": null, + "legacyId": "064e3160-b076-11ec-bb3f-1f063f8e06cf" }, - "type" : "alert", - "references" : [ ], - "namespaces" : [ + "type": "alert", + "references": [], + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-31T19:56:46.314Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-31T19:56:46.314Z" } } } @@ -366,45 +356,45 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:27639570-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ action - daily", - "tags" : [ + "index": ".kibana_alerting_cases", + "id": "alert:27639570-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ action - daily", + "tags": [ "__internal_rule_id:8e2c8550-f13f-4e21-be0c-92148d71a5f1", "__internal_immutable:false", "auto_disabled_8.0" ], - "alertTypeId" : "siem.queryRule", - "consumer" : "siem", + "alertTypeId": "siem.queryRule", + "consumer": "siem", "revision": 0, - "params" : { - "author" : [ ], - "description" : "a", - "ruleId" : "8e2c8550-f13f-4e21-be0c-92148d71a5f1", - "falsePositives" : [ ], - "from" : "now-360s", - "immutable" : false, - "license" : "", - "outputIndex" : "", - "meta" : { - "from" : "1m", - "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + "params": { + "author": [], + "description": "a", + "ruleId": "8e2c8550-f13f-4e21-be0c-92148d71a5f1", + "falsePositives": [], + "from": "now-360s", + "immutable": false, + "license": "", + "outputIndex": "", + "meta": { + "from": "1m", + "kibana_siem_app_url": "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" }, - "maxSignals" : 100, - "riskScore" : 21, - "riskScoreMapping" : [ ], - "severity" : "low", - "severityMapping" : [ ], - "threat" : [ ], - "to" : "now", - "references" : [ ], - "version" : 1, - "exceptionsList" : [ ], - "type" : "query", - "language" : "kuery", - "index" : [ + "maxSignals": 100, + "riskScore": 21, + "riskScoreMapping": [], + "severity": "low", + "severityMapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptionsList": [], + "type": "query", + "language": "kuery", + "index": [ "apm-*-transaction*", "traces-apm*", "auditbeat-*", @@ -414,46 +404,44 @@ "packetbeat-*", "winlogbeat-*" ], - "query" : "*:*", - "filters" : [ ] + "query": "*:*", + "filters": [] }, - "schedule" : { - "interval" : "5m" + "schedule": { + "interval": "5m" }, - "enabled" : true, - "actions" : [ ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:10:06.358Z", - "updatedAt" : "2022-03-30T22:10:06.358Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-03-31T19:58:13.480Z", - "error" : null, - "lastDuration" : 3023 + "enabled": true, + "actions": [], + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:10:06.358Z", + "updatedAt": "2022-03-30T22:10:06.358Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-03-31T19:58:13.480Z", + "error": null, + "lastDuration": 3023 }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "27639570-b076-11ec-bb3f-1f063f8e06cf" + "scheduledTaskId": null, + "legacyId": "27639570-b076-11ec-bb3f-1f063f8e06cf" }, - "type" : "alert", - "references" : [ ], - "namespaces" : [ + "type": "alert", + "references": [], + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-31T19:58:16.503Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-31T19:58:16.503Z" } } } @@ -461,45 +449,45 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:61ec7a40-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ actions - weekly", - "tags" : [ + "index": ".kibana_alerting_cases", + "id": "alert:61ec7a40-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ actions - weekly", + "tags": [ "__internal_rule_id:05fbdd2a-e802-420b-bdc3-95ae0acca454", "__internal_immutable:false", "auto_disabled_8.0" ], - "alertTypeId" : "siem.queryRule", - "consumer" : "siem", + "alertTypeId": "siem.queryRule", + "consumer": "siem", "revision": 0, - "params" : { - "author" : [ ], - "description" : "a", - "ruleId" : "05fbdd2a-e802-420b-bdc3-95ae0acca454", - "falsePositives" : [ ], - "from" : "now-360s", - "immutable" : false, - "license" : "", - "outputIndex" : "", - "meta" : { - "from" : "1m", - "kibana_siem_app_url" : "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" + "params": { + "author": [], + "description": "a", + "ruleId": "05fbdd2a-e802-420b-bdc3-95ae0acca454", + "falsePositives": [], + "from": "now-360s", + "immutable": false, + "license": "", + "outputIndex": "", + "meta": { + "from": "1m", + "kibana_siem_app_url": "https://actions.kb.us-central1.gcp.cloud.es.io:9243/app/security" }, - "maxSignals" : 100, - "riskScore" : 21, - "riskScoreMapping" : [ ], - "severity" : "low", - "severityMapping" : [ ], - "threat" : [ ], - "to" : "now", - "references" : [ ], - "version" : 1, - "exceptionsList" : [ ], - "type" : "query", - "language" : "kuery", - "index" : [ + "maxSignals": 100, + "riskScore": 21, + "riskScoreMapping": [], + "severity": "low", + "severityMapping": [], + "threat": [], + "to": "now", + "references": [], + "version": 1, + "exceptionsList": [], + "type": "query", + "language": "kuery", + "index": [ "apm-*-transaction*", "traces-apm*", "auditbeat-*", @@ -509,46 +497,44 @@ "packetbeat-*", "winlogbeat-*" ], - "query" : "*:*", - "filters" : [ ] + "query": "*:*", + "filters": [] }, - "schedule" : { - "interval" : "5m" + "schedule": { + "interval": "5m" }, - "enabled" : true, - "actions" : [ ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:11:44.516Z", - "updatedAt" : "2022-03-30T22:11:44.516Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-03-31T19:54:22.442Z", - "error" : null, - "lastDuration" : 2612 + "enabled": true, + "actions": [], + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:11:44.516Z", + "updatedAt": "2022-03-30T22:11:44.516Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-03-31T19:54:22.442Z", + "error": null, + "lastDuration": 2612 }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf" + "scheduledTaskId": null, + "legacyId": "61ec7a40-b076-11ec-bb3f-1f063f8e06cf" }, - "type" : "alert", - "references" : [ ], - "namespaces" : [ + "type": "alert", + "references": [], + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-31T19:54:25.054Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-31T19:54:25.054Z" } } } @@ -556,80 +542,78 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:64492ef0-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ actions - weekly", - "tags" : [ + "index": ".kibana_alerting_cases", + "id": "alert:64492ef0-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ actions - weekly", + "tags": [ "__internal_rule_alert_id:61ec7a40-b076-11ec-bb3f-1f063f8e06cf" ], - "alertTypeId" : "siem.notifications", - "consumer" : "siem", + "alertTypeId": "siem.notifications", + "consumer": "siem", "revision": 0, - "params" : { - "ruleAlertId" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf" + "params": { + "ruleAlertId": "61ec7a40-b076-11ec-bb3f-1f063f8e06cf" }, - "schedule" : { - "interval" : "7d" + "schedule": { + "interval": "7d" }, - "enabled" : true, - "actions" : [ + "enabled": true, + "actions": [ { - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Test Actions" + "subject": "Test Actions" }, - "actionTypeId" : ".email", - "actionRef" : "action_0" + "actionTypeId": ".email", + "actionRef": "action_0" } ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:11:48.661Z", - "updatedAt" : "2022-03-30T22:11:48.661Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-03-30T22:11:51.640Z", - "error" : null + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:11:48.661Z", + "updatedAt": "2022-03-30T22:11:48.661Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-03-30T22:11:51.640Z", + "error": null }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "64492ef0-b076-11ec-bb3f-1f063f8e06cf" + "scheduledTaskId": null, + "legacyId": "64492ef0-b076-11ec-bb3f-1f063f8e06cf" }, - "type" : "alert", - "references" : [ + "type": "alert", + "references": [ { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "name" : "action_0", - "type" : "action" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name": "action_0", + "type": "action" }, { - "id" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf", - "name" : "param:alert_0", - "type" : "alert" + "id": "61ec7a40-b076-11ec-bb3f-1f063f8e06cf", + "name": "param:alert_0", + "type": "alert" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:11:51.707Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:11:51.707Z" } } } @@ -637,92 +621,90 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:d42e8210-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ action - every hour", - "tags" : [ - "__internal_rule_alert_id:064e3160-b076-11ec-bb3f-1f063f8e06cf" - ], - "alertTypeId" : "siem.notifications", - "consumer" : "siem", - "revision": 0, - "params" : { - "ruleAlertId" : "064e3160-b076-11ec-bb3f-1f063f8e06cf" - }, - "schedule" : { - "interval" : "1h" + "index": ".kibana_alerting_cases", + "id": "alert:d42e8210-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ action - every hour", + "tags": [ + "__internal_rule_alert_id:064e3160-b076-11ec-bb3f-1f063f8e06cf" + ], + "alertTypeId": "siem.notifications", + "consumer": "siem", + "revision": 0, + "params": { + "ruleAlertId": "064e3160-b076-11ec-bb3f-1f063f8e06cf" + }, + "schedule": { + "interval": "1h" + }, + "enabled": true, + "actions": [ + { + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ + "test@test.com" + ], + "subject": "Rule email" + }, + "actionTypeId": ".email", + "actionRef": "action_0" + }, + { + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" + }, + "actionTypeId": ".slack", + "actionRef": "action_1" + } + ], + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:14:56.318Z", + "updatedAt": "2022-03-30T22:15:12.135Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "pending", + "lastExecutionDate": "2022-03-30T22:14:56.318Z", + "error": null + }, + "meta": { + "versionApiKeyLastmodified": "7.15.2" + }, + "legacyId": "d42e8210-b076-11ec-bb3f-1f063f8e06cf" }, - "enabled" : true, - "actions" : [ + "type": "alert", + "references": [ { - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ - "test@test.com" - ], - "subject" : "Rule email" - }, - "actionTypeId" : ".email", - "actionRef" : "action_0" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name": "action_0", + "type": "action" }, { - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" - }, - "actionTypeId" : ".slack", - "actionRef" : "action_1" + "id": "207fa0e0-c04e-11ec-8a52-4fb92379525a", + "name": "action_1", + "type": "action" + }, + { + "id": "064e3160-b076-11ec-bb3f-1f063f8e06cf", + "name": "param:alert_0", + "type": "alert" } ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:14:56.318Z", - "updatedAt" : "2022-03-30T22:15:12.135Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "pending", - "lastExecutionDate" : "2022-03-30T22:14:56.318Z", - "error" : null - }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" - }, - "legacyId" : "d42e8210-b076-11ec-bb3f-1f063f8e06cf" - }, - "type" : "alert", - "references" : [ - { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "name" : "action_0", - "type" : "action" - }, - { - "id" : "207fa0e0-c04e-11ec-8a52-4fb92379525a", - "name" : "action_1", - "type" : "action" - }, - { - "id" : "064e3160-b076-11ec-bb3f-1f063f8e06cf", - "name" : "param:alert_0", - "type" : "alert" - } - ], - "namespaces" : [ - "default" - ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:15:12.135Z" + "namespaces": [ + "default" + ], + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:15:12.135Z" } } } @@ -730,103 +712,101 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "alert:29ba2fa0-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "alert" : { - "name" : "test w/ action - daily", - "tags" : [ + "index": ".kibana_alerting_cases", + "id": "alert:29ba2fa0-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "alert": { + "name": "test w/ action - daily", + "tags": [ "__internal_rule_alert_id:27639570-b076-11ec-bb3f-1f063f8e06cf" ], - "alertTypeId" : "siem.notifications", - "consumer" : "siem", + "alertTypeId": "siem.notifications", + "consumer": "siem", "revision": 0, - "params" : { - "ruleAlertId" : "27639570-b076-11ec-bb3f-1f063f8e06cf" + "params": { + "ruleAlertId": "27639570-b076-11ec-bb3f-1f063f8e06cf" }, - "schedule" : { - "interval" : "1d" + "schedule": { + "interval": "1d" }, - "enabled" : true, - "actions" : [ + "enabled": true, + "actions": [ { - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Test Actions" + "subject": "Test Actions" }, - "actionTypeId" : ".email", - "actionRef" : "action_0" + "actionTypeId": ".email", + "actionRef": "action_0" } ], - "throttle" : null, - "notifyWhen" : "onActiveAlert", - "apiKeyOwner" : null, - "apiKey" : null, - "createdBy" : "1527796724", - "updatedBy" : "1527796724", - "createdAt" : "2022-03-30T22:10:10.435Z", - "updatedAt" : "2022-03-30T22:10:10.435Z", - "muteAll" : false, - "mutedInstanceIds" : [ ], - "executionStatus" : { - "status" : "ok", - "lastExecutionDate" : "2022-04-01T22:10:15.478Z", - "error" : null, - "lastDuration" : 984 + "throttle": null, + "notifyWhen": "onActiveAlert", + "apiKeyOwner": null, + "apiKey": null, + "createdBy": "1527796724", + "updatedBy": "1527796724", + "createdAt": "2022-03-30T22:10:10.435Z", + "updatedAt": "2022-03-30T22:10:10.435Z", + "muteAll": false, + "mutedInstanceIds": [], + "executionStatus": { + "status": "ok", + "lastExecutionDate": "2022-04-01T22:10:15.478Z", + "error": null, + "lastDuration": 984 }, - "meta" : { - "versionApiKeyLastmodified" : "7.15.2" + "meta": { + "versionApiKeyLastmodified": "7.15.2" }, - "scheduledTaskId" : null, - "legacyId" : "29ba2fa0-b076-11ec-bb3f-1f063f8e06cf", - "monitoring" : { - "run" : { - "history" : [ + "scheduledTaskId": null, + "legacyId": "29ba2fa0-b076-11ec-bb3f-1f063f8e06cf", + "monitoring": { + "run": { + "history": [ { - "duration" : 111, - "success" : true, - "timestamp" : 1648764614215 + "duration": 111, + "success": true, + "timestamp": 1648764614215 }, { - "duration" : 984, - "success" : true, - "timestamp" : 1648851016462 + "duration": 984, + "success": true, + "timestamp": 1648851016462 } ], - "calculated_metrics" : { - "p99" : 984, - "success_ratio" : 1, - "p50" : 547.5, - "p95" : 984 + "calculated_metrics": { + "p99": 984, + "success_ratio": 1, + "p50": 547.5, + "p95": 984 } } } }, - "type" : "alert", - "references" : [ + "type": "alert", + "references": [ { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "name" : "action_0", - "type" : "action" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "name": "action_0", + "type": "action" }, { - "id" : "27639570-b076-11ec-bb3f-1f063f8e06cf", - "name" : "param:alert_0", - "type" : "alert" + "id": "27639570-b076-11ec-bb3f-1f063f8e06cf", + "name": "param:alert_0", + "type": "alert" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "alert" : "8.0.1" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-04-01T22:10:16.467Z" + "typeMigrationVersion": "8.0.1", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-04-01T22:10:16.467Z" } } } @@ -834,30 +814,28 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "siem-detection-engine-rule-actions:926668d0-b075-11ec-bb3f-1f063f8e06cf", - "source" : { - "siem-detection-engine-rule-actions" : { - "actions" : [ ], - "ruleThrottle" : "no_actions", - "alertThrottle" : null + "index": ".kibana_security_solution", + "id": "siem-detection-engine-rule-actions:926668d0-b075-11ec-bb3f-1f063f8e06cf", + "source": { + "siem-detection-engine-rule-actions": { + "actions": [], + "ruleThrottle": "no_actions", + "alertThrottle": null }, - "type" : "siem-detection-engine-rule-actions", - "references" : [ + "type": "siem-detection-engine-rule-actions", + "references": [ { - "id" : "9095ee90-b075-11ec-bb3f-1f063f8e06cf", - "type" : "alert", - "name" : "alert_0" + "id": "9095ee90-b075-11ec-bb3f-1f063f8e06cf", + "type": "alert", + "name": "alert_0" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "siem-detection-engine-rule-actions" : "8.0.0" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:05:55.563Z" + "typeMigrationVersion": "8.0.0", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:05:55.563Z" } } } @@ -865,48 +843,46 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "siem-detection-engine-rule-actions:dde13970-b075-11ec-bb3f-1f063f8e06cf", - "source" : { - "siem-detection-engine-rule-actions" : { - "actions" : [ + "index": ".kibana_security_solution", + "id": "siem-detection-engine-rule-actions:dde13970-b075-11ec-bb3f-1f063f8e06cf", + "source": { + "siem-detection-engine-rule-actions": { + "actions": [ { - "actionRef" : "action_0", - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "actionRef": "action_0", + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Test Actions" + "subject": "Test Actions" }, - "action_type_id" : ".email" + "action_type_id": ".email" } ], - "ruleThrottle" : "rule", - "alertThrottle" : null + "ruleThrottle": "rule", + "alertThrottle": null }, - "type" : "siem-detection-engine-rule-actions", - "references" : [ + "type": "siem-detection-engine-rule-actions", + "references": [ { - "id" : "dc6595f0-b075-11ec-bb3f-1f063f8e06cf", - "type" : "alert", - "name" : "alert_0" + "id": "dc6595f0-b075-11ec-bb3f-1f063f8e06cf", + "type": "alert", + "name": "alert_0" }, { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "type" : "action", - "name" : "action_0" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type": "action", + "name": "action_0" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "siem-detection-engine-rule-actions" : "8.0.0" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:08:02.207Z" + "typeMigrationVersion": "8.0.0", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:08:02.207Z" } } } @@ -914,61 +890,59 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "siem-detection-engine-rule-actions:07aa8d10-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "siem-detection-engine-rule-actions" : { - "actions" : [ + "index": ".kibana_security_solution", + "id": "siem-detection-engine-rule-actions:07aa8d10-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "siem-detection-engine-rule-actions": { + "actions": [ { - "actionRef" : "action_0", - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "actionRef": "action_0", + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Rule email" + "subject": "Rule email" }, - "action_type_id" : ".email" + "action_type_id": ".email" }, { - "actionRef" : "action_1", - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" + "actionRef": "action_1", + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" }, - "action_type_id" : ".slack" + "action_type_id": ".slack" } ], - "ruleThrottle" : "1h", - "alertThrottle" : "1h" + "ruleThrottle": "1h", + "alertThrottle": "1h" }, - "type" : "siem-detection-engine-rule-actions", - "references" : [ + "type": "siem-detection-engine-rule-actions", + "references": [ { - "id" : "064e3160-b076-11ec-bb3f-1f063f8e06cf", - "type" : "alert", - "name" : "alert_0" + "id": "064e3160-b076-11ec-bb3f-1f063f8e06cf", + "type": "alert", + "name": "alert_0" }, { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "type" : "action", - "name" : "action_0" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type": "action", + "name": "action_0" }, { - "id" : "207fa0e0-c04e-11ec-8a52-4fb92379525a", - "type" : "action", - "name" : "action_1" + "id": "207fa0e0-c04e-11ec-8a52-4fb92379525a", + "type": "action", + "name": "action_1" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "siem-detection-engine-rule-actions" : "8.0.0" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:09:12.300Z" + "typeMigrationVersion": "8.0.0", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:09:12.300Z" } } } @@ -976,48 +950,46 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "siem-detection-engine-rule-actions:291ae260-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "siem-detection-engine-rule-actions" : { - "actions" : [ + "index": ".kibana_security_solution", + "id": "siem-detection-engine-rule-actions:291ae260-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "siem-detection-engine-rule-actions": { + "actions": [ { - "actionRef" : "action_0", - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "actionRef": "action_0", + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Test Actions" + "subject": "Test Actions" }, - "action_type_id" : ".email" + "action_type_id": ".email" } ], - "ruleThrottle" : "1d", - "alertThrottle" : "1d" + "ruleThrottle": "1d", + "alertThrottle": "1d" }, - "type" : "siem-detection-engine-rule-actions", - "references" : [ + "type": "siem-detection-engine-rule-actions", + "references": [ { - "id" : "27639570-b076-11ec-bb3f-1f063f8e06cf", - "type" : "alert", - "name" : "alert_0" + "id": "27639570-b076-11ec-bb3f-1f063f8e06cf", + "type": "alert", + "name": "alert_0" }, { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "type" : "action", - "name" : "action_0" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type": "action", + "name": "action_0" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "siem-detection-engine-rule-actions" : "8.0.0" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:10:08.399Z" + "typeMigrationVersion": "8.0.0", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:10:08.399Z" } } } @@ -1025,48 +997,46 @@ { "type": "doc", "value": { - "index" : ".kibana", - "id" : "siem-detection-engine-rule-actions:63aa2fd0-b076-11ec-bb3f-1f063f8e06cf", - "source" : { - "siem-detection-engine-rule-actions" : { - "actions" : [ + "index": ".kibana_security_solution", + "id": "siem-detection-engine-rule-actions:63aa2fd0-b076-11ec-bb3f-1f063f8e06cf", + "source": { + "siem-detection-engine-rule-actions": { + "actions": [ { - "actionRef" : "action_0", - "group" : "default", - "params" : { - "message" : "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", - "to" : [ + "actionRef": "action_0", + "group": "default", + "params": { + "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts", + "to": [ "test@test.com" ], - "subject" : "Test Actions" + "subject": "Test Actions" }, - "action_type_id" : ".email" + "action_type_id": ".email" } ], - "ruleThrottle" : "7d", - "alertThrottle" : "7d" + "ruleThrottle": "7d", + "alertThrottle": "7d" }, - "type" : "siem-detection-engine-rule-actions", - "references" : [ + "type": "siem-detection-engine-rule-actions", + "references": [ { - "id" : "61ec7a40-b076-11ec-bb3f-1f063f8e06cf", - "type" : "alert", - "name" : "alert_0" + "id": "61ec7a40-b076-11ec-bb3f-1f063f8e06cf", + "type": "alert", + "name": "alert_0" }, { - "id" : "c95cb100-b075-11ec-bb3f-1f063f8e06cf", - "type" : "action", - "name" : "action_0" + "id": "c95cb100-b075-11ec-bb3f-1f063f8e06cf", + "type": "action", + "name": "action_0" } ], - "namespaces" : [ + "namespaces": [ "default" ], - "migrationVersion" : { - "siem-detection-engine-rule-actions" : "8.0.0" - }, - "coreMigrationVersion" : "8.1.2", - "updated_at" : "2022-03-30T22:11:46.643Z" + "typeMigrationVersion": "8.0.0", + "coreMigrationVersion": "8.1.2", + "updated_at": "2022-03-30T22:11:46.643Z" } } } diff --git a/x-pack/test/functional/page_objects/grok_debugger_page.ts b/x-pack/test/functional/page_objects/grok_debugger_page.ts index 06848c6b9ed3b..b7bad6ed57b9d 100644 --- a/x-pack/test/functional/page_objects/grok_debugger_page.ts +++ b/x-pack/test/functional/page_objects/grok_debugger_page.ts @@ -9,7 +9,7 @@ import { FtrService } from '../ftr_provider_context'; export class GrokDebuggerPageObject extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); - private readonly aceEditor = this.ctx.getService('aceEditor'); + private readonly monacoEditor = this.ctx.getService('monacoEditor'); private readonly retry = this.ctx.getService('retry'); async simulateButton() { @@ -17,30 +17,19 @@ export class GrokDebuggerPageObject extends FtrService { } async getEventOutput() { - return await this.aceEditor.getValue( - 'grokDebuggerContainer > aceEventOutput > codeEditorContainer' - ); + return await this.testSubjects.getVisibleText('eventOutputCodeBlock'); } async setEventInput(value: string) { - await this.aceEditor.setValue( - 'grokDebuggerContainer > aceEventInput > codeEditorContainer', - value - ); + await this.monacoEditor.setCodeEditorValue(value, 0); } async setPatternInput(pattern: string) { - await this.aceEditor.setValue( - 'grokDebuggerContainer > acePatternInput > codeEditorContainer', - pattern - ); + await this.monacoEditor.setCodeEditorValue(pattern, 1); } async setCustomPatternInput(customPattern: string) { - await this.aceEditor.setValue( - 'grokDebuggerContainer > aceCustomPatternsInput > codeEditorContainer', - customPattern - ); + await this.monacoEditor.setCodeEditorValue(customPattern, 2); } async toggleSetCustomPattern() { diff --git a/x-pack/test/functional/page_objects/infra_home_page.ts b/x-pack/test/functional/page_objects/infra_home_page.ts index 68d69832caf3b..ffc69c2dbca64 100644 --- a/x-pack/test/functional/page_objects/infra_home_page.ts +++ b/x-pack/test/functional/page_objects/infra_home_page.ts @@ -251,7 +251,7 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide }, async openEnterViewNameAndSave() { - await testSubjects.setValue('savedViewViweName', 'View1'); + await testSubjects.setValue('savedViewName', 'View1'); await testSubjects.click('createSavedViewButton'); }, diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index ae0cc601f8cc7..6478d208226ad 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -241,6 +241,7 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { async typeInQueryBar(query: string) { const queryBar = await this.getQueryBar(); + await queryBar.clearValueWithKeyboard(); return queryBar.type(query); }, @@ -249,5 +250,51 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { await testSubjects.click('querySubmitButton'); }, + + // Pagination + getPageNumberButton(pageNumber: number) { + return testSubjects.find(`pagination-button-${pageNumber - 1}`); + }, + + getPageSizeSelector() { + return testSubjects.find('tablePaginationPopoverButton'); + }, + + getPageSizeOption(pageSize: number) { + return testSubjects.find(`tablePagination-${pageSize}-rows`); + }, + + async changePageSize(pageSize: number) { + const pageSizeSelector = await this.getPageSizeSelector(); + await pageSizeSelector.click(); + const pageSizeOption = await this.getPageSizeOption(pageSize); + await pageSizeOption.click(); + }, + + async paginateTo(pageNumber: number) { + const paginationButton = await this.getPageNumberButton(pageNumber); + await paginationButton.click(); + }, + + // Sorting + getDiskLatencyHeader() { + return testSubjects.find('tableHeaderCell_diskLatency_4'); + }, + + getTitleHeader() { + return testSubjects.find('tableHeaderCell_title_1'); + }, + + async sortByDiskLatency() { + const diskLatency = await this.getDiskLatencyHeader(); + const button = await testSubjects.findDescendant('tableHeaderSortButton', diskLatency); + return button.click(); + }, + + async sortByTitle() { + const titleHeader = await this.getTitleHeader(); + const button = await testSubjects.findDescendant('tableHeaderSortButton', titleHeader); + return button.click(); + }, }; } diff --git a/x-pack/test/functional/page_objects/infra_saved_views.ts b/x-pack/test/functional/page_objects/infra_saved_views.ts index 70516cd10aab3..839b10fef1c68 100644 --- a/x-pack/test/functional/page_objects/infra_saved_views.ts +++ b/x-pack/test/functional/page_objects/infra_saved_views.ts @@ -65,13 +65,13 @@ export function InfraSavedViewsProvider({ getService }: FtrProviderContext) { }, async getCreateSavedViewModal() { - return await testSubjects.find('savedViews-createModal'); + return await testSubjects.find('savedViews-upsertModal'); }, async createNewSavedView(name: string) { - await testSubjects.setValue('savedViewViweName', name); + await testSubjects.setValue('savedViewName', name); await testSubjects.click('createSavedViewButton'); - await testSubjects.missingOrFail('savedViews-createModal'); + await testSubjects.missingOrFail('savedViews-upsertModal'); }, async ensureViewIsLoaded(name: string) { diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index e5f0e814e96e4..fbd8b54a2708b 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -636,10 +636,18 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont async isTopLevelAggregation() { return await testSubjects.isEuiSwitchChecked('indexPattern-nesting-switch'); }, + /** + * Cen remove the dimension matching a specific test subject? + */ + async canRemoveDimension(dimensionTestSubj: string) { + await testSubjects.moveMouseTo(`${dimensionTestSubj} > indexPattern-dimension-remove`); + return await testSubjects.isDisplayed(`${dimensionTestSubj} > indexPattern-dimension-remove`); + }, /** * Removes the dimension matching a specific test subject */ async removeDimension(dimensionTestSubj: string) { + await testSubjects.moveMouseTo(`${dimensionTestSubj} > indexPattern-dimension-remove`); await testSubjects.click(`${dimensionTestSubj} > indexPattern-dimension-remove`); }, /** @@ -1061,6 +1069,18 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, {}); }, + async getDatatableCellSpanStyle(rowIndex = 0, colIndex = 0) { + const el = await (await this.getDatatableCell(rowIndex, colIndex)).findByCssSelector('span'); + const styleString = await el.getAttribute('style'); + return styleString.split(';').reduce>((memo, cssLine) => { + const [prop, value] = cssLine.split(':'); + if (prop && value) { + memo[prop.trim()] = value.trim(); + } + return memo; + }, {}); + }, + async getCountOfDatatableColumns() { const table = await find.byCssSelector('.euiDataGrid'); const $ = await table.parseDomContent(); @@ -1687,7 +1707,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont ) { const groupCapitalized = `${group[0].toUpperCase()}${group.slice(1).toLowerCase()}`; const allFieldsForType = await find.allByCssSelector( - `[data-test-subj="lnsIndexPattern${groupCapitalized}Fields"] .unifiedFieldItemButton--${type}` + `[data-test-subj="lnsIndexPattern${groupCapitalized}Fields"] .unifiedFieldListItemButton--${type}` ); // map to testSubjId return Promise.all(allFieldsForType.map((el) => el.getAttribute('data-test-subj'))); diff --git a/x-pack/test/functional/services/actions/index.ts b/x-pack/test/functional/services/actions/index.ts index c7539c37f2c32..7218f3079aafb 100644 --- a/x-pack/test/functional/services/actions/index.ts +++ b/x-pack/test/functional/services/actions/index.ts @@ -10,6 +10,7 @@ import { ActionsCommonServiceProvider } from './common'; import { ActionsOpsgenieServiceProvider } from './opsgenie'; import { ActionsTinesServiceProvider } from './tines'; import { ActionsAPIServiceProvider } from './api'; +import { ActionsSlackServiceProvider } from './slack'; export function ActionsServiceProvider(context: FtrProviderContext) { const common = ActionsCommonServiceProvider(context); @@ -19,5 +20,6 @@ export function ActionsServiceProvider(context: FtrProviderContext) { common: ActionsCommonServiceProvider(context), opsgenie: ActionsOpsgenieServiceProvider(context, common), tines: ActionsTinesServiceProvider(context, common), + slack: ActionsSlackServiceProvider(context, common), }; } diff --git a/x-pack/test/functional/services/actions/slack.ts b/x-pack/test/functional/services/actions/slack.ts new file mode 100644 index 0000000000000..b4297644c7c93 --- /dev/null +++ b/x-pack/test/functional/services/actions/slack.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import type { ActionsCommon } from './common'; + +export interface WebhookConnectorFormFields { + name: string; + url: string; +} + +export interface WebApiConnectorFormFields { + name: string; + token: string; +} + +export function ActionsSlackServiceProvider( + { getService }: FtrProviderContext, + common: ActionsCommon +) { + const testSubjects = getService('testSubjects'); + + return { + async createNewWebhook({ name, url }: WebhookConnectorFormFields) { + await common.openNewConnectorForm('slack'); + + await testSubjects.setValue('nameInput', name); + await testSubjects.setValue('slackWebhookUrlInput', url); + + const flyOutSaveButton = await testSubjects.find('create-connector-flyout-save-btn'); + expect(await flyOutSaveButton.isEnabled()).to.be(true); + await flyOutSaveButton.click(); + }, + async createNewWebAPI({ name, token }: WebApiConnectorFormFields) { + await common.openNewConnectorForm('slack'); + + const webApiTab = await testSubjects.find('.slack_apiButton'); + await webApiTab.click(); + + await testSubjects.setValue('nameInput', name); + await testSubjects.setValue('secrets.token-input', token); + + const flyOutSaveButton = await testSubjects.find('create-connector-flyout-save-btn'); + expect(await flyOutSaveButton.isEnabled()).to.be(true); + await flyOutSaveButton.click(); + }, + }; +} diff --git a/x-pack/test/functional/services/aiops/explain_log_rate_spikes_analysis_groups_table.ts b/x-pack/test/functional/services/aiops/explain_log_rate_spikes_analysis_groups_table.ts index 18cadebbf9afd..b533c50677944 100644 --- a/x-pack/test/functional/services/aiops/explain_log_rate_spikes_analysis_groups_table.ts +++ b/x-pack/test/functional/services/aiops/explain_log_rate_spikes_analysis_groups_table.ts @@ -5,12 +5,17 @@ * 2.0. */ +import expect from '@kbn/expect'; + import { FtrProviderContext } from '../../ftr_provider_context'; export function ExplainLogRateSpikesAnalysisGroupsTableProvider({ getService, }: FtrProviderContext) { + const find = getService('find'); const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const browser = getService('browser'); return new (class AnalysisTable { public async assertSpikeAnalysisTableExists() { @@ -55,5 +60,50 @@ export function ExplainLogRateSpikesAnalysisGroupsTableProvider({ return rows; } + + public rowSelector(rowId: string, subSelector?: string) { + const row = `~aiopsSpikeAnalysisGroupsTable > ~row-${rowId}`; + return !subSelector ? row : `${row} > ${subSelector}`; + } + + public async ensureActionsMenuOpen(rowId: string) { + await retry.tryForTime(30 * 1000, async () => { + await this.ensureActionsMenuClosed(); + + if (!(await find.existsByCssSelector('.euiContextMenuPanel', 1000))) { + await testSubjects.click(this.rowSelector(rowId, 'euiCollapsedItemActionsButton')); + expect(await find.existsByCssSelector('.euiContextMenuPanel', 1000)).to.eql( + true, + 'Actions popover should exist' + ); + } + }); + } + + public async ensureActionsMenuClosed() { + await retry.tryForTime(30 * 1000, async () => { + await browser.pressKeys(browser.keys.ESCAPE); + expect(await find.existsByCssSelector('.euiContextMenuPanel', 1000)).to.eql( + false, + 'Actions popover should not exist' + ); + }); + } + + public async assertRowActions(rowId: string) { + await this.ensureActionsMenuOpen(rowId); + + await testSubjects.existOrFail('aiopsTableActionButtonCopyToClipboard enabled'); + await testSubjects.existOrFail('aiopsTableActionButtonDiscover enabled'); + await testSubjects.existOrFail('aiopsTableActionButtonLogPatternAnalysis enabled'); + + await this.ensureActionsMenuClosed(); + } + + public async clickRowAction(rowId: string, action: string) { + await this.ensureActionsMenuOpen(rowId); + await testSubjects.click(`aiopsTableActionButton${action} enabled`); + await testSubjects.missingOrFail(`aiopsTableActionButton${action} enabled`); + } })(); } diff --git a/x-pack/test/functional/services/aiops/explain_log_rate_spikes_data_generator.ts b/x-pack/test/functional/services/aiops/explain_log_rate_spikes_data_generator.ts index 1a80ac679f29b..228d47bbc746f 100644 --- a/x-pack/test/functional/services/aiops/explain_log_rate_spikes_data_generator.ts +++ b/x-pack/test/functional/services/aiops/explain_log_rate_spikes_data_generator.ts @@ -122,6 +122,10 @@ export function ExplainLogRateSpikesDataGeneratorProvider({ getService }: FtrPro return new (class DataGenerator { public async generateData(dataGenerator: string) { switch (dataGenerator) { + case 'kibana_sample_data_logs': + // will be added via UI + break; + case 'farequote_with_spike': await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); @@ -191,6 +195,10 @@ export function ExplainLogRateSpikesDataGeneratorProvider({ getService }: FtrPro public async removeGeneratedData(dataGenerator: string) { switch (dataGenerator) { + case 'kibana_sample_data_logs': + // do not remove + break; + case 'farequote_with_spike': await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); break; diff --git a/x-pack/test/functional/services/aiops/explain_log_rate_spikes_page.ts b/x-pack/test/functional/services/aiops/explain_log_rate_spikes_page.ts index 3da9ed7c760b7..736437a1d3976 100644 --- a/x-pack/test/functional/services/aiops/explain_log_rate_spikes_page.ts +++ b/x-pack/test/functional/services/aiops/explain_log_rate_spikes_page.ts @@ -229,9 +229,11 @@ export function ExplainLogRateSpikesPageProvider({ }, async assertProgressTitle(expectedProgressTitle: string) { - await testSubjects.existOrFail('aiopProgressTitle'); - const currentProgressTitle = await testSubjects.getVisibleText('aiopProgressTitle'); - expect(currentProgressTitle).to.be(expectedProgressTitle); + await retry.tryForTime(30 * 1000, async () => { + await testSubjects.existOrFail('aiopProgressTitle'); + const currentProgressTitle = await testSubjects.getVisibleText('aiopProgressTitle'); + expect(currentProgressTitle).to.be(expectedProgressTitle); + }); }, async navigateToIndexPatternSelection() { diff --git a/x-pack/test/functional/services/aiops/index.ts b/x-pack/test/functional/services/aiops/index.ts index 4816d37bcff04..8c208f182f3bd 100644 --- a/x-pack/test/functional/services/aiops/index.ts +++ b/x-pack/test/functional/services/aiops/index.ts @@ -11,6 +11,7 @@ import { ExplainLogRateSpikesPageProvider } from './explain_log_rate_spikes_page import { ExplainLogRateSpikesAnalysisTableProvider } from './explain_log_rate_spikes_analysis_table'; import { ExplainLogRateSpikesAnalysisGroupsTableProvider } from './explain_log_rate_spikes_analysis_groups_table'; import { ExplainLogRateSpikesDataGeneratorProvider } from './explain_log_rate_spikes_data_generator'; +import { LogPatternAnalysisPageProvider } from './log_pattern_analysis_page'; export function AiopsProvider(context: FtrProviderContext) { const explainLogRateSpikesPage = ExplainLogRateSpikesPageProvider(context); @@ -18,11 +19,13 @@ export function AiopsProvider(context: FtrProviderContext) { const explainLogRateSpikesAnalysisGroupsTable = ExplainLogRateSpikesAnalysisGroupsTableProvider(context); const explainLogRateSpikesDataGenerator = ExplainLogRateSpikesDataGeneratorProvider(context); + const logPatternAnalysisPageProvider = LogPatternAnalysisPageProvider(context); return { explainLogRateSpikesPage, explainLogRateSpikesAnalysisTable, explainLogRateSpikesAnalysisGroupsTable, explainLogRateSpikesDataGenerator, + logPatternAnalysisPageProvider, }; } diff --git a/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts b/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts new file mode 100644 index 0000000000000..37872b8d7c051 --- /dev/null +++ b/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts @@ -0,0 +1,42 @@ +/* + * 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 type { FtrProviderContext } from '../../ftr_provider_context'; + +export function LogPatternAnalysisPageProvider({ getService, getPageObject }: FtrProviderContext) { + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + + return { + async assertLogCategorizationPageExists() { + await retry.tryForTime(30 * 1000, async () => { + await testSubjects.existOrFail('aiopsLogCategorizationPage'); + }); + }, + + async assertQueryInput(expectedQueryString: string) { + const aiopsQueryInput = await testSubjects.find('aiopsQueryInput'); + const actualQueryString = await aiopsQueryInput.getVisibleText(); + expect(actualQueryString).to.eql( + expectedQueryString, + `Expected query bar text to be '${expectedQueryString}' (got '${actualQueryString}')` + ); + }, + + async assertTotalDocumentCount(expectedFormattedTotalDocCount: string) { + await retry.tryForTime(5000, async () => { + const docCount = await testSubjects.getVisibleText('aiopsTotalDocCount'); + expect(docCount).to.eql( + expectedFormattedTotalDocCount, + `Expected total document count to be '${expectedFormattedTotalDocCount}' (got '${docCount}')` + ); + }); + }, + }; +} diff --git a/x-pack/test/functional/services/cases/single_case_view.ts b/x-pack/test/functional/services/cases/single_case_view.ts index f5c34feedf811..237881f2af78b 100644 --- a/x-pack/test/functional/services/cases/single_case_view.ts +++ b/x-pack/test/functional/services/cases/single_case_view.ts @@ -101,7 +101,7 @@ export function CasesSingleViewServiceProvider({ getService, getPageObject }: Ft async assertCaseDescription(expectedDescription: string) { const desc = await find.byCssSelector( - '[data-test-subj="description-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj="description"] [data-test-subj="scrollable-markdown"]' ); const actualDescription = await desc.getVisibleText(); diff --git a/x-pack/test/functional/services/grok_debugger.js b/x-pack/test/functional/services/grok_debugger.js index 42a80edd70c85..618353130c20e 100644 --- a/x-pack/test/functional/services/grok_debugger.js +++ b/x-pack/test/functional/services/grok_debugger.js @@ -8,18 +8,14 @@ import expect from '@kbn/expect'; export function GrokDebuggerProvider({ getService }) { - const aceEditor = getService('aceEditor'); const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const monacoEditor = getService('monacoEditor'); // test subject selectors const SUBJ_CONTAINER = 'grokDebuggerContainer'; - const SUBJ_UI_ACE_EVENT_INPUT = `${SUBJ_CONTAINER} > aceEventInput > codeEditorContainer`; const SUBJ_UI_ACE_PATTERN_INPUT = `${SUBJ_CONTAINER} > acePatternInput > codeEditorContainer`; - const SUBJ_UI_ACE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} > aceCustomPatternsInput > codeEditorContainer`; - const SUBJ_UI_ACE_EVENT_OUTPUT = `${SUBJ_CONTAINER} > aceEventOutput > codeEditorContainer`; - const SUBJ_BTN_TOGGLE_CUSTOM_PATTERNS_INPUT = `${SUBJ_CONTAINER} > btnToggleCustomPatternsInput`; const SUBJ_BTN_SIMULATE = `${SUBJ_CONTAINER} > btnSimulate`; @@ -29,11 +25,11 @@ export function GrokDebuggerProvider({ getService }) { } async setEventInput(value) { - await aceEditor.setValue(SUBJ_UI_ACE_EVENT_INPUT, value); + await monacoEditor.setCodeEditorValue(value, 0); } async setPatternInput(value) { - await aceEditor.setValue(SUBJ_UI_ACE_PATTERN_INPUT, value); + await monacoEditor.setCodeEditorValue(value, 1); } async toggleCustomPatternsInput() { @@ -41,11 +37,11 @@ export function GrokDebuggerProvider({ getService }) { } async setCustomPatternsInput(value) { - await aceEditor.setValue(SUBJ_UI_ACE_CUSTOM_PATTERNS_INPUT, value); + await monacoEditor.setCodeEditorValue(value, 2); } async getEventOutput() { - return await aceEditor.getValue(SUBJ_UI_ACE_EVENT_OUTPUT); + return await testSubjects.getVisibleText('eventOutputCodeBlock'); } async assertExists() { diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts index dcb76bb23eaf0..4c6944b64a91d 100644 --- a/x-pack/test/functional/services/transform/api.ts +++ b/x-pack/test/functional/services/transform/api.ts @@ -218,13 +218,33 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { async createTransform( transformId: string, transformConfig: PutTransformsRequestSchema, - deferValidation?: boolean + options: { + deferValidation?: boolean; + headers?: object; + } = {} ) { - log.debug(`Creating transform with id '${transformId}'...`); - const { body, status } = await esSupertest - .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) - .send(transformConfig); - this.assertResponseStatusCode(200, status, body); + const { deferValidation, headers } = options; + + if (headers) { + log.debug( + `Creating transform with id '${transformId}' with headers ${JSON.stringify( + headers + )} and defer_validation:${deferValidation}...` + ); + const { body, status } = await esSupertest + .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) + .set(headers) + .send(transformConfig); + this.assertResponseStatusCode(200, status, body); + } else { + log.debug( + `Creating transform with id '${transformId}' and defer_validation:${deferValidation}...` + ); + const { body, status } = await esSupertest + .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) + .send(transformConfig); + this.assertResponseStatusCode(200, status, body); + } await this.waitForTransformToExist( transformId, @@ -264,8 +284,12 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { this.assertResponseStatusCode(200, status, body); }, - async createAndRunTransform(transformId: string, transformConfig: PutTransformsRequestSchema) { - await this.createTransform(transformId, transformConfig); + async createAndRunTransform( + transformId: string, + transformConfig: PutTransformsRequestSchema, + options: { headers?: object } = {} + ) { + await this.createTransform(transformId, transformConfig, { headers: options.headers }); await this.startTransform(transformId); if (transformConfig.sync === undefined) { // batch mode diff --git a/x-pack/test/functional/services/transform/security_common.ts b/x-pack/test/functional/services/transform/security_common.ts index 36670a65211b3..94c059be0fae6 100644 --- a/x-pack/test/functional/services/transform/security_common.ts +++ b/x-pack/test/functional/services/transform/security_common.ts @@ -7,6 +7,7 @@ import { ProvidedType } from '@kbn/test'; +import { Client } from '@elastic/elasticsearch'; import { FtrProviderContext } from '../../ftr_provider_context'; export type TransformSecurityCommon = ProvidedType; @@ -19,6 +20,7 @@ export enum USER { export function TransformSecurityCommonProvider({ getService }: FtrProviderContext) { const security = getService('security'); + const esClient: Client = getService('es'); const roles = [ { @@ -98,6 +100,51 @@ export function TransformSecurityCommonProvider({ getService }: FtrProviderConte } }, + async createApiKeyForTransformUser(username: string) { + const user = users.find((u) => u.name === username); + + if (user === undefined) { + throw new Error(`Can't create api key for user ${user} - user not defined`); + } + + const roleDescriptors = user.roles.reduce>((map, roleName) => { + const userRole = roles.find((r) => r.name === roleName); + + if (userRole) { + map[roleName] = userRole.elasticsearch; + } + return map; + }, {}); + const apiKey = await esClient.security.createApiKey({ + body: { + name: `Transform API Key ${user.full_name}`, + role_descriptors: roleDescriptors, + metadata: user, + }, + }); + return { user: { name: user.name as USER, roles: user.roles }, apiKey }; + }, + + async createApiKeyForTransformUsers() { + const apiKeyForTransformUsers = await Promise.all( + users.map((user) => this.createApiKeyForTransformUser(user.name)) + ); + + return apiKeyForTransformUsers; + }, + + async clearAllTransformApiKeys() { + const existingKeys = await esClient.security.queryApiKeys(); + + if (existingKeys.count > 0) { + await Promise.all( + existingKeys.api_keys.map(async (key) => { + esClient.security.invalidateApiKey({ ids: [key.id] }); + }) + ); + } + }, + async cleanTransformRoles() { for (const role of roles) { await security.role.delete(role.name); diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 69332c8947d58..c48a097de9213 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -1052,9 +1052,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi await retry.tryForTime(60 * 1000, async () => { const allFields = await pageObjects.discover.getAllFieldNames(); if (Array.isArray(allFields)) { - // For some reasons, Discover returns fields with dot (e.g '.avg') with extra space - const fields = allFields.map((n) => n.replace('.​', '.')); - expect(fields).to.contain( + expect(allFields).to.contain( field, `Expected Discover to contain field ${field}, got ${allFields.join()}` ); diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/app_search/setup_guide.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/app_search/setup_guide.ts deleted file mode 100644 index 2995150d73d07..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/app_search/setup_guide.ts +++ /dev/null @@ -1,41 +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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../ftr_provider_context'; - -export default function enterpriseSearchSetupGuideTests({ - getService, - getPageObjects, -}: FtrProviderContext) { - const browser = getService('browser'); - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['appSearch']); - - describe('Setup Guide', function () { - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - - describe('when no enterpriseSearch.host is configured', () => { - it('navigating to the plugin will redirect a user to the setup guide', async () => { - await PageObjects.appSearch.navigateToPage(); - await retry.try(async function () { - const currentUrl = await browser.getCurrentUrl(); - expect(currentUrl).to.contain('/app_search/setup_guide'); - - const documentTitle = await browser.getTitle(); - expect(documentTitle).to.contain('Setup Guide - App Search - Elastic'); - }); - }); - }); - }); -} diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts deleted file mode 100644 index dda2e20745394..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/index.ts +++ /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; 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('Enterprise Search', function () { - loadTestFile(require.resolve('./app_search/setup_guide')); - loadTestFile(require.resolve('./workplace_search/setup_guide')); - }); -} diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/workplace_search/setup_guide.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/workplace_search/setup_guide.ts deleted file mode 100644 index 0be3156d29c02..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/without_host_configured/workplace_search/setup_guide.ts +++ /dev/null @@ -1,41 +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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../ftr_provider_context'; - -export default function enterpriseSearchSetupGuideTests({ - getService, - getPageObjects, -}: FtrProviderContext) { - const browser = getService('browser'); - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['workplaceSearch']); - - describe('Setup Guide', function () { - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - - describe('when no enterpriseSearch.host is configured', () => { - it('navigating to the plugin will redirect a user to the setup guide', async () => { - await PageObjects.workplaceSearch.navigateToPage(); - await retry.try(async function () { - const currentUrl = await browser.getCurrentUrl(); - expect(currentUrl).to.contain('/workplace_search/setup_guide'); - - const documentTitle = await browser.getTitle(); - expect(documentTitle).to.contain('Setup Guide - Workplace Search - Elastic'); - }); - }); - }); - }); -} diff --git a/x-pack/test/functional_enterprise_search/cli_config.ts b/x-pack/test/functional_enterprise_search/cli_config.ts new file mode 100644 index 0000000000000..ec7e75f01c288 --- /dev/null +++ b/x-pack/test/functional_enterprise_search/cli_config.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 { FtrConfigProviderContext } from '@kbn/test'; +import { EnterpriseSearchCypressCliTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const kibanaCommonTestsConfig = await readConfigFile( + require.resolve('../../../test/common/config.js') + ); + const baseConfig = await readConfigFile(require.resolve('./cypress.config')); + + return { + ...kibanaCommonTestsConfig.getAll(), + // default to the xpack functional config + ...baseConfig.getAll(), + + junit: { + reportName: 'X-Pack Enterprise Search Functional Tests with Host Configured', + }, + kbnTestServer: { + ...baseConfig.get('kbnTestServer'), + serverArgs: [ + ...baseConfig.get('kbnTestServer.serverArgs'), + '--enterpriseSearch.host=http://localhost:3022', + ], + }, + testRunner: EnterpriseSearchCypressCliTestRunner, + }; +} diff --git a/x-pack/test/functional_enterprise_search/cypress.config.ts b/x-pack/test/functional_enterprise_search/cypress.config.ts index 9a6918ab0557d..099b92c429299 100644 --- a/x-pack/test/functional_enterprise_search/cypress.config.ts +++ b/x-pack/test/functional_enterprise_search/cypress.config.ts @@ -7,9 +7,6 @@ import { FtrConfigProviderContext } from '@kbn/test'; -// TODO: If Kibana CI doesn't end up using this (e.g., uses Dockerized containers -// instead of the functional test server), we can opt to delete this file later. - export default async function ({ readConfigFile }: FtrConfigProviderContext) { const baseConfig = await readConfigFile(require.resolve('./base_config')); @@ -32,7 +29,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...baseConfig.get('kbnTestServer.serverArgs'), '--csp.strict=false', '--csp.warnLegacyBrowsers=false', - '--enterpriseSearch.host=http://localhost:3002', + '--enterpriseSearch.host=http://localhost:3022', + '--usageCollection.uiCounters.enabled=false', + `--home.disableWelcomeScreen=true`, ], }, }; diff --git a/x-pack/test/functional_enterprise_search/enterprise_search_server.ts b/x-pack/test/functional_enterprise_search/enterprise_search_server.ts index ae588afc7de13..2f9eacff934d8 100644 --- a/x-pack/test/functional_enterprise_search/enterprise_search_server.ts +++ b/x-pack/test/functional_enterprise_search/enterprise_search_server.ts @@ -52,7 +52,7 @@ export async function setupEnterpriseSearch(logger: ToolingLog): Promise { `--name=enterprise-search-ftr`, `--rm`, `-p`, - `3002:3002`, + `3022:3022`, `-e`, `elasticsearch.host='http://host.docker.internal:9220'`, `-e`, @@ -66,9 +66,9 @@ export async function setupEnterpriseSearch(logger: ToolingLog): Promise { `-e`, `ENT_SEARCH_DEFAULT_PASSWORD=changeme`, `-e`, - `ent_search.listen_port=3002`, + `ent_search.listen_port=3022`, `-e`, - `ent_search.external_url='http://localhost:3002'`, + `ent_search.external_url='http://localhost:3022'`, `docker.elastic.co/enterprise-search/enterprise-search:${await getLatestVersion()}`, ]; diff --git a/x-pack/test/functional_enterprise_search/runner.ts b/x-pack/test/functional_enterprise_search/runner.ts index 8dfee33c5b2e7..b8ab1df8d7108 100644 --- a/x-pack/test/functional_enterprise_search/runner.ts +++ b/x-pack/test/functional_enterprise_search/runner.ts @@ -35,12 +35,8 @@ export async function runEnterpriseSearchTests( await withEnterpriseSearch(context, (runnerEnv) => withProcRunner(log, async (procs) => { await procs.run('cypress', { - cmd: 'sh', - args: [ - `${resolve(__dirname, '../../plugins/enterprise_search/cypress.sh')}`, - `${cypressCommand}`, - 'as', - ], + cmd: '../../../node_modules/.bin/cypress', + args: [cypressCommand, '--config-file', './cypress.config.ts', '--browser', 'chrome'], cwd: resolve(__dirname, '../../plugins/enterprise_search'), env: { FORCE_COLOR: '1', diff --git a/x-pack/test/functional_enterprise_search/with_host_configured.config.ts b/x-pack/test/functional_enterprise_search/visual_config.ts similarity index 95% rename from x-pack/test/functional_enterprise_search/with_host_configured.config.ts rename to x-pack/test/functional_enterprise_search/visual_config.ts index a1584b45da092..300d1b23b4191 100644 --- a/x-pack/test/functional_enterprise_search/with_host_configured.config.ts +++ b/x-pack/test/functional_enterprise_search/visual_config.ts @@ -26,7 +26,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...baseConfig.get('kbnTestServer'), serverArgs: [ ...baseConfig.get('kbnTestServer.serverArgs'), - '--enterpriseSearch.host=http://localhost:3002', + '--enterpriseSearch.host=http://localhost:3022', ], }, testRunner: EnterpriseSearchCypressVisualTestRunner, diff --git a/x-pack/test/functional_enterprise_search/without_host_configured.config.ts b/x-pack/test/functional_enterprise_search/without_host_configured.config.ts deleted file mode 100644 index f5af2bddd8531..0000000000000 --- a/x-pack/test/functional_enterprise_search/without_host_configured.config.ts +++ /dev/null @@ -1,24 +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 { resolve } from 'path'; -import { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const baseConfig = await readConfigFile(require.resolve('./base_config')); - - return { - // default to the xpack functional config - ...baseConfig.getAll(), - - testFiles: [resolve(__dirname, './apps/enterprise_search/without_host_configured')], - - junit: { - reportName: 'X-Pack Enterprise Search Functional Tests without Host Configured', - }, - }; -} diff --git a/x-pack/test/functional_execution_context/plugins/alerts/server/plugin.ts b/x-pack/test/functional_execution_context/plugins/alerts/server/plugin.ts index 068fd2356b360..436452c1fdbaf 100644 --- a/x-pack/test/functional_execution_context/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_execution_context/plugins/alerts/server/plugin.ts @@ -82,6 +82,9 @@ export class FixturePlugin implements Plugin params }, + }, }); const router = core.http.createRouter(); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts index 40cbbc53c5596..8823f5144a0fc 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts @@ -51,7 +51,7 @@ export default ({ getService, getPageObject }: FtrProviderContext) => { expect(await title.getVisibleText()).equal(caseTitle); // validate description - const description = await testSubjects.find('user-action-markdown'); + const description = await testSubjects.find('scrollable-markdown'); expect(await description.getVisibleText()).equal('test description'); // validate tag exists diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts index d0df92ac4f7e7..233157e57e518 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts @@ -58,7 +58,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { // validate user action const newComment = await find.byCssSelector( - '[data-test-subj*="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj*="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); expect(await newComment.getVisibleText()).equal('Test comment from automation'); }); @@ -207,7 +207,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { // validate user action const newComment = await find.byCssSelector( - '[data-test-subj*="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj*="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); expect(await newComment.getVisibleText()).equal('Test comment from automation'); }); @@ -244,7 +244,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { // validate user action const newComment = await find.byCssSelector( - '[data-test-subj*="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj*="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); expect(await newComment.getVisibleText()).equal('Test comment from automation'); }); @@ -266,7 +266,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { // validate user action const newComment = await find.byCssSelector( - '[data-test-subj*="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj*="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); expect(await newComment.getVisibleText()).equal('Test comment from automation'); }); @@ -291,7 +291,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { await header.waitUntilLoadingHasFinished(); const editCommentTextArea = await find.byCssSelector( - '[data-test-subj*="user-action-markdown-form"] textarea.euiMarkdownEditorTextArea' + '[data-test-subj*="editable-markdown-form"] textarea.euiMarkdownEditorTextArea' ); await header.waitUntilLoadingHasFinished(); @@ -307,12 +307,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('shows unsaved description message when page is refreshed', async () => { - await testSubjects.click('editable-description-edit-icon'); + await testSubjects.click('description-edit-icon'); await header.waitUntilLoadingHasFinished(); const editCommentTextArea = await find.byCssSelector( - '[data-test-subj*="user-action-markdown-form"] textarea.euiMarkdownEditorTextArea' + '[data-test-subj*="editable-markdown-form"] textarea.euiMarkdownEditorTextArea' ); await header.waitUntilLoadingHasFinished(); @@ -320,6 +320,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { await editCommentTextArea.focus(); await editCommentTextArea.type('Edited description'); + await header.waitUntilLoadingHasFinished(); + await browser.refresh(); await header.waitUntilLoadingHasFinished(); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group2/upgrade.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group2/upgrade.ts index 561bbae70bcca..93d12b5d908da 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group2/upgrade.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group2/upgrade.ts @@ -86,7 +86,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('shows the description correctly', async () => { const desc = await find.byCssSelector( - '[data-test-subj="description-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj="description"] [data-test-subj="scrollable-markdown"]' ); expect(await desc.getVisibleText()).equal(`Testing upgrade! Let's see how it goes.`); @@ -112,7 +112,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('shows the first comment correctly', async () => { const comment = await find.byCssSelector( - '[data-test-subj^="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj^="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); expect(await comment.getVisibleText()).equal(`This is interesting. I am curious also.`); @@ -127,7 +127,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('shows the second comment correctly', async () => { const comments = await find.allByCssSelector( - '[data-test-subj^="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj^="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); const secondComment = comments[1]; @@ -140,7 +140,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('shows the third comment correctly', async () => { const comments = await find.allByCssSelector( - '[data-test-subj^="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj^="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); const thirdComment = comments[2]; @@ -200,7 +200,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { it('shows the fourth comment correctly', async () => { const comments = await find.allByCssSelector( - '[data-test-subj^="comment-create-action"] [data-test-subj="user-action-markdown"]' + '[data-test-subj^="comment-create-action"] [data-test-subj="scrollable-markdown"]' ); const thirdComment = comments[3]; diff --git a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/uptime/simple_down_alert.ts b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/uptime/simple_down_alert.ts index 4064a8a6b7ae2..81a0a9b438af7 100644 --- a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/uptime/simple_down_alert.ts +++ b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/uptime/simple_down_alert.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { MonitorStatusTranslations } from '@kbn/synthetics-plugin/common/translations'; +import { MonitorStatusTranslations } from '@kbn/synthetics-plugin/common/rules/legacy_uptime/translations'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { deleteUptimeSettingsObject } from '../../../../functional/apps/uptime'; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/index.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/index.ts index 1d6420004c0cd..c246f92309d2d 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/index.ts @@ -12,5 +12,6 @@ export default ({ loadTestFile }: FtrProviderContext) => { loadTestFile(require.resolve('./general')); loadTestFile(require.resolve('./opsgenie')); loadTestFile(require.resolve('./tines')); + loadTestFile(require.resolve('./slack')); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/slack.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/slack.ts new file mode 100644 index 0000000000000..f975bed9f965e --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/slack.ts @@ -0,0 +1,208 @@ +/* + * 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 { ObjectRemover } from '../../../lib/object_remover'; +import { generateUniqueKey } from '../../../lib/get_test_data'; +import { createSlackConnectorAndObjectRemover, getConnectorByName } from './utils'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const testSubjects = getService('testSubjects'); + const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); + const retry = getService('retry'); + const supertest = getService('supertest'); + const actions = getService('actions'); + const rules = getService('rules'); + let objectRemover: ObjectRemover; + + describe('Slack', () => { + before(async () => { + objectRemover = await createSlackConnectorAndObjectRemover({ getService }); + }); + + after(async () => { + await objectRemover.removeAll(); + }); + + describe('connector page', () => { + beforeEach(async () => { + await pageObjects.common.navigateToApp('triggersActionsConnectors'); + }); + + it('should only show one slack connector', async () => { + if (await testSubjects.exists('createActionButton')) { + await testSubjects.click('createActionButton'); + } else { + await testSubjects.click('createFirstActionButton'); + } + await testSubjects.existOrFail('.slack-card'); + const slackApiCardExists = await testSubjects.exists('.slack_api-card'); + expect(slackApiCardExists).to.be(false); + }); + + it('should create the webhook connector', async () => { + const connectorName = generateUniqueKey(); + await actions.slack.createNewWebhook({ + name: connectorName, + url: 'https://test.com', + }); + + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql(`Created '${connectorName}'`); + + await pageObjects.triggersActionsUI.searchConnectors(connectorName); + + const searchResults = await pageObjects.triggersActionsUI.getConnectorsList(); + expect(searchResults).to.eql([ + { + name: connectorName, + actionType: 'Slack', + }, + ]); + const connector = await getConnectorByName(connectorName, supertest); + objectRemover.add(connector.id, 'action', 'actions'); + }); + + it('should create the web api connector', async () => { + const connectorName = generateUniqueKey(); + await actions.slack.createNewWebAPI({ + name: connectorName, + token: 'supersecrettoken', + }); + + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql(`Created '${connectorName}'`); + + await pageObjects.triggersActionsUI.searchConnectors(connectorName); + + const searchResults = await pageObjects.triggersActionsUI.getConnectorsList(); + expect(searchResults).to.eql([ + { + name: connectorName, + actionType: 'Slack API', + }, + ]); + const connector = await getConnectorByName(connectorName, supertest); + objectRemover.add(connector.id, 'action', 'actions'); + }); + }); + + describe('rule creation', async () => { + const webhookConnectorName = generateUniqueKey(); + const webApiConnectorName = generateUniqueKey(); + let webApiAction: { id: string }; + let webhookAction: { id: string }; + + const setupRule = async () => { + const ruleName = generateUniqueKey(); + await retry.try(async () => { + await rules.common.defineIndexThresholdAlert(ruleName); + }); + return ruleName; + }; + + const getRuleIdByName = async (name: string) => { + const response = await supertest + .get(`/api/alerts/_find?search=${name}&search_fields=name`) + .expect(200); + return response.body.data[0].id; + }; + + const selectSlackConnectorInRuleAction = async ({ connectorId }: { connectorId: string }) => { + await testSubjects.click('.slack-alerting-ActionTypeSelectOption'); // "Slack" in connector list + await testSubjects.click('selectActionConnector-.slack-0'); + await testSubjects.click(`dropdown-connector-${connectorId}`); + }; + + before(async () => { + webApiAction = await actions.api.createConnector({ + name: webApiConnectorName, + config: {}, + secrets: { token: 'supersecrettoken' }, + connectorTypeId: '.slack_api', + }); + + webhookAction = await actions.api.createConnector({ + name: webhookConnectorName, + config: {}, + secrets: { webhookUrl: 'https://test.com' }, + connectorTypeId: '.slack', + }); + + objectRemover.add(webhookAction.id, 'action', 'actions'); + objectRemover.add(webApiAction.id, 'action', 'actions'); + await pageObjects.common.navigateToApp('triggersActions'); + }); + + it('should save webhook type slack connectors', async () => { + const ruleName = await setupRule(); + + await selectSlackConnectorInRuleAction({ + connectorId: webhookAction.id, + }); + await testSubjects.click('saveRuleButton'); + await pageObjects.triggersActionsUI.searchAlerts(ruleName); + + const ruleId = await getRuleIdByName(ruleName); + objectRemover.add(ruleId, 'rule', 'alerting'); + + const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); + expect(searchResults).to.eql([ + { + duration: '00:00', + interval: '1 min', + name: `${ruleName}Index threshold`, + tags: '', + }, + ]); + + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql(`Created rule "${ruleName}"`); + }); + + it('should save webapi type slack connectors', async () => { + await setupRule(); + await selectSlackConnectorInRuleAction({ + connectorId: webApiAction.id, + }); + + await testSubjects.click('saveRuleButton'); + + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql('Failed to retrieve Slack channels list'); + + // We are not saving the rule yet as we currently have no way + // to mock the internal request that loads the channels list + // uncomment once we have a way to mock the request + + // const ruleName = await setupRule(); + // await selectSlackConnectorInRuleAction({ + // connectorId: webApiAction.id, + // }); + + // await testSubjects.click('saveRuleButton'); + // await pageObjects.triggersActionsUI.searchAlerts(ruleName); + + // const ruleId = await getRuleIdByName(ruleName); + // objectRemover.add(ruleId, 'rule', 'alerting'); + + // const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); + // expect(searchResults).to.eql([ + // { + // duration: '00:00', + // interval: '1 min', + // name: `${ruleName}Index threshold`, + // tags: '', + // }, + // ]); + // const toastTitle = await pageObjects.common.closeToast(); + // expect(toastTitle).to.eql(`Created rule "${ruleName}"`); + }); + }); + }); +}; diff --git a/x-pack/test/functional_with_es_ssl/config.base.ts b/x-pack/test/functional_with_es_ssl/config.base.ts index 71039b211f5db..533fec1944b67 100644 --- a/x-pack/test/functional_with_es_ssl/config.base.ts +++ b/x-pack/test/functional_with_es_ssl/config.base.ts @@ -24,6 +24,7 @@ const enabledActionTypes = [ '.servicenow', '.servicenow-sir', '.slack', + '.slack_api', '.tines', '.webhook', 'test.authorization', diff --git a/x-pack/test/functional_with_es_ssl/plugins/alerts/server/plugin.ts b/x-pack/test/functional_with_es_ssl/plugins/alerts/server/plugin.ts index bbb52e6d98221..66a8890b3da0c 100644 --- a/x-pack/test/functional_with_es_ssl/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/plugins/alerts/server/plugin.ts @@ -6,7 +6,11 @@ */ import { Plugin, CoreSetup } from '@kbn/core/server'; -import { PluginSetupContract as AlertingSetup, RuleType } from '@kbn/alerting-plugin/server'; +import { + PluginSetupContract as AlertingSetup, + RuleType, + RuleTypeParams, +} from '@kbn/alerting-plugin/server'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; // this plugin's dependendencies @@ -26,10 +30,17 @@ export const noopAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = { return { state: {} }; }, producer: 'alerts', + validate: { + params: { validate: (params) => params }, + }, }; +interface AlwaysFiringParams extends RuleTypeParams { + instances: Array<{ id: string; state: any }>; +} + export const alwaysFiringAlertType: RuleType< - { instances: Array<{ id: string; state: any }> }, + AlwaysFiringParams, never, // Only use if defining useSavedObjectReferences hook { globalStateValue: boolean; @@ -66,6 +77,9 @@ export const alwaysFiringAlertType: RuleType< }, }; }, + validate: { + params: { validate: (params) => params as AlwaysFiringParams }, + }, }; export const failingAlertType: RuleType = { @@ -84,6 +98,9 @@ export const failingAlertType: RuleType params }, + }, }; export class AlertingFixturePlugin implements Plugin { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts new file mode 100644 index 0000000000000..54e7a89042b02 --- /dev/null +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { + ENDPOINT_LIST_URL, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { getExceptionResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + + describe('duplicate_exception_lists', () => { + afterEach(async () => { + await deleteAllExceptions(supertest, log); + }); + + it('should duplicate a list with no exception items', async () => { + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const bodyToCompare = removeExceptionListServerGeneratedProperties(body); + expect(bodyToCompare).to.eql({ + ...getExceptionResponseMockWithoutAutoGeneratedValues(), + type: 'detection', + list_id: body.list_id, + name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, + }); + }); + + it('should duplicate a list and its items', async () => { + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMock(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const listBodyToCompare = removeExceptionListServerGeneratedProperties(listBody); + expect(listBodyToCompare).to.eql({ + ...getExceptionResponseMockWithoutAutoGeneratedValues(), + type: 'detection', + list_id: listBody.list_id, + name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, + }); + + expect(body.total).to.eql(1); + }); + + it('should duplicate a list with expired exception items', async () => { + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body.total).to.eql(1); + }); + + it('should duplicate a list and EXCLUDE expired exception items when "include_expired_exceptions" set to "false"', async () => { + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMock(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + expire_time: expiredDate, + }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=false` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body.total).to.eql(0); + }); + + describe('error states', () => { + it('should cause a 409 if list does not exist', async () => { + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=exception_list_id&namespace_type=agnostic&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(404); + + expect(body).to.eql({ + message: 'exception list id: "exception_list_id" does not exist', + status_code: 404, + }); + }); + + it('should cause a 405 if trying to duplicate a reserved exception list type', async () => { + // create an exception list + await supertest.post(ENDPOINT_LIST_URL).set('kbn-xsrf', 'true').expect(200); + + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=endpoint_list&namespace_type=agnostic&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(405); + + expect(body).to.eql({ + message: + 'unable to duplicate exception list with list_id: endpoint_list - action not allowed', + status_code: 405, + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts index bfce9ac8267e7..02b63c732d229 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts @@ -19,6 +19,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./update_list_items')); loadTestFile(require.resolve('./delete_lists')); loadTestFile(require.resolve('./delete_list_items')); + loadTestFile(require.resolve('./duplicate_exception_list')); loadTestFile(require.resolve('./find_lists')); loadTestFile(require.resolve('./find_list_items')); loadTestFile(require.resolve('./find_lists_by_size')); diff --git a/x-pack/test/localization/tests/lens/smokescreen.ts b/x-pack/test/localization/tests/lens/smokescreen.ts index fc8aba2131504..6788556724c6b 100644 --- a/x-pack/test/localization/tests/lens/smokescreen.ts +++ b/x-pack/test/localization/tests/lens/smokescreen.ts @@ -292,7 +292,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await listingTable.searchForItemWithName('lnsXYvis'); await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); await PageObjects.lens.goToTimeRange(); - await testSubjects.click('lnsXY_splitDimensionPanel > indexPattern-dimension-remove'); + await PageObjects.lens.removeDimension('lnsXY_splitDimensionPanel'); await PageObjects.lens.switchToVisualization('line', termTranslator('line')); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', @@ -344,9 +344,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( longLabel ); - expect( - await testSubjects.isDisplayed('lnsXY_yDimensionPanel > indexPattern-dimension-remove') - ).to.equal(true); + expect(await PageObjects.lens.canRemoveDimension('lnsXY_yDimensionPanel')).to.equal(true); await PageObjects.lens.removeDimension('lnsXY_yDimensionPanel'); await testSubjects.missingOrFail('lnsXY_yDimensionPanel > lns-dimensionTrigger'); }); diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index 2bd65ef5dcd3e..dc244a51bb183 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -163,6 +163,7 @@ export default function ({ getService }: FtrProviderContext) { execution_gap_duration_s: 3000, }, }, + revision: 0, }, }, alerting: { diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index 67478db2c8c00..6bc6b02013ed0 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -57,6 +57,7 @@ export default function ({ getService }: FtrProviderContext) { 'actions:.servicenow-itom', 'actions:.servicenow-sir', 'actions:.slack', + 'actions:.slack_api', 'actions:.swimlane', 'actions:.teams', 'actions:.tines', diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap b/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap index e76a791652f93..a66368fe5a8ec 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap @@ -21,10 +21,10 @@ Object { false, ], "kibana.alert.instance.id": Array [ - "apm.transaction_error_rate_opbeans-go_request_ENVIRONMENT_NOT_DEFINED", + "opbeans-go_ENVIRONMENT_NOT_DEFINED_request", ], "kibana.alert.reason": Array [ - "Failed transactions is 50% in the last 5 mins for opbeans-go. Alert when > 30%.", + "Failed transactions is 50% in the last 5 mins for service: opbeans-go, env: Not defined, type: request. Alert when > 30%.", ], "kibana.alert.rule.category": Array [ "Failed transaction rate threshold", @@ -70,6 +70,9 @@ Object { "processor.event": Array [ "transaction", ], + "service.environment": Array [ + "ENVIRONMENT_NOT_DEFINED", + ], "service.name": Array [ "opbeans-go", ], @@ -101,10 +104,10 @@ Object { false, ], "kibana.alert.instance.id": Array [ - "apm.transaction_error_rate_opbeans-go_request_ENVIRONMENT_NOT_DEFINED", + "opbeans-go_ENVIRONMENT_NOT_DEFINED_request", ], "kibana.alert.reason": Array [ - "Failed transactions is 50% in the last 5 mins for opbeans-go. Alert when > 30%.", + "Failed transactions is 50% in the last 5 mins for service: opbeans-go, env: Not defined, type: request. Alert when > 30%.", ], "kibana.alert.rule.category": Array [ "Failed transaction rate threshold", @@ -150,6 +153,9 @@ Object { "processor.event": Array [ "transaction", ], + "service.environment": Array [ + "ENVIRONMENT_NOT_DEFINED", + ], "service.name": Array [ "opbeans-go", ], diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts index 32f47b708e545..680b61cceaf20 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/get_summarized_alerts.ts @@ -15,6 +15,7 @@ import { AlertConsumers, ALERT_REASON, ALERT_INSTANCE_ID, + ALERT_MAINTENANCE_WINDOW_IDS, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import { createLifecycleExecutor, @@ -380,5 +381,141 @@ export default function createGetSummarizedAlertsTest({ getService }: FtrProvide expect(summarizedAlertsExcludingId2.recovered.count).to.eql(0); expect(get(summarizedAlertsExcludingId2.new.data[0], ALERT_INSTANCE_ID)).to.eql(id1); }); + + it('should return new, ongoing, and recovered alerts if there are active maintenance windows', async () => { + const id = 'host-01'; + const maintenanceWindowIds = ['test-id-1', 'test-id-2']; + + // This creates the function that will wrap the solution's rule executor with the RuleRegistry lifecycle + const createLifecycleRuleExecutor = createLifecycleExecutor(logger, ruleDataClient); + const createGetSummarizedAlerts = createGetSummarizedAlertsFn({ + ruleDataClient, + useNamespace: false, + isLifecycleAlert: true, + }); + + // This creates the executor that is passed to the Alerting framework. + const executor = createLifecycleRuleExecutor< + MockRuleParams, + { shouldTriggerAlert: boolean }, + MockAlertState, + MockAlertContext, + MockAllowedActionGroups + >(async function (options) { + const { services, state: previousState } = options; + const { alertWithLifecycle } = services; + + const triggerAlert = previousState.shouldTriggerAlert; + + if (triggerAlert) { + alertWithLifecycle({ + id, + fields: { + [ALERT_REASON]: 'Test alert is firing', + }, + }); + } + + return Promise.resolve({ state: { shouldTriggerAlert: triggerAlert } }); + }); + + const getSummarizedAlerts = createGetSummarizedAlerts(); + + // Create the options with the minimal amount of values to test the lifecycle executor + const options = { + spaceId: 'default', + rule: { + id, + name: 'test rule', + ruleTypeId: 'observability.test.fake', + ruleTypeName: 'test', + consumer: 'observability', + producer: 'observability.test', + }, + services: { + alertFactory: getMockAlertFactory(), + shouldWriteAlerts: sinon.stub().returns(true), + }, + flappingSettings: DEFAULT_FLAPPING_SETTINGS, + maintenanceWindowIds, + } as unknown as RuleExecutorOptions< + MockRuleParams, + WrappedLifecycleRuleState<{ shouldTriggerAlert: boolean }>, + { [x: string]: unknown }, + { [x: string]: unknown }, + string + >; + + const getState = ( + shouldTriggerAlert: boolean, + alerts: Record + ) => ({ wrapped: { shouldTriggerAlert }, trackedAlerts: alerts, trackedAlertsRecovered: {} }); + + // Execute the rule the first time - this creates a new alert + const execution1Uuid = uuidv4(); + const execution1Result = await executor({ + ...options, + startedAt: new Date(), + state: getState(true, {}), + executionId: execution1Uuid, + }); + + const execution1SummarizedAlerts = await getSummarizedAlerts({ + ruleId: id, + executionUuid: execution1Uuid, + spaceId: 'default', + excludedAlertInstanceIds: [], + }); + expect(execution1SummarizedAlerts.new.count).to.eql(1); + expect(execution1SummarizedAlerts.ongoing.count).to.eql(0); + expect(execution1SummarizedAlerts.recovered.count).to.eql(0); + expect(get(execution1SummarizedAlerts.new.data[0], ALERT_MAINTENANCE_WINDOW_IDS)).to.eql( + maintenanceWindowIds + ); + + // Execute again to update the existing alert + const execution2Uuid = uuidv4(); + const execution2Result = await executor({ + ...options, + startedAt: new Date(), + state: getState(true, execution1Result.state.trackedAlerts), + executionId: execution2Uuid, + }); + + const execution2SummarizedAlerts = await getSummarizedAlerts({ + ruleId: id, + executionUuid: execution2Uuid, + spaceId: 'default', + excludedAlertInstanceIds: [], + }); + expect(execution2SummarizedAlerts.new.count).to.eql(0); + expect(execution2SummarizedAlerts.ongoing.count).to.eql(1); + expect(execution2SummarizedAlerts.recovered.count).to.eql(0); + expect(get(execution2SummarizedAlerts.ongoing.data[0], ALERT_MAINTENANCE_WINDOW_IDS)).to.eql( + maintenanceWindowIds + ); + + // Execute again to recover the alert + const execution3Uuid = uuidv4(); + await executor({ + ...options, + startedAt: new Date(), + state: getState(false, execution2Result.state.trackedAlerts), + executionId: execution3Uuid, + }); + + const execution3SummarizedAlerts = await getSummarizedAlerts({ + ruleId: id, + executionUuid: execution3Uuid, + spaceId: 'default', + excludedAlertInstanceIds: [], + }); + expect(execution3SummarizedAlerts.new.count).to.eql(0); + expect(execution3SummarizedAlerts.ongoing.count).to.eql(0); + expect(execution3SummarizedAlerts.recovered.count).to.eql(1); + expect( + get(execution3SummarizedAlerts.recovered.data[0], ALERT_MAINTENANCE_WINDOW_IDS) + ).to.eql(maintenanceWindowIds); + }); }); } diff --git a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index ad130efa1f4d8..4ab39d75c5d02 100644 --- a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -54,7 +54,7 @@ "type": "doc", "value": { "id": "index-pattern:defaultspace-index-pattern-id", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "timeFieldName": "@timestamp", @@ -113,7 +113,7 @@ "type": "doc", "value": { "id": "dashboard:defaultspace-dashboard-id", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "dashboard": { "description": "", @@ -147,7 +147,7 @@ "type": "doc", "value": { "id": "index-pattern:space1-index-pattern-id", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "timeFieldName": "@timestamp", @@ -208,7 +208,7 @@ "type": "doc", "value": { "id": "dashboard:space1-dashboard-id", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "dashboard": { "description": "", @@ -242,7 +242,7 @@ "type": "doc", "value": { "id": "index-pattern:space2-index-pattern-id", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "index-pattern": { "timeFieldName": "@timestamp", @@ -303,7 +303,7 @@ "type": "doc", "value": { "id": "dashboard:space2-dashboard-id", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "dashboard": { "description": "", @@ -770,7 +770,7 @@ "type": "doc", "value": { "id": "index-pattern:inbound-reference-origin-match-1-newId", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "inbound-reference-origin-match-1", "index-pattern": { @@ -789,7 +789,7 @@ "type": "doc", "value": { "id": "index-pattern:inbound-reference-origin-match-2a", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "inbound-reference-origin-match-2", "index-pattern": { @@ -808,7 +808,7 @@ "type": "doc", "value": { "id": "index-pattern:inbound-reference-origin-match-2b", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "inbound-reference-origin-match-2", "index-pattern": { diff --git a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json index 34ddbc9ac7846..407f0fd182fe7 100644 --- a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json +++ b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json @@ -8,12 +8,279 @@ "index": ".kibana_$KIBANA_PACKAGE_VERSION_001", "mappings": { "dynamic": "false", + "_meta": { + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] + } + }, "properties": { "space": { "dynamic": false, "type": "object" }, - "type": { "type": "keyword" }, + "type": { + "type": "keyword" + }, + "migrationVersion": { + "dynamic": "true", + "type": "object" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1", + "mapping": { + "total_fields": { + "limit": 1500 + } + } + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana_analytics_$KIBANA_PACKAGE_VERSION": {}, + ".kibana_analytics": {} + }, + "index": ".kibana_analytics_$KIBANA_PACKAGE_VERSION_001", + "mappings": { + "dynamic": "false", + "_meta": { + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] + } + }, + "properties": { + "space": { + "dynamic": false, + "type": "object" + }, + "type": { + "type": "keyword" + }, "migrationVersion": { "dynamic": "true", "type": "object" @@ -26,7 +293,9 @@ "number_of_replicas": "0", "number_of_shards": "1", "mapping": { - "total_fields": { "limit": 1500 } + "total_fields": { + "limit": 1500 + } } } } diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index.ts index f8e1dcd445462..768cce8a408f4 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index.ts @@ -7,29 +7,102 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; +export const indexThresholdRuleName = 'kibana sites - low bytes'; +export const metricThresholdRuleName = 'network metric packets'; + export default function ({ loadTestFile, getService }: FtrProviderContext) { const browser = getService('browser'); const actions = getService('actions'); const rules = getService('rules'); describe('stack alerting', function () { + let itRuleId: string; + let mtRuleId: string; + let serverLogConnectorId: string; before(async () => { await browser.setWindowSize(1920, 1080); - await actions.api.createConnector({ - name: 'server-log-connector', + ({ id: serverLogConnectorId } = await actions.api.createConnector({ + name: 'my-server-log-connector', config: {}, secrets: {}, connectorTypeId: '.server-log', - }); + })); + ({ id: itRuleId } = await rules.api.createRule({ + consumer: 'alerts', + name: indexThresholdRuleName, + notifyWhen: 'onActionGroupChange', + params: { + index: ['kibana_sample_data_logs'], + timeField: '@timestamp', + aggType: 'sum', + aggField: 'bytes', + groupBy: 'top', + termField: 'host.keyword', + termSize: 4, + timeWindowSize: 24, + timeWindowUnit: 'h', + thresholdComparator: '>', + threshold: [4200], + }, + ruleTypeId: '.index-threshold', + schedule: { interval: '1m' }, + actions: [ + { + group: 'threshold met', + id: serverLogConnectorId, + params: { + level: 'info', + message: 'Test', + }, + }, + ], + })); + ({ id: mtRuleId } = await rules.api.createRule({ + consumer: 'infrastructure', + name: metricThresholdRuleName, + notifyWhen: 'onActionGroupChange', + params: { + criteria: [ + { + aggType: 'max', + comparator: '>', + threshold: [0], + timeSize: 3, + timeUnit: 's', + metric: 'network.packets', + }, + ], + sourceId: 'default', + alertOnNoData: false, + alertOnGroupDisappear: false, + groupBy: ['network.name'], + }, + ruleTypeId: 'metrics.alert.threshold', + schedule: { interval: '1m' }, + actions: [ + { + group: 'metrics.threshold.fired', + id: serverLogConnectorId, + params: { + level: 'info', + message: 'Test Metric Threshold rule', + }, + }, + ], + })); }); after(async () => { + await rules.api.deleteRule(itRuleId); + await rules.api.deleteRule(mtRuleId); await rules.api.deleteAllRules(); + await actions.api.deleteConnector(serverLogConnectorId); await actions.api.deleteAllConnectors(); }); loadTestFile(require.resolve('./list_view')); loadTestFile(require.resolve('./index_threshold_rule')); + loadTestFile(require.resolve('./metrics_threshold_rule')); loadTestFile(require.resolve('./tracking_containment_rule')); }); } diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts index ac3be7d1814a5..6b3e657659607 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/list_view.ts @@ -6,63 +6,15 @@ */ import { FtrProviderContext } from '../../../ftr_provider_context'; +import { indexThresholdRuleName } from '.'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const commonScreenshots = getService('commonScreenshots'); const screenshotDirectories = ['response_ops_docs', 'stack_alerting']; const pageObjects = getPageObjects(['common', 'header']); - const actions = getService('actions'); - const rules = getService('rules'); const testSubjects = getService('testSubjects'); - const ruleName = 'kibana sites - low bytes'; describe('list view', function () { - let ruleId: string; - let connectorId: string; - before(async () => { - ({ id: connectorId } = await actions.api.createConnector({ - name: 'my-server-log-connector', - config: {}, - secrets: {}, - connectorTypeId: '.server-log', - })); - ({ id: ruleId } = await rules.api.createRule({ - consumer: 'alerts', - name: ruleName, - notifyWhen: 'onActionGroupChange', - params: { - index: ['kibana_sample_data_logs'], - timeField: '@timestamp', - aggType: 'sum', - aggField: 'bytes', - groupBy: 'top', - termField: 'host.keyword', - termSize: 4, - timeWindowSize: 24, - timeWindowUnit: 'h', - thresholdComparator: '>', - threshold: [4200], - }, - ruleTypeId: '.index-threshold', - schedule: { interval: '1m' }, - actions: [ - { - group: 'threshold met', - id: connectorId, - params: { - level: 'info', - message: 'Test', - }, - }, - ], - })); - }); - - after(async () => { - await rules.api.deleteRule(ruleId); - await actions.api.deleteConnector(connectorId); - }); - it('rules list screenshot', async () => { await pageObjects.common.navigateToApp('triggersActions'); await pageObjects.header.waitUntilLoadingHasFinished(); @@ -94,9 +46,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('rule detail screenshots', async () => { await pageObjects.common.navigateToApp('triggersActions'); await pageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.setValue('ruleSearchField', ruleName); + await testSubjects.setValue('ruleSearchField', indexThresholdRuleName); const rulesList = await testSubjects.find('rulesList'); - const alertRule = await rulesList.findByCssSelector(`[title="${ruleName}"]`); + const alertRule = await rulesList.findByCssSelector(`[title="${indexThresholdRuleName}"]`); await alertRule.click(); await pageObjects.header.waitUntilLoadingHasFinished(); await commonScreenshots.takeScreenshot( diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/metrics_threshold_rule.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/metrics_threshold_rule.ts new file mode 100644 index 0000000000000..e091ab4da27ed --- /dev/null +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/metrics_threshold_rule.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 { FtrProviderContext } from '../../../ftr_provider_context'; +import { metricThresholdRuleName } from '.'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const actions = getService('actions'); + const browser = getService('browser'); + const commonScreenshots = getService('commonScreenshots'); + const testSubjects = getService('testSubjects'); + const pageObjects = getPageObjects(['common', 'header']); + const screenshotDirectories = ['response_ops_docs', 'stack_alerting']; + const emailConnectorName = 'Email connector 1'; + + describe('metric threshold rule', function () { + let emailConnectorId: string; + before(async () => { + ({ id: emailConnectorId } = await actions.api.createConnector({ + name: emailConnectorName, + config: { + service: 'other', + from: 'bob@example.com', + host: 'some.non.existent.com', + port: 25, + }, + secrets: { + user: 'bob', + password: 'supersecret', + }, + connectorTypeId: '.email', + })); + }); + + after(async () => { + await actions.api.deleteConnector(emailConnectorId); + }); + + it('example metric threshold rule conditions and actions', async () => { + await pageObjects.common.navigateToApp('triggersActions'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.setValue('ruleSearchField', metricThresholdRuleName); + await browser.pressKeys(browser.keys.ENTER); + const actionPanel = await testSubjects.find('collapsedItemActions'); + await actionPanel.click(); + const editRuleMenu = await testSubjects.find('editRule'); + await editRuleMenu.click(); + const expandExpression = await testSubjects.find('expandRow'); + await expandExpression.click(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await commonScreenshots.takeScreenshot( + 'rule-flyout-rule-conditions', + screenshotDirectories, + 1400, + 1500 + ); + + const serverLogAction = await testSubjects.find('alertActionAccordion-0'); + const removeConnectorButton = await serverLogAction.findByCssSelector( + '[aria-label="Delete"]' + ); + await removeConnectorButton.click(); + + await testSubjects.click('.email-alerting-ActionTypeSelectOption'); + await testSubjects.scrollIntoView('addAlertActionButton'); + await commonScreenshots.takeScreenshot( + 'rule-flyout-action-details', + screenshotDirectories, + 1400, + 1024 + ); + await testSubjects.scrollIntoView('addAlertActionButton'); + await testSubjects.click('messageAddVariableButton'); + await commonScreenshots.takeScreenshot( + 'rule-flyout-action-variables', + screenshotDirectories, + 1400, + 1024 + ); + + const cancelEditButton = await testSubjects.find('cancelSaveEditedRuleButton'); + await cancelEditButton.click(); + const confirmCancelButton = await testSubjects.find('confirmModalConfirmButton'); + await confirmCancelButton.click(); + }); + }); +} diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts index 247b25d2be2ca..4da22a8e807a8 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts @@ -12,8 +12,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const screenshotDirectories = ['response_ops_docs', 'stack_connectors']; const pageObjects = getPageObjects(['common', 'header']); const actions = getService('actions'); - const testSubjects = getService('testSubjects'); + const browser = getService('browser'); const comboBox = getService('comboBox'); + const find = getService('find'); + const testSubjects = getService('testSubjects'); const testIndex = `test-index`; const indexDocument = `{\n` + @@ -21,13 +23,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { `"rule_name": "{{rule.name}}",\n` + `"alert_id": "{{alert.id}}",\n` + `"context_message": "{{context.message}}"\n`; + const emailConnectorName = 'my-email-connector'; describe('connector types', function () { + let emailConnectorId: string; + before(async () => { + ({ id: emailConnectorId } = await actions.api.createConnector({ + name: emailConnectorName, + config: { + service: 'other', + from: 'bob@example.com', + host: 'some.non.existent.com', + port: 25, + }, + secrets: { + user: 'bob', + password: 'supersecret', + }, + connectorTypeId: '.email', + })); + }); + beforeEach(async () => { await pageObjects.common.navigateToApp('connectors'); await pageObjects.header.waitUntilLoadingHasFinished(); }); + after(async () => { + await actions.api.deleteConnector(emailConnectorId); + }); + it('server log connector screenshots', async () => { await pageObjects.common.navigateToApp('connectors'); await pageObjects.header.waitUntilLoadingHasFinished(); @@ -57,5 +82,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); await flyOutCancelButton.click(); }); + + it('email connector screenshots', async () => { + await pageObjects.common.navigateToApp('connectors'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await actions.common.openNewConnectorForm('email'); + await testSubjects.setValue('nameInput', 'Gmail connector'); + await testSubjects.setValue('emailFromInput', 'test@gmail.com'); + await testSubjects.setValue('emailServiceSelectInput', 'gmail'); + await commonScreenshots.takeScreenshot('email-connector', screenshotDirectories); + const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); + await flyOutCancelButton.click(); + }); + + it('test email connector screenshots', async () => { + const searchBox = await find.byCssSelector('[data-test-subj="actionsList"] .euiFieldSearch'); + await searchBox.click(); + await searchBox.clearValue(); + await searchBox.type('my actionTypeId:(.email)'); + await searchBox.pressKeys(browser.keys.ENTER); + const connectorList = await testSubjects.find('actionsTable'); + const emailConnector = await connectorList.findByCssSelector( + `[title="${emailConnectorName}"]` + ); + await emailConnector.click(); + const testButton = await testSubjects.find('testConnectorTab'); + await testButton.click(); + await testSubjects.setValue('comboBoxSearchInput', 'elastic@gmail.com'); + await testSubjects.setValue('subjectInput', 'Test subject'); + await testSubjects.setValue('messageTextArea', 'Enter message text'); + /* timing issue sometimes happens with the combobox so we just try to set the subjectInput again */ + await testSubjects.setValue('subjectInput', 'Test subject'); + await commonScreenshots.takeScreenshot( + 'email-params-test', + screenshotDirectories, + 1400, + 1024 + ); + }); }); } diff --git a/x-pack/test/security_api_integration/tests/audit/audit_log.ts b/x-pack/test/security_api_integration/tests/audit/audit_log.ts index d2bd365ec4c97..2173b9d8e2123 100644 --- a/x-pack/test/security_api_integration/tests/audit/audit_log.ts +++ b/x-pack/test/security_api_integration/tests/audit/audit_log.ts @@ -15,8 +15,7 @@ export default function ({ getService }: FtrProviderContext) { const retry = getService('retry'); const { username, password } = getService('config').get('servers.kibana'); - // FLAKY: https://github.com/elastic/kibana/issues/119267 - describe.skip('Audit Log', function () { + describe('Audit Log', function () { const logFilePath = Path.resolve(__dirname, '../../plugins/audit_log/audit.log'); const logFile = new FileWrapper(logFilePath, retry); @@ -27,12 +26,12 @@ export default function ({ getService }: FtrProviderContext) { it('logs audit events when reading and writing saved objects', async () => { await supertest.get('/audit_log?query=param').set('kbn-xsrf', 'foo').expect(204); await retry.waitFor('logs event in the dest file', async () => await logFile.isNotEmpty()); - const content = await logFile.readJSON(); const httpEvent = content.find((c) => c.event.action === 'http_request'); expect(httpEvent).to.be.ok(); expect(httpEvent.trace.id).to.be.ok(); + expect(httpEvent.user.name).to.be(username); expect(httpEvent.kibana.space_id).to.be('default'); expect(httpEvent.http.request.method).to.be('get'); @@ -45,7 +44,12 @@ export default function ({ getService }: FtrProviderContext) { expect(createEvent.user.name).to.be(username); expect(createEvent.kibana.space_id).to.be('default'); - const findEvent = content.find((c) => c.event.action === 'saved_object_find'); + // There are two 'saved_object_find' events in the log. One is by the fleet app for + // "epm - packages", the other is by the user for a dashboard (this is the one we are + // concerned with). + const findEvent = content.find( + (c) => c.event.action === 'saved_object_find' && c.kibana.saved_object.type === 'dashboard' + ); expect(findEvent).to.be.ok(); expect(findEvent.trace.id).to.be.ok(); expect(findEvent.user.name).to.be(username); @@ -65,7 +69,6 @@ export default function ({ getService }: FtrProviderContext) { }) .expect(200); await retry.waitFor('logs event in the dest file', async () => await logFile.isNotEmpty()); - const content = await logFile.readJSON(); const loginEvent = content.find((c) => c.event.action === 'user_login'); @@ -90,7 +93,6 @@ export default function ({ getService }: FtrProviderContext) { }) .expect(401); await retry.waitFor('logs event in the dest file', async () => await logFile.isNotEmpty()); - const content = await logFile.readJSON(); const loginEvent = content.find((c) => c.event.action === 'user_login'); diff --git a/x-pack/test/security_solution_cypress/es_archives/auditbeat/data.json b/x-pack/test/security_solution_cypress/es_archives/auditbeat/data.json index ec69dcaa9d0f7..a4e42ac0335a9 100644 --- a/x-pack/test/security_solution_cypress/es_archives/auditbeat/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/auditbeat/data.json @@ -204,7 +204,8 @@ "executable" : "/bin/zsh", "name" : "zsh", "args" : [ - "-zsh" + "-zsh", + "unique" ], "entity_id" : "q6pltOhTWlQx3BCE", "entry_leader": { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index 5d19932090168..735285753ea23 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -33,27 +33,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'Actions', ], [ - 'Host-dpu1a2r2yi', + 'Host-9qenwrl9ko', 'x', 'x', 'Warning', - 'macOS', - '10.2.17.24, 10.56.215.200,10.254.196.130', - 'x', - 'x', - '', - ], - [ - 'Host-rs9wp4o6l9', - 'x', - 'x', - 'Success', 'Linux', - '10.138.79.131, 10.170.160.154', + '10.56.228.101, 10.201.120.140,10.236.180.146', 'x', 'x', '', ], + ['Host-qw2bti801m', 'x', 'x', 'Failure', 'macOS', '10.244.59.227', 'x', 'x', ''], [ 'Host-u5jy6j0pwb', 'x', diff --git a/x-pack/test/session_view/basic/tests/get_total_io_bytes_route.ts b/x-pack/test/session_view/basic/tests/get_total_io_bytes_route.ts index e804d34318e82..ecc09ab719740 100644 --- a/x-pack/test/session_view/basic/tests/get_total_io_bytes_route.ts +++ b/x-pack/test/session_view/basic/tests/get_total_io_bytes_route.ts @@ -9,6 +9,8 @@ import expect from '@kbn/expect'; import { GET_TOTAL_IO_BYTES_ROUTE } from '@kbn/session-view-plugin/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +const MOCK_INDEX = 'logs-endpoint.events.process*'; +const MOCK_SESSION_START_TIME = '2022-05-08T13:44:00.13Z'; const MOCK_SESSION_ENTITY_ID = 'MDEwMTAxMDEtMDEwMS0wMTAxLTAxMDEtMDEwMTAxMDEwMTAxLTUyMDU3LTEzMjk2NDkxMDQwLjEzMDAwMDAwMA=='; const MOCK_TOTAL_BYTES = 8192; // sum of total_captured_bytes field in io_events es archive @@ -31,7 +33,9 @@ export default function getTotalIOBytesTests({ getService }: FtrProviderContext) it(`${GET_TOTAL_IO_BYTES_ROUTE} returns a page of IO events`, async () => { const response = await supertest.get(GET_TOTAL_IO_BYTES_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, }); expect(response.status).to.be(200); expect(response.body.total).to.be(MOCK_TOTAL_BYTES); @@ -39,6 +43,8 @@ export default function getTotalIOBytesTests({ getService }: FtrProviderContext) it(`${GET_TOTAL_IO_BYTES_ROUTE} returns 0 for invalid query`, async () => { const response = await supertest.get(GET_TOTAL_IO_BYTES_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, + sessionStartTime: MOCK_SESSION_START_TIME, sessionEntityId: 'xyz', }); expect(response.status).to.be(200); diff --git a/x-pack/test/session_view/basic/tests/io_events_route.ts b/x-pack/test/session_view/basic/tests/io_events_route.ts index f419d0d398725..2b1abe5bb60c0 100644 --- a/x-pack/test/session_view/basic/tests/io_events_route.ts +++ b/x-pack/test/session_view/basic/tests/io_events_route.ts @@ -9,6 +9,8 @@ import expect from '@kbn/expect'; import { IO_EVENTS_ROUTE } from '@kbn/session-view-plugin/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +const MOCK_INDEX = 'logs-endpoint.events.process*'; +const MOCK_SESSION_START_TIME = '2022-05-08T13:44:00.13Z'; const MOCK_SESSION_ENTITY_ID = 'MDEwMTAxMDEtMDEwMS0wMTAxLTAxMDEtMDEwMTAxMDEwMTAxLTUyMDU3LTEzMjk2NDkxMDQwLjEzMDAwMDAwMA=='; const MOCK_IO_EVENT_TOTAL = 8; @@ -33,7 +35,9 @@ export default function ioEventsTests({ getService }: FtrProviderContext) { it(`${IO_EVENTS_ROUTE} returns a page of IO events`, async () => { const response = await supertest.get(IO_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, }); expect(response.status).to.be(200); @@ -53,7 +57,9 @@ export default function ioEventsTests({ getService }: FtrProviderContext) { it(`${IO_EVENTS_ROUTE} returns a page of IO events (w cursor)`, async () => { const response = await supertest.get(IO_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, cursor: MOCK_CURSOR, }); diff --git a/x-pack/test/session_view/basic/tests/process_events_route.ts b/x-pack/test/session_view/basic/tests/process_events_route.ts index 18f272e0e02dd..c5eb0f4b88648 100644 --- a/x-pack/test/session_view/basic/tests/process_events_route.ts +++ b/x-pack/test/session_view/basic/tests/process_events_route.ts @@ -26,6 +26,8 @@ import { noKibanaPrivileges, } from '../../../rule_registry/common/lib/authentication/users'; +const MOCK_INDEX = 'logs-endpoint.events.process*'; +const MOCK_SESSION_START_TIME = '2022-05-08T13:44:00.13Z'; const MOCK_SESSION_ENTITY_ID = 'MDEwMTAxMDEtMDEwMS0wMTAxLTAxMDEtMDEwMTAxMDEwMTAxLTUyMDU3LTEzMjk2NDkxMDQwLjEzMDAwMDAwMA=='; @@ -58,7 +60,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) { it(`${PROCESS_EVENTS_ROUTE} returns a page of process events`, async () => { const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data }); expect(response.status).to.be(200); @@ -68,7 +72,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) { it(`${PROCESS_EVENTS_ROUTE} returns a page of process events (w alerts) (paging forward)`, async () => { const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data cursor: '2022-05-10T20:39:23.6817084Z', // paginating from the timestamp of the first alert. }); @@ -83,7 +89,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) { it(`${PROCESS_EVENTS_ROUTE} returns a page of process events (w alerts) (paging backwards)`, async () => { const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data cursor: '2022-05-10T20:39:23.6817084Z', forward: false, @@ -113,7 +121,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) { .auth(username, password) .set('kbn-xsrf', 'true') .query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data cursor: '2022-05-10T20:39:23.6817084Z', // paginating from the timestamp of the first alert. }); @@ -134,7 +144,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) { .auth(username, password) .set('kbn-xsrf', 'true') .query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, cursor: '2022-05-10T20:39:23.6817084Z', // paginating from the timestamp of the first alert. }); expect(response.status).to.be(200); @@ -185,7 +197,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) { it(`${PROCESS_EVENTS_ROUTE} returns a page of process events`, async () => { const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({ + index: MOCK_INDEX, sessionEntityId: MOCK_SESSION_ENTITY_ID, + sessionStartTime: MOCK_SESSION_START_TIME, pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data }); expect(response.status).to.be(200); diff --git a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index 6d37b745fcde7..3271ff05975f4 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -98,7 +98,7 @@ "type": "_doc", "value": { "id": "dashboard:cts_dashboard_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "cts_dashboard", "dashboard": { @@ -130,7 +130,7 @@ "type": "_doc", "value": { "id": "visualization:cts_vis_1_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "visualization": { "title": "CTS vis 1 from default space", @@ -159,7 +159,7 @@ "type": "_doc", "value": { "id": "visualization:cts_vis_2_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "visualization": { "title": "CTS vis 2 from default space", @@ -188,7 +188,7 @@ "type": "_doc", "value": { "id": "visualization:cts_vis_3_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "cts_vis_3", "visualization": { @@ -218,7 +218,7 @@ "type": "_doc", "value": { "id": "dashboard:cts_dashboard_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "cts_dashboard", "dashboard": { @@ -254,7 +254,7 @@ "type": "_doc", "value": { "id": "visualization:cts_vis_1_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "visualization": { "title": "CTS vis 1 from space_1 space", @@ -283,7 +283,7 @@ "type": "_doc", "value": { "id": "visualization:cts_vis_2_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "visualization": { "title": "CTS vis 2 from space_1 space", @@ -312,7 +312,7 @@ "type": "_doc", "value": { "id": "visualization:cts_vis_3_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "cts_vis_3", "visualization": { @@ -342,7 +342,7 @@ "type": "_doc", "value": { "id": "index-pattern:cts_ip_1_default", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "cts_ip_1", "index-pattern": { @@ -361,7 +361,7 @@ "type": "_doc", "value": { "id": "index-pattern:cts_ip_1_space_1", - "index": ".kibana", + "index": ".kibana_analytics", "source": { "originId": "cts_ip_1", "index-pattern": { diff --git a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json index 3f837ae5fa208..340a1003d50a9 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json @@ -7,7 +7,259 @@ }, "index": ".kibana_$KIBANA_PACKAGE_VERSION_001", "mappings": { - "dynamic": "false" + "dynamic": "false", + "_meta": { + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1", + "mapping": { + "total_fields": { + "limit": 1500 + } + } + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana_analytics_$KIBANA_PACKAGE_VERSION": {}, + ".kibana_analytics": {} + }, + "index": ".kibana_analytics_$KIBANA_PACKAGE_VERSION_001", + "mappings": { + "dynamic": "false", + "_meta": { + "indexTypesMap": { + ".kibana_task_manager": [ + "task" + ], + ".kibana": [ + "apm-indices", + "apm-server-schema", + "apm-service-group", + "apm-telemetry", + "app_search_telemetry", + "application_usage_daily", + "application_usage_totals", + "config", + "config-global", + "core-usage-stats", + "enterprise_search_telemetry", + "event_loop_delays_daily", + "file", + "file-upload-usage-collection-telemetry", + "fileShare", + "guided-onboarding-guide-state", + "guided-onboarding-plugin-state", + "infrastructure-monitoring-log-view", + "infrastructure-ui-source", + "inventory-view", + "legacy-url-alias", + "metrics-explorer-view", + "ml-job", + "ml-module", + "ml-trained-model", + "monitoring-telemetry", + "sample-data-telemetry", + "slo", + "space", + "spaces-usage-stats", + "synthetics-monitor", + "synthetics-param", + "synthetics-privates-locations", + "tag", + "telemetry", + "ui-metric", + "upgrade-assistant-ml-upgrade-operation", + "upgrade-assistant-reindex-operation", + "uptime-dynamic-settings", + "uptime-synthetics-api-key", + "url", + "usage-counters", + "workplace_search_telemetry" + ], + ".kibana_ingest": [ + "epm-packages", + "epm-packages-assets", + "fleet-fleet-server-host", + "fleet-message-signing-keys", + "fleet-preconfiguration-deletion-record", + "fleet-proxy", + "ingest-agent-policies", + "ingest-download-sources", + "ingest-outputs", + "ingest-package-policies", + "ingest_manager_settings" + ], + ".kibana_security_solution": [ + "csp-rule-template", + "endpoint:user-artifact-manifest", + "exception-list", + "exception-list-agnostic", + "osquery-manager-usage-metric", + "osquery-pack", + "osquery-pack-asset", + "osquery-saved-query", + "security-rule", + "security-solution-signals-migration", + "siem-detection-engine-rule-actions", + "siem-ui-timeline", + "siem-ui-timeline-note", + "siem-ui-timeline-pinned-event" + ], + ".kibana_alerting_cases": [ + "action", + "action_task_params", + "alert", + "api_key_pending_invalidation", + "cases", + "cases-comments", + "cases-configure", + "cases-connector-mappings", + "cases-telemetry", + "cases-user-actions", + "connector_token", + "maintenance-window", + "rules-settings" + ], + ".kibana_analytics": [ + "canvas-element", + "canvas-workpad", + "canvas-workpad-template", + "dashboard", + "graph-workspace", + "index-pattern", + "kql-telemetry", + "lens", + "lens-ui-telemetry", + "map", + "query", + "search", + "search-session", + "search-telemetry", + "visualization" + ] + } + } }, "settings": { "index": { @@ -15,7 +267,9 @@ "number_of_replicas": "0", "number_of_shards": "1", "mapping": { - "total_fields": { "limit": 1500 } + "total_fields": { + "limit": 1500 + } } } } diff --git a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts index b7e698244ee29..8de4482474d8e 100644 --- a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts +++ b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts @@ -6,6 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; export function getUrlPrefix(spaceId?: string) { @@ -39,7 +40,7 @@ export function getTestScenariosForSpace(spaceId: string) { export function getAggregatedSpaceData(es: Client, objectTypes: string[]) { return es.search({ - index: '.kibana', + index: ALL_SAVED_OBJECT_INDICES, body: { size: 0, runtime_mappings: { diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.ts b/x-pack/test/spaces_api_integration/common/suites/delete.ts index a1c73125ede28..b1ad6241e11df 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import { getAggregatedSpaceData, getTestScenariosForSpace } from '../lib/space_test_utils'; import { MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -105,7 +106,7 @@ export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: S // Since Space 2 was deleted, any multi-namespace objects that existed in that space // are updated to remove it, and of those, any that don't exist in any space are deleted. const multiNamespaceResponse = await es.search>({ - index: '.kibana', + index: ALL_SAVED_OBJECT_INDICES, size: 100, body: { query: { terms: { type: ['sharedtype'] } } }, }); diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts index 4719d0e5164a6..002ed6c3e6515 100644 --- a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts +++ b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts @@ -6,12 +6,13 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import type { SuperTest } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; +import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { SPACES } from '../lib/spaces'; import { getUrlPrefix } from '../../../saved_object_api_integration/common/lib/saved_object_test_utils'; -import { +import type { ExpectResponseBody, TestDefinition, TestSuite, @@ -62,7 +63,8 @@ export function disableLegacyUrlAliasesTestSuiteFactory( } const esResponse = await es.get( { - index: '.kibana', + // affected by the .kibana split, assumes LEGACY_URL_ALIAS_TYPE is stored in .kibana + index: MAIN_SAVED_OBJECT_INDEX, id: `${LEGACY_URL_ALIAS_TYPE}:${targetSpace}:${targetType}:${sourceId}`, }, { ignore: [404] } diff --git a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts index 5528ceaa27602..14eb97c38c6ee 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts @@ -14,6 +14,7 @@ import { SavedObjectsErrorHelpers, SavedObjectsUpdateObjectsSpacesResponse, } from '@kbn/core/server'; +import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import { SPACES } from '../lib/spaces'; import { expectResponses, @@ -98,11 +99,11 @@ export function updateObjectsSpacesTestSuiteFactory( if (expectAliasDifference !== undefined) { // if we deleted an object that had an alias pointing to it, the alias should have been deleted as well if (!hasRefreshed) { - await es.indices.refresh({ index: '.kibana' }); // alias deletion uses refresh: false, so we need to manually refresh the index before searching + await es.indices.refresh({ index: ALL_SAVED_OBJECT_INDICES }); // alias deletion uses refresh: false, so we need to manually refresh the index before searching hasRefreshed = true; } const searchResponse = await es.search({ - index: '.kibana', + index: ALL_SAVED_OBJECT_INDICES, body: { size: 0, query: { terms: { type: ['legacy-url-alias'] } }, diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 2e92bd5f38c3d..dc89799511927 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -122,5 +122,7 @@ "@kbn/securitysolution-io-ts-alerting-types", "@kbn/alerting-state-types", "@kbn/assetManager-plugin", + "@kbn/guided-onboarding-plugin", + "@kbn/field-formats-plugin", ] } diff --git a/yarn.lock b/yarn.lock index bd8668fa91fc1..da8427476a383 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3077,6 +3077,10 @@ version "0.0.0" uid "" +"@kbn/content-management-utils@link:packages/kbn-content-management-utils": + version "0.0.0" + uid "" + "@kbn/controls-example-plugin@link:examples/controls_example": version "0.0.0" uid "" @@ -3861,6 +3865,18 @@ version "0.0.0" uid "" +"@kbn/core-user-settings-server-internal@link:packages/core/user-settings/core-user-settings-server-internal": + version "0.0.0" + uid "" + +"@kbn/core-user-settings-server-mocks@link:packages/core/user-settings/core-user-settings-server-mocks": + version "0.0.0" + uid "" + +"@kbn/core-user-settings-server@link:packages/core/user-settings/core-user-settings-server": + version "0.0.0" + uid "" + "@kbn/core@link:src/core": version "0.0.0" uid "" @@ -4545,6 +4561,10 @@ version "0.0.0" uid "" +"@kbn/ml-error-utils@link:x-pack/packages/ml/error_utils": + version "0.0.0" + uid "" + "@kbn/ml-is-defined@link:x-pack/packages/ml/is_defined": version "0.0.0" uid "" @@ -4633,6 +4653,10 @@ version "0.0.0" uid "" +"@kbn/observability-onboarding-plugin@link:x-pack/plugins/observability_onboarding": + version "0.0.0" + uid "" + "@kbn/observability-plugin@link:x-pack/plugins/observability": version "0.0.0" uid "" @@ -5457,6 +5481,10 @@ version "0.0.0" uid "" +"@kbn/url-state@link:packages/kbn-url-state": + version "0.0.0" + uid "" + "@kbn/usage-collection-plugin@link:src/plugins/usage_collection": version "0.0.0" uid "" @@ -10050,10 +10078,10 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.2, ajv@^6.12.4, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.0.1, ajv@^8.11.0, ajv@^8.8.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== +ajv@^8.0.0, ajv@^8.0.1, ajv@^8.12.0, ajv@^8.8.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -10991,11 +11019,16 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== -base64-js@1.3.1, base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.0, base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@1.3.1, base64-js@^1.0.2, base64-js@^1.2.0, base64-js@^1.3.0, base64-js@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== +base64-js@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + base64url@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" @@ -12471,13 +12504,20 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^1.5.1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== + dependencies: + safe-buffer "~5.1.1" + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -15215,11 +15255,16 @@ esrecurse@^4.1.0, esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= +estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" @@ -21943,11 +21988,16 @@ object-identity-map@^1.0.2: dependencies: object.entries "^1.1.0" -object-inspect@^1.11.0, object-inspect@^1.6.0, object-inspect@^1.7.0, object-inspect@^1.9.0: +object-inspect@^1.11.0, object-inspect@^1.7.0, object-inspect@^1.9.0: version "1.11.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== +object-inspect@^1.6.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" @@ -22312,7 +22362,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.3.0: +p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.2, p-limit@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -22373,6 +22423,11 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-reflect@2.1.0, p-reflect@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-reflect/-/p-reflect-2.1.0.tgz#5d67c7b3c577c4e780b9451fc9129675bd99fe67" + integrity sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg== + p-retry@^4.2.0, p-retry@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.5.0.tgz#6685336b3672f9ee8174d3769a660cb5e488521d" @@ -22381,6 +22436,14 @@ p-retry@^4.2.0, p-retry@^4.5.0: "@types/retry" "^0.12.0" retry "^0.12.0" +p-settle@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/p-settle/-/p-settle-4.1.1.tgz#37fbceb2b02c9efc28658fc8d36949922266035f" + integrity sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ== + dependencies: + p-limit "^2.2.2" + p-reflect "^2.1.0" + p-timeout@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" @@ -26373,9 +26436,9 @@ state-toggle@^1.0.0: integrity sha1-0g+aYWu08MO5i5GSLSW2QKorxCU= static-eval@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.5.tgz#f0782e66999c4b3651cda99d9ce59c507d188f71" - integrity sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA== + version "2.1.0" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.1.0.tgz#a16dbe54522d7fa5ef1389129d813fd47b148014" + integrity sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw== dependencies: escodegen "^1.11.1" @@ -29561,10 +29624,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.0.0.tgz#0af50509b33d0dc62fde7a4ec17750b8e453cc5c" - integrity sha512-tmVsKzZovAYNDIaUinfz+VDclraQpPUnAME+JawosgWRMphInDded/PuY0xmU5dOhyeYZsI0nz5yd8dPYsdLTA== +xterm@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0.tgz#3e160d60e6801c864b55adf19171c49d2ff2b4fc" + integrity sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ== y18n@^3.2.0: version "3.2.2" From 270960be30d9dbd5de16dbed7baccd35a316772f Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 26 Apr 2023 10:57:56 -0500 Subject: [PATCH 055/202] translate strings --- src/plugins/event_annotation/.i18nrc.json | 6 ++++++ .../event_annotation_group_editor.tsx | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100755 src/plugins/event_annotation/.i18nrc.json diff --git a/src/plugins/event_annotation/.i18nrc.json b/src/plugins/event_annotation/.i18nrc.json new file mode 100755 index 0000000000000..0be009b9d4364 --- /dev/null +++ b/src/plugins/event_annotation/.i18nrc.json @@ -0,0 +1,6 @@ +{ + "prefix": "eventAnnotations", + "paths": { + "eventAnnotations": "." + } +} diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx index fd87a15880c77..bb81d99155182 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx +++ b/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx @@ -8,6 +8,8 @@ import { EuiFieldText, EuiForm, EuiFormRow, EuiText, EuiTextArea, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import React from 'react'; import { EventAnnotationGroupConfig } from '../../common'; @@ -31,10 +33,16 @@ export const EventAnnotationGroupEditor = ({ margin-bottom: ${euiTheme.size.base}; `} > -

    Details

    +

    + +

    - + - + Date: Wed, 26 Apr 2023 15:14:36 -0500 Subject: [PATCH 056/202] add data view editing to annotation group listing page --- src/plugins/event_annotation/kibana.jsonc | 3 +- .../event_annotation_group_editor.test.tsx | 58 ++++++++++++++++++- .../event_annotation_group_editor.tsx | 36 +++++++++++- .../public/components/table_list.test.tsx | 35 ++++++++++- .../public/components/table_list.tsx | 41 ++++++++++++- .../public/get_table_list.tsx | 3 + src/plugins/event_annotation/public/plugin.ts | 5 ++ src/plugins/event_annotation/tsconfig.json | 3 +- 8 files changed, 174 insertions(+), 10 deletions(-) diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index 97774e6fac54f..01d928293fa09 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -12,7 +12,8 @@ "savedObjectsManagement", "data", "presentationUtil", - "visualizations" + "visualizations", + "dataViews" ], "optionalPlugins": [ "savedObjectsTagging", diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx index 6fa8592919b26..7dd9a64416e8c 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx +++ b/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx @@ -11,19 +11,27 @@ import { EventAnnotationGroupConfig } from '../../common'; import { shallow, ShallowWrapper } from 'enzyme'; import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; -import { EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; +import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; describe('event annotation group editor', () => { const mockTaggingApi = taggingApiMock.create(); + const dataViewId = 'my-index-pattern'; + const adHocDataViewId = 'ad-hoc'; + const adHocDataViewSpec = { + id: adHocDataViewId, + title: 'Ad Hoc Data View', + }; + const group: EventAnnotationGroupConfig = { annotations: [], description: '', tags: [], - indexPatternId: 'my-index-pattern', + indexPatternId: dataViewId, title: 'My group', ignoreGlobalFilters: false, }; + it('reports group updates', () => { const updateMock = jest.fn(); const wrapper = shallow( @@ -31,6 +39,17 @@ describe('event annotation group editor', () => { group={group} update={updateMock} savedObjectsTagging={mockTaggingApi} + dataViewListItems={[ + { + id: dataViewId, + title: 'My Data View', + }, + { + id: adHocDataViewId, + title: 'Ad Hoc Data View', + }, + ]} + adHocDataViewSpec={adHocDataViewSpec} /> ); @@ -56,6 +75,16 @@ describe('event annotation group editor', () => { .find(mockTaggingApi.ui.components.SavedObjectSaveModalTagSelector) .prop('onTagsSelected')(['im a new tag!']); + const setDataViewId = (id: string) => + ( + wrapper.find( + "[data-test-subj='annotationDataViewSelection']" + ) as ShallowWrapper + ).prop('onChange')!({ target: { value: id } } as React.ChangeEvent); + + setDataViewId(adHocDataViewId); + setDataViewId(dataViewId); + expect(updateMock.mock.calls).toMatchInlineSnapshot(` Array [ Array [ @@ -90,6 +119,31 @@ describe('event annotation group editor', () => { "title": "My group", }, ], + Array [ + Object { + "annotations": Array [], + "dataViewSpec": Object { + "id": "ad-hoc", + "title": "Ad Hoc Data View", + }, + "description": "", + "ignoreGlobalFilters": false, + "indexPatternId": "ad-hoc", + "tags": Array [], + "title": "My group", + }, + ], + Array [ + Object { + "annotations": Array [], + "dataViewSpec": undefined, + "description": "", + "ignoreGlobalFilters": false, + "indexPatternId": "my-index-pattern", + "tags": Array [], + "title": "My group", + }, + ], ] `); }); diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx index bb81d99155182..1b3934976e9a3 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx +++ b/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx @@ -6,8 +6,17 @@ * Side Public License, v 1. */ -import { EuiFieldText, EuiForm, EuiFormRow, EuiText, EuiTextArea, useEuiTheme } from '@elastic/eui'; +import { + EuiFieldText, + EuiForm, + EuiFormRow, + EuiSelect, + EuiText, + EuiTextArea, + useEuiTheme, +} from '@elastic/eui'; import { css } from '@emotion/react'; +import type { DataViewListItem, DataViewSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; @@ -18,10 +27,14 @@ export const EventAnnotationGroupEditor = ({ group, update, savedObjectsTagging, + dataViewListItems, + adHocDataViewSpec, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; savedObjectsTagging: SavedObjectsTaggingApi; + dataViewListItems: DataViewListItem[]; + adHocDataViewSpec: DataViewSpec | undefined; }) => { const { euiTheme } = useEuiTheme(); @@ -81,6 +94,27 @@ export const EventAnnotationGroupEditor = ({ } /> + + ({ + value, + text: name ?? title, + }))} + value={group.indexPatternId} + onChange={({ target: { value } }) => + update({ + ...group, + indexPatternId: value, + dataViewSpec: value === adHocDataViewSpec?.id ? adHocDataViewSpec : undefined, + }) + } + /> + ); diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index da8d687f32c7a..f20198b65b0d8 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -24,13 +24,19 @@ import { act } from 'react-dom/test-utils'; import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; describe('annotation list view', () => { + const adHocDVId = 'ad-hoc'; + const group: EventAnnotationGroupConfig = { annotations: [], description: '', tags: [], - indexPatternId: 'my-index-pattern', + indexPatternId: adHocDVId, title: 'My group', ignoreGlobalFilters: false, + dataViewSpec: { + id: adHocDVId, + title: 'Ad hoc data view', + }, }; let wrapper: ShallowWrapper; @@ -66,6 +72,12 @@ describe('annotation list view', () => { parentProps={{ onFetchSuccess: () => {}, }} + dataViewListItems={[ + { + id: 'some-id', + title: 'Some data view', + }, + ]} /> ); }); @@ -73,7 +85,7 @@ describe('annotation list view', () => { it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot(` " - + " `); }); @@ -155,6 +167,7 @@ describe('annotation list view', () => { it('edits existing group', async () => { expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); + const initialBouncerValue = wrapper.find(TableList).prop('refreshListBouncer'); act(() => { wrapper.find(TableList).prop('editItem')!({ id: '1234' } as UserContentCommonSchema); @@ -169,6 +182,23 @@ describe('annotation list view', () => { const updatedGroup = { ...group, tags: ['my-new-tag'] }; + expect(wrapper.find(EventAnnotationGroupEditor).prop('adHocDataViewSpec')).toBe( + group.dataViewSpec + ); + expect(wrapper.find(EventAnnotationGroupEditor).prop('dataViewListItems')) + .toMatchInlineSnapshot(` + Array [ + Object { + "id": "some-id", + "title": "Some data view", + }, + Object { + "id": "ad-hoc", + "title": "Ad hoc data view", + }, + ] + `); + wrapper.find(EventAnnotationGroupEditor).prop('update')(updatedGroup); ( @@ -185,6 +215,7 @@ describe('annotation list view', () => { ); expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); + expect(wrapper.find(TableList).prop('refreshListBouncer')).not.toBe(initialBouncerValue); // (should refresh list) }); }); }); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index 9a0553eecba10..4ec5d86e33f9f 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -25,6 +25,7 @@ import { } from '@elastic/eui'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; +import { DataViewListItem, DataViewSpec } from '@kbn/data-views-plugin/common'; import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; @@ -39,12 +40,14 @@ export const EventAnnotationGroupTableList = ({ visualizeCapabilities, savedObjectsTagging, parentProps, + dataViewListItems: globalDataViewListItems, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; visualizeCapabilities: Record>; savedObjectsTagging: SavedObjectsTaggingApi; parentProps: TableListTabParentProps; + dataViewListItems: DataViewListItem[]; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -77,18 +80,49 @@ export const EventAnnotationGroupTableList = ({ [eventAnnotationService, listingLimit] ); - const [groupToEditInfo, setGroupToEditInfo] = useState<{ + const [adHocDataViewSpec, setAdHocDataViewSpec] = useState(); + + const dataViewListItems = useMemo(() => { + const items = [...globalDataViewListItems]; + if (adHocDataViewSpec) { + const { id, name, title } = adHocDataViewSpec; + const adHocListItem: DataViewListItem = { + id: id!, + title: name ?? title!, + }; + items.push(adHocListItem); + } + return items; + }, [adHocDataViewSpec, globalDataViewListItems]); + + const [groupToEditInfo, _setGroupToEditInfo] = useState<{ group: EventAnnotationGroupConfig; id?: string; }>(); + const setGroupToEditInfo = useCallback( + (newGroupToEditInfo?: { group: EventAnnotationGroupConfig; id?: string }) => { + if (newGroupToEditInfo?.id !== groupToEditInfo?.id) { + // this should only happen once when the group is first loaded for editing + setAdHocDataViewSpec(newGroupToEditInfo?.group.dataViewSpec); + } + _setGroupToEditInfo(newGroupToEditInfo); + }, + [groupToEditInfo?.id] + ); + const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); const flyout = groupToEditInfo ? ( setGroupToEditInfo(undefined)} size={'s'}> -

    Edit annotation group

    +

    + +

    @@ -97,6 +131,8 @@ export const EventAnnotationGroupTableList = ({ group={groupToEditInfo.group} update={(group) => setGroupToEditInfo({ ...groupToEditInfo, group })} savedObjectsTagging={savedObjectsTagging} + dataViewListItems={dataViewListItems} + adHocDataViewSpec={adHocDataViewSpec} /> @@ -140,7 +176,6 @@ export const EventAnnotationGroupTableList = ({ <> refreshListBouncer={refreshListBouncer} - // createItem={createNewGroup} tableCaption={i18n.translate('eventAnnotation.tableList.listTitle', { defaultMessage: 'Annotation Library', })} diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index 23aca27102652..b6b444a1db43e 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -15,6 +15,7 @@ import { } from '@kbn/content-management-table-list'; import { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { DataViewListItem } from '@kbn/data-views-plugin/common'; import { EventAnnotationGroupTableList } from './components/table_list'; import { EventAnnotationServiceType } from './event_annotation_service/types'; @@ -23,6 +24,7 @@ export interface EventAnnotationListingPageServices { savedObjectsTagging: SavedObjectsTaggingApi; eventAnnotationService: EventAnnotationServiceType; PresentationUtilContextProvider: FC; + dataViewListItems: DataViewListItem[]; } export const getTableList = ( @@ -44,6 +46,7 @@ export const getTableList = ( eventAnnotationService={services.eventAnnotationService} visualizeCapabilities={services.core.application.capabilities.visualize} parentProps={parentProps} + dataViewListItems={services.dataViewListItems} /> ); diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index b665fd4a767fb..1d80fd51032d4 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -11,6 +11,7 @@ import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/ import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public/types'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; import { EventAnnotationService } from './event_annotation_service'; @@ -28,6 +29,7 @@ export interface EventAnnotationStartDependencies { data: DataPublicPluginStart; savedObjectsTagging: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; + dataViews: DataViewsPublicPluginStart; } interface SetupDependencies { @@ -66,11 +68,14 @@ export class EventAnnotationPlugin pluginsStart.savedObjectsManagement ).getService(); + const dataViewListItems = await pluginsStart.dataViews.getIdsWithTitle(); + const services: EventAnnotationListingPageServices = { core: coreStart, savedObjectsTagging: pluginsStart.savedObjectsTagging, eventAnnotationService, PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, + dataViewListItems, }; // TODO wire parent props diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 2347d98cf6381..fd96fada180c5 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -25,7 +25,8 @@ "@kbn/saved-objects-tagging-plugin", "@kbn/presentation-util-plugin", "@kbn/content-management-table-list", - "@kbn/visualizations-plugin" + "@kbn/visualizations-plugin", + "@kbn/data-views-plugin" ], "exclude": [ "target/**/*", From 61963eeeb5330325e8626183f4d60639d91207a1 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 28 Apr 2023 12:45:30 -0500 Subject: [PATCH 057/202] layer the group editing architecture --- .../group_editor_flyout.test.tsx.snap | 51 +++++++ ...est.tsx => group_editor_controls.test.tsx} | 34 +++-- ...p_editor.tsx => group_editor_controls.tsx} | 24 +++- .../components/group_editor_flyout.test.tsx | 81 ++++++++++++ .../public/components/group_editor_flyout.tsx | 86 ++++++++++++ .../public/components/table_list.test.tsx | 34 +---- .../public/components/table_list.tsx | 125 +++--------------- 7 files changed, 288 insertions(+), 147 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap rename src/plugins/event_annotation/public/components/{event_annotation_group_editor.test.tsx => group_editor_controls.test.tsx} (89%) rename src/plugins/event_annotation/public/components/{event_annotation_group_editor.tsx => group_editor_controls.tsx} (82%) create mode 100644 src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx create mode 100644 src/plugins/event_annotation/public/components/group_editor_flyout.tsx diff --git a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap new file mode 100644 index 0000000000000..4d30533f4fff0 --- /dev/null +++ b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`group editor flyout renders controls 1`] = ` +Object { + "dataViewListItems": Array [ + Object { + "id": "some-id", + "title": "My Data View", + }, + ], + "group": Object { + "annotations": Array [], + "description": "", + "ignoreGlobalFilters": false, + "indexPatternId": "some-id", + "tags": Array [], + "title": "My group", + }, + "savedObjectsTagging": Object { + "cache": Object { + "getState": [MockFunction], + "getState$": [MockFunction], + }, + "client": Object { + "create": [MockFunction], + "delete": [MockFunction], + "get": [MockFunction], + "getAll": [MockFunction], + "update": [MockFunction], + }, + "ui": Object { + "components": Object { + "SavedObjectSaveModalTagSelector": [MockFunction], + "TagList": [MockFunction], + "TagSelector": [MockFunction], + }, + "convertNameToReference": [MockFunction], + "getSearchBarFilter": [MockFunction], + "getTableColumnDefinition": [MockFunction], + "getTag": [MockFunction], + "getTagIdFromName": [MockFunction], + "getTagIdsFromReferences": [MockFunction], + "getTagList": [MockFunction], + "hasTagDecoration": [MockFunction], + "parseSearchQuery": [MockFunction], + "updateTagsReferences": [MockFunction], + }, + }, + "update": [MockFunction], +} +`; diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx similarity index 89% rename from src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx rename to src/plugins/event_annotation/public/components/group_editor_controls.test.tsx index 7dd9a64416e8c..b72df627c81fb 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_editor.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx @@ -9,7 +9,7 @@ import React, { ChangeEvent, FormEvent } from 'react'; import { EventAnnotationGroupConfig } from '../../common'; import { shallow, ShallowWrapper } from 'enzyme'; -import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; +import { GroupEditorControls } from './group_editor_controls'; import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; @@ -30,12 +30,13 @@ describe('event annotation group editor', () => { indexPatternId: dataViewId, title: 'My group', ignoreGlobalFilters: false, + dataViewSpec: adHocDataViewSpec, }; it('reports group updates', () => { const updateMock = jest.fn(); const wrapper = shallow( - { title: 'Ad Hoc Data View', }, ]} - adHocDataViewSpec={adHocDataViewSpec} /> ); @@ -82,14 +82,18 @@ describe('event annotation group editor', () => { ) as ShallowWrapper ).prop('onChange')!({ target: { value: id } } as React.ChangeEvent); - setDataViewId(adHocDataViewId); setDataViewId(dataViewId); + setDataViewId(adHocDataViewId); expect(updateMock.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { "annotations": Array [], + "dataViewSpec": Object { + "id": "ad-hoc", + "title": "Ad Hoc Data View", + }, "description": "", "ignoreGlobalFilters": false, "indexPatternId": "my-index-pattern", @@ -100,6 +104,10 @@ describe('event annotation group editor', () => { Array [ Object { "annotations": Array [], + "dataViewSpec": Object { + "id": "ad-hoc", + "title": "Ad Hoc Data View", + }, "description": "im a new description!", "ignoreGlobalFilters": false, "indexPatternId": "my-index-pattern", @@ -110,6 +118,10 @@ describe('event annotation group editor', () => { Array [ Object { "annotations": Array [], + "dataViewSpec": Object { + "id": "ad-hoc", + "title": "Ad Hoc Data View", + }, "description": "", "ignoreGlobalFilters": false, "indexPatternId": "my-index-pattern", @@ -122,13 +134,10 @@ describe('event annotation group editor', () => { Array [ Object { "annotations": Array [], - "dataViewSpec": Object { - "id": "ad-hoc", - "title": "Ad Hoc Data View", - }, + "dataViewSpec": undefined, "description": "", "ignoreGlobalFilters": false, - "indexPatternId": "ad-hoc", + "indexPatternId": "my-index-pattern", "tags": Array [], "title": "My group", }, @@ -136,10 +145,13 @@ describe('event annotation group editor', () => { Array [ Object { "annotations": Array [], - "dataViewSpec": undefined, + "dataViewSpec": Object { + "id": "ad-hoc", + "title": "Ad Hoc Data View", + }, "description": "", "ignoreGlobalFilters": false, - "indexPatternId": "my-index-pattern", + "indexPatternId": "ad-hoc", "tags": Array [], "title": "My group", }, diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.tsx similarity index 82% rename from src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx rename to src/plugins/event_annotation/public/components/group_editor_controls.tsx index 1b3934976e9a3..ff835de625840 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_editor.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.tsx @@ -20,24 +20,38 @@ import type { DataViewListItem, DataViewSpec } from '@kbn/data-views-plugin/comm import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import React from 'react'; +import React, { useMemo, useState } from 'react'; import { EventAnnotationGroupConfig } from '../../common'; -export const EventAnnotationGroupEditor = ({ +export const GroupEditorControls = ({ group, update, savedObjectsTagging, - dataViewListItems, - adHocDataViewSpec, + dataViewListItems: globalDataViewListItems, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; savedObjectsTagging: SavedObjectsTaggingApi; dataViewListItems: DataViewListItem[]; - adHocDataViewSpec: DataViewSpec | undefined; }) => { const { euiTheme } = useEuiTheme(); + // save the spec for the life of the component since the user might change their mind after selecting another data view + const [adHocDataViewSpec] = useState(group.dataViewSpec); + + const dataViewListItems = useMemo(() => { + const items = [...globalDataViewListItems]; + if (adHocDataViewSpec) { + const { id, name, title } = adHocDataViewSpec; + const adHocListItem: DataViewListItem = { + id: id!, + title: name ?? title!, + }; + items.push(adHocListItem); + } + return items; + }, [adHocDataViewSpec, globalDataViewListItems]); + return ( <> { + (component.find(selector) as ShallowWrapper[0]>).prop('onClick')!( + {} as any + ); +}; + +describe('group editor flyout', () => { + const group: EventAnnotationGroupConfig = { + annotations: [], + description: '', + tags: [], + indexPatternId: 'some-id', + title: 'My group', + ignoreGlobalFilters: false, + }; + + const mockTaggingApi = taggingApiMock.create(); + + let component: ShallowWrapper; + let onSave: jest.Mock; + let onClose: jest.Mock; + let updateGroup: jest.Mock; + + beforeEach(() => { + onSave = jest.fn(); + onClose = jest.fn(); + updateGroup = jest.fn(); + component = shallow( + + ); + }); + + it('renders controls', () => { + expect(component.find(GroupEditorControls).props()).toMatchSnapshot(); + }); + it('signals close', () => { + component.find(EuiFlyout).prop('onClose')({} as MouseEvent); + simulateButtonClick(component, '[data-test-subj="cancelGroupEdit"]'); + + expect(onClose).toHaveBeenCalledTimes(2); + }); + it('signals save', () => { + simulateButtonClick(component, '[data-test-subj="saveAnnotationGroup"]'); + + expect(onSave).toHaveBeenCalledTimes(1); + }); + it('reports group updates', () => { + const newGroup = { ...group, description: 'new description' }; + component.find(GroupEditorControls).prop('update')(newGroup); + + expect(updateGroup).toHaveBeenCalledWith(newGroup); + }); +}); diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx new file mode 100644 index 0000000000000..487b56d6adbfc --- /dev/null +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -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 { + EuiFlyout, + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiButton, + htmlIdGenerator, +} from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import { EventAnnotationGroupConfig } from '../../common'; +import { GroupEditorControls } from './group_editor_controls'; + +export const GroupEditorFlyout = ({ + group, + updateGroup, + onClose, + onSave, + savedObjectsTagging, + dataViewListItems, +}: { + group: EventAnnotationGroupConfig; + updateGroup: (newGroup: EventAnnotationGroupConfig) => void; + onClose: () => void; + onSave: () => void; + savedObjectsTagging: SavedObjectsTaggingApi; + dataViewListItems: DataViewListItem[]; +}) => { + const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); + + return ( + + + +

    + +

    +
    +
    + + + + + + + + + + + + + + + + + + + +
    + ); +}; diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index f20198b65b0d8..be18ceb25fffd 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -19,9 +19,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EventAnnotationGroupConfig, EVENT_ANNOTATION_GROUP_TYPE } from '../../common'; import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/mocks'; -import { EuiButton, EuiFlyout } from '@elastic/eui'; import { act } from 'react-dom/test-utils'; -import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; +import { GroupEditorFlyout } from './group_editor_flyout'; describe('annotation list view', () => { const adHocDVId = 'ad-hoc'; @@ -166,7 +165,7 @@ describe('annotation list view', () => { }); it('edits existing group', async () => { - expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); + expect(wrapper.find(GroupEditorFlyout).exists()).toBeFalsy(); const initialBouncerValue = wrapper.find(TableList).prop('refreshListBouncer'); act(() => { @@ -178,34 +177,13 @@ describe('annotation list view', () => { expect(mockEventAnnotationService.loadAnnotationGroup).toHaveBeenCalledWith('1234'); - expect(wrapper.find(EuiFlyout).exists()).toBeTruthy(); + expect(wrapper.find(GroupEditorFlyout).exists()).toBeTruthy(); const updatedGroup = { ...group, tags: ['my-new-tag'] }; - expect(wrapper.find(EventAnnotationGroupEditor).prop('adHocDataViewSpec')).toBe( - group.dataViewSpec - ); - expect(wrapper.find(EventAnnotationGroupEditor).prop('dataViewListItems')) - .toMatchInlineSnapshot(` - Array [ - Object { - "id": "some-id", - "title": "Some data view", - }, - Object { - "id": "ad-hoc", - "title": "Ad hoc data view", - }, - ] - `); - - wrapper.find(EventAnnotationGroupEditor).prop('update')(updatedGroup); + wrapper.find(GroupEditorFlyout).prop('updateGroup')(updatedGroup); - ( - wrapper.find('[data-test-subj="saveAnnotationGroup"]') as ShallowWrapper< - Parameters[0] - > - ).prop('onClick')!({} as any); + wrapper.find(GroupEditorFlyout).prop('onSave')(); await new Promise((resolve) => setTimeout(resolve, 0)); @@ -214,7 +192,7 @@ describe('annotation list view', () => { '1234' ); - expect(wrapper.find(EuiFlyout).exists()).toBeFalsy(); + expect(wrapper.find(GroupEditorFlyout).exists()).toBeFalsy(); expect(wrapper.find(TableList).prop('refreshListBouncer')).not.toBe(initialBouncerValue); // (should refresh list) }); }); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index 4ec5d86e33f9f..c2d609979b457 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -6,30 +6,17 @@ * Side Public License, v 1. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { TableList, TableListTabParentProps } from '@kbn/content-management-table-list'; import { i18n } from '@kbn/i18n'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; -import { - EuiButton, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlyoutHeader, - EuiTitle, - htmlIdGenerator, -} from '@elastic/eui'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { DataViewListItem, DataViewSpec } from '@kbn/data-views-plugin/common'; +import { DataViewListItem } from '@kbn/data-views-plugin/common'; import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; -import { EventAnnotationGroupEditor } from './event_annotation_group_editor'; import { EventAnnotationGroupContent } from '../../common/types'; +import { GroupEditorFlyout } from './group_editor_flyout'; export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; export const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; @@ -40,7 +27,7 @@ export const EventAnnotationGroupTableList = ({ visualizeCapabilities, savedObjectsTagging, parentProps, - dataViewListItems: globalDataViewListItems, + dataViewListItems, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; @@ -80,96 +67,28 @@ export const EventAnnotationGroupTableList = ({ [eventAnnotationService, listingLimit] ); - const [adHocDataViewSpec, setAdHocDataViewSpec] = useState(); - - const dataViewListItems = useMemo(() => { - const items = [...globalDataViewListItems]; - if (adHocDataViewSpec) { - const { id, name, title } = adHocDataViewSpec; - const adHocListItem: DataViewListItem = { - id: id!, - title: name ?? title!, - }; - items.push(adHocListItem); - } - return items; - }, [adHocDataViewSpec, globalDataViewListItems]); - - const [groupToEditInfo, _setGroupToEditInfo] = useState<{ + const [groupToEditInfo, setGroupToEditInfo] = useState<{ group: EventAnnotationGroupConfig; - id?: string; + id: string; }>(); - const setGroupToEditInfo = useCallback( - (newGroupToEditInfo?: { group: EventAnnotationGroupConfig; id?: string }) => { - if (newGroupToEditInfo?.id !== groupToEditInfo?.id) { - // this should only happen once when the group is first loaded for editing - setAdHocDataViewSpec(newGroupToEditInfo?.group.dataViewSpec); - } - _setGroupToEditInfo(newGroupToEditInfo); - }, - [groupToEditInfo?.id] - ); - - const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); - const flyout = groupToEditInfo ? ( - setGroupToEditInfo(undefined)} size={'s'}> - - -

    - -

    -
    -
    - - - setGroupToEditInfo({ ...groupToEditInfo, group })} - savedObjectsTagging={savedObjectsTagging} - dataViewListItems={dataViewListItems} - adHocDataViewSpec={adHocDataViewSpec} - /> - - - - - - setGroupToEditInfo(undefined)}> - - - - - - (groupToEditInfo.id - ? eventAnnotationService.updateAnnotationGroup( - groupToEditInfo.group, - groupToEditInfo.id - ) - : eventAnnotationService.createAnnotationGroup(groupToEditInfo.group) - ).then(() => { - setGroupToEditInfo(undefined); - refreshList(); - }) - } - > - - - - - -
    + setGroupToEditInfo({ group: newGroup, id: groupToEditInfo.id })} + onClose={() => setGroupToEditInfo(undefined)} + onSave={() => + (groupToEditInfo.id + ? eventAnnotationService.updateAnnotationGroup(groupToEditInfo.group, groupToEditInfo.id) + : eventAnnotationService.createAnnotationGroup(groupToEditInfo.group) + ).then(() => { + setGroupToEditInfo(undefined); + refreshList(); + }) + } + savedObjectsTagging={savedObjectsTagging} + dataViewListItems={dataViewListItems} + /> ) : undefined; return ( From de55ebd37ac5851f18a77b4ec5dbe1a1602e7406 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 28 Apr 2023 22:09:23 -0500 Subject: [PATCH 058/202] Partial annotation appearance editing --- .../common/fetch_event_annotations/utils.ts | 2 +- src/plugins/event_annotation/common/types.ts | 2 +- src/plugins/event_annotation/kibana.jsonc | 3 +- .../annotations_panel.tsx | 357 ++++++++++++++++++ .../annotations_config_panel/helpers.ts | 129 +++++++ .../annotations_config_panel/icon_set.ts | 107 ++++++ .../annotations_config_panel/index.scss | 19 + .../annotations_config_panel/index.tsx | 9 + .../manual_annotation_panel.tsx | 121 ++++++ .../query_annotation_panel.tsx | 115 ++++++ .../range_annotation_panel.tsx | 145 +++++++ .../tooltip_annotation_panel.tsx | 208 ++++++++++ .../annotations_config_panel/types.ts | 13 + .../components/group_editor_controls.tsx | 28 +- .../public/components/group_editor_flyout.tsx | 8 +- src/plugins/event_annotation/tsconfig.json | 4 +- .../common/types.ts | 9 + .../public/components/index.ts | 2 + .../components}/line_style_settings.tsx | 15 +- .../public/index.ts | 1 + .../visualization_ui_components/tsconfig.json | 2 +- .../annotations_panel.tsx | 7 +- .../reference_line_panel.tsx | 8 +- 23 files changed, 1287 insertions(+), 27 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/helpers.ts create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/icon_set.ts create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/index.scss create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/index.tsx create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/query_annotation_panel.tsx create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/tooltip_annotation_panel.tsx create mode 100644 src/plugins/event_annotation/public/components/annotations_config_panel/types.ts create mode 100644 src/plugins/visualization_ui_components/common/types.ts rename {x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared => src/plugins/visualization_ui_components/public/components}/line_style_settings.tsx (93%) diff --git a/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts b/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts index 7af00db63160e..4db89229665d5 100644 --- a/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts +++ b/src/plugins/event_annotation/common/fetch_event_annotations/utils.ts @@ -12,6 +12,7 @@ import { omit, pick } from 'lodash'; import dateMath from '@kbn/datemath'; import moment from 'moment'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { LineStyle } from '@kbn/visualization-ui-components/common/types'; import { ManualEventAnnotationOutput, ManualPointEventAnnotationOutput, @@ -22,7 +23,6 @@ import { annotationColumns, AvailableAnnotationIcon, EventAnnotationOutput, - LineStyle, PointStyleProps, } from '../types'; diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 502b99c5c21cc..a3af72ea8c120 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -10,6 +10,7 @@ import type { UserContentCommonSchema } from '@kbn/content-management-table-list import { DataViewSpec, KibanaQueryOutput } from '@kbn/data-plugin/common'; import { DatatableColumn } from '@kbn/expressions-plugin/common'; import { $Values } from '@kbn/utility-types'; +import { LineStyle } from '@kbn/visualization-ui-components/common/types'; import { AvailableAnnotationIcons } from './constants'; import { ManualEventAnnotationOutput, @@ -21,7 +22,6 @@ import { QueryPointEventAnnotationOutput, } from './query_point_event_annotation/types'; -export type LineStyle = 'solid' | 'dashed' | 'dotted'; export type Fill = 'inside' | 'outside' | 'none'; export type ManualAnnotationType = 'manual'; export type QueryAnnotationType = 'query'; diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index 01d928293fa09..f1c2db4224dcb 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -21,7 +21,8 @@ "requiredBundles": [ "savedObjectsFinder", "dataViews", - "kibanaReact" + "kibanaReact", + "visualizationUiComponents", ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx new file mode 100644 index 0000000000000..9ae347a1c9081 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx @@ -0,0 +1,357 @@ +/* + * 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 './index.scss'; +import React, { useCallback, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiButtonGroup } from '@elastic/eui'; +import { + IconSelectSetting, + DimensionEditorSection, + NameInput, + ColorPicker, + LineStyleSettings, +} from '@kbn/visualization-ui-components/public'; +import { isQueryAnnotationConfig, isRangeAnnotationConfig } from '../..'; +import { AvailableAnnotationIcon, EventAnnotationConfig } from '../../../common'; +// import { isHorizontalChart } from '../../state_helpers'; +import { + defaultAnnotationColor, + defaultAnnotationLabel, + defaultAnnotationRangeColor, +} from './helpers'; +import { annotationsIconSet } from './icon_set'; +import { sanitizeProperties } from './helpers'; + +export const AnnotationsPanel = ({ + annotation: currentAnnotation, + onAnnotationChange, +}: { + annotation: EventAnnotationConfig; + onAnnotationChange: (annotation: EventAnnotationConfig) => void; + // dataView: DataView; + // datatableUtilities: DatatableUtilitiesService; + // formatFactory: FormatFactory; + // paletteService: PaletteRegistry; +}) => { + // const { hasFieldData } = useExistingFieldsReader(); + + const isQueryBased = isQueryAnnotationConfig(currentAnnotation); + const isRange = isRangeAnnotationConfig(currentAnnotation); + + const [queryInputShouldOpen, setQueryInputShouldOpen] = React.useState(false); + useEffect(() => { + setQueryInputShouldOpen(!isQueryBased); + }, [isQueryBased]); + + const update = useCallback( + (newAnnotation: Partial | undefined) => + newAnnotation && + onAnnotationChange(sanitizeProperties({ ...currentAnnotation, ...newAnnotation })), + [currentAnnotation, onAnnotationChange] + ); + + return ( + <> + {/* + + { + const typeFromId = id.replace( + 'lens_xyChart_annotation_', + '' + ) as EventAnnotationConfig['type']; + if (currentAnnotation?.type === typeFromId) { + return; + } + if (typeFromId === 'query') { + // If coming from a range type, it requires some additional resets + const additionalRangeResets = isRangeAnnotationConfig(currentAnnotation) + ? { + label: + currentAnnotation.label === defaultRangeAnnotationLabel + ? defaultAnnotationLabel + : currentAnnotation.label, + color: toLineAnnotationColor(currentAnnotation.color), + } + : {}; + return update({ + type: typeFromId, + timeField: + (dataView.timeFieldName || + // fallback to the first avaiable date field in the dataView + dataView.fields.find(({ type: fieldType }) => fieldType === 'date') + ?.displayName) ?? + '', + key: { type: 'point_in_time' }, + ...additionalRangeResets, + }); + } + // From query to manual annotation + return update({ + type: typeFromId, + key: { type: 'point_in_time', timestamp: moment().toISOString() }, + }); + }} + isFullWidth + /> + + {isQueryBased ? ( + + ) : ( + + )} + */} + + { + update({ label: value }); + }} + /> + {!isRange && ( + <> + + currentIcon={currentAnnotation.icon} + setIcon={(icon) => update({ icon })} + defaultIcon="triangle" + customIconSet={annotationsIconSet} + /> + {/* + {(textDecorationSelected) => { + if (textDecorationSelected !== 'field') { + return null; + } + const options = dataView.fields + .filter(({ displayName, type }) => displayName && type !== 'document') + .map( + (field) => + ({ + label: field.displayName, + value: { + type: 'field', + field: field.name, + dataType: field.type, + }, + exists: hasFieldData(dataView.id!, field.name), + compatible: true, + 'data-test-subj': `lnsXY-annotation-fieldOption-${field.name}`, + } as FieldOption) + ); + const selectedField = (currentAnnotation as QueryPointEventAnnotationConfig) + .textField; + + const fieldIsValid = selectedField + ? Boolean(dataView.getFieldByName(selectedField)) + : true; + return ( + <> + + + + ); + }} + */} + + + )} + {isRange && ( + + { + update({ + outside: id === `lens_xyChart_fillStyle_outside`, + }); + }} + isFullWidth + /> + + )} + + + update({ isHidden: ev.target.checked })} + /> + {/* {isQueryBased && currentAnnotation && ( + + + + + */} + + + ); +}; + +const ConfigPanelGenericSwitch = ({ + label, + ['data-test-subj']: dataTestSubj, + value, + onChange, +}: { + label: string; + 'data-test-subj': string; + value: boolean; + onChange: (event: EuiSwitchEvent) => void; +}) => ( + + + +); diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/helpers.ts b/src/plugins/event_annotation/public/components/annotations_config_panel/helpers.ts new file mode 100644 index 0000000000000..98590766b32f9 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/helpers.ts @@ -0,0 +1,129 @@ +/* + * 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 { search } from '@kbn/data-plugin/public'; +import { transparentize } from '@elastic/eui'; +// import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; +import Color from 'color'; +import { pick } from 'lodash'; +// import moment from 'moment'; +import { euiLightVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; +import { isQueryAnnotationConfig, isRangeAnnotationConfig } from '../..'; +import type { + EventAnnotationConfig, + RangeEventAnnotationConfig, + PointInTimeEventAnnotationConfig, + QueryPointEventAnnotationConfig, +} from '../../../common'; +// import type { FramePublicAPI } from '../../../../types'; +// import type { XYDataLayerConfig } from '../../types'; + +export const defaultAnnotationColor = euiLightVars.euiColorAccent; +// Do not compute it live as dependencies will add tens of Kbs to the plugin +export const defaultAnnotationRangeColor = `#F04E981A`; // defaultAnnotationColor with opacity 0.1 + +export const defaultAnnotationLabel = i18n.translate('xpack.lens.xyChart.defaultAnnotationLabel', { + defaultMessage: 'Event', +}); + +export const defaultRangeAnnotationLabel = i18n.translate( + 'xpack.lens.xyChart.defaultRangeAnnotationLabel', + { + defaultMessage: 'Event range', + } +); + +export const toRangeAnnotationColor = (color = defaultAnnotationColor) => { + return new Color(transparentize(color, 0.1)).hexa(); +}; + +export const toLineAnnotationColor = (color = defaultAnnotationRangeColor) => { + return new Color(transparentize(color, 1)).hex(); +}; + +// export const getEndTimestamp = ( +// datatableUtilities: DatatableUtilitiesService, +// startTime: string, +// { activeData, dateRange }: FramePublicAPI, +// dataLayers: XYDataLayerConfig[] +// ) => { +// const startTimeNumber = moment(startTime).valueOf(); +// const dateRangeFraction = +// (moment(dateRange.toDate).valueOf() - moment(dateRange.fromDate).valueOf()) * 0.1; +// const fallbackValue = moment(startTimeNumber + dateRangeFraction).toISOString(); +// const dataLayersId = dataLayers.map(({ layerId }) => layerId); +// if ( +// !dataLayersId.length || +// !activeData || +// Object.entries(activeData) +// .filter(([key]) => dataLayersId.includes(key)) +// .every(([, { rows }]) => !rows || !rows.length) +// ) { +// return fallbackValue; +// } +// const xColumn = activeData?.[dataLayersId[0]].columns.find( +// (column) => column.id === dataLayers[0].xAccessor +// ); +// if (!xColumn) { +// return fallbackValue; +// } + +// const dateInterval = datatableUtilities.getDateHistogramMeta(xColumn)?.interval; +// if (!dateInterval) return fallbackValue; +// const intervalDuration = search.aggs.parseInterval(dateInterval); +// if (!intervalDuration) return fallbackValue; +// return moment(startTimeNumber + 3 * intervalDuration.as('milliseconds')).toISOString(); +// }; + +export const sanitizeProperties = (annotation: EventAnnotationConfig) => { + if (isRangeAnnotationConfig(annotation)) { + const rangeAnnotation: RangeEventAnnotationConfig = pick(annotation, [ + 'type', + 'label', + 'key', + 'id', + 'isHidden', + 'color', + 'outside', + ]); + return rangeAnnotation; + } + if (isQueryAnnotationConfig(annotation)) { + const lineAnnotation: QueryPointEventAnnotationConfig = pick(annotation, [ + 'type', + 'id', + 'label', + 'key', + 'timeField', + 'isHidden', + 'lineStyle', + 'lineWidth', + 'color', + 'icon', + 'textVisibility', + 'textField', + 'filter', + 'extraFields', + ]); + return lineAnnotation; + } + const lineAnnotation: PointInTimeEventAnnotationConfig = pick(annotation, [ + 'type', + 'id', + 'label', + 'key', + 'isHidden', + 'lineStyle', + 'lineWidth', + 'color', + 'icon', + 'textVisibility', + ]); + return lineAnnotation; +}; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/icon_set.ts b/src/plugins/event_annotation/public/components/annotations_config_panel/icon_set.ts new file mode 100644 index 0000000000000..c50d7c0418f24 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/icon_set.ts @@ -0,0 +1,107 @@ +/* + * 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 { IconTriangle, IconCircle } from '@kbn/chart-icons'; +import type { IconSet } from '@kbn/visualization-ui-components/public'; +import { AvailableAnnotationIcon } from '../../../common'; + +export const annotationsIconSet: IconSet = [ + { + value: 'asterisk', + label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: 'alert', + label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: 'bell', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: 'bolt', + label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: 'bug', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: 'circle', + label: i18n.translate('xpack.lens.xyChart.iconSelect.circleIconLabel', { + defaultMessage: 'Circle', + }), + icon: IconCircle, + canFill: true, + }, + + { + value: 'editorComment', + label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: 'flag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: 'heart', + label: i18n.translate('xpack.lens.xyChart.iconSelect.heartLabel', { defaultMessage: 'Heart' }), + }, + { + value: 'mapMarker', + label: i18n.translate('xpack.lens.xyChart.iconSelect.mapMarkerLabel', { + defaultMessage: 'Map Marker', + }), + }, + { + value: 'pinFilled', + label: i18n.translate('xpack.lens.xyChart.iconSelect.mapPinLabel', { + defaultMessage: 'Map Pin', + }), + }, + { + value: 'starEmpty', + label: i18n.translate('xpack.lens.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), + }, + { + value: 'starFilled', + label: i18n.translate('xpack.lens.xyChart.iconSelect.starFilledLabel', { + defaultMessage: 'Star filled', + }), + }, + { + value: 'tag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, + { + value: 'triangle', + label: i18n.translate('xpack.lens.xyChart.iconSelect.triangleIconLabel', { + defaultMessage: 'Triangle', + }), + icon: IconTriangle, + shouldRotate: true, + canFill: true, + }, +]; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/index.scss b/src/plugins/event_annotation/public/components/annotations_config_panel/index.scss new file mode 100644 index 0000000000000..93bf0e2c72662 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/index.scss @@ -0,0 +1,19 @@ +.lnsRowCompressedMargin+.lnsRowCompressedMargin { + margin-top: $euiSizeS; +} + +.lnsConfigPanelNoPadding { + padding: 0; +} + +.lnsConfigPanelDate__label { + min-width: 56px; // makes both labels ("from" and "to") the same width +} + +.lnsConfigPanelAnnotations__addButton { + margin-top: $euiSizeXS; +} + +.lnsConfigPanelAnnotations__fieldPicker { + cursor: pointer; +} diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/index.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/index.tsx new file mode 100644 index 0000000000000..4dd51d46b0cfa --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/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 { AnnotationsPanel } from './annotations_panel'; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx new file mode 100644 index 0000000000000..76e89c498675c --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.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 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 type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import React from 'react'; +import { isRangeAnnotationConfig } from '../..'; +import { ConfigPanelRangeDatePicker } from './range_annotation_panel'; +import type { ManualEventAnnotationType } from './types'; + +export const ConfigPanelManualAnnotation = ({ + annotation, + onChange, +}: { + annotation?: ManualEventAnnotationType | undefined; + onChange: (annotation: Partial | undefined) => void; +}) => { + const isRange = isRangeAnnotationConfig(annotation); + return ( + <> + {isRange ? ( + <> + { + if (date) { + const currentEndTime = moment(annotation?.key.endTimestamp).valueOf(); + if (currentEndTime < date.valueOf()) { + const currentStartTime = moment(annotation?.key.timestamp).valueOf(); + const dif = currentEndTime - currentStartTime; + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + timestamp: date.toISOString(), + endTimestamp: moment(date.valueOf() + dif).toISOString(), + }, + }); + } else { + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + timestamp: date.toISOString(), + }, + }); + } + } + }} + label={i18n.translate('xpack.lens.xyChart.annotationDate', { + defaultMessage: 'Annotation date', + })} + /> + { + if (date) { + const currentStartTime = moment(annotation?.key.timestamp).valueOf(); + if (currentStartTime > date.valueOf()) { + const currentEndTime = moment(annotation?.key.endTimestamp).valueOf(); + const dif = currentEndTime - currentStartTime; + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + endTimestamp: date.toISOString(), + timestamp: moment(date.valueOf() - dif).toISOString(), + }, + }); + } else { + onChange({ + key: { + ...(annotation?.key || { type: 'range' }), + endTimestamp: date.toISOString(), + }, + }); + } + } + }} + /> + + ) : ( + { + if (date) { + onChange({ + key: { + ...(annotation?.key || { type: 'point_in_time' }), + timestamp: date.toISOString(), + }, + }); + } + }} + /> + )} + {/* */} + + ); +}; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/query_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/query_annotation_panel.tsx new file mode 100644 index 0000000000000..e3327040881d3 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/query_annotation_panel.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 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 { EuiFormRow } from '@elastic/eui'; +import type { Query } from '@kbn/data-plugin/common'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; +import { + FieldOption, + FieldOptionValue, + FieldPicker, + FilterQueryInput, +} from '@kbn/visualization-ui-components/public'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { QueryPointEventAnnotationConfig } from '../../../common'; + +export const defaultQuery: Query = { + query: '', + language: 'kuery', +}; + +export const ConfigPanelQueryAnnotation = ({ + annotation, + dataView, + onChange, + queryInputShouldOpen, +}: { + annotation?: QueryPointEventAnnotationConfig; + onChange: (annotations: Partial | undefined) => void; + dataView: DataView; + queryInputShouldOpen?: boolean; +}) => { + const { hasFieldData } = useExistingFieldsReader(); + // list only date fields + const options = dataView.fields + .filter((field) => field.type === 'date' && field.displayName) + .map((field) => { + return { + label: field.displayName, + value: { + type: 'field', + field: field.name, + dataType: field.type, + }, + exists: dataView.id ? hasFieldData(dataView.id, field.name) : false, + compatible: true, + 'data-test-subj': `lns-fieldOption-${field.name}`, + } as FieldOption; + }); + + const selectedField = annotation?.timeField || dataView.timeFieldName || options[0]?.value.field; + const fieldIsValid = selectedField ? Boolean(dataView.getFieldByName(selectedField)) : true; + + return ( + <> + + { + onChange({ filter: { type: 'kibana_query', ...query } }); + }} + dataView={dataView} + appName="TODO" + queryInputServices={{}} + /> + + + + + + + ); +}; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx new file mode 100644 index 0000000000000..c59d404e5f50d --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx @@ -0,0 +1,145 @@ +/* + * 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 type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { + EuiFormRow, + EuiSwitch, + EuiText, + EuiFormControlLayout, + EuiFormLabel, + EuiDatePicker, +} from '@elastic/eui'; +import moment from 'moment'; +import { isRangeAnnotationConfig } from '../..'; +import type { PointInTimeEventAnnotationConfig, RangeEventAnnotationConfig } from '../../../common'; +// import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; +import { defaultRangeAnnotationLabel, defaultAnnotationLabel } from './helpers'; +import { toLineAnnotationColor, toRangeAnnotationColor } from './helpers'; +import type { ManualEventAnnotationType } from './types'; + +export const ConfigPanelApplyAsRangeSwitch = ({ + annotation, + datatableUtilities, + onChange, +}: { + annotation?: ManualEventAnnotationType; + datatableUtilities: DatatableUtilitiesService; + onChange: (annotations: Partial | undefined) => void; +}) => { + const isRange = isRangeAnnotationConfig(annotation); + return ( + + + {i18n.translate('xpack.lens.xyChart.applyAsRange', { + defaultMessage: 'Apply as range', + })} +
    + } + checked={isRange} + onChange={() => { + if (isRange) { + const newPointAnnotation: PointInTimeEventAnnotationConfig = { + type: 'manual', + key: { + type: 'point_in_time', + timestamp: annotation.key.timestamp, + }, + id: annotation.id, + label: + annotation.label === defaultRangeAnnotationLabel + ? defaultAnnotationLabel + : annotation.label, + color: toLineAnnotationColor(annotation.color), + isHidden: annotation.isHidden, + }; + onChange(newPointAnnotation); + } else if (annotation) { + const fromTimestamp = moment(annotation?.key.timestamp); + const newRangeAnnotation: RangeEventAnnotationConfig = { + type: 'manual', + key: { + type: 'range', + timestamp: annotation.key.timestamp, + endTimestamp: '', + // endTimestamp: getEndTimestamp( + // datatableUtilities, + // fromTimestamp.toISOString(), + // frame, + // dataLayers + // ), + }, + id: annotation.id, + label: + annotation.label === defaultAnnotationLabel + ? defaultRangeAnnotationLabel + : annotation.label, + color: toRangeAnnotationColor(annotation.color), + isHidden: annotation.isHidden, + }; + onChange(newRangeAnnotation); + } + }} + compressed + /> + + ); +}; + +export const ConfigPanelRangeDatePicker = ({ + value, + label, + prependLabel, + onChange, + dataTestSubj = 'lnsXY_annotation_date_picker', +}: { + value: moment.Moment; + prependLabel?: string; + label?: string; + onChange: (val: moment.Moment | null) => void; + dataTestSubj?: string; +}) => { + return ( + + {prependLabel ? ( + {prependLabel} + } + > + + + ) : ( + + )} + + ); +}; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/tooltip_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/tooltip_annotation_panel.tsx new file mode 100644 index 0000000000000..2770d730085f1 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/tooltip_annotation_panel.tsx @@ -0,0 +1,208 @@ +/* + * 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 { htmlIdGenerator, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useMemo } from 'react'; +import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; +import { + FieldOption, + FieldOptionValue, + FieldPicker, + useDebouncedValue, + NewBucketButton, + DragDropBuckets, + DraggableBucketContainer, + FieldsBucketContainer, +} from '@kbn/visualization-ui-components/public'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { QueryPointEventAnnotationConfig } from '../../../common'; + +export const MAX_TOOLTIP_FIELDS_SIZE = 2; + +const generateId = htmlIdGenerator(); +const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); + +export interface FieldInputsProps { + currentConfig: QueryPointEventAnnotationConfig; + setConfig: (config: QueryPointEventAnnotationConfig) => void; + dataView: DataView; + invalidFields?: string[]; +} + +interface WrappedValue { + id: string; + value: string | undefined; + isNew?: boolean; +} + +type SafeWrappedValue = Omit & { value: string }; + +function removeNewEmptyField(v: WrappedValue): v is SafeWrappedValue { + return v.value != null; +} + +export function TooltipSection({ + currentConfig, + setConfig, + dataView, + invalidFields, +}: FieldInputsProps) { + const { hasFieldData } = useExistingFieldsReader(); + const onChangeWrapped = useCallback( + (values: WrappedValue[]) => { + setConfig({ + ...currentConfig, + extraFields: values.filter(removeNewEmptyField).map(({ value }) => value), + }); + }, + [setConfig, currentConfig] + ); + const { wrappedValues, rawValuesLookup } = useMemo(() => { + const rawValues = currentConfig.extraFields ?? []; + return { + wrappedValues: rawValues.map((value) => ({ id: generateId(), value })), + rawValuesLookup: new Set(rawValues), + }; + }, [currentConfig]); + + const { inputValue: localValues, handleInputChange } = useDebouncedValue({ + onChange: onChangeWrapped, + value: wrappedValues, + }); + + const onFieldSelectChange = useCallback( + (choice, index = 0) => { + const fields = [...localValues]; + + if (dataView.getFieldByName(choice.field)) { + fields[index] = { id: generateId(), value: choice.field }; + + // update the layer state + handleInputChange(fields); + } + }, + [localValues, dataView, handleInputChange] + ); + + const newBucketButton = ( + { + handleInputChange([...localValues, { id: generateId(), value: undefined, isNew: true }]); + }} + label={i18n.translate('xpack.lens.xyChart.annotation.tooltip.addField', { + defaultMessage: 'Add field', + })} + isDisabled={localValues.length > MAX_TOOLTIP_FIELDS_SIZE} + /> + ); + + if (localValues.length === 0) { + return ( + <> + + + + {i18n.translate('xpack.lens.xyChart.annotation.tooltip.noFields', { + defaultMessage: 'None selected', + })} + + + + {newBucketButton} + + ); + } + + const options = dataView.fields + .filter( + ({ displayName, type }) => + displayName && !rawValuesLookup.has(displayName) && supportedTypes.has(type) + ) + .map( + (field) => + ({ + label: field.displayName, + value: { + type: 'field', + field: field.name, + dataType: field.type, + }, + exists: dataView.id ? hasFieldData(dataView.id, field.name) : false, // TODO check this logic + compatible: true, + 'data-test-subj': `lnsXY-annotation-tooltip-fieldOption-${field.name}`, + } as FieldOption) + ) + .sort((a, b) => a.label.localeCompare(b.label)); + + return ( + <> + { + handleInputChange(updatedValues); + }} + droppableId="ANNOTATION_TOOLTIP_DROPPABLE_AREA" + items={localValues} + bgColor="subdued" + > + {localValues.map(({ id, value, isNew }, index, arrayRef) => { + const fieldIsValid = value ? Boolean(dataView.getFieldByName(value)) : true; + + return ( + { + handleInputChange(arrayRef.filter((_, i) => i !== index)); + }} + removeTitle={i18n.translate( + 'xpack.lens.xyChart.annotation.tooltip.deleteButtonLabel', + { + defaultMessage: 'Delete', + } + )} + isNotDraggable={arrayRef.length < 2} + Container={FieldsBucketContainer} + isInsidePanel={true} + data-test-subj={`lnsXY-annotation-tooltip-${index}`} + > + { + onFieldSelectChange(choice, index); + }} + fieldIsInvalid={!fieldIsValid} + className="lnsConfigPanelAnnotations__fieldPicker" + data-test-subj={`lnsXY-annotation-tooltip-field-picker--${index}`} + autoFocus={isNew && value == null} + /> + + ); + })} + + {newBucketButton} + + ); +} diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/types.ts b/src/plugins/event_annotation/public/components/annotations_config_panel/types.ts new file mode 100644 index 0000000000000..094543e4f71d8 --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/types.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 type { PointInTimeEventAnnotationConfig, RangeEventAnnotationConfig } from '../../../common'; + +export type ManualEventAnnotationType = + | PointInTimeEventAnnotationConfig + | RangeEventAnnotationConfig; diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.tsx index ff835de625840..97d7c5c37c591 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.tsx @@ -7,10 +7,12 @@ */ import { + EuiButton, EuiFieldText, EuiForm, EuiFormRow, EuiSelect, + EuiSpacer, EuiText, EuiTextArea, useEuiTheme, @@ -21,16 +23,21 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import React, { useMemo, useState } from 'react'; -import { EventAnnotationGroupConfig } from '../../common'; +import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; +import { AnnotationsPanel } from './annotations_config_panel'; export const GroupEditorControls = ({ group, update, + setSelectedAnnotation, + selectedAnnotation, savedObjectsTagging, dataViewListItems: globalDataViewListItems, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; + selectedAnnotation: EventAnnotationConfig | undefined; + setSelectedAnnotation: (annotation: EventAnnotationConfig) => void; savedObjectsTagging: SavedObjectsTaggingApi; dataViewListItems: DataViewListItem[]; }) => { @@ -52,7 +59,7 @@ export const GroupEditorControls = ({ return items; }, [adHocDataViewSpec, globalDataViewListItems]); - return ( + return !selectedAnnotation ? ( <> + +
    + {group.annotations.map((annotation) => ( + <> + setSelectedAnnotation(annotation)}> + {annotation.label} + + + + ))} +
    +
    + ) : ( + setSelectedAnnotation({ ...selectedAnnotation, ...changes })} + /> ); }; diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index 487b56d6adbfc..ccaff9f42aea9 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -18,11 +18,11 @@ import { EuiButton, htmlIdGenerator, } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; -import { EventAnnotationGroupConfig } from '../../common'; +import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; import { GroupEditorControls } from './group_editor_controls'; export const GroupEditorFlyout = ({ @@ -42,6 +42,8 @@ export const GroupEditorFlyout = ({ }) => { const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); + const [selectedAnnotation, setSelectedAnnotation] = useState(); + return ( @@ -59,6 +61,8 @@ export const GroupEditorFlyout = ({ diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index fd96fada180c5..0b926feb1f543 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -26,7 +26,9 @@ "@kbn/presentation-util-plugin", "@kbn/content-management-table-list", "@kbn/visualizations-plugin", - "@kbn/data-views-plugin" + "@kbn/data-views-plugin", + "@kbn/visualization-ui-components", + "@kbn/chart-icons" ], "exclude": [ "target/**/*", diff --git a/src/plugins/visualization_ui_components/common/types.ts b/src/plugins/visualization_ui_components/common/types.ts new file mode 100644 index 0000000000000..7da9f13aced8d --- /dev/null +++ b/src/plugins/visualization_ui_components/common/types.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 type LineStyle = 'solid' | 'dashed' | 'dotted'; diff --git a/src/plugins/visualization_ui_components/public/components/index.ts b/src/plugins/visualization_ui_components/public/components/index.ts index 4de1805bc472a..b2af81f00f117 100644 --- a/src/plugins/visualization_ui_components/public/components/index.ts +++ b/src/plugins/visualization_ui_components/public/components/index.ts @@ -28,6 +28,8 @@ export * from './dimension_editor_section'; export * from './dimension_buttons'; +export * from './line_style_settings'; + export type { AccessorConfig } from './dimension_buttons'; export type { FieldOptionValue, FieldOption, DataType } from './field_picker'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/line_style_settings.tsx b/src/plugins/visualization_ui_components/public/components/line_style_settings.tsx similarity index 93% rename from x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/line_style_settings.tsx rename to src/plugins/visualization_ui_components/public/components/line_style_settings.tsx index a479daeb75919..3df2837971f9f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/line_style_settings.tsx +++ b/src/plugins/visualization_ui_components/public/components/line_style_settings.tsx @@ -1,8 +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. + * 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'; @@ -14,23 +15,21 @@ import { EuiFlexItem, EuiFormRow, } from '@elastic/eui'; -import { LineStyle } from '@kbn/expression-xy-plugin/common'; - -import { idPrefix } from '../dimension_editor'; +import { LineStyle } from '../../common/types'; interface LineStyleConfig { - lineStyle?: Exclude; + lineStyle?: LineStyle; lineWidth?: number; } export const LineStyleSettings = ({ currentConfig, setConfig, - isHorizontal, + idPrefix, }: { currentConfig?: LineStyleConfig; setConfig: (config: LineStyleConfig) => void; - isHorizontal: boolean; + idPrefix: string; }) => { return ( <> diff --git a/src/plugins/visualization_ui_components/public/index.ts b/src/plugins/visualization_ui_components/public/index.ts index 63a283ccf1dbe..93436a2b856c0 100644 --- a/src/plugins/visualization_ui_components/public/index.ts +++ b/src/plugins/visualization_ui_components/public/index.ts @@ -31,6 +31,7 @@ export { isQueryValid, DimensionEditorSection, DimensionButton, + LineStyleSettings, } from './components'; export type { diff --git a/src/plugins/visualization_ui_components/tsconfig.json b/src/plugins/visualization_ui_components/tsconfig.json index 7a5784a9c6310..f0c45472f7d6b 100644 --- a/src/plugins/visualization_ui_components/tsconfig.json +++ b/src/plugins/visualization_ui_components/tsconfig.json @@ -23,7 +23,7 @@ "@kbn/core-notifications-browser", "@kbn/core-doc-links-browser", "@kbn/core", - "@kbn/ui-theme" + "@kbn/ui-theme", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index 9cb5f4d64079e..4de2f3154a3f0 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -33,12 +33,11 @@ import { useDebouncedValue, DimensionEditorSection, ColorPicker, + LineStyleSettings, } from '@kbn/visualization-ui-components/public'; import { FormatFactory } from '../../../../../common/types'; -import { isHorizontalChart } from '../../state_helpers'; import { defaultAnnotationLabel, defaultRangeAnnotationLabel } from '../../annotations/helpers'; import { TextDecorationSetting } from '../shared/marker_decoration_settings'; -import { LineStyleSettings } from '../shared/line_style_settings'; import { updateLayer } from '..'; import { annotationsIconSet } from './icon_set'; import type { VisualizationDimensionEditorProps } from '../../../../types'; @@ -47,6 +46,7 @@ import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; import { TooltipSection } from './tooltip_annotation_panel'; import { sanitizeProperties, toLineAnnotationColor } from './helpers'; +import { idPrefix } from '../dimension_editor'; export const AnnotationsPanel = ( props: VisualizationDimensionEditorProps & { @@ -56,7 +56,6 @@ export const AnnotationsPanel = ( } ) => { const { state, setState, layerId, accessor, frame } = props; - const isHorizontal = isHorizontalChart(state.layers); const { hasFieldData } = useExistingFieldsReader(); const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ @@ -285,7 +284,7 @@ export const AnnotationsPanel = ( }} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx index d160777eeaa4d..c0e27445bd688 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx @@ -14,6 +14,7 @@ import { useDebouncedValue, IconSelectSetting, ColorPicker, + LineStyleSettings, } from '@kbn/visualization-ui-components/public'; import type { VisualizationDimensionEditorProps } from '../../../../types'; import { State, XYState, XYReferenceLineLayerConfig, YConfig } from '../../types'; @@ -26,7 +27,6 @@ import { MarkerDecorationPosition, TextDecorationSetting, } from '../shared/marker_decoration_settings'; -import { LineStyleSettings } from '../shared/line_style_settings'; import { referenceLineIconsSet } from './icon_set'; import { defaultReferenceLineColor } from '../../color_assignment'; @@ -88,11 +88,7 @@ export const ReferenceLinePanel = ( setConfig={setConfig} currentConfig={localConfig} /> - + Date: Mon, 1 May 2023 13:59:10 -0500 Subject: [PATCH 059/202] add text decoration setting --- src/plugins/event_annotation/jest.config.js | 7 +- src/plugins/event_annotation/kibana.jsonc | 1 + .../group_editor_flyout.test.tsx.snap | 5 +- .../annotations_panel.tsx | 35 +++-- .../components/group_editor_controls.test.tsx | 76 +++++++---- .../components/group_editor_controls.tsx | 51 +++++--- .../components/group_editor_flyout.test.tsx | 6 +- .../public/components/group_editor_flyout.tsx | 13 +- .../public/components/table_list.test.tsx | 6 +- .../public/components/table_list.tsx | 11 +- .../public/get_table_list.tsx | 8 +- src/plugins/event_annotation/public/plugin.ts | 6 +- src/plugins/event_annotation/tsconfig.json | 3 +- .../public/components/index.ts | 2 + .../components/text_decoration_setting.tsx | 122 ++++++++++++++++++ .../public/index.ts | 1 + .../annotations_panel.tsx | 4 +- .../reference_line_panel.tsx | 12 +- .../shared/marker_decoration_settings.tsx | 106 +-------------- 19 files changed, 280 insertions(+), 195 deletions(-) create mode 100644 src/plugins/visualization_ui_components/public/components/text_decoration_setting.tsx diff --git a/src/plugins/event_annotation/jest.config.js b/src/plugins/event_annotation/jest.config.js index 61269c91a8bfd..e7e58e57eb37d 100644 --- a/src/plugins/event_annotation/jest.config.js +++ b/src/plugins/event_annotation/jest.config.js @@ -10,7 +10,10 @@ module.exports = { preset: '@kbn/test', rootDir: '../../..', roots: ['/src/plugins/event_annotation'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_ann', + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_annotation', coverageReporters: ['text', 'html'], - collectCoverageFrom: ['/src/plugins/event_ann/{common,public,server}/**/*.{ts,tsx}'], + collectCoverageFrom: [ + '/src/plugins/event_annotation/{common,public,server}/**/*.{ts,tsx}', + ], + setupFiles: ['jest-canvas-mock'], }; diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index f1c2db4224dcb..9e3b5684093df 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -23,6 +23,7 @@ "dataViews", "kibanaReact", "visualizationUiComponents", + "unifiedFieldList" ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap index 4d30533f4fff0..1b0f381040f9c 100644 --- a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap +++ b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap @@ -2,7 +2,8 @@ exports[`group editor flyout renders controls 1`] = ` Object { - "dataViewListItems": Array [ + "createDataView": [MockFunction], + "dataViews": Array [ Object { "id": "some-id", "title": "My Data View", @@ -46,6 +47,8 @@ Object { "updateTagsReferences": [MockFunction], }, }, + "selectedAnnotation": undefined, + "setSelectedAnnotation": [Function], "update": [MockFunction], } `; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx index 9ae347a1c9081..8ee33314084fa 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx @@ -9,17 +9,26 @@ import './index.scss'; import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiButtonGroup } from '@elastic/eui'; +import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiButtonGroup, EuiSpacer } from '@elastic/eui'; import { IconSelectSetting, DimensionEditorSection, NameInput, ColorPicker, LineStyleSettings, + TextDecorationSetting, + FieldPicker, + FieldOption, } from '@kbn/visualization-ui-components/public'; +import type { FieldOptionValue } from '@kbn/visualization-ui-components/public'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; import { isQueryAnnotationConfig, isRangeAnnotationConfig } from '../..'; -import { AvailableAnnotationIcon, EventAnnotationConfig } from '../../../common'; -// import { isHorizontalChart } from '../../state_helpers'; +import { + AvailableAnnotationIcon, + EventAnnotationConfig, + QueryPointEventAnnotationConfig, +} from '../../../common'; import { defaultAnnotationColor, defaultAnnotationLabel, @@ -31,10 +40,11 @@ import { sanitizeProperties } from './helpers'; export const AnnotationsPanel = ({ annotation: currentAnnotation, onAnnotationChange, + dataView, }: { annotation: EventAnnotationConfig; onAnnotationChange: (annotation: EventAnnotationConfig) => void; - // dataView: DataView; + dataView: DataView; // datatableUtilities: DatatableUtilitiesService; // formatFactory: FormatFactory; // paletteService: PaletteRegistry; @@ -56,6 +66,8 @@ export const AnnotationsPanel = ({ [currentAnnotation, onAnnotationChange] ); + const { hasFieldData } = useExistingFieldsReader(); + return ( <> {/* - {/* {(textDecorationSelected) => { @@ -207,6 +217,7 @@ export const AnnotationsPanel = ({ const fieldIsValid = selectedField ? Boolean(dataView.getFieldByName(selectedField)) : true; + return ( <> @@ -224,7 +235,7 @@ export const AnnotationsPanel = ({ options={options} onChoose={function (choice: FieldOptionValue | undefined): void { if (choice) { - setAnnotations({ textField: choice.field, textVisibility: true }); + update({ textField: choice.field, textVisibility: true }); } }} fieldIsInvalid={!fieldIsValid} @@ -234,7 +245,7 @@ export const AnnotationsPanel = ({ ); }} - */} + { - const mockTaggingApi = taggingApiMock.create(); - const dataViewId = 'my-index-pattern'; const adHocDataViewId = 'ad-hoc'; const adHocDataViewSpec = { @@ -33,29 +33,49 @@ describe('event annotation group editor', () => { dataViewSpec: adHocDataViewSpec, }; - it('reports group updates', () => { - const updateMock = jest.fn(); - const wrapper = shallow( + let wrapper: ReactWrapper; + const updateMock = jest.fn(); + + const TagSelector = (_props: { onTagsSelected: (tags: string[]) => void }) =>
    ; + + beforeEach(async () => { + wrapper = mountWithIntl( + Promise.resolve({ + id: spec.id, + title: spec.title, + toSpec: () => spec, + } as unknown as DataView) + } /> ); - (wrapper.find("[data-test-subj='annotationGroupTitle']") as ShallowWrapper).prop( - 'onChange' - )!({ + await act(async () => { + await new Promise((resolve) => setImmediate(resolve)); + wrapper.update(); + }); + }); + + it('reports group updates', () => { + ( + wrapper.find( + "EuiFieldText[data-test-subj='annotationGroupTitle']" + ) as ReactWrapper + ).prop('onChange')!({ target: { value: 'im a new title!', } as Partial as EventTarget, @@ -63,23 +83,23 @@ describe('event annotation group editor', () => { ( wrapper.find( - "[data-test-subj='annotationGroupDescription']" - ) as ShallowWrapper + "EuiTextArea[data-test-subj='annotationGroupDescription']" + ) as ReactWrapper ).prop('onChange')!({ target: { value: 'im a new description!', }, } as ChangeEvent); - wrapper - .find(mockTaggingApi.ui.components.SavedObjectSaveModalTagSelector) - .prop('onTagsSelected')(['im a new tag!']); + act(() => { + wrapper.find(TagSelector).prop('onTagsSelected')(['im a new tag!']); + }); const setDataViewId = (id: string) => ( wrapper.find( - "[data-test-subj='annotationDataViewSelection']" - ) as ShallowWrapper + "EuiSelect[data-test-subj='annotationDataViewSelection']" + ) as ReactWrapper ).prop('onChange')!({ target: { value: id } } as React.ChangeEvent); setDataViewId(dataViewId); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.tsx index 97d7c5c37c591..efabed30b50b3 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.tsx @@ -18,11 +18,11 @@ import { useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; -import type { DataViewListItem, DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import React, { useMemo, useState } from 'react'; +import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import React, { useEffect, useMemo, useState } from 'react'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; import { AnnotationsPanel } from './annotations_config_panel'; @@ -31,33 +31,41 @@ export const GroupEditorControls = ({ update, setSelectedAnnotation, selectedAnnotation, - savedObjectsTagging, - dataViewListItems: globalDataViewListItems, + TagSelector, + dataViews: globalDataViews, + createDataView, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; selectedAnnotation: EventAnnotationConfig | undefined; setSelectedAnnotation: (annotation: EventAnnotationConfig) => void; - savedObjectsTagging: SavedObjectsTaggingApi; - dataViewListItems: DataViewListItem[]; + TagSelector: SavedObjectsTaggingApiUiComponent['SavedObjectSaveModalTagSelector']; + dataViews: DataView[]; + createDataView: (spec: DataViewSpec) => Promise; }) => { const { euiTheme } = useEuiTheme(); // save the spec for the life of the component since the user might change their mind after selecting another data view - const [adHocDataViewSpec] = useState(group.dataViewSpec); + const [adHocDataView, setAdHocDataView] = useState(); - const dataViewListItems = useMemo(() => { - const items = [...globalDataViewListItems]; - if (adHocDataViewSpec) { - const { id, name, title } = adHocDataViewSpec; - const adHocListItem: DataViewListItem = { - id: id!, - title: name ?? title!, - }; - items.push(adHocListItem); + useEffect(() => { + if (group.dataViewSpec) { + createDataView(group.dataViewSpec).then(setAdHocDataView); + } + }, [createDataView, group.dataViewSpec]); + + const dataViews = useMemo(() => { + const items = [...globalDataViews]; + if (adHocDataView) { + items.push(adHocDataView); } return items; - }, [adHocDataViewSpec, globalDataViewListItems]); + }, [adHocDataView, globalDataViews]); + + const currentDataView = useMemo( + () => dataViews.find((dataView) => dataView.id === group.indexPatternId) || dataViews[0], + [dataViews, group.indexPatternId] + ); return !selectedAnnotation ? ( <> @@ -105,7 +113,7 @@ export const GroupEditorControls = ({ /> - update({ @@ -122,7 +130,7 @@ export const GroupEditorControls = ({ > ({ + options={dataViews.map(({ id: value, title, name }) => ({ value, text: name ?? title, }))} @@ -131,7 +139,7 @@ export const GroupEditorControls = ({ update({ ...group, indexPatternId: value, - dataViewSpec: value === adHocDataViewSpec?.id ? adHocDataViewSpec : undefined, + dataViewSpec: value === adHocDataView?.id ? adHocDataView.toSpec(false) : undefined, }) } /> @@ -154,6 +162,7 @@ export const GroupEditorControls = ({ setSelectedAnnotation({ ...selectedAnnotation, ...changes })} + dataView={currentDataView} /> ); }; diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx index a140e0982e49a..40d33f12450c7 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx @@ -13,6 +13,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; import { GroupEditorControls } from './group_editor_controls'; import { GroupEditorFlyout } from './group_editor_flyout'; +import { DataView } from '@kbn/data-views-plugin/common'; const simulateButtonClick = (component: ShallowWrapper, selector: string) => { (component.find(selector) as ShallowWrapper[0]>).prop('onClick')!( @@ -47,13 +48,14 @@ describe('group editor flyout', () => { onSave={onSave} onClose={onClose} updateGroup={updateGroup} - dataViewListItems={[ + dataViews={[ { id: 'some-id', title: 'My Data View', - }, + } as DataView, ]} savedObjectsTagging={mockTaggingApi} + createDataView={jest.fn()} /> ); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index ccaff9f42aea9..cc7df1a6927a9 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -21,7 +21,7 @@ import { import React, { useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; import { GroupEditorControls } from './group_editor_controls'; @@ -31,14 +31,16 @@ export const GroupEditorFlyout = ({ onClose, onSave, savedObjectsTagging, - dataViewListItems, + dataViews, + createDataView, }: { group: EventAnnotationGroupConfig; updateGroup: (newGroup: EventAnnotationGroupConfig) => void; onClose: () => void; onSave: () => void; savedObjectsTagging: SavedObjectsTaggingApi; - dataViewListItems: DataViewListItem[]; + dataViews: DataView[]; + createDataView: (spec: DataViewSpec) => Promise; }) => { const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); @@ -63,8 +65,9 @@ export const GroupEditorFlyout = ({ update={updateGroup} selectedAnnotation={selectedAnnotation} setSelectedAnnotation={setSelectedAnnotation} - savedObjectsTagging={savedObjectsTagging} - dataViewListItems={dataViewListItems} + TagSelector={savedObjectsTagging.ui.components.SavedObjectSaveModalTagSelector} + dataViews={dataViews} + createDataView={createDataView} /> diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index be18ceb25fffd..e16dce6dbe006 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -21,6 +21,7 @@ import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/moc import { act } from 'react-dom/test-utils'; import { GroupEditorFlyout } from './group_editor_flyout'; +import { DataView } from '@kbn/data-views-plugin/common'; describe('annotation list view', () => { const adHocDVId = 'ad-hoc'; @@ -71,12 +72,13 @@ describe('annotation list view', () => { parentProps={{ onFetchSuccess: () => {}, }} - dataViewListItems={[ + dataViews={[ { id: 'some-id', title: 'Some data view', - }, + } as DataView, ]} + createDataView={() => Promise.resolve({} as DataView)} /> ); }); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index c2d609979b457..7cc7bdcb985ee 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; import { EventAnnotationGroupContent } from '../../common/types'; @@ -27,14 +27,16 @@ export const EventAnnotationGroupTableList = ({ visualizeCapabilities, savedObjectsTagging, parentProps, - dataViewListItems, + dataViews, + createDataView, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; visualizeCapabilities: Record>; savedObjectsTagging: SavedObjectsTaggingApi; parentProps: TableListTabParentProps; - dataViewListItems: DataViewListItem[]; + dataViews: DataView[]; + createDataView: (spec: DataViewSpec) => Promise; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -87,7 +89,8 @@ export const EventAnnotationGroupTableList = ({ }) } savedObjectsTagging={savedObjectsTagging} - dataViewListItems={dataViewListItems} + dataViews={dataViews} + createDataView={createDataView} /> ) : undefined; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index b6b444a1db43e..80bdc7c02c5d1 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -15,7 +15,7 @@ import { } from '@kbn/content-management-table-list'; import { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { EventAnnotationGroupTableList } from './components/table_list'; import { EventAnnotationServiceType } from './event_annotation_service/types'; @@ -24,7 +24,8 @@ export interface EventAnnotationListingPageServices { savedObjectsTagging: SavedObjectsTaggingApi; eventAnnotationService: EventAnnotationServiceType; PresentationUtilContextProvider: FC; - dataViewListItems: DataViewListItem[]; + dataViews: DataView[]; + createDataView: (spec: DataViewSpec) => Promise; } export const getTableList = ( @@ -46,7 +47,8 @@ export const getTableList = ( eventAnnotationService={services.eventAnnotationService} visualizeCapabilities={services.core.application.capabilities.visualize} parentProps={parentProps} - dataViewListItems={services.dataViewListItems} + dataViews={services.dataViews} + createDataView={services.createDataView} /> ); diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 1d80fd51032d4..856b82b0117f6 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -68,14 +68,16 @@ export class EventAnnotationPlugin pluginsStart.savedObjectsManagement ).getService(); - const dataViewListItems = await pluginsStart.dataViews.getIdsWithTitle(); + const ids = await pluginsStart.dataViews.getIds(); + const dataViews = await Promise.all(ids.map((id) => pluginsStart.dataViews.get(id))); const services: EventAnnotationListingPageServices = { core: coreStart, savedObjectsTagging: pluginsStart.savedObjectsTagging, eventAnnotationService, PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, - dataViewListItems, + dataViews, + createDataView: pluginsStart.dataViews.create.bind(pluginsStart.dataViews), }; // TODO wire parent props diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 0b926feb1f543..4806367278320 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -28,7 +28,8 @@ "@kbn/visualizations-plugin", "@kbn/data-views-plugin", "@kbn/visualization-ui-components", - "@kbn/chart-icons" + "@kbn/chart-icons", + "@kbn/unified-field-list" ], "exclude": [ "target/**/*", diff --git a/src/plugins/visualization_ui_components/public/components/index.ts b/src/plugins/visualization_ui_components/public/components/index.ts index b2af81f00f117..778baf7d487f0 100644 --- a/src/plugins/visualization_ui_components/public/components/index.ts +++ b/src/plugins/visualization_ui_components/public/components/index.ts @@ -30,6 +30,8 @@ export * from './dimension_buttons'; export * from './line_style_settings'; +export * from './text_decoration_setting'; + export type { AccessorConfig } from './dimension_buttons'; export type { FieldOptionValue, FieldOption, DataType } from './field_picker'; diff --git a/src/plugins/visualization_ui_components/public/components/text_decoration_setting.tsx b/src/plugins/visualization_ui_components/public/components/text_decoration_setting.tsx new file mode 100644 index 0000000000000..ec6ccecf345c1 --- /dev/null +++ b/src/plugins/visualization_ui_components/public/components/text_decoration_setting.tsx @@ -0,0 +1,122 @@ +/* + * 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 { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useState } from 'react'; + +interface TextDecorationConfig { + textVisibility?: boolean; + textField?: string; +} + +function getSelectedOption( + { textField, textVisibility }: TextDecorationConfig = {}, + isQueryBased?: boolean +) { + if (!textVisibility) { + return 'none'; + } + if (isQueryBased && textField) { + return 'field'; + } + return 'name'; +} + +export function TextDecorationSetting({ + idPrefix, + currentConfig, + setConfig, + isQueryBased, + children, +}: { + idPrefix: string; + currentConfig?: TextDecorationConfig; + setConfig: (config: TextDecorationConfig) => void; + isQueryBased?: boolean; + /** A children render function for custom sub fields on textDecoration change */ + children?: (textDecoration: 'none' | 'name' | 'field') => JSX.Element | null; +}) { + // To model the temporary state for label based on field when user didn't pick up the field yet, + // use a local state + const [selectedVisibleOption, setVisibleOption] = useState<'none' | 'name' | 'field'>( + getSelectedOption(currentConfig, isQueryBased) + ); + const options = [ + { + id: `${idPrefix}none`, + label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.none', { + defaultMessage: 'None', + }), + 'data-test-subj': 'lnsXY_textVisibility_none', + }, + { + id: `${idPrefix}name`, + label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.name', { + defaultMessage: 'Name', + }), + 'data-test-subj': 'lnsXY_textVisibility_name', + }, + ]; + if (isQueryBased) { + options.push({ + id: `${idPrefix}field`, + label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.field', { + defaultMessage: 'Field', + }), + 'data-test-subj': 'lnsXY_textVisibility_field', + }); + } + + return ( + +
    + { + const chosenOption = id.replace(idPrefix, '') as 'none' | 'name' | 'field'; + if (chosenOption === 'none') { + setConfig({ + textVisibility: false, + textField: undefined, + }); + } else if (chosenOption === 'name') { + setConfig({ + textVisibility: true, + textField: undefined, + }); + } else if (chosenOption === 'field') { + setConfig({ + textVisibility: Boolean(currentConfig?.textField), + }); + } + + setVisibleOption(chosenOption); + }} + isFullWidth + /> + {children?.(selectedVisibleOption)} +
    +
    + ); +} diff --git a/src/plugins/visualization_ui_components/public/index.ts b/src/plugins/visualization_ui_components/public/index.ts index 93436a2b856c0..fbbb06bd1efc0 100644 --- a/src/plugins/visualization_ui_components/public/index.ts +++ b/src/plugins/visualization_ui_components/public/index.ts @@ -32,6 +32,7 @@ export { DimensionEditorSection, DimensionButton, LineStyleSettings, + TextDecorationSetting, } from './components'; export type { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index 4de2f3154a3f0..9ff2d0abe1b89 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -34,10 +34,10 @@ import { DimensionEditorSection, ColorPicker, LineStyleSettings, + TextDecorationSetting, } from '@kbn/visualization-ui-components/public'; import { FormatFactory } from '../../../../../common/types'; import { defaultAnnotationLabel, defaultRangeAnnotationLabel } from '../../annotations/helpers'; -import { TextDecorationSetting } from '../shared/marker_decoration_settings'; import { updateLayer } from '..'; import { annotationsIconSet } from './icon_set'; import type { VisualizationDimensionEditorProps } from '../../../../types'; @@ -220,9 +220,9 @@ export const AnnotationsPanel = ( customIconSet={annotationsIconSet} /> - + setConfig({ icon })} currentIcon={localConfig?.icon} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx index 2ea03f2cf9884..87528f2789d28 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/shared/marker_decoration_settings.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { IconPosition } from '@kbn/expression-xy-plugin/common'; @@ -83,110 +83,6 @@ export interface MarkerDecorationConfig { textField?: string; } -function getSelectedOption( - { textField, textVisibility }: MarkerDecorationConfig = {}, - isQueryBased?: boolean -) { - if (!textVisibility) { - return 'none'; - } - if (isQueryBased && textField) { - return 'field'; - } - return 'name'; -} - -export function TextDecorationSetting({ - currentConfig, - setConfig, - isQueryBased, - children, -}: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; - isQueryBased?: boolean; - /** A children render function for custom sub fields on textDecoration change */ - children?: (textDecoration: 'none' | 'name' | 'field') => JSX.Element | null; -}) { - // To model the temporary state for label based on field when user didn't pick up the field yet, - // use a local state - const [selectedVisibleOption, setVisibleOption] = useState<'none' | 'name' | 'field'>( - getSelectedOption(currentConfig, isQueryBased) - ); - const options = [ - { - id: `${idPrefix}none`, - label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.none', { - defaultMessage: 'None', - }), - 'data-test-subj': 'lnsXY_textVisibility_none', - }, - { - id: `${idPrefix}name`, - label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.name', { - defaultMessage: 'Name', - }), - 'data-test-subj': 'lnsXY_textVisibility_name', - }, - ]; - if (isQueryBased) { - options.push({ - id: `${idPrefix}field`, - label: i18n.translate('xpack.lens.xyChart.lineMarker.textVisibility.field', { - defaultMessage: 'Field', - }), - 'data-test-subj': 'lnsXY_textVisibility_field', - }); - } - - return ( - -
    - { - const chosenOption = id.replace(idPrefix, '') as 'none' | 'name' | 'field'; - if (chosenOption === 'none') { - setConfig({ - textVisibility: false, - textField: undefined, - }); - } else if (chosenOption === 'name') { - setConfig({ - textVisibility: true, - textField: undefined, - }); - } else if (chosenOption === 'field') { - setConfig({ - textVisibility: Boolean(currentConfig?.textField), - }); - } - - setVisibleOption(chosenOption); - }} - isFullWidth - /> - {children?.(selectedVisibleOption)} -
    -
    - ); -} - export function MarkerDecorationPosition({ currentConfig, setConfig, From 6f2e8c850e3ec863adba752e2db0147967d0fc4f Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 1 May 2023 14:28:57 -0500 Subject: [PATCH 060/202] add tooltip section --- .../annotations_config_panel/annotations_panel.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx index 8ee33314084fa..21dfe07e32fa7 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx @@ -36,6 +36,7 @@ import { } from './helpers'; import { annotationsIconSet } from './icon_set'; import { sanitizeProperties } from './helpers'; +import { TooltipSection } from './tooltip_annotation_panel'; export const AnnotationsPanel = ({ annotation: currentAnnotation, @@ -318,7 +319,8 @@ export const AnnotationsPanel = ({ value={Boolean(currentAnnotation.isHidden)} onChange={(ev) => update({ isHidden: ev.target.checked })} /> - {/* {isQueryBased && currentAnnotation && ( + + {isQueryBased && currentAnnotation && (
    - */} - + + )} ); }; From 5430f4181884125a4aa1e58265273be34e264b50 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 1 May 2023 15:10:46 -0500 Subject: [PATCH 061/202] add manual annotation controls --- src/plugins/event_annotation/kibana.jsonc | 1 + .../annotations_panel.tsx | 37 +++++++++++------- .../annotations_config_panel/helpers.ts | 39 ------------------- .../manual_annotation_panel.tsx | 20 +++++++--- .../range_annotation_panel.tsx | 21 ++++------ .../components/group_editor_controls.tsx | 1 + 6 files changed, 46 insertions(+), 73 deletions(-) diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index 9e3b5684093df..dae47a886abbf 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -19,6 +19,7 @@ "savedObjectsTagging", ], "requiredBundles": [ + "data", "savedObjectsFinder", "dataViews", "kibanaReact", diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx index 21dfe07e32fa7..444fd348f1164 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx @@ -23,29 +23,38 @@ import { import type { FieldOptionValue } from '@kbn/visualization-ui-components/public'; import { DataView } from '@kbn/data-views-plugin/common'; import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; +import moment from 'moment'; import { isQueryAnnotationConfig, isRangeAnnotationConfig } from '../..'; import { AvailableAnnotationIcon, EventAnnotationConfig, + PointInTimeEventAnnotationConfig, QueryPointEventAnnotationConfig, } from '../../../common'; import { defaultAnnotationColor, defaultAnnotationLabel, defaultAnnotationRangeColor, + defaultRangeAnnotationLabel, + toLineAnnotationColor, } from './helpers'; import { annotationsIconSet } from './icon_set'; import { sanitizeProperties } from './helpers'; import { TooltipSection } from './tooltip_annotation_panel'; +import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; export const AnnotationsPanel = ({ annotation: currentAnnotation, onAnnotationChange, dataView, + getDefaultRangeEnd, + calendarClassName, }: { annotation: EventAnnotationConfig; onAnnotationChange: (annotation: EventAnnotationConfig) => void; dataView: DataView; + getDefaultRangeEnd: (rangeStart: string) => string; + calendarClassName?: string; // datatableUtilities: DatatableUtilitiesService; // formatFactory: FormatFactory; // paletteService: PaletteRegistry; @@ -71,7 +80,7 @@ export const AnnotationsPanel = ({ return ( <> - {/* {isQueryBased ? ( - + // +
    ) : ( )} - */} + { return new Color(transparentize(color, 1)).hex(); }; -// export const getEndTimestamp = ( -// datatableUtilities: DatatableUtilitiesService, -// startTime: string, -// { activeData, dateRange }: FramePublicAPI, -// dataLayers: XYDataLayerConfig[] -// ) => { -// const startTimeNumber = moment(startTime).valueOf(); -// const dateRangeFraction = -// (moment(dateRange.toDate).valueOf() - moment(dateRange.fromDate).valueOf()) * 0.1; -// const fallbackValue = moment(startTimeNumber + dateRangeFraction).toISOString(); -// const dataLayersId = dataLayers.map(({ layerId }) => layerId); -// if ( -// !dataLayersId.length || -// !activeData || -// Object.entries(activeData) -// .filter(([key]) => dataLayersId.includes(key)) -// .every(([, { rows }]) => !rows || !rows.length) -// ) { -// return fallbackValue; -// } -// const xColumn = activeData?.[dataLayersId[0]].columns.find( -// (column) => column.id === dataLayers[0].xAccessor -// ); -// if (!xColumn) { -// return fallbackValue; -// } - -// const dateInterval = datatableUtilities.getDateHistogramMeta(xColumn)?.interval; -// if (!dateInterval) return fallbackValue; -// const intervalDuration = search.aggs.parseInterval(dateInterval); -// if (!intervalDuration) return fallbackValue; -// return moment(startTimeNumber + 3 * intervalDuration.as('milliseconds')).toISOString(); -// }; - export const sanitizeProperties = (annotation: EventAnnotationConfig) => { if (isRangeAnnotationConfig(annotation)) { const rangeAnnotation: RangeEventAnnotationConfig = pick(annotation, [ diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx index 76e89c498675c..ec0081df10800 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx @@ -11,15 +11,22 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React from 'react'; import { isRangeAnnotationConfig } from '../..'; -import { ConfigPanelRangeDatePicker } from './range_annotation_panel'; +import { + ConfigPanelApplyAsRangeSwitch, + ConfigPanelRangeDatePicker, +} from './range_annotation_panel'; import type { ManualEventAnnotationType } from './types'; export const ConfigPanelManualAnnotation = ({ annotation, onChange, + getDefaultRangeEnd, + calendarClassName, }: { annotation?: ManualEventAnnotationType | undefined; onChange: (annotation: Partial | undefined) => void; + getDefaultRangeEnd: (rangeStart: string) => string; + calendarClassName: string | undefined; }) => { const isRange = isRangeAnnotationConfig(annotation); return ( @@ -28,6 +35,7 @@ export const ConfigPanelManualAnnotation = ({ <> )} - {/* */} + getDefaultRangeEnd={getDefaultRangeEnd} + /> ); }; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx index c59d404e5f50d..1f42d056b18f1 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { @@ -20,19 +19,18 @@ import { import moment from 'moment'; import { isRangeAnnotationConfig } from '../..'; import type { PointInTimeEventAnnotationConfig, RangeEventAnnotationConfig } from '../../../common'; -// import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; import { defaultRangeAnnotationLabel, defaultAnnotationLabel } from './helpers'; import { toLineAnnotationColor, toRangeAnnotationColor } from './helpers'; import type { ManualEventAnnotationType } from './types'; export const ConfigPanelApplyAsRangeSwitch = ({ annotation, - datatableUtilities, onChange, + getDefaultRangeEnd, }: { annotation?: ManualEventAnnotationType; - datatableUtilities: DatatableUtilitiesService; onChange: (annotations: Partial | undefined) => void; + getDefaultRangeEnd: (rangeStart: string) => string; }) => { const isRange = isRangeAnnotationConfig(annotation); return ( @@ -65,19 +63,12 @@ export const ConfigPanelApplyAsRangeSwitch = ({ }; onChange(newPointAnnotation); } else if (annotation) { - const fromTimestamp = moment(annotation?.key.timestamp); const newRangeAnnotation: RangeEventAnnotationConfig = { type: 'manual', key: { type: 'range', timestamp: annotation.key.timestamp, - endTimestamp: '', - // endTimestamp: getEndTimestamp( - // datatableUtilities, - // fromTimestamp.toISOString(), - // frame, - // dataLayers - // ), + endTimestamp: getDefaultRangeEnd(annotation.key.timestamp), }, id: annotation.id, label: @@ -101,12 +92,14 @@ export const ConfigPanelRangeDatePicker = ({ label, prependLabel, onChange, + calendarClassName, dataTestSubj = 'lnsXY_annotation_date_picker', }: { value: moment.Moment; prependLabel?: string; label?: string; onChange: (val: moment.Moment | null) => void; + calendarClassName: string | undefined; dataTestSubj?: string; }) => { return ( @@ -120,7 +113,7 @@ export const ConfigPanelRangeDatePicker = ({ } > ) : ( setSelectedAnnotation({ ...selectedAnnotation, ...changes })} dataView={currentDataView} + getDefaultRangeEnd={(rangeStart) => rangeStart} /> ); }; From 4022e8fa2497ca883e071b25e4cc1b33a71b9d00 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 1 May 2023 16:03:01 -0500 Subject: [PATCH 062/202] add query-based annotation control --- src/plugins/event_annotation/kibana.jsonc | 4 +++- .../annotations_panel.tsx | 20 ++++++++++--------- .../query_annotation_panel.tsx | 5 ++++- .../components/group_editor_controls.test.tsx | 2 ++ .../components/group_editor_controls.tsx | 4 ++++ .../components/group_editor_flyout.test.tsx | 2 ++ .../public/components/group_editor_flyout.tsx | 4 ++++ .../public/components/table_list.test.tsx | 2 ++ .../public/components/table_list.tsx | 4 ++++ .../public/get_table_list.tsx | 3 +++ src/plugins/event_annotation/public/plugin.ts | 18 ++++++++++++++--- .../public/components/index.ts | 2 ++ .../query_input/filter_query_input.scss | 7 +++++++ .../query_input/filter_query_input.tsx | 3 ++- .../public/index.ts | 1 + 15 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index dae47a886abbf..627d43aa2ba51 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -13,7 +13,9 @@ "data", "presentationUtil", "visualizations", - "dataViews" + "dataViews", + "unifiedSearch", + "kibanaUtils" ], "optionalPlugins": [ "savedObjectsTagging", diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx index 444fd348f1164..110d967cd055d 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx @@ -19,6 +19,7 @@ import { TextDecorationSetting, FieldPicker, FieldOption, + type QueryInputServices, } from '@kbn/visualization-ui-components/public'; import type { FieldOptionValue } from '@kbn/visualization-ui-components/public'; import { DataView } from '@kbn/data-views-plugin/common'; @@ -42,6 +43,7 @@ import { annotationsIconSet } from './icon_set'; import { sanitizeProperties } from './helpers'; import { TooltipSection } from './tooltip_annotation_panel'; import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; +import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; export const AnnotationsPanel = ({ annotation: currentAnnotation, @@ -49,12 +51,14 @@ export const AnnotationsPanel = ({ dataView, getDefaultRangeEnd, calendarClassName, + queryInputServices, }: { annotation: EventAnnotationConfig; onAnnotationChange: (annotation: EventAnnotationConfig) => void; dataView: DataView; getDefaultRangeEnd: (rangeStart: string) => string; calendarClassName?: string; + queryInputServices: QueryInputServices; // datatableUtilities: DatatableUtilitiesService; // formatFactory: FormatFactory; // paletteService: PaletteRegistry; @@ -157,15 +161,13 @@ export const AnnotationsPanel = ({ /> {isQueryBased ? ( - // -
    + ) : ( | undefined) => void; dataView: DataView; queryInputShouldOpen?: boolean; + queryInputServices: QueryInputServices; }) => { const { hasFieldData } = useExistingFieldsReader(); // list only date fields @@ -78,7 +81,7 @@ export const ConfigPanelQueryAnnotation = ({ }} dataView={dataView} appName="TODO" - queryInputServices={{}} + queryInputServices={queryInputServices} /> diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx index 1d27f3998377d..9b62674036969 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx @@ -14,6 +14,7 @@ import { GroupEditorControls } from './group_editor_controls'; import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { act } from 'react-dom/test-utils'; +import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; describe('event annotation group editor', () => { const dataViewId = 'my-index-pattern'; @@ -61,6 +62,7 @@ describe('event annotation group editor', () => { toSpec: () => spec, } as unknown as DataView) } + queryInputServices={{} as QueryInputServices} /> ); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.tsx index ab1439d14038d..bc94bbb1a7328 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.tsx @@ -22,6 +22,7 @@ import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import React, { useEffect, useMemo, useState } from 'react'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; import { AnnotationsPanel } from './annotations_config_panel'; @@ -34,6 +35,7 @@ export const GroupEditorControls = ({ TagSelector, dataViews: globalDataViews, createDataView, + queryInputServices, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; @@ -42,6 +44,7 @@ export const GroupEditorControls = ({ TagSelector: SavedObjectsTaggingApiUiComponent['SavedObjectSaveModalTagSelector']; dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; + queryInputServices: QueryInputServices; }) => { const { euiTheme } = useEuiTheme(); @@ -164,6 +167,7 @@ export const GroupEditorControls = ({ onAnnotationChange={(changes) => setSelectedAnnotation({ ...selectedAnnotation, ...changes })} dataView={currentDataView} getDefaultRangeEnd={(rangeStart) => rangeStart} + queryInputServices={queryInputServices} /> ); }; diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx index 40d33f12450c7..a4e285a1ce3dd 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx @@ -14,6 +14,7 @@ import React from 'react'; import { GroupEditorControls } from './group_editor_controls'; import { GroupEditorFlyout } from './group_editor_flyout'; import { DataView } from '@kbn/data-views-plugin/common'; +import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; const simulateButtonClick = (component: ShallowWrapper, selector: string) => { (component.find(selector) as ShallowWrapper[0]>).prop('onClick')!( @@ -56,6 +57,7 @@ describe('group editor flyout', () => { ]} savedObjectsTagging={mockTaggingApi} createDataView={jest.fn()} + queryInputServices={{} as QueryInputServices} /> ); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index cc7df1a6927a9..e39d9549ceaf0 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -22,6 +22,7 @@ import React, { useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; import { GroupEditorControls } from './group_editor_controls'; @@ -33,6 +34,7 @@ export const GroupEditorFlyout = ({ savedObjectsTagging, dataViews, createDataView, + queryInputServices, }: { group: EventAnnotationGroupConfig; updateGroup: (newGroup: EventAnnotationGroupConfig) => void; @@ -41,6 +43,7 @@ export const GroupEditorFlyout = ({ savedObjectsTagging: SavedObjectsTaggingApi; dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; + queryInputServices: QueryInputServices; }) => { const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); @@ -68,6 +71,7 @@ export const GroupEditorFlyout = ({ TagSelector={savedObjectsTagging.ui.components.SavedObjectSaveModalTagSelector} dataViews={dataViews} createDataView={createDataView} + queryInputServices={queryInputServices} /> diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index e16dce6dbe006..2a43df9ae8f74 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -22,6 +22,7 @@ import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/moc import { act } from 'react-dom/test-utils'; import { GroupEditorFlyout } from './group_editor_flyout'; import { DataView } from '@kbn/data-views-plugin/common'; +import { QueryInputServices } from '@kbn/visualization-ui-components/public'; describe('annotation list view', () => { const adHocDVId = 'ad-hoc'; @@ -79,6 +80,7 @@ describe('annotation list view', () => { } as DataView, ]} createDataView={() => Promise.resolve({} as DataView)} + queryInputServices={{} as QueryInputServices} /> ); }); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index 7cc7bdcb985ee..50902a8d276fe 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -13,6 +13,7 @@ import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; import { EventAnnotationGroupContent } from '../../common/types'; @@ -29,6 +30,7 @@ export const EventAnnotationGroupTableList = ({ parentProps, dataViews, createDataView, + queryInputServices, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; @@ -37,6 +39,7 @@ export const EventAnnotationGroupTableList = ({ parentProps: TableListTabParentProps; dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; + queryInputServices: QueryInputServices; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -91,6 +94,7 @@ export const EventAnnotationGroupTableList = ({ savedObjectsTagging={savedObjectsTagging} dataViews={dataViews} createDataView={createDataView} + queryInputServices={queryInputServices} /> ) : undefined; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index 80bdc7c02c5d1..6ede34ee5a604 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -16,6 +16,7 @@ import { import { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { EventAnnotationGroupTableList } from './components/table_list'; import { EventAnnotationServiceType } from './event_annotation_service/types'; @@ -26,6 +27,7 @@ export interface EventAnnotationListingPageServices { PresentationUtilContextProvider: FC; dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; + queryInputServices: QueryInputServices; } export const getTableList = ( @@ -49,6 +51,7 @@ export const getTableList = ( parentProps={parentProps} dataViews={services.dataViews} createDataView={services.createDataView} + queryInputServices={services.queryInputServices} /> ); diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 856b82b0117f6..887b698f9a41d 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -6,14 +6,16 @@ * Side Public License, v 1. */ -import { type Plugin, type CoreSetup, type CoreStart } from '@kbn/core/public'; +import type { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; -import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public/types'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { EventAnnotationService } from './event_annotation_service'; import { manualPointEventAnnotation, @@ -23,13 +25,13 @@ import { } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; import { EventAnnotationListingPageServices, getTableList } from './get_table_list'; - export interface EventAnnotationStartDependencies { savedObjectsManagement: SavedObjectsManagementPluginStart; data: DataPublicPluginStart; savedObjectsTagging: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; dataViews: DataViewsPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; } interface SetupDependencies { @@ -78,6 +80,16 @@ export class EventAnnotationPlugin PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, dataViews, createDataView: pluginsStart.dataViews.create.bind(pluginsStart.dataViews), + queryInputServices: { + http: coreStart.http, + docLinks: coreStart.docLinks, + notifications: coreStart.notifications, + uiSettings: coreStart.uiSettings, + dataViews: pluginsStart.dataViews, + unifiedSearch: pluginsStart.unifiedSearch, + data: pluginsStart.data, + storage: new Storage(localStorage), + }, }; // TODO wire parent props diff --git a/src/plugins/visualization_ui_components/public/components/index.ts b/src/plugins/visualization_ui_components/public/components/index.ts index 778baf7d487f0..e20879f9e9990 100644 --- a/src/plugins/visualization_ui_components/public/components/index.ts +++ b/src/plugins/visualization_ui_components/public/components/index.ts @@ -37,3 +37,5 @@ export type { AccessorConfig } from './dimension_buttons'; export type { FieldOptionValue, FieldOption, DataType } from './field_picker'; export type { IconSet } from './icon_select'; + +export type { QueryInputServices } from './query_input'; diff --git a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss new file mode 100644 index 0000000000000..38a6f7909f5e5 --- /dev/null +++ b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss @@ -0,0 +1,7 @@ +// TODO - use emotion instead +.filterQueryInput__popoverButton { + @include euiTextBreakWord; + @include euiFontSizeS; + min-height: $euiSizeXL; + width: 100%; +} \ No newline at end of file diff --git a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx index 2747f87c90f31..9d17025fce8d3 100644 --- a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx +++ b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx @@ -22,6 +22,7 @@ import type { DataViewBase, Query } from '@kbn/es-query'; import { useDebouncedValue } from '../debounced_value'; import { QueryInput, validateQuery } from '.'; import type { QueryInputServices } from '.'; +import './filter_query_input.scss'; const filterByLabel = i18n.translate('visualizationUiComponents.filterQueryInput.label', { defaultMessage: 'Filter by', @@ -109,7 +110,7 @@ export function FilterQueryInput({ {/* Empty for spacing */} { setFilterPopoverOpen(!filterPopoverOpen); diff --git a/src/plugins/visualization_ui_components/public/index.ts b/src/plugins/visualization_ui_components/public/index.ts index fbbb06bd1efc0..d4b15f28d33c1 100644 --- a/src/plugins/visualization_ui_components/public/index.ts +++ b/src/plugins/visualization_ui_components/public/index.ts @@ -41,6 +41,7 @@ export type { FieldOption, IconSet, AccessorConfig, + QueryInputServices, } from './components'; export function plugin() { From e71dd26fe50c346bbdab0ee913dc3890ffc5ce31 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 1 May 2023 16:07:28 -0500 Subject: [PATCH 063/202] some clean up --- .../annotations_config_panel/annotations_panel.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx index 110d967cd055d..5ab33546ca220 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx @@ -59,11 +59,8 @@ export const AnnotationsPanel = ({ getDefaultRangeEnd: (rangeStart: string) => string; calendarClassName?: string; queryInputServices: QueryInputServices; - // datatableUtilities: DatatableUtilitiesService; - // formatFactory: FormatFactory; - // paletteService: PaletteRegistry; }) => { - // const { hasFieldData } = useExistingFieldsReader(); + const { hasFieldData } = useExistingFieldsReader(); const isQueryBased = isQueryAnnotationConfig(currentAnnotation); const isRange = isRangeAnnotationConfig(currentAnnotation); @@ -80,8 +77,6 @@ export const AnnotationsPanel = ({ [currentAnnotation, onAnnotationChange] ); - const { hasFieldData } = useExistingFieldsReader(); - return ( <> Date: Mon, 1 May 2023 16:10:33 -0500 Subject: [PATCH 064/202] some renaming --- .../annotation_editor_controls.tsx} | 2 +- .../helpers.ts | 0 .../icon_set.ts | 0 .../index.scss | 0 .../index.tsx | 2 +- .../manual_annotation_panel.tsx | 0 .../query_annotation_panel.tsx | 0 .../range_annotation_panel.tsx | 0 .../tooltip_annotation_panel.tsx | 0 .../types.ts | 0 .../public/components/group_editor_controls.tsx | 4 ++-- 11 files changed, 4 insertions(+), 4 deletions(-) rename src/plugins/event_annotation/public/components/{annotations_config_panel/annotations_panel.tsx => annotation_editor_controls/annotation_editor_controls.tsx} (99%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/helpers.ts (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/icon_set.ts (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/index.scss (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/index.tsx (82%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/manual_annotation_panel.tsx (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/query_annotation_panel.tsx (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/range_annotation_panel.tsx (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/tooltip_annotation_panel.tsx (100%) rename src/plugins/event_annotation/public/components/{annotations_config_panel => annotation_editor_controls}/types.ts (100%) diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx similarity index 99% rename from src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index 5ab33546ca220..312987b4069cb 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/annotations_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -45,7 +45,7 @@ import { TooltipSection } from './tooltip_annotation_panel'; import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; -export const AnnotationsPanel = ({ +export const AnnotationEditorControls = ({ annotation: currentAnnotation, onAnnotationChange, dataView, diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/helpers.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/helpers.ts similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/helpers.ts rename to src/plugins/event_annotation/public/components/annotation_editor_controls/helpers.ts diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/icon_set.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/icon_set.ts similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/icon_set.ts rename to src/plugins/event_annotation/public/components/annotation_editor_controls/icon_set.ts diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/index.scss b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.scss similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/index.scss rename to src/plugins/event_annotation/public/components/annotation_editor_controls/index.scss diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/index.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx similarity index 82% rename from src/plugins/event_annotation/public/components/annotations_config_panel/index.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx index 4dd51d46b0cfa..43fe1440fbff6 100644 --- a/src/plugins/event_annotation/public/components/annotations_config_panel/index.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { AnnotationsPanel } from './annotations_panel'; +export { AnnotationEditorControls } from './annotation_editor_controls'; diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/manual_annotation_panel.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/query_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/query_annotation_panel.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/range_annotation_panel.tsx similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/range_annotation_panel.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/range_annotation_panel.tsx diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/tooltip_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/tooltip_annotation_panel.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx diff --git a/src/plugins/event_annotation/public/components/annotations_config_panel/types.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/types.ts similarity index 100% rename from src/plugins/event_annotation/public/components/annotations_config_panel/types.ts rename to src/plugins/event_annotation/public/components/annotation_editor_controls/types.ts diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.tsx index bc94bbb1a7328..267ac83dcfb00 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.tsx @@ -25,7 +25,7 @@ import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-os import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import React, { useEffect, useMemo, useState } from 'react'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; -import { AnnotationsPanel } from './annotations_config_panel'; +import { AnnotationEditorControls } from './annotation_editor_controls'; export const GroupEditorControls = ({ group, @@ -162,7 +162,7 @@ export const GroupEditorControls = ({ ) : ( - setSelectedAnnotation({ ...selectedAnnotation, ...changes })} dataView={currentDataView} From 8bfa4a18a7d656ddf525038d64e05879b9039818 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 1 May 2023 19:06:14 -0500 Subject: [PATCH 065/202] use annotation editor controls in Lens --- .../annotation_editor_controls/index.test.tsx | 692 ++++++++++++++++++ .../{index.tsx => index.ts} | 1 + .../public/components/index.ts | 9 + src/plugins/event_annotation/public/index.ts | 1 + src/plugins/event_annotation/tsconfig.json | 2 +- .../visualizations/xy/annotations/helpers.tsx | 2 +- .../lens/public/visualizations/xy/index.ts | 11 +- .../visualizations/xy/visualization.tsx | 9 +- .../annotations_panel.tsx | 443 +++-------- .../annotations_config_panel/helpers.ts | 116 --- .../annotations_config_panel/icon_set.ts | 106 --- .../annotations_config_panel/index.scss | 19 - .../annotations_config_panel/index.test.tsx | 688 ----------------- .../manual_annotation_panel.tsx | 131 ---- .../query_annotation_panel.tsx | 127 ---- .../range_annotation_panel.tsx | 154 ---- .../tooltip_annotation_panel.tsx | 209 ------ .../annotations_config_panel/types.ts | 15 - .../xy/xy_config_panel/dimension_editor.tsx | 25 +- .../xy_config_panel/xy_config_panel.test.tsx | 20 +- .../visualizations/xy/xy_suggestions.test.ts | 2 + 21 files changed, 819 insertions(+), 1963 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx rename src/plugins/event_annotation/public/components/annotation_editor_controls/{index.tsx => index.ts} (89%) create mode 100644 src/plugins/event_annotation/public/components/index.ts delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx new file mode 100644 index 0000000000000..5900c19a4941a --- /dev/null +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx @@ -0,0 +1,692 @@ +/* + * 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. + */ + +// TODO - translate these tests to work with AnnotationEditorControls component +export {}; + +// import React from 'react'; +// import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +// import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; +// import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +// import { AnnotationsPanel } from '.'; +// import { FramePublicAPI } from '../../../../types'; +// import { DatasourcePublicAPI } from '../../../..'; +// import { createMockFramePublicAPI } from '../../../../mocks'; +// import { State, XYState } from '../../types'; +// import { Position } from '@elastic/charts'; +// import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; +// import moment from 'moment'; +// import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +// import { createMockDataViewsState } from '../../../../data_views_service/mocks'; +// import { createMockedIndexPattern } from '../../../../datasources/form_based/mocks'; +// import { act } from 'react-dom/test-utils'; +// import { EuiButtonGroup } from '@elastic/eui'; + +// jest.mock('lodash', () => { +// const original = jest.requireActual('lodash'); + +// return { +// ...original, +// debounce: (fn: unknown) => fn, +// }; +// }); + +// jest.mock('@kbn/unified-search-plugin/public', () => ({ +// QueryStringInput: () => { +// return 'QueryStringInput'; +// }, +// })); + +// const customLineStaticAnnotation: EventAnnotationConfig = { +// id: 'ann1', +// type: 'manual', +// key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, +// label: 'Event', +// icon: 'triangle' as const, +// color: 'red', +// lineStyle: 'dashed' as const, +// lineWidth: 3, +// }; + +// describe('AnnotationsPanel', () => { +// const datatableUtilities = createDatatableUtilitiesMock(); +// let frame: FramePublicAPI; + +// function testState(): State { +// return { +// legend: { isVisible: true, position: Position.Right }, +// valueLabels: 'hide', +// preferredSeriesType: 'bar', +// layers: [ +// { +// layerType: LayerTypes.ANNOTATIONS, +// layerId: 'annotation', +// indexPatternId: 'indexPattern1', +// annotations: [customLineStaticAnnotation], +// ignoreGlobalFilters: true, +// hide: false, +// simpleView: false, +// }, +// ], +// }; +// } + +// beforeEach(() => { +// frame = createMockFramePublicAPI({ datasourceLayers: {} }); +// }); + +// describe('Dimension Editor', () => { +// test('shows correct options for line annotations', () => { +// const state = testState(); +// const component = mount( +// +// ); + +// expect( +// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').prop('selected') +// ).toEqual(moment('2022-03-18T08:25:00.000Z')); +// expect( +// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').exists() +// ).toBeFalsy(); +// expect( +// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').exists() +// ).toBeFalsy(); +// expect( +// component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') +// ).toEqual(false); +// expect( +// component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') +// ).toEqual('Event'); +// expect( +// component.find('EuiComboBox[data-test-subj="lns-icon-select"]').prop('selectedOptions') +// ).toEqual([{ label: 'Triangle', value: 'triangle' }]); +// expect(component.find('TextDecorationSetting').exists()).toBeTruthy(); +// expect(component.find('LineStyleSettings').exists()).toBeTruthy(); +// expect( +// component.find('EuiButtonGroup[data-test-subj="lns-xyAnnotation-fillStyle"]').exists() +// ).toBeFalsy(); +// }); +// test('shows correct options for range annotations', () => { +// const state = testState(); +// state.layers[0] = { +// annotations: [ +// { +// color: 'red', +// icon: 'triangle', +// id: 'ann1', +// type: 'manual', +// isHidden: undefined, +// key: { +// endTimestamp: '2022-03-21T10:49:00.000Z', +// timestamp: '2022-03-18T08:25:00.000Z', +// type: 'range', +// }, +// label: 'Event range', +// lineStyle: 'dashed', +// lineWidth: 3, +// }, +// ], +// layerId: 'annotation', +// layerType: 'annotations', +// indexPatternId: 'indexPattern1', +// ignoreGlobalFilters: true, +// hide: false, +// simpleView: false, +// }; +// const component = mount( +// +// ); + +// expect( +// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').prop('selected') +// ).toEqual(moment('2022-03-18T08:25:00.000Z')); +// expect( +// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').prop('selected') +// ).toEqual(moment('2022-03-21T10:49:00.000Z')); +// expect( +// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').exists() +// ).toBeFalsy(); +// expect( +// component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') +// ).toEqual(true); +// expect( +// component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') +// ).toEqual('Event range'); +// expect(component.find('EuiComboBox[data-test-subj="lns-icon-select"]').exists()).toBeFalsy(); +// expect(component.find('TextDecorationSetting').exists()).toBeFalsy(); +// expect(component.find('LineStyleSettings').exists()).toBeFalsy(); +// expect(component.find('[data-test-subj="lns-xyAnnotation-fillStyle"]').exists()).toBeTruthy(); +// }); + +// test('calculates correct endTimstamp and transparent color when switching for range annotation and back', () => { +// const state = testState(); +// const setState = jest.fn(); +// const component = mount( +// +// ); + +// component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); + +// expect(setState).toBeCalledWith({ +// ...state, +// layers: [ +// { +// annotations: [ +// { +// color: '#FF00001A', +// id: 'ann1', +// isHidden: undefined, +// label: 'Event range', +// type: 'manual', +// key: { +// endTimestamp: '2022-03-21T10:49:00.000Z', +// timestamp: '2022-03-18T08:25:00.000Z', +// type: 'range', +// }, +// }, +// ], +// indexPatternId: 'indexPattern1', +// layerId: 'annotation', +// layerType: 'annotations', +// ignoreGlobalFilters: true, +// hide: false, +// simpleView: false, +// }, +// ], +// }); +// component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); +// expect(setState).toBeCalledWith({ +// ...state, +// layers: [ +// { +// annotations: [ +// { +// color: '#FF0000', +// id: 'ann1', +// isHidden: undefined, +// key: { +// timestamp: '2022-03-18T08:25:00.000Z', +// type: 'point_in_time', +// }, +// label: 'Event', +// type: 'manual', +// }, +// ], +// indexPatternId: 'indexPattern1', +// layerId: 'annotation', +// layerType: 'annotations', +// ignoreGlobalFilters: true, +// hide: false, +// simpleView: false, +// }, +// ], +// }); +// }); + +// test('shows correct options for query based', () => { +// const state = testState(); +// const indexPattern = createMockedIndexPattern(); +// state.layers[0] = { +// annotations: [ +// { +// color: 'red', +// icon: 'triangle', +// id: 'ann1', +// type: 'query', +// isHidden: undefined, +// timeField: 'timestamp', +// key: { +// type: 'point_in_time', +// }, +// label: 'Query based event', +// lineStyle: 'dashed', +// lineWidth: 3, +// filter: { type: 'kibana_query', query: '', language: 'kuery' }, +// }, +// ], +// layerId: 'annotation', +// layerType: 'annotations', +// indexPatternId: indexPattern.id, +// ignoreGlobalFilters: true, +// hide: false, +// simpleView: false, +// }; +// const frameMock = createMockFramePublicAPI({ +// datasourceLayers: {}, +// dataViews: createMockDataViewsState({ +// indexPatterns: { [indexPattern.id]: indexPattern }, +// }), +// }); + +// const component = mount( +// +// ); + +// expect( +// component.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]').exists() +// ).toBeTruthy(); +// expect( +// component.find('[data-test-subj="lnsXY-annotation-query-based-query-input"]').exists() +// ).toBeTruthy(); + +// // The provided indexPattern has 2 date fields +// expect( +// component +// .find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]') +// .at(0) +// .prop('options') +// ).toHaveLength(2); +// // When in query mode a new "field" option is added to the previous 2 ones +// expect( +// component.find('[data-test-subj="lns-lineMarker-text-visibility"]').at(0).prop('options') +// ).toHaveLength(3); +// expect( +// component.find('[data-test-subj="lnsXY-annotation-tooltip-add_field"]').exists() +// ).toBeTruthy(); +// }); + +// test('should prefill timeField with the default time field when switching to query based annotations', () => { +// const state = testState(); +// const indexPattern = createMockedIndexPattern(); +// state.layers[0] = { +// annotations: [customLineStaticAnnotation], +// layerId: 'annotation', +// layerType: 'annotations', +// ignoreGlobalFilters: true, +// indexPatternId: indexPattern.id, +// hide: false, +// simpleView: false, +// }; +// const frameMock = createMockFramePublicAPI({ +// datasourceLayers: {}, +// dataViews: createMockDataViewsState({ +// indexPatterns: { [indexPattern.id]: indexPattern }, +// }), +// }); + +// const setState = jest.fn(); + +// const component = mount( +// +// ); + +// act(() => { +// component +// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) +// .find(EuiButtonGroup) +// .prop('onChange')!('lens_xyChart_annotation_query'); +// }); +// component.update(); + +// expect(setState).toHaveBeenCalledWith( +// expect.objectContaining({ +// layers: [ +// expect.objectContaining({ +// annotations: [expect.objectContaining({ timeField: 'timestamp' })], +// }), +// ], +// }) +// ); +// }); + +// test('should avoid to retain specific manual configurations when switching to query based annotations', () => { +// const state = testState(); +// const indexPattern = createMockedIndexPattern(); +// state.layers[0] = { +// annotations: [customLineStaticAnnotation], +// layerId: 'annotation', +// layerType: 'annotations', +// ignoreGlobalFilters: true, +// indexPatternId: indexPattern.id, +// hide: false, +// simpleView: false, +// }; +// const frameMock = createMockFramePublicAPI({ +// datasourceLayers: {}, +// dataViews: createMockDataViewsState({ +// indexPatterns: { [indexPattern.id]: indexPattern }, +// }), +// }); + +// const setState = jest.fn(); + +// const component = mount( +// +// ); + +// act(() => { +// component +// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) +// .find(EuiButtonGroup) +// .prop('onChange')!('lens_xyChart_annotation_query'); +// }); +// component.update(); + +// expect(setState).toHaveBeenCalledWith( +// expect.objectContaining({ +// layers: [ +// expect.objectContaining({ +// annotations: [ +// expect.objectContaining({ +// key: expect.not.objectContaining({ timestamp: expect.any('string') }), +// }), +// ], +// }), +// ], +// }) +// ); +// }); + +// test('should avoid to retain range manual configurations when switching to query based annotations', () => { +// const state = testState(); +// const indexPattern = createMockedIndexPattern(); +// state.layers[0] = { +// annotations: [ +// { +// color: 'red', +// icon: 'triangle', +// id: 'ann1', +// type: 'manual', +// isHidden: undefined, +// key: { +// endTimestamp: '2022-03-21T10:49:00.000Z', +// timestamp: '2022-03-18T08:25:00.000Z', +// type: 'range', +// }, +// label: 'Event range', +// lineStyle: 'dashed', +// lineWidth: 3, +// }, +// ], +// layerId: 'annotation', +// layerType: 'annotations', +// ignoreGlobalFilters: true, +// indexPatternId: indexPattern.id, +// hide: false, +// simpleView: false, +// }; +// const frameMock = createMockFramePublicAPI({ +// datasourceLayers: {}, +// dataViews: createMockDataViewsState({ +// indexPatterns: { [indexPattern.id]: indexPattern }, +// }), +// }); + +// const setState = jest.fn(); + +// const component = mount( +// +// ); + +// act(() => { +// component +// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) +// .find(EuiButtonGroup) +// .prop('onChange')!('lens_xyChart_annotation_query'); +// }); +// component.update(); + +// expect(setState).toHaveBeenCalledWith( +// expect.objectContaining({ +// layers: [ +// expect.objectContaining({ +// annotations: [ +// expect.objectContaining({ label: expect.not.stringContaining('Event range') }), +// ], +// }), +// ], +// }) +// ); +// }); + +// test('should set a default tiemstamp when switching from query based to manual annotations', () => { +// const state = testState(); +// const indexPattern = createMockedIndexPattern(); +// state.layers[0] = { +// annotations: [ +// { +// color: 'red', +// icon: 'triangle', +// id: 'ann1', +// type: 'query', +// isHidden: undefined, +// timeField: 'timestamp', +// key: { +// type: 'point_in_time', +// }, +// label: 'Query based event', +// lineStyle: 'dashed', +// lineWidth: 3, +// filter: { type: 'kibana_query', query: '', language: 'kuery' }, +// }, +// ], +// layerId: 'annotation', +// layerType: 'annotations', +// indexPatternId: indexPattern.id, +// ignoreGlobalFilters: true, +// hide: false, +// simpleView: false, +// }; +// const frameMock = createMockFramePublicAPI({ +// datasourceLayers: {}, +// dataViews: createMockDataViewsState({ +// indexPatterns: { [indexPattern.id]: indexPattern }, +// }), +// }); + +// const setState = jest.fn(); + +// const component = mount( +// +// ); + +// act(() => { +// component +// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) +// .find(EuiButtonGroup) +// .prop('onChange')!('lens_xyChart_annotation_manual'); +// }); +// component.update(); + +// expect(setState).toHaveBeenCalledWith( +// expect.objectContaining({ +// layers: [ +// expect.objectContaining({ +// annotations: [ +// expect.objectContaining({ +// key: { type: 'point_in_time', timestamp: expect.any(String) }, +// }), +// ], +// }), +// ], +// }) +// ); + +// // also check query specific props are not carried over +// expect(setState).toHaveBeenCalledWith( +// expect.objectContaining({ +// layers: [ +// expect.objectContaining({ +// annotations: [expect.not.objectContaining({ timeField: 'timestamp' })], +// }), +// ], +// }) +// ); +// }); + +// test('should fallback to the first date field available in the dataView if not time-based', () => { +// const state = testState(); +// const indexPattern = createMockedIndexPattern({ timeFieldName: '' }); +// state.layers[0] = { +// annotations: [customLineStaticAnnotation], +// layerId: 'annotation', +// layerType: 'annotations', +// ignoreGlobalFilters: true, +// indexPatternId: indexPattern.id, +// hide: false, +// simpleView: false, +// }; +// const frameMock = createMockFramePublicAPI({ +// datasourceLayers: {}, +// dataViews: createMockDataViewsState({ +// indexPatterns: { [indexPattern.id]: indexPattern }, +// }), +// }); + +// const setState = jest.fn(); + +// const component = mount( +// +// ); + +// act(() => { +// component +// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) +// .find(EuiButtonGroup) +// .prop('onChange')!('lens_xyChart_annotation_query'); +// }); +// component.update(); + +// expect(setState).toHaveBeenCalledWith( +// expect.objectContaining({ +// layers: [ +// expect.objectContaining({ +// annotations: [expect.objectContaining({ timeField: 'timestampLabel' })], +// }), +// ], +// }) +// ); +// }); +// }); +// }); diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts similarity index 89% rename from src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx rename to src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts index 43fe1440fbff6..021339e9d6683 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts @@ -7,3 +7,4 @@ */ export { AnnotationEditorControls } from './annotation_editor_controls'; +export { annotationsIconSet } from './icon_set'; diff --git a/src/plugins/event_annotation/public/components/index.ts b/src/plugins/event_annotation/public/components/index.ts new file mode 100644 index 0000000000000..4a8d66671c62d --- /dev/null +++ b/src/plugins/event_annotation/public/components/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 './annotation_editor_controls'; diff --git a/src/plugins/event_annotation/public/index.ts b/src/plugins/event_annotation/public/index.ts index 58f6e2c7c9f22..aa9921c84e24b 100644 --- a/src/plugins/event_annotation/public/index.ts +++ b/src/plugins/event_annotation/public/index.ts @@ -21,3 +21,4 @@ export { isManualPointAnnotationConfig, isQueryAnnotationConfig, } from './event_annotation_service/helpers'; +export { AnnotationEditorControls, annotationsIconSet } from './components'; diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 4806367278320..39c043a7d8ce3 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -29,7 +29,7 @@ "@kbn/data-views-plugin", "@kbn/visualization-ui-components", "@kbn/chart-icons", - "@kbn/unified-field-list" + "@kbn/unified-field-list-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index 4a3591bddf42c..dc1e95a2e52e1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -12,6 +12,7 @@ import { defaultAnnotationRangeColor, isQueryAnnotationConfig, isRangeAnnotationConfig, + annotationsIconSet, } from '@kbn/event-annotation-plugin/public'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { IconChartBarAnnotations } from '@kbn/chart-icons'; @@ -20,7 +21,6 @@ import type { AccessorConfig } from '@kbn/visualization-ui-components/public'; import { isDraggedDataViewField } from '../../../utils'; import type { FramePublicAPI, Visualization } from '../../../types'; import { isHorizontalChart } from '../state_helpers'; -import { annotationsIconSet } from '../xy_config_panel/annotations_config_panel/icon_set'; import type { XYState, XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../types'; import { checkScaleOperation, diff --git a/x-pack/plugins/lens/public/visualizations/xy/index.ts b/x-pack/plugins/lens/public/visualizations/xy/index.ts index 244d47cd5b114..6f590000d512a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/index.ts @@ -30,7 +30,15 @@ export class XyVisualization { const { getXyVisualization } = await import('../../async_services'); const [ coreStart, - { charts, data, fieldFormats, eventAnnotation, unifiedSearch, savedObjectsTagging }, + { + charts, + data, + fieldFormats, + eventAnnotation, + unifiedSearch, + savedObjectsTagging, + dataViews, + }, ] = await core.getStartServices(); const [palettes, eventAnnotationService] = await Promise.all([ charts.palettes.getPalettes(), @@ -47,6 +55,7 @@ export class XyVisualization { useLegacyTimeAxis, kibanaTheme: core.theme, unifiedSearch, + dataViewsService: dataViews, savedObjectsTagging, }); }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 813f1e28b7332..93cb2c1d39dc7 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -26,6 +26,7 @@ import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common' import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { isEqual } from 'lodash'; import type { AccessorConfig } from '@kbn/visualization-ui-components/public'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -37,8 +38,8 @@ import { import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { + DataDimensionEditor, DataDimensionEditorDataSectionExtra, - DimensionEditor, } from './xy_config_panel/dimension_editor'; import { LayerHeader, LayerHeaderContent } from './xy_config_panel/layer_header'; import type { @@ -129,6 +130,7 @@ export const getXyVisualization = ({ kibanaTheme, eventAnnotationService, unifiedSearch, + dataViewsService, savedObjectsTagging, }: { core: CoreStart; @@ -140,6 +142,7 @@ export const getXyVisualization = ({ useLegacyTimeAxis: boolean; kibanaTheme: ThemeServiceStart; unifiedSearch: UnifiedSearchPublicPluginStart; + dataViewsService: DataViewsPublicPluginStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; }): Visualization => ({ id: XY_ID, @@ -641,9 +644,9 @@ export const getXyVisualization = ({ const dimensionEditor = isReferenceLayer(layer) ? ( ) : isAnnotationsLayer(layer) ? ( - + ) : ( - + ); render( diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index 9ff2d0abe1b89..35fa11957bf37 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -5,58 +5,29 @@ * 2.0. */ -import './index.scss'; -import React, { useCallback, useEffect } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiButtonGroup, EuiSpacer } from '@elastic/eui'; -import type { PaletteRegistry } from '@kbn/coloring'; +import React, { useCallback, useEffect, useState } from 'react'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import { - defaultAnnotationColor, - defaultAnnotationRangeColor, - isQueryAnnotationConfig, - isRangeAnnotationConfig, -} from '@kbn/event-annotation-plugin/public'; -import { - EventAnnotationConfig, - PointInTimeEventAnnotationConfig, - QueryPointEventAnnotationConfig, -} from '@kbn/event-annotation-plugin/common'; +import { AnnotationEditorControls } from '@kbn/event-annotation-plugin/public'; +import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useDebouncedValue } from '@kbn/visualization-ui-components/public'; +import { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public'; import moment from 'moment'; -import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; -import { - IconSelectSetting, - FieldOption, - FieldOptionValue, - FieldPicker, - NameInput, - useDebouncedValue, - DimensionEditorSection, - ColorPicker, - LineStyleSettings, - TextDecorationSetting, -} from '@kbn/visualization-ui-components/public'; -import { FormatFactory } from '../../../../../common/types'; -import { defaultAnnotationLabel, defaultRangeAnnotationLabel } from '../../annotations/helpers'; +import { search } from '@kbn/data-plugin/public'; +import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; +import { LensAppServices } from '../../../../app_plugin/types'; import { updateLayer } from '..'; -import { annotationsIconSet } from './icon_set'; -import type { VisualizationDimensionEditorProps } from '../../../../types'; -import type { State, XYState, XYAnnotationLayerConfig } from '../../types'; -import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; -import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; -import { TooltipSection } from './tooltip_annotation_panel'; -import { sanitizeProperties, toLineAnnotationColor } from './helpers'; -import { idPrefix } from '../dimension_editor'; +import type { FramePublicAPI, VisualizationDimensionEditorProps } from '../../../../types'; +import type { State, XYState, XYAnnotationLayerConfig, XYDataLayerConfig } from '../../types'; +import { isDataLayer } from '../../visualization_helpers'; export const AnnotationsPanel = ( props: VisualizationDimensionEditorProps & { datatableUtilities: DatatableUtilitiesService; - formatFactory: FormatFactory; - paletteService: PaletteRegistry; + dataViewsService: DataViewsPublicPluginStart; } ) => { const { state, setState, layerId, accessor, frame } = props; - const { hasFieldData } = useExistingFieldsReader(); const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ value: state, @@ -70,26 +41,15 @@ export const AnnotationsPanel = ( const currentAnnotation = localLayer.annotations?.find((c) => c.id === accessor); - const isQueryBased = isQueryAnnotationConfig(currentAnnotation); - const isRange = isRangeAnnotationConfig(currentAnnotation); - const [queryInputShouldOpen, setQueryInputShouldOpen] = React.useState(false); - useEffect(() => { - setQueryInputShouldOpen(!isQueryBased); - }, [isQueryBased]); - - const setAnnotations = useCallback( - (annotation: Partial | undefined) => { + const setAnnotation = useCallback( + (annotation: EventAnnotationConfig) => { if (annotation == null) { return; } const newConfigs = [...(localLayer.annotations || [])]; const existingIndex = newConfigs.findIndex((c) => c.id === accessor); if (existingIndex !== -1) { - const existingConfig = newConfigs[existingIndex]; - newConfigs[existingIndex] = sanitizeProperties({ - ...existingConfig, - ...annotation, - }); + newConfigs[existingIndex] = annotation; } else { throw new Error( 'should never happen because annotation is created before config panel is opened' @@ -100,305 +60,80 @@ export const AnnotationsPanel = ( [accessor, index, localState, localLayer, setLocalState] ); - return ( - <> - - - { - const typeFromId = id.replace( - 'lens_xyChart_annotation_', - '' - ) as EventAnnotationConfig['type']; - if (currentAnnotation?.type === typeFromId) { - return; - } - if (typeFromId === 'query') { - const currentIndexPattern = - frame.dataViews.indexPatterns[localLayer.indexPatternId]; - // If coming from a range type, it requires some additional resets - const additionalRangeResets = isRangeAnnotationConfig(currentAnnotation) - ? { - label: - currentAnnotation.label === defaultRangeAnnotationLabel - ? defaultAnnotationLabel - : currentAnnotation.label, - color: toLineAnnotationColor(currentAnnotation.color), - } - : {}; - return setAnnotations({ - type: typeFromId, - timeField: - (currentIndexPattern.timeFieldName || - // fallback to the first avaiable date field in the dataView - currentIndexPattern.fields.find(({ type: fieldType }) => fieldType === 'date') - ?.displayName) ?? - '', - key: { type: 'point_in_time' }, - ...additionalRangeResets, - }); - } - // From query to manual annotation - return setAnnotations({ - type: typeFromId, - key: { type: 'point_in_time', timestamp: moment().toISOString() }, - }); - }} - isFullWidth - /> - - {isQueryBased ? ( - - ) : ( - - )} - - - { - setAnnotations({ label: value }); - }} - /> - {!isRange && ( - <> - setAnnotations({ icon })} - defaultIcon="triangle" - currentIcon={currentAnnotation?.icon} - customIconSet={annotationsIconSet} - /> - - {(textDecorationSelected) => { - if (textDecorationSelected !== 'field') { - return null; - } - const currentIndexPattern = - frame.dataViews.indexPatterns[localLayer.indexPatternId]; - const options = currentIndexPattern.fields - .filter(({ displayName, type }) => displayName && type !== 'document') - .map( - (field) => - ({ - label: field.displayName, - value: { - type: 'field', - field: field.name, - dataType: field.type, - }, - exists: hasFieldData(currentIndexPattern.id, field.name), - compatible: true, - 'data-test-subj': `lnsXY-annotation-fieldOption-${field.name}`, - } as FieldOption) - ); - const selectedField = (currentAnnotation as QueryPointEventAnnotationConfig) - .textField; + const [currentDataView, setCurrentDataView] = useState(); + + useEffect(() => { + const updateDataView = async () => { + let dataView: DataView; + const availableIds = await props.dataViewsService.getIds(); + if (availableIds.includes(localLayer.indexPatternId)) { + dataView = await props.dataViewsService.get(localLayer.indexPatternId); + } else { + dataView = await props.dataViewsService.create( + frame.dataViews.indexPatterns[localLayer.indexPatternId].spec + ); + } + setCurrentDataView(dataView); + }; + + updateDataView(); + }, [frame.dataViews.indexPatterns, localLayer.indexPatternId, props.dataViewsService]); - const fieldIsValid = selectedField - ? Boolean(currentIndexPattern.getFieldByName(selectedField)) - : true; - return ( - <> - - - - ); - }} - - - - )} - {isRange && ( - - { - setAnnotations({ - outside: id === `lens_xyChart_fillStyle_outside`, - }); - }} - isFullWidth - /> - - )} + const queryInputServices = useKibana().services; - - setAnnotations({ isHidden: ev.target.checked })} - /> - - {isQueryBased && currentAnnotation && ( - - - - - - )} - - ); -}; + if (!currentAnnotation) { + throw new Error('Annotation not found... this should never happen!'); + } -const ConfigPanelGenericSwitch = ({ - label, - ['data-test-subj']: dataTestSubj, - value, - onChange, -}: { - label: string; - 'data-test-subj': string; - value: boolean; - onChange: (event: EuiSwitchEvent) => void; -}) => ( - - setAnnotation(newAnnotation)} + dataView={currentDataView} + getDefaultRangeEnd={(rangeStart) => + getEndTimestamp( + props.datatableUtilities, + rangeStart, + frame, + localState.layers.filter(isDataLayer) + ) + } + queryInputServices={queryInputServices} + calendarClassName={DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS} /> - -); + ) : null; +}; + +const getEndTimestamp = ( + datatableUtilities: DatatableUtilitiesService, + startTime: string, + { activeData, dateRange }: FramePublicAPI, + dataLayers: XYDataLayerConfig[] +) => { + const startTimeNumber = moment(startTime).valueOf(); + const dateRangeFraction = + (moment(dateRange.toDate).valueOf() - moment(dateRange.fromDate).valueOf()) * 0.1; + const fallbackValue = moment(startTimeNumber + dateRangeFraction).toISOString(); + const dataLayersId = dataLayers.map(({ layerId }) => layerId); + if ( + !dataLayersId.length || + !activeData || + Object.entries(activeData) + .filter(([key]) => dataLayersId.includes(key)) + .every(([, { rows }]) => !rows || !rows.length) + ) { + return fallbackValue; + } + const xColumn = activeData?.[dataLayersId[0]].columns.find( + (column) => column.id === dataLayers[0].xAccessor + ); + if (!xColumn) { + return fallbackValue; + } + + const dateInterval = datatableUtilities.getDateHistogramMeta(xColumn)?.interval; + if (!dateInterval) return fallbackValue; + const intervalDuration = search.aggs.parseInterval(dateInterval); + if (!intervalDuration) return fallbackValue; + return moment(startTimeNumber + 3 * intervalDuration.as('milliseconds')).toISOString(); +}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts deleted file mode 100644 index 2ca0f9530bbe1..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/helpers.ts +++ /dev/null @@ -1,116 +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 { search } from '@kbn/data-plugin/public'; -import { transparentize } from '@elastic/eui'; -import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import type { - EventAnnotationConfig, - RangeEventAnnotationConfig, - PointInTimeEventAnnotationConfig, - QueryPointEventAnnotationConfig, -} from '@kbn/event-annotation-plugin/common'; -import { - defaultAnnotationColor, - defaultAnnotationRangeColor, - isQueryAnnotationConfig, - isRangeAnnotationConfig, -} from '@kbn/event-annotation-plugin/public'; -import Color from 'color'; -import { pick } from 'lodash'; -import moment from 'moment'; -import type { FramePublicAPI } from '../../../../types'; -import type { XYDataLayerConfig } from '../../types'; - -export const toRangeAnnotationColor = (color = defaultAnnotationColor) => { - return new Color(transparentize(color, 0.1)).hexa(); -}; - -export const toLineAnnotationColor = (color = defaultAnnotationRangeColor) => { - return new Color(transparentize(color, 1)).hex(); -}; - -export const getEndTimestamp = ( - datatableUtilities: DatatableUtilitiesService, - startTime: string, - { activeData, dateRange }: FramePublicAPI, - dataLayers: XYDataLayerConfig[] -) => { - const startTimeNumber = moment(startTime).valueOf(); - const dateRangeFraction = - (moment(dateRange.toDate).valueOf() - moment(dateRange.fromDate).valueOf()) * 0.1; - const fallbackValue = moment(startTimeNumber + dateRangeFraction).toISOString(); - const dataLayersId = dataLayers.map(({ layerId }) => layerId); - if ( - !dataLayersId.length || - !activeData || - Object.entries(activeData) - .filter(([key]) => dataLayersId.includes(key)) - .every(([, { rows }]) => !rows || !rows.length) - ) { - return fallbackValue; - } - const xColumn = activeData?.[dataLayersId[0]].columns.find( - (column) => column.id === dataLayers[0].xAccessor - ); - if (!xColumn) { - return fallbackValue; - } - - const dateInterval = datatableUtilities.getDateHistogramMeta(xColumn)?.interval; - if (!dateInterval) return fallbackValue; - const intervalDuration = search.aggs.parseInterval(dateInterval); - if (!intervalDuration) return fallbackValue; - return moment(startTimeNumber + 3 * intervalDuration.as('milliseconds')).toISOString(); -}; - -export const sanitizeProperties = (annotation: EventAnnotationConfig) => { - if (isRangeAnnotationConfig(annotation)) { - const rangeAnnotation: RangeEventAnnotationConfig = pick(annotation, [ - 'type', - 'label', - 'key', - 'id', - 'isHidden', - 'color', - 'outside', - ]); - return rangeAnnotation; - } - if (isQueryAnnotationConfig(annotation)) { - const lineAnnotation: QueryPointEventAnnotationConfig = pick(annotation, [ - 'type', - 'id', - 'label', - 'key', - 'timeField', - 'isHidden', - 'lineStyle', - 'lineWidth', - 'color', - 'icon', - 'textVisibility', - 'textField', - 'filter', - 'extraFields', - ]); - return lineAnnotation; - } - const lineAnnotation: PointInTimeEventAnnotationConfig = pick(annotation, [ - 'type', - 'id', - 'label', - 'key', - 'isHidden', - 'lineStyle', - 'lineWidth', - 'color', - 'icon', - 'textVisibility', - ]); - return lineAnnotation; -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts deleted file mode 100644 index 3e67cd8e5c14e..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/icon_set.ts +++ /dev/null @@ -1,106 +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'; -import { AvailableAnnotationIcon } from '@kbn/event-annotation-plugin/common'; -import { IconTriangle, IconCircle } from '@kbn/chart-icons'; -import { type IconSet } from '@kbn/visualization-ui-components/public'; - -export const annotationsIconSet: IconSet = [ - { - value: 'asterisk', - label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { - defaultMessage: 'Asterisk', - }), - }, - { - value: 'alert', - label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { - defaultMessage: 'Alert', - }), - }, - { - value: 'bell', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { - defaultMessage: 'Bell', - }), - }, - { - value: 'bolt', - label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { - defaultMessage: 'Bolt', - }), - }, - { - value: 'bug', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { - defaultMessage: 'Bug', - }), - }, - { - value: 'circle', - label: i18n.translate('xpack.lens.xyChart.iconSelect.circleIconLabel', { - defaultMessage: 'Circle', - }), - icon: IconCircle, - canFill: true, - }, - - { - value: 'editorComment', - label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { - defaultMessage: 'Comment', - }), - }, - { - value: 'flag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { - defaultMessage: 'Flag', - }), - }, - { - value: 'heart', - label: i18n.translate('xpack.lens.xyChart.iconSelect.heartLabel', { defaultMessage: 'Heart' }), - }, - { - value: 'mapMarker', - label: i18n.translate('xpack.lens.xyChart.iconSelect.mapMarkerLabel', { - defaultMessage: 'Map Marker', - }), - }, - { - value: 'pinFilled', - label: i18n.translate('xpack.lens.xyChart.iconSelect.mapPinLabel', { - defaultMessage: 'Map Pin', - }), - }, - { - value: 'starEmpty', - label: i18n.translate('xpack.lens.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), - }, - { - value: 'starFilled', - label: i18n.translate('xpack.lens.xyChart.iconSelect.starFilledLabel', { - defaultMessage: 'Star filled', - }), - }, - { - value: 'tag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { - defaultMessage: 'Tag', - }), - }, - { - value: 'triangle', - label: i18n.translate('xpack.lens.xyChart.iconSelect.triangleIconLabel', { - defaultMessage: 'Triangle', - }), - icon: IconTriangle, - shouldRotate: true, - canFill: true, - }, -]; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss deleted file mode 100644 index 93bf0e2c72662..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.scss +++ /dev/null @@ -1,19 +0,0 @@ -.lnsRowCompressedMargin+.lnsRowCompressedMargin { - margin-top: $euiSizeS; -} - -.lnsConfigPanelNoPadding { - padding: 0; -} - -.lnsConfigPanelDate__label { - min-width: 56px; // makes both labels ("from" and "to") the same width -} - -.lnsConfigPanelAnnotations__addButton { - margin-top: $euiSizeXS; -} - -.lnsConfigPanelAnnotations__fieldPicker { - cursor: pointer; -} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx deleted file mode 100644 index 3945f9aab5f2e..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx +++ /dev/null @@ -1,688 +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 { mountWithIntl as mount } from '@kbn/test-jest-helpers'; -import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; -import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import { AnnotationsPanel } from '.'; -import { FramePublicAPI } from '../../../../types'; -import { DatasourcePublicAPI } from '../../../..'; -import { createMockFramePublicAPI } from '../../../../mocks'; -import { State, XYState } from '../../types'; -import { Position } from '@elastic/charts'; -import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import moment from 'moment'; -import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; -import { createMockDataViewsState } from '../../../../data_views_service/mocks'; -import { createMockedIndexPattern } from '../../../../datasources/form_based/mocks'; -import { act } from 'react-dom/test-utils'; -import { EuiButtonGroup } from '@elastic/eui'; - -jest.mock('lodash', () => { - const original = jest.requireActual('lodash'); - - return { - ...original, - debounce: (fn: unknown) => fn, - }; -}); - -jest.mock('@kbn/unified-search-plugin/public', () => ({ - QueryStringInput: () => { - return 'QueryStringInput'; - }, -})); - -const customLineStaticAnnotation: EventAnnotationConfig = { - id: 'ann1', - type: 'manual', - key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, - label: 'Event', - icon: 'triangle' as const, - color: 'red', - lineStyle: 'dashed' as const, - lineWidth: 3, -}; - -describe('AnnotationsPanel', () => { - const datatableUtilities = createDatatableUtilitiesMock(); - let frame: FramePublicAPI; - - function testState(): State { - return { - legend: { isVisible: true, position: Position.Right }, - valueLabels: 'hide', - preferredSeriesType: 'bar', - layers: [ - { - layerType: LayerTypes.ANNOTATIONS, - layerId: 'annotation', - indexPatternId: 'indexPattern1', - annotations: [customLineStaticAnnotation], - ignoreGlobalFilters: true, - hide: false, - simpleView: false, - }, - ], - }; - } - - beforeEach(() => { - frame = createMockFramePublicAPI({ datasourceLayers: {} }); - }); - - describe('Dimension Editor', () => { - test('shows correct options for line annotations', () => { - const state = testState(); - const component = mount( - - ); - - expect( - component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').prop('selected') - ).toEqual(moment('2022-03-18T08:25:00.000Z')); - expect( - component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').exists() - ).toBeFalsy(); - expect( - component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').exists() - ).toBeFalsy(); - expect( - component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') - ).toEqual(false); - expect( - component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') - ).toEqual('Event'); - expect( - component.find('EuiComboBox[data-test-subj="lns-icon-select"]').prop('selectedOptions') - ).toEqual([{ label: 'Triangle', value: 'triangle' }]); - expect(component.find('TextDecorationSetting').exists()).toBeTruthy(); - expect(component.find('LineStyleSettings').exists()).toBeTruthy(); - expect( - component.find('EuiButtonGroup[data-test-subj="lns-xyAnnotation-fillStyle"]').exists() - ).toBeFalsy(); - }); - test('shows correct options for range annotations', () => { - const state = testState(); - state.layers[0] = { - annotations: [ - { - color: 'red', - icon: 'triangle', - id: 'ann1', - type: 'manual', - isHidden: undefined, - key: { - endTimestamp: '2022-03-21T10:49:00.000Z', - timestamp: '2022-03-18T08:25:00.000Z', - type: 'range', - }, - label: 'Event range', - lineStyle: 'dashed', - lineWidth: 3, - }, - ], - layerId: 'annotation', - layerType: 'annotations', - indexPatternId: 'indexPattern1', - ignoreGlobalFilters: true, - hide: false, - simpleView: false, - }; - const component = mount( - - ); - - expect( - component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').prop('selected') - ).toEqual(moment('2022-03-18T08:25:00.000Z')); - expect( - component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').prop('selected') - ).toEqual(moment('2022-03-21T10:49:00.000Z')); - expect( - component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').exists() - ).toBeFalsy(); - expect( - component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') - ).toEqual(true); - expect( - component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') - ).toEqual('Event range'); - expect(component.find('EuiComboBox[data-test-subj="lns-icon-select"]').exists()).toBeFalsy(); - expect(component.find('TextDecorationSetting').exists()).toBeFalsy(); - expect(component.find('LineStyleSettings').exists()).toBeFalsy(); - expect(component.find('[data-test-subj="lns-xyAnnotation-fillStyle"]').exists()).toBeTruthy(); - }); - - test('calculates correct endTimstamp and transparent color when switching for range annotation and back', () => { - const state = testState(); - const setState = jest.fn(); - const component = mount( - - ); - - component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); - - expect(setState).toBeCalledWith({ - ...state, - layers: [ - { - annotations: [ - { - color: '#FF00001A', - id: 'ann1', - isHidden: undefined, - label: 'Event range', - type: 'manual', - key: { - endTimestamp: '2022-03-21T10:49:00.000Z', - timestamp: '2022-03-18T08:25:00.000Z', - type: 'range', - }, - }, - ], - indexPatternId: 'indexPattern1', - layerId: 'annotation', - layerType: 'annotations', - ignoreGlobalFilters: true, - hide: false, - simpleView: false, - }, - ], - }); - component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); - expect(setState).toBeCalledWith({ - ...state, - layers: [ - { - annotations: [ - { - color: '#FF0000', - id: 'ann1', - isHidden: undefined, - key: { - timestamp: '2022-03-18T08:25:00.000Z', - type: 'point_in_time', - }, - label: 'Event', - type: 'manual', - }, - ], - indexPatternId: 'indexPattern1', - layerId: 'annotation', - layerType: 'annotations', - ignoreGlobalFilters: true, - hide: false, - simpleView: false, - }, - ], - }); - }); - - test('shows correct options for query based', () => { - const state = testState(); - const indexPattern = createMockedIndexPattern(); - state.layers[0] = { - annotations: [ - { - color: 'red', - icon: 'triangle', - id: 'ann1', - type: 'query', - isHidden: undefined, - timeField: 'timestamp', - key: { - type: 'point_in_time', - }, - label: 'Query based event', - lineStyle: 'dashed', - lineWidth: 3, - filter: { type: 'kibana_query', query: '', language: 'kuery' }, - }, - ], - layerId: 'annotation', - layerType: 'annotations', - indexPatternId: indexPattern.id, - ignoreGlobalFilters: true, - hide: false, - simpleView: false, - }; - const frameMock = createMockFramePublicAPI({ - datasourceLayers: {}, - dataViews: createMockDataViewsState({ - indexPatterns: { [indexPattern.id]: indexPattern }, - }), - }); - - const component = mount( - - ); - - expect( - component.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]').exists() - ).toBeTruthy(); - expect( - component.find('[data-test-subj="lnsXY-annotation-query-based-query-input"]').exists() - ).toBeTruthy(); - - // The provided indexPattern has 2 date fields - expect( - component - .find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]') - .at(0) - .prop('options') - ).toHaveLength(2); - // When in query mode a new "field" option is added to the previous 2 ones - expect( - component.find('[data-test-subj="lns-lineMarker-text-visibility"]').at(0).prop('options') - ).toHaveLength(3); - expect( - component.find('[data-test-subj="lnsXY-annotation-tooltip-add_field"]').exists() - ).toBeTruthy(); - }); - - test('should prefill timeField with the default time field when switching to query based annotations', () => { - const state = testState(); - const indexPattern = createMockedIndexPattern(); - state.layers[0] = { - annotations: [customLineStaticAnnotation], - layerId: 'annotation', - layerType: 'annotations', - ignoreGlobalFilters: true, - indexPatternId: indexPattern.id, - hide: false, - simpleView: false, - }; - const frameMock = createMockFramePublicAPI({ - datasourceLayers: {}, - dataViews: createMockDataViewsState({ - indexPatterns: { [indexPattern.id]: indexPattern }, - }), - }); - - const setState = jest.fn(); - - const component = mount( - - ); - - act(() => { - component - .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) - .find(EuiButtonGroup) - .prop('onChange')!('lens_xyChart_annotation_query'); - }); - component.update(); - - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: [ - expect.objectContaining({ - annotations: [expect.objectContaining({ timeField: 'timestamp' })], - }), - ], - }) - ); - }); - - test('should avoid to retain specific manual configurations when switching to query based annotations', () => { - const state = testState(); - const indexPattern = createMockedIndexPattern(); - state.layers[0] = { - annotations: [customLineStaticAnnotation], - layerId: 'annotation', - layerType: 'annotations', - ignoreGlobalFilters: true, - indexPatternId: indexPattern.id, - hide: false, - simpleView: false, - }; - const frameMock = createMockFramePublicAPI({ - datasourceLayers: {}, - dataViews: createMockDataViewsState({ - indexPatterns: { [indexPattern.id]: indexPattern }, - }), - }); - - const setState = jest.fn(); - - const component = mount( - - ); - - act(() => { - component - .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) - .find(EuiButtonGroup) - .prop('onChange')!('lens_xyChart_annotation_query'); - }); - component.update(); - - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: [ - expect.objectContaining({ - annotations: [ - expect.objectContaining({ - key: expect.not.objectContaining({ timestamp: expect.any('string') }), - }), - ], - }), - ], - }) - ); - }); - - test('should avoid to retain range manual configurations when switching to query based annotations', () => { - const state = testState(); - const indexPattern = createMockedIndexPattern(); - state.layers[0] = { - annotations: [ - { - color: 'red', - icon: 'triangle', - id: 'ann1', - type: 'manual', - isHidden: undefined, - key: { - endTimestamp: '2022-03-21T10:49:00.000Z', - timestamp: '2022-03-18T08:25:00.000Z', - type: 'range', - }, - label: 'Event range', - lineStyle: 'dashed', - lineWidth: 3, - }, - ], - layerId: 'annotation', - layerType: 'annotations', - ignoreGlobalFilters: true, - indexPatternId: indexPattern.id, - hide: false, - simpleView: false, - }; - const frameMock = createMockFramePublicAPI({ - datasourceLayers: {}, - dataViews: createMockDataViewsState({ - indexPatterns: { [indexPattern.id]: indexPattern }, - }), - }); - - const setState = jest.fn(); - - const component = mount( - - ); - - act(() => { - component - .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) - .find(EuiButtonGroup) - .prop('onChange')!('lens_xyChart_annotation_query'); - }); - component.update(); - - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: [ - expect.objectContaining({ - annotations: [ - expect.objectContaining({ label: expect.not.stringContaining('Event range') }), - ], - }), - ], - }) - ); - }); - - test('should set a default tiemstamp when switching from query based to manual annotations', () => { - const state = testState(); - const indexPattern = createMockedIndexPattern(); - state.layers[0] = { - annotations: [ - { - color: 'red', - icon: 'triangle', - id: 'ann1', - type: 'query', - isHidden: undefined, - timeField: 'timestamp', - key: { - type: 'point_in_time', - }, - label: 'Query based event', - lineStyle: 'dashed', - lineWidth: 3, - filter: { type: 'kibana_query', query: '', language: 'kuery' }, - }, - ], - layerId: 'annotation', - layerType: 'annotations', - indexPatternId: indexPattern.id, - ignoreGlobalFilters: true, - hide: false, - simpleView: false, - }; - const frameMock = createMockFramePublicAPI({ - datasourceLayers: {}, - dataViews: createMockDataViewsState({ - indexPatterns: { [indexPattern.id]: indexPattern }, - }), - }); - - const setState = jest.fn(); - - const component = mount( - - ); - - act(() => { - component - .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) - .find(EuiButtonGroup) - .prop('onChange')!('lens_xyChart_annotation_manual'); - }); - component.update(); - - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: [ - expect.objectContaining({ - annotations: [ - expect.objectContaining({ - key: { type: 'point_in_time', timestamp: expect.any(String) }, - }), - ], - }), - ], - }) - ); - - // also check query specific props are not carried over - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: [ - expect.objectContaining({ - annotations: [expect.not.objectContaining({ timeField: 'timestamp' })], - }), - ], - }) - ); - }); - - test('should fallback to the first date field available in the dataView if not time-based', () => { - const state = testState(); - const indexPattern = createMockedIndexPattern({ timeFieldName: '' }); - state.layers[0] = { - annotations: [customLineStaticAnnotation], - layerId: 'annotation', - layerType: 'annotations', - ignoreGlobalFilters: true, - indexPatternId: indexPattern.id, - hide: false, - simpleView: false, - }; - const frameMock = createMockFramePublicAPI({ - datasourceLayers: {}, - dataViews: createMockDataViewsState({ - indexPatterns: { [indexPattern.id]: indexPattern }, - }), - }); - - const setState = jest.fn(); - - const component = mount( - - ); - - act(() => { - component - .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) - .find(EuiButtonGroup) - .prop('onChange')!('lens_xyChart_annotation_query'); - }); - component.update(); - - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: [ - expect.objectContaining({ - annotations: [expect.objectContaining({ timeField: 'timestampLabel' })], - }), - ], - }) - ); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.tsx deleted file mode 100644 index 795d076f22f0f..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/manual_annotation_panel.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 { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import { isRangeAnnotationConfig } from '@kbn/event-annotation-plugin/public'; -import { i18n } from '@kbn/i18n'; -import moment from 'moment'; -import React from 'react'; -import type { FramePublicAPI } from '../../../../types'; -import type { XYState } from '../../types'; -import { - ConfigPanelRangeDatePicker, - ConfigPanelApplyAsRangeSwitch, -} from './range_annotation_panel'; -import type { ManualEventAnnotationType } from './types'; - -export const ConfigPanelManualAnnotation = ({ - annotation, - frame, - state, - onChange, - datatableUtilities, -}: { - annotation?: ManualEventAnnotationType | undefined; - onChange: (annotation: Partial | undefined) => void; - datatableUtilities: DatatableUtilitiesService; - frame: FramePublicAPI; - state: XYState; -}) => { - const isRange = isRangeAnnotationConfig(annotation); - return ( - <> - {isRange ? ( - <> - { - if (date) { - const currentEndTime = moment(annotation?.key.endTimestamp).valueOf(); - if (currentEndTime < date.valueOf()) { - const currentStartTime = moment(annotation?.key.timestamp).valueOf(); - const dif = currentEndTime - currentStartTime; - onChange({ - key: { - ...(annotation?.key || { type: 'range' }), - timestamp: date.toISOString(), - endTimestamp: moment(date.valueOf() + dif).toISOString(), - }, - }); - } else { - onChange({ - key: { - ...(annotation?.key || { type: 'range' }), - timestamp: date.toISOString(), - }, - }); - } - } - }} - label={i18n.translate('xpack.lens.xyChart.annotationDate', { - defaultMessage: 'Annotation date', - })} - /> - { - if (date) { - const currentStartTime = moment(annotation?.key.timestamp).valueOf(); - if (currentStartTime > date.valueOf()) { - const currentEndTime = moment(annotation?.key.endTimestamp).valueOf(); - const dif = currentEndTime - currentStartTime; - onChange({ - key: { - ...(annotation?.key || { type: 'range' }), - endTimestamp: date.toISOString(), - timestamp: moment(date.valueOf() - dif).toISOString(), - }, - }); - } else { - onChange({ - key: { - ...(annotation?.key || { type: 'range' }), - endTimestamp: date.toISOString(), - }, - }); - } - } - }} - /> - - ) : ( - { - if (date) { - onChange({ - key: { - ...(annotation?.key || { type: 'point_in_time' }), - timestamp: date.toISOString(), - }, - }); - } - }} - /> - )} - - - ); -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx deleted file mode 100644 index e502efe559597..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/query_annotation_panel.tsx +++ /dev/null @@ -1,127 +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 { EuiFormRow } from '@elastic/eui'; -import type { Query } from '@kbn/data-plugin/common'; -import type { QueryPointEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; -import { - FieldOption, - FilterQueryInput, - FieldOptionValue, - FieldPicker, -} from '@kbn/visualization-ui-components/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { LENS_APP_NAME } from '../../../../../common/constants'; -import type { FramePublicAPI } from '../../../../types'; -import type { XYState, XYAnnotationLayerConfig } from '../../types'; -import { LensAppServices } from '../../../../app_plugin/types'; - -export const defaultQuery: Query = { - query: '', - language: 'kuery', -}; - -export const ConfigPanelQueryAnnotation = ({ - annotation, - frame, - state, - onChange, - layer, - queryInputShouldOpen, -}: { - annotation?: QueryPointEventAnnotationConfig; - onChange: (annotations: Partial | undefined) => void; - frame: FramePublicAPI; - state: XYState; - layer: XYAnnotationLayerConfig; - queryInputShouldOpen?: boolean; -}) => { - const currentIndexPattern = frame.dataViews.indexPatterns[layer.indexPatternId]; - const { hasFieldData } = useExistingFieldsReader(); - // list only date fields - const options = currentIndexPattern.fields - .filter((field) => field.type === 'date' && field.displayName) - .map((field) => { - return { - label: field.displayName, - value: { - type: 'field', - field: field.name, - dataType: field.type, - }, - exists: hasFieldData(currentIndexPattern.id, field.name), - compatible: true, - 'data-test-subj': `lns-fieldOption-${field.name}`, - } as FieldOption; - }); - - const selectedField = - annotation?.timeField || currentIndexPattern.timeFieldName || options[0]?.value.field; - const fieldIsValid = selectedField - ? Boolean(currentIndexPattern.getFieldByName(selectedField)) - : true; - - return ( - <> - - { - onChange({ filter: { type: 'kibana_query', ...query } }); - }} - data-test-subj="lnsXY-annotation-query-based-query-input" - dataView={currentIndexPattern} - appName={LENS_APP_NAME} - queryInputServices={useKibana().services} - /> - - - - - - - ); -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx deleted file mode 100644 index 1bed2d760514b..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/range_annotation_panel.tsx +++ /dev/null @@ -1,154 +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 { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import type { - PointInTimeEventAnnotationConfig, - RangeEventAnnotationConfig, -} from '@kbn/event-annotation-plugin/common'; -import { isRangeAnnotationConfig } from '@kbn/event-annotation-plugin/public'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { - EuiFormRow, - EuiSwitch, - EuiText, - EuiFormControlLayout, - EuiFormLabel, - EuiDatePicker, -} from '@elastic/eui'; -import moment from 'moment'; -import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; -import type { FramePublicAPI } from '../../../../types'; -import { defaultRangeAnnotationLabel, defaultAnnotationLabel } from '../../annotations/helpers'; -import type { XYState } from '../../types'; -import { getDataLayers } from '../../visualization_helpers'; -import { toLineAnnotationColor, getEndTimestamp, toRangeAnnotationColor } from './helpers'; -import type { ManualEventAnnotationType } from './types'; - -export const ConfigPanelApplyAsRangeSwitch = ({ - annotation, - datatableUtilities, - onChange, - frame, - state, -}: { - annotation?: ManualEventAnnotationType; - datatableUtilities: DatatableUtilitiesService; - onChange: (annotations: Partial | undefined) => void; - frame: FramePublicAPI; - state: XYState; -}) => { - const isRange = isRangeAnnotationConfig(annotation); - return ( - - - {i18n.translate('xpack.lens.xyChart.applyAsRange', { - defaultMessage: 'Apply as range', - })} - - } - checked={isRange} - onChange={() => { - if (isRange) { - const newPointAnnotation: PointInTimeEventAnnotationConfig = { - type: 'manual', - key: { - type: 'point_in_time', - timestamp: annotation.key.timestamp, - }, - id: annotation.id, - label: - annotation.label === defaultRangeAnnotationLabel - ? defaultAnnotationLabel - : annotation.label, - color: toLineAnnotationColor(annotation.color), - isHidden: annotation.isHidden, - }; - onChange(newPointAnnotation); - } else if (annotation) { - const fromTimestamp = moment(annotation?.key.timestamp); - const dataLayers = getDataLayers(state.layers); - const newRangeAnnotation: RangeEventAnnotationConfig = { - type: 'manual', - key: { - type: 'range', - timestamp: annotation.key.timestamp, - endTimestamp: getEndTimestamp( - datatableUtilities, - fromTimestamp.toISOString(), - frame, - dataLayers - ), - }, - id: annotation.id, - label: - annotation.label === defaultAnnotationLabel - ? defaultRangeAnnotationLabel - : annotation.label, - color: toRangeAnnotationColor(annotation.color), - isHidden: annotation.isHidden, - }; - onChange(newRangeAnnotation); - } - }} - compressed - /> - - ); -}; - -export const ConfigPanelRangeDatePicker = ({ - value, - label, - prependLabel, - onChange, - dataTestSubj = 'lnsXY_annotation_date_picker', -}: { - value: moment.Moment; - prependLabel?: string; - label?: string; - onChange: (val: moment.Moment | null) => void; - dataTestSubj?: string; -}) => { - return ( - - {prependLabel ? ( - {prependLabel} - } - > - - - ) : ( - - )} - - ); -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx deleted file mode 100644 index 12b2926fa7b14..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/tooltip_annotation_panel.tsx +++ /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 { htmlIdGenerator, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React, { useCallback, useMemo } from 'react'; -import { QueryPointEventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; -import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; -import { - FieldOption, - FieldOptionValue, - FieldPicker, -} from '@kbn/visualization-ui-components/public'; -import { - useDebouncedValue, - NewBucketButton, - DragDropBuckets, - DraggableBucketContainer, - FieldsBucketContainer, -} from '@kbn/visualization-ui-components/public'; -import type { IndexPattern } from '../../../../types'; - -export const MAX_TOOLTIP_FIELDS_SIZE = 2; - -const generateId = htmlIdGenerator(); -const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); - -export interface FieldInputsProps { - currentConfig: QueryPointEventAnnotationConfig; - setConfig: (config: QueryPointEventAnnotationConfig) => void; - indexPattern: IndexPattern; - invalidFields?: string[]; -} - -interface WrappedValue { - id: string; - value: string | undefined; - isNew?: boolean; -} - -type SafeWrappedValue = Omit & { value: string }; - -function removeNewEmptyField(v: WrappedValue): v is SafeWrappedValue { - return v.value != null; -} - -export function TooltipSection({ - currentConfig, - setConfig, - indexPattern, - invalidFields, -}: FieldInputsProps) { - const { hasFieldData } = useExistingFieldsReader(); - const onChangeWrapped = useCallback( - (values: WrappedValue[]) => { - setConfig({ - ...currentConfig, - extraFields: values.filter(removeNewEmptyField).map(({ value }) => value), - }); - }, - [setConfig, currentConfig] - ); - const { wrappedValues, rawValuesLookup } = useMemo(() => { - const rawValues = currentConfig.extraFields ?? []; - return { - wrappedValues: rawValues.map((value) => ({ id: generateId(), value })), - rawValuesLookup: new Set(rawValues), - }; - }, [currentConfig]); - - const { inputValue: localValues, handleInputChange } = useDebouncedValue({ - onChange: onChangeWrapped, - value: wrappedValues, - }); - - const onFieldSelectChange = useCallback( - (choice, index = 0) => { - const fields = [...localValues]; - - if (indexPattern.getFieldByName(choice.field)) { - fields[index] = { id: generateId(), value: choice.field }; - - // update the layer state - handleInputChange(fields); - } - }, - [localValues, indexPattern, handleInputChange] - ); - - const newBucketButton = ( - { - handleInputChange([...localValues, { id: generateId(), value: undefined, isNew: true }]); - }} - label={i18n.translate('xpack.lens.xyChart.annotation.tooltip.addField', { - defaultMessage: 'Add field', - })} - isDisabled={localValues.length > MAX_TOOLTIP_FIELDS_SIZE} - /> - ); - - if (localValues.length === 0) { - return ( - <> - - - - {i18n.translate('xpack.lens.xyChart.annotation.tooltip.noFields', { - defaultMessage: 'None selected', - })} - - - - {newBucketButton} - - ); - } - - const options = indexPattern.fields - .filter( - ({ displayName, type }) => - displayName && !rawValuesLookup.has(displayName) && supportedTypes.has(type) - ) - .map( - (field) => - ({ - label: field.displayName, - value: { - type: 'field', - field: field.name, - dataType: field.type, - }, - exists: hasFieldData(indexPattern.id, field.name), - compatible: true, - 'data-test-subj': `lnsXY-annotation-tooltip-fieldOption-${field.name}`, - } as FieldOption) - ) - .sort((a, b) => a.label.localeCompare(b.label)); - - return ( - <> - { - handleInputChange(updatedValues); - }} - droppableId="ANNOTATION_TOOLTIP_DROPPABLE_AREA" - items={localValues} - bgColor="subdued" - > - {localValues.map(({ id, value, isNew }, index, arrayRef) => { - const fieldIsValid = value ? Boolean(indexPattern.getFieldByName(value)) : true; - - return ( - { - handleInputChange(arrayRef.filter((_, i) => i !== index)); - }} - removeTitle={i18n.translate( - 'xpack.lens.xyChart.annotation.tooltip.deleteButtonLabel', - { - defaultMessage: 'Delete', - } - )} - isNotDraggable={arrayRef.length < 2} - Container={FieldsBucketContainer} - isInsidePanel={true} - data-test-subj={`lnsXY-annotation-tooltip-${index}`} - > - { - onFieldSelectChange(choice, index); - }} - fieldIsInvalid={!fieldIsValid} - className="lnsConfigPanelAnnotations__fieldPicker" - data-test-subj={`lnsXY-annotation-tooltip-field-picker--${index}`} - autoFocus={isNew && value == null} - /> - - ); - })} - - {newBucketButton} - - ); -} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts deleted file mode 100644 index f446afb6be265..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/types.ts +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - PointInTimeEventAnnotationConfig, - RangeEventAnnotationConfig, -} from '@kbn/event-annotation-plugin/common'; - -export type ManualEventAnnotationType = - | PointInTimeEventAnnotationConfig - | RangeEventAnnotationConfig; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index f5d19edd81c5e..4ce681eedbd51 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -9,7 +9,6 @@ import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import { useDebouncedValue } from '@kbn/visualization-ui-components/public'; import { ColorPicker } from '@kbn/visualization-ui-components/public'; import type { VisualizationDimensionEditorProps } from '../../../types'; @@ -17,9 +16,7 @@ import { State, XYState, XYDataLayerConfig, YConfig, YAxisMode } from '../types' import { FormatFactory } from '../../../../common/types'; import { getSeriesColor, isHorizontalChart } from '../state_helpers'; import { PalettePicker } from '../../../shared_components'; -import { getDataLayers, isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; -import { ReferenceLinePanel } from './reference_line_config_panel'; -import { AnnotationsPanel } from './annotations_config_panel'; +import { getDataLayers } from '../visualization_helpers'; import { CollapseSetting } from '../../../shared_components/collapse_setting'; import { getSortedAccessors } from '../to_expression'; import { getColorAssignments, getAssignedColorConfig } from '../color_assignment'; @@ -42,26 +39,6 @@ export function updateLayer( export const idPrefix = htmlIdGenerator()(); -export function DimensionEditor( - props: VisualizationDimensionEditorProps & { - datatableUtilities: DatatableUtilitiesService; - formatFactory: FormatFactory; - paletteService: PaletteRegistry; - } -) { - const { state, layerId } = props; - const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index]; - if (isAnnotationsLayer(layer)) { - return ; - } - - if (isReferenceLayer(layer)) { - return ; - } - return ; -} - export function DataDimensionEditor( props: VisualizationDimensionEditorProps & { formatFactory: FormatFactory; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx index 6caa8238808e9..252c3de6b8e57 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/xy_config_panel.test.tsx @@ -8,9 +8,8 @@ import React from 'react'; import { mountWithIntl as mount, shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { EuiButtonGroupProps, EuiButtonGroup } from '@elastic/eui'; -import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { XyToolbar } from '.'; -import { DimensionEditor } from './dimension_editor'; +import { DataDimensionEditor } from './dimension_editor'; import { AxisSettingsPopover } from './axis_settings_popover'; import { FramePublicAPI, DatasourcePublicAPI } from '../../../types'; import { State, XYState, XYDataLayerConfig } from '../types'; @@ -254,12 +253,10 @@ describe('XY Config panels', () => { }); describe('Dimension Editor', () => { - const datatableUtilities = createDatatableUtilitiesMock(); - test('shows the correct axis side options when in horizontal mode', () => { const state = testState(); const component = mount( - { ...state, layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], }} - datatableUtilities={datatableUtilities} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} panelRef={React.createRef()} @@ -290,14 +286,13 @@ describe('XY Config panels', () => { test('shows the default axis side options when not in horizontal mode', () => { const state = testState(); const component = mount( - { ], } as XYState; const component = mount( - { accessor="bar" groupId="left" state={state} - datatableUtilities={datatableUtilities} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} panelRef={React.createRef()} @@ -376,7 +370,7 @@ describe('XY Config panels', () => { } as XYState; const component = mount( - { accessor="bar" groupId="left" state={state} - datatableUtilities={datatableUtilities} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} panelRef={React.createRef()} @@ -422,7 +415,7 @@ describe('XY Config panels', () => { } as XYState; const component = mount( - { accessor="bar" groupId="left" state={state} - datatableUtilities={datatableUtilities} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} panelRef={React.createRef()} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 04f6a34e5a874..cdc5abfab1023 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -25,6 +25,7 @@ import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; jest.mock('../../id_generator'); @@ -38,6 +39,7 @@ const xyVisualization = getXyVisualization({ storage: {} as IStorageWrapper, data: dataPluginMock.createStartContract(), unifiedSearch: unifiedSearchPluginMock.createStartContract(), + dataViewsService: {} as DataViewsPublicPluginStart, }); describe('xy_suggestions', () => { From e2931057aa39333bbc169b42eb8d1cd3bc256d36 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 2 May 2023 11:44:57 -0500 Subject: [PATCH 066/202] filtering non-compatible data view fields --- .../annotation_editor_controls.tsx | 7 +++++-- .../query_annotation_panel.tsx | 2 ++ .../tooltip_annotation_panel.tsx | 2 ++ src/plugins/visualization_ui_components/kibana.jsonc | 6 +++++- .../visualization_ui_components/public/index.ts | 2 ++ .../visualization_ui_components/public/util.ts | 12 ++++++++++++ .../plugins/lens/public/data_views_service/loader.ts | 4 ++-- 7 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/plugins/visualization_ui_components/public/util.ts diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index 312987b4069cb..c5031d55fae62 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -7,6 +7,7 @@ */ import './index.scss'; +import { isFieldLensCompatible } from '@kbn/visualization-ui-components/public'; import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiButtonGroup, EuiSpacer } from '@elastic/eui'; @@ -139,8 +140,9 @@ export const AnnotationEditorControls = ({ timeField: (dataView.timeFieldName || // fallback to the first avaiable date field in the dataView - dataView.fields.find(({ type: fieldType }) => fieldType === 'date') - ?.displayName) ?? + dataView.fields + .filter(isFieldLensCompatible) + .find(({ type: fieldType }) => fieldType === 'date')?.displayName) ?? '', key: { type: 'point_in_time' }, ...additionalRangeResets, @@ -203,6 +205,7 @@ export const AnnotationEditorControls = ({ return null; } const options = dataView.fields + .filter(isFieldLensCompatible) .filter(({ displayName, type }) => displayName && type !== 'document') .map( (field) => diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx index 4d722eedb6a48..aca11648baff6 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx @@ -19,6 +19,7 @@ import { type QueryInputServices, } from '@kbn/visualization-ui-components/public'; import type { DataView } from '@kbn/data-views-plugin/common'; +import { isFieldLensCompatible } from '@kbn/visualization-ui-components/public'; import type { QueryPointEventAnnotationConfig } from '../../../common'; export const defaultQuery: Query = { @@ -42,6 +43,7 @@ export const ConfigPanelQueryAnnotation = ({ const { hasFieldData } = useExistingFieldsReader(); // list only date fields const options = dataView.fields + .filter(isFieldLensCompatible) .filter((field) => field.type === 'date' && field.displayName) .map((field) => { return { diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx index 2770d730085f1..6088ab4c2a9aa 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx @@ -21,6 +21,7 @@ import { FieldsBucketContainer, } from '@kbn/visualization-ui-components/public'; import { DataView } from '@kbn/data-views-plugin/common'; +import { isFieldLensCompatible } from '@kbn/visualization-ui-components/public'; import { QueryPointEventAnnotationConfig } from '../../../common'; export const MAX_TOOLTIP_FIELDS_SIZE = 2; @@ -126,6 +127,7 @@ export function TooltipSection({ } const options = dataView.fields + .filter(isFieldLensCompatible) .filter( ({ displayName, type }) => displayName && !rawValuesLookup.has(displayName) && supportedTypes.has(type) diff --git a/src/plugins/visualization_ui_components/kibana.jsonc b/src/plugins/visualization_ui_components/kibana.jsonc index f7d42af513338..6a2ed84a93149 100644 --- a/src/plugins/visualization_ui_components/kibana.jsonc +++ b/src/plugins/visualization_ui_components/kibana.jsonc @@ -8,7 +8,11 @@ "browser": true, "requiredBundles": [ "unifiedSearch", - "unifiedFieldList" + "unifiedFieldList", + "dataViews" + ], + "extraPublicDirs": [ + "common" ] } } diff --git a/src/plugins/visualization_ui_components/public/index.ts b/src/plugins/visualization_ui_components/public/index.ts index d4b15f28d33c1..8abe0a220aee3 100644 --- a/src/plugins/visualization_ui_components/public/index.ts +++ b/src/plugins/visualization_ui_components/public/index.ts @@ -35,6 +35,8 @@ export { TextDecorationSetting, } from './components'; +export { isFieldLensCompatible } from './util'; + export type { DataType, FieldOptionValue, diff --git a/src/plugins/visualization_ui_components/public/util.ts b/src/plugins/visualization_ui_components/public/util.ts new file mode 100644 index 0000000000000..2eb71ee858759 --- /dev/null +++ b/src/plugins/visualization_ui_components/public/util.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 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 { DataViewField, isNestedField } from '@kbn/data-views-plugin/common'; + +export const isFieldLensCompatible = (field: DataViewField) => + !isNestedField(field) && (!!field.aggregatable || !!field.scripted); diff --git a/x-pack/plugins/lens/public/data_views_service/loader.ts b/x-pack/plugins/lens/public/data_views_service/loader.ts index abd8a48815122..ef48761d39a6e 100644 --- a/x-pack/plugins/lens/public/data_views_service/loader.ts +++ b/x-pack/plugins/lens/public/data_views_service/loader.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isNestedField } from '@kbn/data-views-plugin/common'; +import { isFieldLensCompatible } from '@kbn/visualization-ui-components/public'; import type { DataViewsContract, DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; import { keyBy } from 'lodash'; import { IndexPattern, IndexPatternField, IndexPatternMap, IndexPatternRef } from '../types'; @@ -30,7 +30,7 @@ export function convertDataViewIntoLensIndexPattern( restrictionRemapper: (name: string) => string = onRestrictionMapping ): IndexPattern { const newFields = dataView.fields - .filter((field) => !isNestedField(field) && (!!field.aggregatable || !!field.scripted)) + .filter(isFieldLensCompatible) .map((field): IndexPatternField => { // Convert the getters on the index pattern service into plain JSON const base = { From a0e7ef86aff8801d05b21e5edf481a96c485923c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 2 May 2023 16:29:05 -0500 Subject: [PATCH 067/202] create new annotations from library --- .../common/create_copied_annotation.ts | 24 +++++++++ src/plugins/event_annotation/common/index.ts | 12 ++++- .../common/manual_event_annotation/index.ts | 22 ++++++++ .../query_point_event_annotation/index.ts | 20 ++++++++ .../components/group_editor_controls.test.tsx | 16 +++++- .../components/group_editor_controls.tsx | 37 +++++++++++++- .../event_annotation_service/helpers.ts | 8 --- .../event_annotation_service/service.tsx | 2 +- .../visualizations/xy/annotations/helpers.tsx | 50 ++----------------- 9 files changed, 132 insertions(+), 59 deletions(-) create mode 100644 src/plugins/event_annotation/common/create_copied_annotation.ts diff --git a/src/plugins/event_annotation/common/create_copied_annotation.ts b/src/plugins/event_annotation/common/create_copied_annotation.ts new file mode 100644 index 0000000000000..caab8923c855e --- /dev/null +++ b/src/plugins/event_annotation/common/create_copied_annotation.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 { getDefaultManualAnnotation } from './manual_event_annotation'; +import { EventAnnotationConfig } from './types'; + +export const createCopiedAnnotation = ( + newId: string, + timestamp: string, + source?: EventAnnotationConfig +): EventAnnotationConfig => { + if (!source) { + return getDefaultManualAnnotation(newId, timestamp); + } + return { + ...source, + id: newId, + }; +}; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index 106632d2049ca..c9e399aa02999 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -18,8 +18,16 @@ export type { QueryPointEventAnnotationArgs, QueryPointEventAnnotationOutput, } from './query_point_event_annotation/types'; -export { manualPointEventAnnotation, manualRangeEventAnnotation } from './manual_event_annotation'; -export { queryPointEventAnnotation } from './query_point_event_annotation'; +export { + manualPointEventAnnotation, + manualRangeEventAnnotation, + getDefaultManualAnnotation, +} from './manual_event_annotation'; +export { + queryPointEventAnnotation, + getDefaultQueryAnnotation, +} from './query_point_event_annotation'; +export { createCopiedAnnotation } from './create_copied_annotation'; export { eventAnnotationGroup } from './event_annotation_group'; export type { EventAnnotationGroupArgs } from './event_annotation_group'; diff --git a/src/plugins/event_annotation/common/manual_event_annotation/index.ts b/src/plugins/event_annotation/common/manual_event_annotation/index.ts index a178211164822..12d00cefc75b4 100644 --- a/src/plugins/event_annotation/common/manual_event_annotation/index.ts +++ b/src/plugins/event_annotation/common/manual_event_annotation/index.ts @@ -9,6 +9,7 @@ import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { i18n } from '@kbn/i18n'; import { AvailableAnnotationIcons } from '../constants'; +import { EventAnnotationConfig } from '../types'; import type { ManualRangeEventAnnotationArgs, @@ -163,3 +164,24 @@ export const manualRangeEventAnnotation: ExpressionFunctionDefinition< }; }, }; + +export const defaultAnnotationLabel = i18n.translate( + 'eventAnnotation.manualAnnotation.defaultAnnotationLabel', + { + defaultMessage: 'Event', + } +); + +export const getDefaultManualAnnotation = ( + id: string, + timestamp: string +): EventAnnotationConfig => ({ + label: defaultAnnotationLabel, + type: 'manual', + key: { + type: 'point_in_time', + timestamp, + }, + icon: 'triangle', + id, +}); diff --git a/src/plugins/event_annotation/common/query_point_event_annotation/index.ts b/src/plugins/event_annotation/common/query_point_event_annotation/index.ts index cb9ba882a9f89..6c0ee0bd62eb7 100644 --- a/src/plugins/event_annotation/common/query_point_event_annotation/index.ts +++ b/src/plugins/event_annotation/common/query_point_event_annotation/index.ts @@ -9,6 +9,7 @@ import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { i18n } from '@kbn/i18n'; import { AvailableAnnotationIcons } from '../constants'; +import { EventAnnotationConfig } from '../types'; import type { QueryPointEventAnnotationArgs, QueryPointEventAnnotationOutput } from './types'; @@ -111,3 +112,22 @@ export const queryPointEventAnnotation: ExpressionFunctionDefinition< }; }, }; + +export const getDefaultQueryAnnotation = ( + id: string, + fieldName: string, + timeField: string +): EventAnnotationConfig => ({ + filter: { + type: 'kibana_query', + query: `${fieldName}: *`, + language: 'kuery', + }, + timeField, + type: 'query', + key: { + type: 'point_in_time', + }, + id, + label: `${fieldName}: *`, +}); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx index 9b62674036969..869b612b974f1 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx @@ -15,6 +15,7 @@ import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { act } from 'react-dom/test-utils'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import { AnnotationEditorControls } from '.'; describe('event annotation group editor', () => { const dataViewId = 'my-index-pattern'; @@ -35,11 +36,13 @@ describe('event annotation group editor', () => { }; let wrapper: ReactWrapper; - const updateMock = jest.fn(); + let updateMock: jest.Mock; const TagSelector = (_props: { onTagsSelected: (tags: string[]) => void }) =>
    ; beforeEach(async () => { + updateMock = jest.fn(); + wrapper = mountWithIntl( { ] `); }); + + it('adds a new annotation group', () => { + act(() => { + wrapper.find('button[data-test-subj="addAnnotation"]').simulate('click'); + }); + + expect(updateMock).toHaveBeenCalledTimes(1); + const newAnnotations = (updateMock.mock.calls[0][0] as EventAnnotationGroupConfig).annotations; + expect(newAnnotations.length).toBe(group.annotations.length + 1); + expect(wrapper.exists(AnnotationEditorControls)); // annotation controls opened + }); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls.tsx index 267ac83dcfb00..d110c71d306cd 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls.tsx @@ -24,7 +24,12 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import React, { useEffect, useMemo, useState } from 'react'; -import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; +import { v4 as uuidv4 } from 'uuid'; +import { + createCopiedAnnotation, + EventAnnotationConfig, + EventAnnotationGroupConfig, +} from '../../common'; import { AnnotationEditorControls } from './annotation_editor_controls'; export const GroupEditorControls = ({ @@ -70,6 +75,11 @@ export const GroupEditorControls = ({ [dataViews, group.indexPatternId] ); + const [newAnnotationId, setNewAnnotationId] = useState(uuidv4()); + useEffect(() => { + setNewAnnotationId(uuidv4()); + }, [group.annotations.length]); + return !selectedAnnotation ? ( <> - +
    {group.annotations.map((annotation) => ( <> @@ -157,6 +171,25 @@ export const GroupEditorControls = ({ ))} + { + const newAnnotation = createCopiedAnnotation( + newAnnotationId, + new Date().toISOString() + ); + + update({ ...group, annotations: [...group.annotations, newAnnotation] }); + + setSelectedAnnotation(newAnnotation); + }} + > + +
    diff --git a/src/plugins/event_annotation/public/event_annotation_service/helpers.ts b/src/plugins/event_annotation/public/event_annotation_service/helpers.ts index afe64a5a47eb2..31dabb34cdc09 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/helpers.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/helpers.ts @@ -5,7 +5,6 @@ * 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 { euiLightVars } from '@kbn/ui-theme'; import { EventAnnotationConfig, @@ -17,13 +16,6 @@ export const defaultAnnotationColor = euiLightVars.euiColorAccent; // Do not compute it live as dependencies will add tens of Kbs to the plugin export const defaultAnnotationRangeColor = `#F04E981A`; // defaultAnnotationColor with opacity 0.1 -export const defaultAnnotationLabel = i18n.translate( - 'eventAnnotation.manualAnnotation.defaultAnnotationLabel', - { - defaultMessage: 'Event', - } -); - export const isRangeAnnotationConfig = ( annotation?: EventAnnotationConfig ): annotation is RangeEventAnnotationConfig => { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 31c4615760408..82ea828b69186 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -21,6 +21,7 @@ import { import type { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; +import { defaultAnnotationLabel } from '../../common/manual_event_annotation'; import { EventAnnotationGroupContent } from '../../common/types'; import { EventAnnotationConfig, @@ -32,7 +33,6 @@ import { EventAnnotationServiceType } from './types'; import { defaultAnnotationColor, defaultAnnotationRangeColor, - defaultAnnotationLabel, isRangeAnnotationConfig, isQueryAnnotationConfig, } from './helpers'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index dc1e95a2e52e1..d929d0e7271ff 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -14,7 +14,11 @@ import { isRangeAnnotationConfig, annotationsIconSet, } from '@kbn/event-annotation-plugin/public'; -import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; +import { + createCopiedAnnotation, + EventAnnotationConfig, + getDefaultQueryAnnotation, +} from '@kbn/event-annotation-plugin/common'; import { IconChartBarAnnotations } from '@kbn/chart-icons'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { AccessorConfig } from '@kbn/visualization-ui-components/public'; @@ -130,50 +134,6 @@ export const getAnnotationsSupportedLayer = ( }; }; -const getDefaultManualAnnotation = (id: string, timestamp: string): EventAnnotationConfig => ({ - label: defaultAnnotationLabel, - type: 'manual', - key: { - type: 'point_in_time', - timestamp, - }, - icon: 'triangle', - id, -}); - -const getDefaultQueryAnnotation = ( - id: string, - fieldName: string, - timeField: string -): EventAnnotationConfig => ({ - filter: { - type: 'kibana_query', - query: `${fieldName}: *`, - language: 'kuery', - }, - timeField, - type: 'query', - key: { - type: 'point_in_time', - }, - id, - label: `${fieldName}: *`, -}); - -const createCopiedAnnotation = ( - newId: string, - timestamp: string, - source?: EventAnnotationConfig -): EventAnnotationConfig => { - if (!source) { - return getDefaultManualAnnotation(newId, timestamp); - } - return { - ...source, - id: newId, - }; -}; - export const onAnnotationDrop: Visualization['onDrop'] = ({ prevState, frame, From 69b62105ed24571e4aa5112a32a6f827c4226baa Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 3 May 2023 11:42:08 -0500 Subject: [PATCH 068/202] allow user to go back to group editing --- .../group_editor_flyout.test.tsx.snap | 45 ++++++------------- .../components/group_editor_flyout.test.tsx | 35 +++++++++++++-- .../public/components/group_editor_flyout.tsx | 45 +++++++++++++------ 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap index 1b0f381040f9c..915a6b4dc32fd 100644 --- a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap +++ b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap @@ -2,6 +2,7 @@ exports[`group editor flyout renders controls 1`] = ` Object { + "TagSelector": [MockFunction], "createDataView": [MockFunction], "dataViews": Array [ Object { @@ -10,43 +11,25 @@ Object { }, ], "group": Object { - "annotations": Array [], + "annotations": Array [ + Object { + "icon": "triangle", + "id": "my-id", + "key": Object { + "timestamp": "some-timestamp", + "type": "point_in_time", + }, + "label": "Event", + "type": "manual", + }, + ], "description": "", "ignoreGlobalFilters": false, "indexPatternId": "some-id", "tags": Array [], "title": "My group", }, - "savedObjectsTagging": Object { - "cache": Object { - "getState": [MockFunction], - "getState$": [MockFunction], - }, - "client": Object { - "create": [MockFunction], - "delete": [MockFunction], - "get": [MockFunction], - "getAll": [MockFunction], - "update": [MockFunction], - }, - "ui": Object { - "components": Object { - "SavedObjectSaveModalTagSelector": [MockFunction], - "TagList": [MockFunction], - "TagSelector": [MockFunction], - }, - "convertNameToReference": [MockFunction], - "getSearchBarFilter": [MockFunction], - "getTableColumnDefinition": [MockFunction], - "getTag": [MockFunction], - "getTagIdFromName": [MockFunction], - "getTagIdsFromReferences": [MockFunction], - "getTagList": [MockFunction], - "hasTagDecoration": [MockFunction], - "parseSearchQuery": [MockFunction], - "updateTagsReferences": [MockFunction], - }, - }, + "queryInputServices": Object {}, "selectedAnnotation": undefined, "setSelectedAnnotation": [Function], "update": [MockFunction], diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx index a4e285a1ce3dd..bd95f83d88ee2 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx @@ -7,7 +7,7 @@ */ import { EuiButton, EuiFlyout } from '@elastic/eui'; -import { EventAnnotationGroupConfig } from '../../common'; +import { EventAnnotationGroupConfig, getDefaultManualAnnotation } from '../../common'; import { taggingApiMock } from '@kbn/saved-objects-tagging-oss-plugin/public/api.mock'; import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; @@ -22,9 +22,17 @@ const simulateButtonClick = (component: ShallowWrapper, selector: string) => { ); }; +const SELECTORS = { + SAVE_BUTTON: '[data-test-subj="saveAnnotationGroup"]', + CANCEL_BUTTON: '[data-test-subj="cancelGroupEdit"]', + BACK_BUTTON: '[data-test-subj="backToGroupSettings"]', +}; + describe('group editor flyout', () => { + const annotation = getDefaultManualAnnotation('my-id', 'some-timestamp'); + const group: EventAnnotationGroupConfig = { - annotations: [], + annotations: [annotation], description: '', tags: [], indexPatternId: 'some-id', @@ -67,12 +75,12 @@ describe('group editor flyout', () => { }); it('signals close', () => { component.find(EuiFlyout).prop('onClose')({} as MouseEvent); - simulateButtonClick(component, '[data-test-subj="cancelGroupEdit"]'); + simulateButtonClick(component, SELECTORS.CANCEL_BUTTON); expect(onClose).toHaveBeenCalledTimes(2); }); it('signals save', () => { - simulateButtonClick(component, '[data-test-subj="saveAnnotationGroup"]'); + simulateButtonClick(component, SELECTORS.SAVE_BUTTON); expect(onSave).toHaveBeenCalledTimes(1); }); @@ -82,4 +90,23 @@ describe('group editor flyout', () => { expect(updateGroup).toHaveBeenCalledWith(newGroup); }); + test('specific annotation editing', () => { + const assertGroupEditingState = () => { + expect(component.exists(SELECTORS.SAVE_BUTTON)).toBeTruthy(); + expect(component.exists(SELECTORS.CANCEL_BUTTON)).toBeTruthy(); + expect(component.exists(SELECTORS.BACK_BUTTON)).toBeFalsy(); + }; + + assertGroupEditingState(); + + component.find(GroupEditorControls).prop('setSelectedAnnotation')(annotation); + + expect(component.exists(SELECTORS.BACK_BUTTON)).toBeTruthy(); + expect(component.exists(SELECTORS.SAVE_BUTTON)).toBeFalsy(); + expect(component.exists(SELECTORS.CANCEL_BUTTON)).toBeFalsy(); + + component.find(SELECTORS.BACK_BUTTON).simulate('click'); + + assertGroupEditingState(); + }); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index e39d9549ceaf0..d7dff29968fdd 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -77,19 +77,38 @@ export const GroupEditorFlyout = ({ - - - - - - - - - - + {selectedAnnotation ? ( + + setSelectedAnnotation(undefined)} + > + + + + ) : ( + <> + + + + + + + + + + + + )} From 1434f488982ffaf53548d23b813cd29f1c519ef6 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 3 May 2023 13:37:43 -0500 Subject: [PATCH 069/202] move dimension trigger styles to component --- .../buttons/empty_dimension_button.tsx | 103 ++++++++++++------ .../config_panel/layer_panel.scss | 29 ++--- .../editor_frame/config_panel/layer_panel.tsx | 14 +-- .../dimension_trigger/index.tsx | 26 ++++- 4 files changed, 101 insertions(+), 71 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index f0ef1508c5076..bea6868f14054 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -16,6 +16,9 @@ import { DropType, DropTargetSwapDuplicateCombine, } from '@kbn/dom-drag-drop'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { DimensionTrigger } from '../../../../shared_components/dimension_trigger'; import { isDraggedField } from '../../../../utils'; import { generateId } from '../../../../id_generator'; @@ -53,52 +56,80 @@ const defaultButtonLabels = { ), }; +const EmptyDimensionButtonInner = ({ + label, + ariaLabel, + onClick, + dataTestSubj, + iconType, +}: { + label: React.ReactNode; + ariaLabel: string; + onClick: () => void; + dataTestSubj?: string; + iconType?: string; +}) => ( + { + onClick(); + }} + > + + +); + const DefaultEmptyButton = ({ columnId, group, onClick }: EmptyButtonProps) => { const { buttonAriaLabel, buttonLabel } = group.labels || {}; return ( - { - onClick(columnId); - }} - > - {buttonLabel || defaultButtonLabels.label} - + onClick(columnId)} + /> ); }; const SuggestedValueButton = ({ columnId, group, onClick }: EmptyButtonProps) => ( - + } + ariaLabel={i18n.translate('xpack.lens.indexPattern.suggestedValueAriaLabel', { defaultMessage: 'Suggested value: {value} for {groupLabel}', values: { value: group.suggestedValue?.(), groupLabel: group.groupLabel }, })} - data-test-subj="lns-empty-dimension-suggested-value" - onClick={() => { - onClick(columnId); - }} - > - - + dataTestSubj="lns-empty-dimension-suggested-value" + iconType="plusInCircleFilled" + onClick={() => onClick(columnId)} + /> ); export function EmptyDimensionButton({ diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index 1ff1cd513cee9..9b217510fa42b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -41,19 +41,19 @@ } // Add border to the top of the next same panel - & + & { + &+& { border-top: $euiBorderThin; margin-top: 0; } - & > * { + &>* { margin-bottom: 0; } // Targeting EUI class as we are unable to apply a class to this element in component &, .euiFormRow__fieldWrapper { - & > * + * { + &>*+* { margin-top: $euiSizeS; } } @@ -80,7 +80,7 @@ position: relative; // NativeRenderer is messing this up - > div { + >div { flex-grow: 1; } @@ -106,34 +106,19 @@ .lnsLayerPanel__dimensionContainer { position: relative; - & + & { + &+& { margin-top: $euiSizeS; } } -.lnsLayerPanel__triggerText { - width: 100%; - padding: $euiSizeXS $euiSizeS; - word-break: break-word; - font-weight: $euiFontWeightRegular; -} - .lnsLayerPanel__dimensionLink { &:hover { text-decoration: none; } } -.lnsLayerPanel__triggerTextLabel { - transition: background-color $euiAnimSpeedFast ease-in-out; - - &:hover { - text-decoration: underline; - } -} - .domDragDrop-isReplacing { - .lnsLayerPanel__triggerText { + .dimensionTrigger__textLabel { text-decoration: line-through; } } @@ -178,4 +163,4 @@ background-color: transparent; text-decoration-thickness: $euiBorderWidthThin !important; } -} +} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index cad30547f71ac..b688ff30f5f74 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -22,6 +22,7 @@ import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; import { DragDropIdentifier, ReorderProvider, DropType } from '@kbn/dom-drag-drop'; import { DimensionButton } from '@kbn/visualization-ui-components/public'; +import { DimensionTrigger } from '../../../shared_components/dimension_trigger'; import { LayerActions } from './layer_actions'; import { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { NativeRenderer } from '../../../native_renderer'; @@ -630,16 +631,11 @@ export function LayerPanel( box-shadow: none !important; `} > - - - {group.fakeFinalAccessor.label} - - + />
    )} diff --git a/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx b/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx index d705c50017a8d..865389ddf9919 100644 --- a/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx +++ b/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx @@ -8,6 +8,8 @@ import { EuiText, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { EuiTextProps } from '@elastic/eui/src/components/text/text'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; export const defaultDimensionTriggerTooltip = (

    @@ -27,8 +29,8 @@ export const DimensionTrigger = ({ color, dataTestSubj, }: { - label: string; - id: string; + label: React.ReactNode; + id?: string; color?: EuiTextProps['color']; dataTestSubj?: string; }) => { @@ -37,12 +39,28 @@ export const DimensionTrigger = ({ size="s" id={id} color={color} - className="lnsLayerPanel__triggerText" + css={css` + width: 100%; + padding: ${euiThemeVars.euiSizeXS} ${euiThemeVars.euiSizeS}; + word-break: break-word; + font-weight: ${euiThemeVars.euiFontWeightRegular}; + `} data-test-subj={dataTestSubj || 'lns-dimensionTrigger'} > - {label} + + {label} + From 65160506b5e2b4af3b8e7873d5f780626570fe4f Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 3 May 2023 16:29:23 -0500 Subject: [PATCH 070/202] moving more empty dimension button styling inline --- .../buttons/empty_dimension_button.tsx | 80 ++++++++++--------- .../config_panel/layer_panel.scss | 9 --- 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index bea6868f14054..536b43c4c4ec4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -68,38 +68,42 @@ const EmptyDimensionButtonInner = ({ onClick: () => void; dataTestSubj?: string; iconType?: string; -}) => ( - { - onClick(); - }} - > - - -); +}) => { + return ( + { + onClick(); + }} + > + + + ); +}; const DefaultEmptyButton = ({ columnId, group, onClick }: EmptyButtonProps) => { const { buttonAriaLabel, buttonLabel } = group.labels || {}; @@ -236,13 +240,11 @@ export function EmptyDimensionButton({ onDrop={handleOnDrop} dropTypes={dropTypes} > -

    - {typeof group.suggestedValue?.() === 'number' ? ( - - ) : ( - - )} -
    + {typeof group.suggestedValue?.() === 'number' ? ( + + ) : ( + + )}
    ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index 9b217510fa42b..454cf6bd8ff10 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -94,15 +94,6 @@ } } -.lnsLayerPanel__dimension--empty { - border: $euiBorderWidthThin dashed $euiBorderColor !important; - - &:focus, - &:focus-within { - @include euiFocusRing; - } -} - .lnsLayerPanel__dimensionContainer { position: relative; From 114d5f753560cc3f14aabeb330ec63ef4f9d12b2 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 3 May 2023 16:42:15 -0500 Subject: [PATCH 071/202] move dimension trigger and empty dimension button to shared plugin --- .../dimension_buttons/empty_button.tsx | 62 +++++++++++++++++++ .../components/dimension_buttons/index.ts | 4 ++ .../components/dimension_buttons/trigger.tsx | 6 +- .../public/index.ts | 2 + .../datasources/form_based/form_based.tsx | 2 +- .../text_based/text_based_languages.tsx | 2 +- .../buttons/empty_dimension_button.tsx | 54 +--------------- .../editor_frame/config_panel/layer_panel.tsx | 3 +- .../visualizations/xy/visualization.tsx | 3 +- 9 files changed, 77 insertions(+), 61 deletions(-) create mode 100644 src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx rename x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx => src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx (90%) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx new file mode 100644 index 0000000000000..e239b983abb80 --- /dev/null +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx @@ -0,0 +1,62 @@ +/* + * 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 { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { DimensionTrigger } from './trigger'; + +export const EmptyDimensionButton = ({ + label, + ariaLabel, + onClick, + dataTestSubj, + iconType, +}: { + label: React.ReactNode; + ariaLabel: string; + onClick: () => void; + dataTestSubj?: string; + iconType?: string; +}) => { + return ( + { + onClick(); + }} + > + + + ); +}; diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/index.ts b/src/plugins/visualization_ui_components/public/components/dimension_buttons/index.ts index 54df2c7911488..b3037035fb57b 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/index.ts +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/index.ts @@ -8,4 +8,8 @@ export * from './dimension_button'; +export * from './empty_button'; + +export * from './trigger'; + export type { AccessorConfig } from './types'; diff --git a/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx similarity index 90% rename from x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx rename to src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx index 865389ddf9919..944945d56e1c6 100644 --- a/x-pack/plugins/lens/public/shared_components/dimension_trigger/index.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx @@ -1,9 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { EuiText, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; diff --git a/src/plugins/visualization_ui_components/public/index.ts b/src/plugins/visualization_ui_components/public/index.ts index 8abe0a220aee3..cb203bd80a15d 100644 --- a/src/plugins/visualization_ui_components/public/index.ts +++ b/src/plugins/visualization_ui_components/public/index.ts @@ -31,6 +31,8 @@ export { isQueryValid, DimensionEditorSection, DimensionButton, + DimensionTrigger, + EmptyDimensionButton, LineStyleSettings, TextDecorationSetting, } from './components'; diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx index dbe6fcafd0ed0..740acb195219d 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx @@ -26,6 +26,7 @@ import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/publi import { EuiButton } from '@elastic/eui'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { DraggingIdentifier } from '@kbn/dom-drag-drop'; +import { DimensionTrigger } from '@kbn/visualization-ui-components/public'; import type { DatasourceDimensionEditorProps, DatasourceDimensionTriggerProps, @@ -99,7 +100,6 @@ import { DOCUMENT_FIELD_NAME } from '../../../common/constants'; import { isColumnOfType } from './operations/definitions/helpers'; import { LayerSettingsPanel } from './layer_settings'; import { FormBasedLayer } from '../..'; -import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { filterAndSortUserMessages } from '../../app_plugin/get_application_user_messages'; export type { OperationType, GenericIndexPatternColumn } from './operations'; export { deleteColumn } from './operations'; diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index c3da573af3433..de137531daa03 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -19,6 +19,7 @@ import type { ExpressionsStart, DatatableColumnType } from '@kbn/expressions-plu import type { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { euiThemeVars } from '@kbn/ui-theme'; +import { DimensionTrigger } from '@kbn/visualization-ui-components/public'; import { DatasourceDimensionEditorProps, DatasourceDataPanelProps, @@ -42,7 +43,6 @@ import type { import { FieldSelect } from './field_select'; import type { Datasource, IndexPatternMap } from '../../types'; import { LayerPanel } from './layerpanel'; -import { DimensionTrigger } from '../../shared_components/dimension_trigger'; function getLayerReferenceName(layerId: string) { return `textBasedLanguages-datasource-layer-${layerId}`; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index 536b43c4c4ec4..faa74d078ea50 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -6,7 +6,6 @@ */ import React, { useMemo, useState, useEffect, useContext } from 'react'; -import { EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { @@ -16,9 +15,7 @@ import { DropType, DropTargetSwapDuplicateCombine, } from '@kbn/dom-drag-drop'; -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { DimensionTrigger } from '../../../../shared_components/dimension_trigger'; +import { EmptyDimensionButton as EmptyDimensionButtonInner } from '@kbn/visualization-ui-components/public'; import { isDraggedField } from '../../../../utils'; import { generateId } from '../../../../id_generator'; @@ -56,55 +53,6 @@ const defaultButtonLabels = { ), }; -const EmptyDimensionButtonInner = ({ - label, - ariaLabel, - onClick, - dataTestSubj, - iconType, -}: { - label: React.ReactNode; - ariaLabel: string; - onClick: () => void; - dataTestSubj?: string; - iconType?: string; -}) => { - return ( - { - onClick(); - }} - > - - - ); -}; - const DefaultEmptyButton = ({ columnId, group, onClick }: EmptyButtonProps) => { const { buttonAriaLabel, buttonLabel } = group.labels || {}; return ( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index b688ff30f5f74..6386e8ad3020a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -21,8 +21,7 @@ import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; import { DragDropIdentifier, ReorderProvider, DropType } from '@kbn/dom-drag-drop'; -import { DimensionButton } from '@kbn/visualization-ui-components/public'; -import { DimensionTrigger } from '../../../shared_components/dimension_trigger'; +import { DimensionButton, DimensionTrigger } from '@kbn/visualization-ui-components/public'; import { LayerActions } from './layer_actions'; import { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { NativeRenderer } from '../../../native_renderer'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 93cb2c1d39dc7..0c7b71a6f0ba0 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -25,7 +25,7 @@ import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin import { EventAnnotationGroupConfig } from '@kbn/event-annotation-plugin/common'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { isEqual } from 'lodash'; -import type { AccessorConfig } from '@kbn/visualization-ui-components/public'; +import { type AccessorConfig, DimensionTrigger } from '@kbn/visualization-ui-components/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { generateId } from '../../id_generator'; import { @@ -108,7 +108,6 @@ import { groupAxesByType } from './axes_configuration'; import type { XYState } from './types'; import { ReferenceLinePanel } from './xy_config_panel/reference_line_config_panel'; import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; -import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; import { createAnnotationActions } from './annotations/actions'; From f00e34927feb401dad2af3a060a032d8c35e2a47 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 3 May 2023 19:05:16 -0500 Subject: [PATCH 072/202] move dimension button styles inline --- .../dimension_buttons/dimension_button.tsx | 33 +++++++++++++++++-- .../config_panel/layer_panel.scss | 30 ----------------- .../editor_frame/config_panel/layer_panel.tsx | 11 ++++--- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx index 2e75a1704d615..4bfd096279354 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx @@ -7,7 +7,14 @@ */ import React from 'react'; -import { EuiButtonIcon, EuiLink, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiButtonIcon, + EuiLink, + EuiToolTip, + EuiFlexGroup, + EuiFlexItem, + useEuiFontSize, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -41,7 +48,18 @@ export function DimensionButton({ message?: Message; }) { return ( -
    +
    @@ -82,10 +100,21 @@ export function DimensionButton({ })} onClick={() => onRemoveClick(accessorConfig.columnId)} css={css` + margin-right: $euiSizeS; + visibility: hidden; + opacity: 0; color: ${euiThemeVars.euiTextSubduedColor}; + &:hover { color: ${euiThemeVars.euiColorDangerText}; } + + &:hover, + &:focus { + visibility: visible; + opacity: 1; + transition: opacity ${euiThemeVars.euiAnimSpeedFast} ease-in-out; + } `} /> diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index 454cf6bd8ff10..0e7cb846c704d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -64,36 +64,6 @@ padding: $euiSizeS $euiSize; } -.lnsLayerPanel__dimensionRemove { - margin-right: $euiSizeS; - visibility: hidden; - opacity: 0; -} - -.lnsLayerPanel__dimension { - @include euiFontSizeS; - border-radius: $euiBorderRadius; - display: flex; - align-items: center; - overflow: hidden; - min-height: $euiSizeXL; - position: relative; - - // NativeRenderer is messing this up - >div { - flex-grow: 1; - } - - &:hover, - &:focus { - .lnsLayerPanel__dimensionRemove { - visibility: visible; - opacity: 1; - transition: opacity $euiAnimSpeedFast ease-in-out; - } - } -} - .lnsLayerPanel__dimensionContainer { position: relative; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 6386e8ad3020a..690ad4222b62e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -572,7 +572,6 @@ export function LayerPanel( indexPatterns={dataViews.indexPatterns} > From 7fc428c0b0d792c6ce3a97b19fe8d3b3a71a46d4 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 3 May 2023 19:11:20 -0500 Subject: [PATCH 073/202] correct border-radius on empty button --- .../public/components/dimension_buttons/empty_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx index e239b983abb80..024154a31aa3e 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx @@ -29,7 +29,7 @@ export const EmptyDimensionButton = ({ Date: Thu, 4 May 2023 11:47:10 -0500 Subject: [PATCH 074/202] restore remove button --- .../dimension_buttons/dimension_button.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx index 4bfd096279354..a00b56480a694 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx @@ -58,6 +58,15 @@ export function DimensionButton({ overflow: hidden; min-height: ${euiThemeVars.euiSizeXL}; position: relative; + + &:hover, + &:focus { + .lnsLayerPanel__dimensionRemove { + visibility: visible; + opacity: 1; + transition: opacity ${euiThemeVars.euiAnimSpeedFast} ease-in-out; + } + } `} > @@ -100,7 +109,7 @@ export function DimensionButton({ })} onClick={() => onRemoveClick(accessorConfig.columnId)} css={css` - margin-right: $euiSizeS; + margin-right: ${euiThemeVars.euiSizeS}; visibility: hidden; opacity: 0; color: ${euiThemeVars.euiTextSubduedColor}; @@ -108,13 +117,6 @@ export function DimensionButton({ &:hover { color: ${euiThemeVars.euiColorDangerText}; } - - &:hover, - &:focus { - visibility: visible; - opacity: 1; - transition: opacity ${euiThemeVars.euiAnimSpeedFast} ease-in-out; - } `} /> From d6aeae73ee7fd7d12aa2388fc9dd9e3c009b0c68 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 12:00:30 -0500 Subject: [PATCH 075/202] move more styles to dimension_button --- .../dimension_buttons/dimension_button.tsx | 6 +++ .../config_panel/layer_panel.scss | 47 +++++++------------ 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx index a00b56480a694..d6250abf5d3cd 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button.tsx @@ -74,6 +74,12 @@ export function DimensionButton({ onClick(accessorConfig.columnId)} aria-label={triggerLinkA11yText(label)} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index 0e7cb846c704d..c9425e7e2a87f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -64,33 +64,6 @@ padding: $euiSizeS $euiSize; } -.lnsLayerPanel__dimensionContainer { - position: relative; - - &+& { - margin-top: $euiSizeS; - } -} - -.lnsLayerPanel__dimensionLink { - &:hover { - text-decoration: none; - } -} - -.domDragDrop-isReplacing { - .dimensionTrigger__textLabel { - text-decoration: line-through; - } -} - -.lnsLayerPanel__triggerTextContent { - // Make EUI button content not centered - justify-content: flex-start; - padding: 0 !important; // sass-lint:disable-line no-important - color: $euiTextSubduedColor; -} - .lnsLayerPanel__styleEditor { padding: $euiSize; } @@ -115,12 +88,26 @@ } } +// Start dimension style overrides + +.lnsLayerPanel__dimensionContainer { + position: relative; + + &+& { + margin-top: $euiSizeS; + } +} + +.domDragDrop-isReplacing { + .dimensionTrigger__textLabel { + text-decoration: line-through; + } +} + // Added .lnsLayerPanel__dimension specificity required for animation style override .lnsLayerPanel__dimension .lnsLayerPanel__dimensionLink { - width: 100%; - &:focus { - @include passDownFocusRing('.lnsLayerPanel__triggerTextLabel'); + @include passDownFocusRing('.dimensionTrigger__textLabel'); background-color: transparent; text-decoration-thickness: $euiBorderWidthThin !important; } From d8baa41e5734516851d47bc3b4f7b3edb09327f0 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 13:19:19 -0500 Subject: [PATCH 076/202] restore empty button drag drop behavior --- .../buttons/empty_dimension_button.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index faa74d078ea50..1e2b3ff4b4c05 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -16,6 +16,8 @@ import { DropTargetSwapDuplicateCombine, } from '@kbn/dom-drag-drop'; import { EmptyDimensionButton as EmptyDimensionButtonInner } from '@kbn/visualization-ui-components/public'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { isDraggedField } from '../../../../utils'; import { generateId } from '../../../../id_generator'; @@ -188,11 +190,17 @@ export function EmptyDimensionButton({ onDrop={handleOnDrop} dropTypes={dropTypes} > - {typeof group.suggestedValue?.() === 'number' ? ( - - ) : ( - - )} +
    + {typeof group.suggestedValue?.() === 'number' ? ( + + ) : ( + + )} +
    ); From 38f60f8119c057db1dc0eb055ceda937dedccc8e Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 14:25:05 -0500 Subject: [PATCH 077/202] move palette dimension button styles inline --- .../dimension_buttons/palette_indicator.tsx | 20 ++++++++++++++++++- .../config_panel/layer_panel.scss | 20 ------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/palette_indicator.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/palette_indicator.tsx index 5befa0ece26e1..48d48cb2e71bc 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/palette_indicator.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/palette_indicator.tsx @@ -8,14 +8,32 @@ import React from 'react'; import { EuiColorPaletteDisplay } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { AccessorConfig } from './types'; export function PaletteIndicator({ accessorConfig }: { accessorConfig: AccessorConfig }) { if (accessorConfig.triggerIconType !== 'colorBy' || !accessorConfig.palette) return null; return ( -
    +
    diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index c9425e7e2a87f..3afcc0173ca2e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -68,26 +68,6 @@ padding: $euiSize; } -.lnsLayerPanel__colorIndicator { - margin-left: $euiSizeS; -} - -.lnsLayerPanel__paletteContainer { - position: absolute; - bottom: 0; - left: 0; - right: 0; -} - -.lnsLayerPanel__palette { - height: $euiSizeXS / 2; - border-radius: 0 0 ($euiBorderRadius - 1px) ($euiBorderRadius - 1px); - - &::after { - border: none; - } -} - // Start dimension style overrides .lnsLayerPanel__dimensionContainer { From 40a45709c4a481e59b5722184715b60fabed7c4f Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 14:45:47 -0500 Subject: [PATCH 078/202] drag and drop sort of working --- packages/kbn-dom-drag-drop/README.md | 13 +-- src/plugins/event_annotation/kibana.jsonc | 3 +- .../get_annotation_accessor.ts | 32 ++++++ .../group_editor_controls.test.tsx | 4 +- .../group_editor_controls.tsx | 102 ++++++++++++------ .../components/group_editor_controls/index.ts | 10 ++ .../public/components/index.ts | 2 + .../public/get_table_list.tsx | 41 +++---- src/plugins/event_annotation/public/index.ts | 2 +- .../common/index.ts | 9 ++ .../dimension_button_icon.tsx | 6 +- .../public/index.ts | 2 + .../public/types.ts | 11 ++ x-pack/plugins/lens/common/types.ts | 4 +- .../visualizations/xy/annotations/helpers.tsx | 26 +---- .../visualizations/xy/visualization.tsx | 8 +- 16 files changed, 182 insertions(+), 93 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts rename src/plugins/event_annotation/public/components/{ => group_editor_controls}/group_editor_controls.test.tsx (98%) rename src/plugins/event_annotation/public/components/{ => group_editor_controls}/group_editor_controls.tsx (66%) create mode 100644 src/plugins/event_annotation/public/components/group_editor_controls/index.ts create mode 100644 src/plugins/visualization_ui_components/common/index.ts create mode 100644 src/plugins/visualization_ui_components/public/types.ts diff --git a/packages/kbn-dom-drag-drop/README.md b/packages/kbn-dom-drag-drop/README.md index c0145516a811e..892bb60675d92 100644 --- a/packages/kbn-dom-drag-drop/README.md +++ b/packages/kbn-dom-drag-drop/README.md @@ -23,9 +23,7 @@ const context = useContext(DragContext); In your child application, place a `ChildDragDropProvider` at the root of that, and spread the context into it: ```js - - ... your child app here ... - +... your child app here ... ``` This enables your child application to share the same drag / drop context as the root application. @@ -71,9 +69,7 @@ return ( To create a reordering group, surround the elements from the same group with a `ReorderProvider`: ```js - - ... elements from one group here ... - +... elements from one group here ... ``` The children `DragDrop` components must have props defined as in the example: @@ -85,8 +81,8 @@ The children `DragDrop` components must have props defined as in the example: ``` - diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index 627d43aa2ba51..1099c467d502f 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -15,7 +15,8 @@ "visualizations", "dataViews", "unifiedSearch", - "kibanaUtils" + "kibanaUtils", + "visualizationUiComponents" ], "optionalPlugins": [ "savedObjectsTagging", diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts b/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts new file mode 100644 index 0000000000000..59cdfe15d0814 --- /dev/null +++ b/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.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 type { AccessorConfig } from '@kbn/visualization-ui-components/public'; +import type { EventAnnotationConfig } from '../../../common'; +import { + defaultAnnotationColor, + defaultAnnotationRangeColor, + isRangeAnnotationConfig, +} from '../../event_annotation_service/helpers'; +import { annotationsIconSet } from '../annotation_editor_controls'; + +export const getAnnotationAccessor = (annotation: EventAnnotationConfig): AccessorConfig => { + const annotationIcon = !isRangeAnnotationConfig(annotation) + ? annotationsIconSet.find((option) => option.value === annotation?.icon) || + annotationsIconSet.find((option) => option.value === 'triangle') + : undefined; + const icon = annotationIcon?.icon ?? annotationIcon?.value; + return { + columnId: annotation.id, + triggerIconType: annotation.isHidden ? 'invisible' : icon ? 'custom' : 'color', + customIcon: icon, + color: + annotation?.color || + (isRangeAnnotationConfig(annotation) ? defaultAnnotationRangeColor : defaultAnnotationColor), + }; +}; diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx similarity index 98% rename from src/plugins/event_annotation/public/components/group_editor_controls.test.tsx rename to src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index 869b612b974f1..435fe3a60c306 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -7,7 +7,7 @@ */ import React, { ChangeEvent, FormEvent } from 'react'; -import { EventAnnotationGroupConfig } from '../../common'; +import { EventAnnotationGroupConfig } from '../../../common'; import { ReactWrapper } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { GroupEditorControls } from './group_editor_controls'; @@ -15,7 +15,7 @@ import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { act } from 'react-dom/test-utils'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; -import { AnnotationEditorControls } from '.'; +import { AnnotationEditorControls } from '..'; describe('event annotation group editor', () => { const dataViewId = 'my-index-pattern'; diff --git a/src/plugins/event_annotation/public/components/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx similarity index 66% rename from src/plugins/event_annotation/public/components/group_editor_controls.tsx rename to src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index d110c71d306cd..59b1d72b6b431 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -7,30 +7,36 @@ */ import { - EuiButton, EuiFieldText, EuiForm, EuiFormRow, EuiSelect, - EuiSpacer, EuiText, EuiTextArea, useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import { DragContext, DragDrop, ReorderProvider } from '@kbn/dom-drag-drop'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; -import React, { useEffect, useMemo, useState } from 'react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { + DimensionButton, + DimensionTrigger, + EmptyDimensionButton, + QueryInputServices, +} from '@kbn/visualization-ui-components/public'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; import { createCopiedAnnotation, EventAnnotationConfig, EventAnnotationGroupConfig, -} from '../../common'; -import { AnnotationEditorControls } from './annotation_editor_controls'; +} from '../../../common'; +import { AnnotationEditorControls } from '../annotation_editor_controls'; +import { getAnnotationAccessor } from './get_annotation_accessor'; export const GroupEditorControls = ({ group, @@ -80,6 +86,12 @@ export const GroupEditorControls = ({ setNewAnnotationId(uuidv4()); }, [group.annotations.length]); + const { dragging } = useContext(DragContext); + + const reorderableGroup = group.annotations.map(({ id }) => ({ + id, + })); + return !selectedAnnotation ? ( <>
    - {group.annotations.map((annotation) => ( - <> - setSelectedAnnotation(annotation)}> - {annotation.label} - - - - ))} - { - const newAnnotation = createCopiedAnnotation( - newAnnotationId, - new Date().toISOString() - ); + + {group.annotations.map((annotation, index) => ( +
    + + setSelectedAnnotation(annotation)} + onRemoveClick={() => {}} + accessorConfig={getAnnotationAccessor(annotation)} + label={annotation.label} + > + + + +
    + ))} +
    - update({ ...group, annotations: [...group.annotations, newAnnotation] }); - - setSelectedAnnotation(newAnnotation); - }} +
    - { + const newAnnotation = createCopiedAnnotation( + newAnnotationId, + new Date().toISOString() + ); + + update({ ...group, annotations: [...group.annotations, newAnnotation] }); + + setSelectedAnnotation(newAnnotation); + }} /> - +
    diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/index.ts b/src/plugins/event_annotation/public/components/group_editor_controls/index.ts new file mode 100644 index 0000000000000..016048f7f667f --- /dev/null +++ b/src/plugins/event_annotation/public/components/group_editor_controls/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 './group_editor_controls'; +export * from './get_annotation_accessor'; diff --git a/src/plugins/event_annotation/public/components/index.ts b/src/plugins/event_annotation/public/components/index.ts index 4a8d66671c62d..983a30d85db6c 100644 --- a/src/plugins/event_annotation/public/components/index.ts +++ b/src/plugins/event_annotation/public/components/index.ts @@ -7,3 +7,5 @@ */ export * from './annotation_editor_controls'; + +export * from './group_editor_controls'; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index 6ede34ee5a604..e0ed6080f4d6a 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -17,6 +17,7 @@ import { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import { RootDragDropProvider } from '@kbn/dom-drag-drop'; import { EventAnnotationGroupTableList } from './components/table_list'; import { EventAnnotationServiceType } from './event_annotation_service/types'; @@ -35,24 +36,26 @@ export const getTableList = ( services: EventAnnotationListingPageServices ) => { return ( - - - + + + + + ); }; diff --git a/src/plugins/event_annotation/public/index.ts b/src/plugins/event_annotation/public/index.ts index aa9921c84e24b..d9a3f94298aad 100644 --- a/src/plugins/event_annotation/public/index.ts +++ b/src/plugins/event_annotation/public/index.ts @@ -21,4 +21,4 @@ export { isManualPointAnnotationConfig, isQueryAnnotationConfig, } from './event_annotation_service/helpers'; -export { AnnotationEditorControls, annotationsIconSet } from './components'; +export { AnnotationEditorControls, annotationsIconSet, getAnnotationAccessor } from './components'; diff --git a/src/plugins/visualization_ui_components/common/index.ts b/src/plugins/visualization_ui_components/common/index.ts new file mode 100644 index 0000000000000..3f66f3b659d8a --- /dev/null +++ b/src/plugins/visualization_ui_components/common/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 {}; diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button_icon.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button_icon.tsx index bcb3ddb1e44bb..6294c27254a56 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button_icon.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/dimension_button_icon.tsx @@ -9,10 +9,14 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import type { AccessorConfig, Message } from './types'; const baseIconProps = { - className: 'lnsLayerPanel__colorIndicator', + css: css` + margin-left: ${euiThemeVars.euiSizeS}; + `, } as const; const getIconFromAccessorConfig = (accessorConfig: AccessorConfig) => ( diff --git a/src/plugins/visualization_ui_components/public/index.ts b/src/plugins/visualization_ui_components/public/index.ts index cb203bd80a15d..2598b7537179e 100644 --- a/src/plugins/visualization_ui_components/public/index.ts +++ b/src/plugins/visualization_ui_components/public/index.ts @@ -48,6 +48,8 @@ export type { QueryInputServices, } from './components'; +export type { FormatFactory } from './types'; + export function plugin() { return new VisualizationUiComponentsPlugin(); } diff --git a/src/plugins/visualization_ui_components/public/types.ts b/src/plugins/visualization_ui_components/public/types.ts new file mode 100644 index 0000000000000..8d259c3b90f90 --- /dev/null +++ b/src/plugins/visualization_ui_components/public/types.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 type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; + +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index 3c6830ba50b0d..ee89551dfc36a 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -9,7 +9,6 @@ import type { Filter, FilterMeta } from '@kbn/es-query'; import type { Position } from '@elastic/charts'; import type { $Values } from '@kbn/utility-types'; import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; -import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import type { ColorMode } from '@kbn/charts-plugin/common'; import type { LegendSize } from '@kbn/visualizations-plugin/common'; import { CategoryDisplay, LegendDisplay, NumberDisplay, PieChartTypes } from './constants'; @@ -21,8 +20,7 @@ export type { AllowedPartitionOverrides } from '@kbn/expression-partition-vis-pl export type { AllowedSettingsOverrides } from '@kbn/charts-plugin/common'; export type { AllowedGaugeOverrides } from '@kbn/expression-gauge-plugin/common'; export type { AllowedXYOverrides } from '@kbn/expression-xy-plugin/common'; - -export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; +export type { FormatFactory } from '@kbn/visualization-ui-components/public'; export interface DateRange { fromDate: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx index d929d0e7271ff..1421a54be484d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/helpers.tsx @@ -8,11 +8,8 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { - defaultAnnotationColor, - defaultAnnotationRangeColor, + getAnnotationAccessor, isQueryAnnotationConfig, - isRangeAnnotationConfig, - annotationsIconSet, } from '@kbn/event-annotation-plugin/public'; import { createCopiedAnnotation, @@ -21,7 +18,6 @@ import { } from '@kbn/event-annotation-plugin/common'; import { IconChartBarAnnotations } from '@kbn/chart-icons'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import type { AccessorConfig } from '@kbn/visualization-ui-components/public'; import { isDraggedDataViewField } from '../../../utils'; import type { FramePublicAPI, Visualization } from '../../../types'; import { isHorizontalChart } from '../state_helpers'; @@ -407,26 +403,8 @@ export const setAnnotationsDimension: Visualization['setDimension'] = ( }; }; -export const getSingleColorAnnotationConfig = ( - annotation: EventAnnotationConfig -): AccessorConfig => { - const annotationIcon = !isRangeAnnotationConfig(annotation) - ? annotationsIconSet.find((option) => option.value === annotation?.icon) || - annotationsIconSet.find((option) => option.value === 'triangle') - : undefined; - const icon = annotationIcon?.icon ?? annotationIcon?.value; - return { - columnId: annotation.id, - triggerIconType: annotation.isHidden ? 'invisible' : icon ? 'custom' : 'color', - customIcon: icon, - color: - annotation?.color || - (isRangeAnnotationConfig(annotation) ? defaultAnnotationRangeColor : defaultAnnotationColor), - }; -}; - export const getAnnotationsAccessorColorConfig = (layer: XYAnnotationLayerConfig) => - layer.annotations.map((annotation) => getSingleColorAnnotationConfig(annotation)); + layer.annotations.map((annotation) => getAnnotationAccessor(annotation)); export const getAnnotationsConfiguration = ({ state, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 0c7b71a6f0ba0..c8500f4814a57 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -14,7 +14,10 @@ import type { PaletteRegistry } from '@kbn/coloring'; import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { CoreStart, SavedObjectReference, ThemeServiceStart } from '@kbn/core/public'; -import type { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { + EventAnnotationServiceType, + getAnnotationAccessor, +} from '@kbn/event-annotation-plugin/public'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -82,7 +85,6 @@ import { getUniqueLabels, onAnnotationDrop, isDateHistogram, - getSingleColorAnnotationConfig, } from './annotations/helpers'; import { checkXAccessorCompatibility, @@ -1128,7 +1130,7 @@ function getVisualizationInfo( palette.push( ...layer.annotations .filter(({ isHidden }) => !isHidden) - .map((annotation) => getSingleColorAnnotationConfig(annotation).color) + .map((annotation) => getAnnotationAccessor(annotation).color) ); } From 43910e54de335e174326e5e049b2f36937971918 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 15:52:43 -0500 Subject: [PATCH 079/202] apply annotation changes to group --- .../group_editor_controls.test.tsx | 35 +++++++++++++++++-- .../group_editor_controls.tsx | 33 +++++++++-------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index 435fe3a60c306..5f0fdd50fe1da 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -7,7 +7,7 @@ */ import React, { ChangeEvent, FormEvent } from 'react'; -import { EventAnnotationGroupConfig } from '../../../common'; +import { EventAnnotationGroupConfig, getDefaultManualAnnotation } from '../../../common'; import { ReactWrapper } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { GroupEditorControls } from './group_editor_controls'; @@ -17,6 +17,13 @@ import { act } from 'react-dom/test-utils'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { AnnotationEditorControls } from '..'; +jest.mock('@elastic/eui', () => { + return { + ...jest.requireActual('@elastic/eui'), + EuiDatePicker: () => <>, // for some reason this component caused an infinite loop when the props updated + }; +}); + describe('event annotation group editor', () => { const dataViewId = 'my-index-pattern'; const adHocDataViewId = 'ad-hoc'; @@ -37,11 +44,13 @@ describe('event annotation group editor', () => { let wrapper: ReactWrapper; let updateMock: jest.Mock; + let setSelectedAnnotationMock: jest.Mock; const TagSelector = (_props: { onTagsSelected: (tags: string[]) => void }) =>
    ; beforeEach(async () => { updateMock = jest.fn(); + setSelectedAnnotationMock = jest.fn(); wrapper = mountWithIntl( { ] as DataView[] } selectedAnnotation={undefined} - setSelectedAnnotation={jest.fn()} + setSelectedAnnotation={setSelectedAnnotationMock} createDataView={(spec) => Promise.resolve({ id: spec.id, @@ -190,9 +199,29 @@ describe('event annotation group editor', () => { wrapper.find('button[data-test-subj="addAnnotation"]').simulate('click'); }); - expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock).toHaveBeenCalledTimes(2); const newAnnotations = (updateMock.mock.calls[0][0] as EventAnnotationGroupConfig).annotations; expect(newAnnotations.length).toBe(group.annotations.length + 1); expect(wrapper.exists(AnnotationEditorControls)); // annotation controls opened }); + + it('incorporates annotation updates into group', () => { + const annotations = [getDefaultManualAnnotation('1', ''), getDefaultManualAnnotation('2', '')]; + + act(() => { + wrapper.setProps({ + selectedAnnotation: annotations[0], + // group: { ...group, annotations }, + }); + }); + + wrapper.find(AnnotationEditorControls).prop('onAnnotationChange')({ + ...annotations[0], + color: 'newColor', + }); + + expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock.mock.calls[0][0].annotations[0]).toMatchInlineSnapshot(`undefined`); + expect(setSelectedAnnotationMock).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 59b1d72b6b431..67c15c2e3c366 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -6,15 +6,7 @@ * Side Public License, v 1. */ -import { - EuiFieldText, - EuiForm, - EuiFormRow, - EuiSelect, - EuiText, - EuiTextArea, - useEuiTheme, -} from '@elastic/eui'; +import { EuiFieldText, EuiForm, EuiFormRow, EuiSelect, EuiText, EuiTextArea } from '@elastic/eui'; import { css } from '@emotion/react'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { DragContext, DragDrop, ReorderProvider } from '@kbn/dom-drag-drop'; @@ -28,7 +20,7 @@ import { EmptyDimensionButton, QueryInputServices, } from '@kbn/visualization-ui-components/public'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; import { createCopiedAnnotation, @@ -41,7 +33,7 @@ import { getAnnotationAccessor } from './get_annotation_accessor'; export const GroupEditorControls = ({ group, update, - setSelectedAnnotation, + setSelectedAnnotation: _setSelectedAnnotation, selectedAnnotation, TagSelector, dataViews: globalDataViews, @@ -57,8 +49,6 @@ export const GroupEditorControls = ({ createDataView: (spec: DataViewSpec) => Promise; queryInputServices: QueryInputServices; }) => { - const { euiTheme } = useEuiTheme(); - // save the spec for the life of the component since the user might change their mind after selecting another data view const [adHocDataView, setAdHocDataView] = useState(); @@ -68,6 +58,19 @@ export const GroupEditorControls = ({ } }, [createDataView, group.dataViewSpec]); + const setSelectedAnnotation = useCallback( + (newSelection: EventAnnotationConfig) => { + update({ + ...group, + annotations: group.annotations.map((annotation) => + annotation.id === newSelection.id ? newSelection : annotation + ), + }); + _setSelectedAnnotation(newSelection); + }, + [_setSelectedAnnotation, group, update] + ); + const dataViews = useMemo(() => { const items = [...globalDataViews]; if (adHocDataView) { @@ -97,7 +100,7 @@ export const GroupEditorControls = ({

    @@ -178,6 +181,7 @@ export const GroupEditorControls = ({ {group.annotations.map((annotation, index) => (
    { From c4b37b1b328069bcc43f4d28d126de7a34590357 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 17:21:08 -0500 Subject: [PATCH 080/202] remove annotations --- .../group_editor_controls.test.tsx | 24 +++++++++++++++++-- .../group_editor_controls.tsx | 7 +++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index 5f0fdd50fe1da..dfe83f6cbfb22 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -211,7 +211,7 @@ describe('event annotation group editor', () => { act(() => { wrapper.setProps({ selectedAnnotation: annotations[0], - // group: { ...group, annotations }, + group: { ...group, annotations }, }); }); @@ -221,7 +221,27 @@ describe('event annotation group editor', () => { }); expect(updateMock).toHaveBeenCalledTimes(1); - expect(updateMock.mock.calls[0][0].annotations[0]).toMatchInlineSnapshot(`undefined`); + expect(updateMock.mock.calls[0][0].annotations[0].color).toBe('newColor'); expect(setSelectedAnnotationMock).toHaveBeenCalledTimes(1); }); + + it('removes an annotation from a group', () => { + const annotations = [getDefaultManualAnnotation('1', ''), getDefaultManualAnnotation('2', '')]; + + act(() => { + wrapper.setProps({ + group: { ...group, annotations }, + }); + }); + + act(() => { + wrapper + .find('button[data-test-subj="indexPattern-dimension-remove"]') + .last() + .simulate('click'); + }); + + expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock.mock.calls[0][0].annotations).toEqual(annotations.slice(0, 1)); + }); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 67c15c2e3c366..26876027c1b7f 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -205,7 +205,12 @@ export const GroupEditorControls = ({ defaultMessage: 'Annotations', })} onClick={() => setSelectedAnnotation(annotation)} - onRemoveClick={() => {}} + onRemoveClick={() => + update({ + ...group, + annotations: group.annotations.filter(({ id }) => id !== annotation.id), + }) + } accessorConfig={getAnnotationAccessor(annotation)} label={annotation.label} > From c7a359c582a7b688669b99640ab0fc6e5c0967f3 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 17:44:09 -0500 Subject: [PATCH 081/202] scroll to top when view changes --- .../public/components/group_editor_flyout.tsx | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index d7dff29968fdd..93492fa992d15 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -18,7 +18,7 @@ import { EuiButton, htmlIdGenerator, } from '@elastic/eui'; -import React, { useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; @@ -46,8 +46,27 @@ export const GroupEditorFlyout = ({ queryInputServices: QueryInputServices; }) => { const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); + const flyoutBodyOverflowRef = useRef(null); + useEffect(() => { + if (!flyoutBodyOverflowRef.current) { + flyoutBodyOverflowRef.current = document.querySelector('.euiFlyoutBody__overflow'); + } + }, []); - const [selectedAnnotation, setSelectedAnnotation] = useState(); + const resetContentScroll = useCallback( + () => flyoutBodyOverflowRef.current && flyoutBodyOverflowRef.current.scroll(0, 0), + [] + ); + + const [selectedAnnotation, _setSelectedAnnotation] = useState(); + const setSelectedAnnotation = useCallback( + (newValue: EventAnnotationConfig | undefined) => { + if ((!newValue && selectedAnnotation) || (newValue && !selectedAnnotation)) + resetContentScroll(); + _setSelectedAnnotation(newValue); + }, + [resetContentScroll, selectedAnnotation] + ); return ( From c66d403f80520ad09e230a42ceaec68feead8131 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 4 May 2023 17:52:18 -0500 Subject: [PATCH 082/202] fix adding new annotation --- .../components/group_editor_controls/group_editor_controls.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 26876027c1b7f..d5e73000cbb6f 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -236,9 +236,8 @@ export const GroupEditorControls = ({ new Date().toISOString() ); - update({ ...group, annotations: [...group.annotations, newAnnotation] }); - setSelectedAnnotation(newAnnotation); + update({ ...group, annotations: [...group.annotations, newAnnotation] }); }} />
    From cbfb39000774e0a0027dd3a62530b23cc28772f6 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 5 May 2023 11:55:45 -0500 Subject: [PATCH 083/202] drag-and-drop reordering (only mouse) --- .../group_editor_controls.tsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index d5e73000cbb6f..5adb5b2a10a85 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -184,21 +184,35 @@ export const GroupEditorControls = ({ key={index} css={css` margin-top: ${euiThemeVars.euiSizeS}; + position: relative; // this is to properly contain the absolutely-positioned drop target in DragDrop `} > { + const newAnnotations = [...group.annotations]; + const sourceIndex = newAnnotations.findIndex(({ id }) => id === dropped.id); + const targetIndex = index; + + if (sourceIndex !== targetIndex) { + const temp = newAnnotations[sourceIndex]; + newAnnotations[sourceIndex] = newAnnotations[targetIndex]; + newAnnotations[targetIndex] = temp; + } + + update({ ...group, annotations: newAnnotations }); + }} > Date: Fri, 5 May 2023 13:38:10 -0500 Subject: [PATCH 084/202] move annotation list to own component --- .../group_editor_controls/annotation_list.tsx | 118 ++++++++++++++++++ .../group_editor_controls.tsx | 114 ++--------------- 2 files changed, 127 insertions(+), 105 deletions(-) create mode 100644 src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx new file mode 100644 index 0000000000000..93bd6fa0583e3 --- /dev/null +++ b/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx @@ -0,0 +1,118 @@ +/* + * 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 { css } from '@emotion/react'; +import { DragContext, DragDrop, ReorderProvider } from '@kbn/dom-drag-drop'; +import { + DimensionButton, + DimensionTrigger, + EmptyDimensionButton, +} from '@kbn/visualization-ui-components/public'; +import React, { useContext, useEffect, useState } from 'react'; +import { v4 as uuidv4 } from 'uuid'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; +import { createCopiedAnnotation, EventAnnotationConfig } from '../../../common'; +import { getAnnotationAccessor } from '..'; + +export const AnnotationList = ({ + annotations, + selectAnnotation, + update: updateAnnotations, +}: { + annotations: EventAnnotationConfig[]; + selectAnnotation: (annotation: EventAnnotationConfig) => void; + update: (annotations: EventAnnotationConfig[]) => void; +}) => { + const [newAnnotationId, setNewAnnotationId] = useState(uuidv4()); + useEffect(() => { + setNewAnnotationId(uuidv4()); + }, [annotations.length]); + + const { dragging } = useContext(DragContext); + + const reorderableGroup = annotations.map(({ id }) => ({ + id, + })); + + return ( +
    + + {annotations.map((annotation, index) => ( +
    + { + const newAnnotations = [...annotations]; + const sourceIndex = newAnnotations.findIndex(({ id }) => id === dropped.id); + const targetIndex = index; + + if (sourceIndex !== targetIndex) { + const temp = newAnnotations[sourceIndex]; + newAnnotations[sourceIndex] = newAnnotations[targetIndex]; + newAnnotations[targetIndex] = temp; + } + + updateAnnotations(newAnnotations); + }} + > + selectAnnotation(annotation)} + onRemoveClick={() => + updateAnnotations(annotations.filter(({ id }) => id !== annotation.id)) + } + accessorConfig={getAnnotationAccessor(annotation)} + label={annotation.label} + > + + + +
    + ))} +
    + +
    + { + const newAnnotation = createCopiedAnnotation(newAnnotationId, new Date().toISOString()); + + selectAnnotation(newAnnotation); + updateAnnotations([...annotations, newAnnotation]); + }} + /> +
    +
    + ); +}; diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 5adb5b2a10a85..711928e0d03f0 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -9,26 +9,15 @@ import { EuiFieldText, EuiForm, EuiFormRow, EuiSelect, EuiText, EuiTextArea } from '@elastic/eui'; import { css } from '@emotion/react'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; -import { DragContext, DragDrop, ReorderProvider } from '@kbn/dom-drag-drop'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { euiThemeVars } from '@kbn/ui-theme'; -import { - DimensionButton, - DimensionTrigger, - EmptyDimensionButton, - QueryInputServices, -} from '@kbn/visualization-ui-components/public'; -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { v4 as uuidv4 } from 'uuid'; -import { - createCopiedAnnotation, - EventAnnotationConfig, - EventAnnotationGroupConfig, -} from '../../../common'; +import { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../../common'; import { AnnotationEditorControls } from '../annotation_editor_controls'; -import { getAnnotationAccessor } from './get_annotation_accessor'; +import { AnnotationList } from './annotation_list'; export const GroupEditorControls = ({ group, @@ -84,17 +73,6 @@ export const GroupEditorControls = ({ [dataViews, group.indexPatternId] ); - const [newAnnotationId, setNewAnnotationId] = useState(uuidv4()); - useEffect(() => { - setNewAnnotationId(uuidv4()); - }, [group.annotations.length]); - - const { dragging } = useContext(DragContext); - - const reorderableGroup = group.annotations.map(({ id }) => ({ - id, - })); - return !selectedAnnotation ? ( <> -
    - - {group.annotations.map((annotation, index) => ( -
    - { - const newAnnotations = [...group.annotations]; - const sourceIndex = newAnnotations.findIndex(({ id }) => id === dropped.id); - const targetIndex = index; - - if (sourceIndex !== targetIndex) { - const temp = newAnnotations[sourceIndex]; - newAnnotations[sourceIndex] = newAnnotations[targetIndex]; - newAnnotations[targetIndex] = temp; - } - - update({ ...group, annotations: newAnnotations }); - }} - > - setSelectedAnnotation(annotation)} - onRemoveClick={() => - update({ - ...group, - annotations: group.annotations.filter(({ id }) => id !== annotation.id), - }) - } - accessorConfig={getAnnotationAccessor(annotation)} - label={annotation.label} - > - - - -
    - ))} -
    - -
    - { - const newAnnotation = createCopiedAnnotation( - newAnnotationId, - new Date().toISOString() - ); - - setSelectedAnnotation(newAnnotation); - update({ ...group, annotations: [...group.annotations, newAnnotation] }); - }} - /> -
    -
    + update({ ...group, annotations: newAnnotations })} + /> From ae31280378f2afb86456c9ce52400f686881ad4d Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 5 May 2023 14:25:42 -0500 Subject: [PATCH 085/202] drag drop to create new annotation --- .../group_editor_controls/annotation_list.tsx | 146 ++++++++++-------- .../dimension_buttons/empty_button.tsx | 2 + 2 files changed, 82 insertions(+), 66 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx index 93bd6fa0583e3..f29638e202548 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/annotation_list.tsx @@ -7,13 +7,13 @@ */ import { css } from '@emotion/react'; -import { DragContext, DragDrop, ReorderProvider } from '@kbn/dom-drag-drop'; +import { DragContext, DragDrop, DropTargetSwapDuplicateCombine } from '@kbn/dom-drag-drop'; import { DimensionButton, DimensionTrigger, EmptyDimensionButton, } from '@kbn/visualization-ui-components/public'; -import React, { useContext, useEffect, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; @@ -36,82 +36,96 @@ export const AnnotationList = ({ const { dragging } = useContext(DragContext); - const reorderableGroup = annotations.map(({ id }) => ({ - id, - })); + const addAnnotationText = i18n.translate('eventAnnotation.annotationList.add', { + defaultMessage: 'Add annotation', + }); + + const addNewAnnotation = useCallback( + (sourceAnnotationId?: string) => { + const source = sourceAnnotationId + ? annotations.find(({ id }) => id === sourceAnnotationId) + : undefined; + const newAnnotation = createCopiedAnnotation( + newAnnotationId, + new Date().toISOString(), + source + ); + + if (!source) { + selectAnnotation(newAnnotation); + } + updateAnnotations([...annotations, newAnnotation]); + }, + [annotations, newAnnotationId, selectAnnotation, updateAnnotations] + ); return (
    - - {annotations.map((annotation, index) => ( -
    ( +
    + - { - const newAnnotations = [...annotations]; - const sourceIndex = newAnnotations.findIndex(({ id }) => id === dropped.id); - const targetIndex = index; - - if (sourceIndex !== targetIndex) { - const temp = newAnnotations[sourceIndex]; - newAnnotations[sourceIndex] = newAnnotations[targetIndex]; - newAnnotations[targetIndex] = temp; - } - - updateAnnotations(newAnnotations); - }} + selectAnnotation(annotation)} + onRemoveClick={() => + updateAnnotations(annotations.filter(({ id }) => id !== annotation.id)) + } + accessorConfig={getAnnotationAccessor(annotation)} + label={annotation.label} > - selectAnnotation(annotation)} - onRemoveClick={() => - updateAnnotations(annotations.filter(({ id }) => id !== annotation.id)) - } - accessorConfig={getAnnotationAccessor(annotation)} - label={annotation.label} - > - - - -
    - ))} - + + + +
    + ))}
    - { - const newAnnotation = createCopiedAnnotation(newAnnotationId, new Date().toISOString()); - - selectAnnotation(newAnnotation); - updateAnnotations([...annotations, newAnnotation]); + + onDrop={({ id: sourceId }) => addNewAnnotation(sourceId)} + > + addNewAnnotation()} + /> +
    ); diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx index 024154a31aa3e..23a9427236cc5 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx @@ -18,6 +18,7 @@ export const EmptyDimensionButton = ({ onClick, dataTestSubj, iconType, + ...otherProps // from Drag&Drop integration }: { label: React.ReactNode; ariaLabel: string; @@ -27,6 +28,7 @@ export const EmptyDimensionButton = ({ }) => { return ( Date: Fri, 5 May 2023 14:38:41 -0500 Subject: [PATCH 086/202] closing flyout first closes annotation editing --- .../components/group_editor_flyout.test.tsx | 35 +++++++++++++------ .../public/components/group_editor_flyout.tsx | 4 ++- .../public/components/table_list.test.tsx | 12 ++++++- .../public/components/table_list.tsx | 24 +++++++------ 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx index bd95f83d88ee2..2acf54c2c84dd 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx @@ -28,6 +28,18 @@ const SELECTORS = { BACK_BUTTON: '[data-test-subj="backToGroupSettings"]', }; +const assertGroupEditingState = (component: ShallowWrapper) => { + expect(component.exists(SELECTORS.SAVE_BUTTON)).toBeTruthy(); + expect(component.exists(SELECTORS.CANCEL_BUTTON)).toBeTruthy(); + expect(component.exists(SELECTORS.BACK_BUTTON)).toBeFalsy(); +}; + +const assertAnnotationEditingState = (component: ShallowWrapper) => { + expect(component.exists(SELECTORS.BACK_BUTTON)).toBeTruthy(); + expect(component.exists(SELECTORS.SAVE_BUTTON)).toBeFalsy(); + expect(component.exists(SELECTORS.CANCEL_BUTTON)).toBeFalsy(); +}; + describe('group editor flyout', () => { const annotation = getDefaultManualAnnotation('my-id', 'some-timestamp'); @@ -91,22 +103,23 @@ describe('group editor flyout', () => { expect(updateGroup).toHaveBeenCalledWith(newGroup); }); test('specific annotation editing', () => { - const assertGroupEditingState = () => { - expect(component.exists(SELECTORS.SAVE_BUTTON)).toBeTruthy(); - expect(component.exists(SELECTORS.CANCEL_BUTTON)).toBeTruthy(); - expect(component.exists(SELECTORS.BACK_BUTTON)).toBeFalsy(); - }; - - assertGroupEditingState(); + assertGroupEditingState(component); component.find(GroupEditorControls).prop('setSelectedAnnotation')(annotation); - expect(component.exists(SELECTORS.BACK_BUTTON)).toBeTruthy(); - expect(component.exists(SELECTORS.SAVE_BUTTON)).toBeFalsy(); - expect(component.exists(SELECTORS.CANCEL_BUTTON)).toBeFalsy(); + assertAnnotationEditingState(component); component.find(SELECTORS.BACK_BUTTON).simulate('click'); - assertGroupEditingState(); + assertGroupEditingState(component); + }); + it('removes active annotation instead of signaling close', () => { + component.find(GroupEditorControls).prop('setSelectedAnnotation')(annotation); + + assertAnnotationEditingState(component); + + component.find(EuiFlyout).prop('onClose')({} as MouseEvent); + + assertGroupEditingState(component); }); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index 93492fa992d15..3798a41396996 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -29,7 +29,7 @@ import { GroupEditorControls } from './group_editor_controls'; export const GroupEditorFlyout = ({ group, updateGroup, - onClose, + onClose: parentOnClose, onSave, savedObjectsTagging, dataViews, @@ -68,6 +68,8 @@ export const GroupEditorFlyout = ({ [resetContentScroll, selectedAnnotation] ); + const onClose = () => (selectedAnnotation ? setSelectedAnnotation(undefined) : parentOnClose()); + return ( diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 2a43df9ae8f74..8e8e7fc3bca4a 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -88,7 +88,7 @@ describe('annotation list view', () => { it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot(` " - + " `); }); @@ -199,5 +199,15 @@ describe('annotation list view', () => { expect(wrapper.find(GroupEditorFlyout).exists()).toBeFalsy(); expect(wrapper.find(TableList).prop('refreshListBouncer')).not.toBe(initialBouncerValue); // (should refresh list) }); + + it('opens editor when title is clicked', async () => { + act(() => { + wrapper.find(TableList).prop('onClickTitle')!({ id: '1234' } as UserContentCommonSchema); + }); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(wrapper.find(GroupEditorFlyout).exists()).toBeTruthy(); + }); }); }); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index 50902a8d276fe..1eed240ecfc97 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -72,6 +72,17 @@ export const EventAnnotationGroupTableList = ({ [eventAnnotationService, listingLimit] ); + const editItem = useCallback( + ({ id }: EventAnnotationGroupContent) => { + if (visualizeCapabilities.save) { + eventAnnotationService + .loadAnnotationGroup(id) + .then((group) => setGroupToEditInfo({ group, id })); + } + }, + [eventAnnotationService, visualizeCapabilities.save] + ); + const [groupToEditInfo, setGroupToEditInfo] = useState<{ group: EventAnnotationGroupConfig; id: string; @@ -111,14 +122,7 @@ export const EventAnnotationGroupTableList = ({ ? (items) => eventAnnotationService.deleteAnnotationGroups(items.map(({ id }) => id)) : undefined } - editItem={ - visualizeCapabilities.save - ? ({ id }) => - eventAnnotationService - .loadAnnotationGroup(id) - .then((group) => setGroupToEditInfo({ group, id })) - : undefined - } + editItem={editItem} listingLimit={listingLimit} initialPageSize={initialPageSize} initialFilter={''} @@ -129,9 +133,7 @@ export const EventAnnotationGroupTableList = ({ entityNamePlural={i18n.translate('eventAnnotation.tableList.entityNamePlural', { defaultMessage: 'annotation groups', })} - onClickTitle={(item) => { - // TODO - what happens if I click here? - }} + onClickTitle={editItem} {...parentProps} /> {flyout} From 9cdc146d99468121eaaf1a9278650fea5d963a6b Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 5 May 2023 14:55:48 -0500 Subject: [PATCH 087/202] make query popover wide --- .../public/components/query_input/filter_query_input.scss | 4 ++++ .../public/components/query_input/filter_query_input.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss index 38a6f7909f5e5..b971b65e897ce 100644 --- a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss +++ b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.scss @@ -4,4 +4,8 @@ @include euiFontSizeS; min-height: $euiSizeXL; width: 100%; +} + +.filterQueryInput__popover { + width: $euiSize * 60; } \ No newline at end of file diff --git a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx index 9d17025fce8d3..e998e48eefe99 100644 --- a/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx +++ b/src/plugins/visualization_ui_components/public/components/query_input/filter_query_input.tsx @@ -102,7 +102,7 @@ export function FilterQueryInput({ isOpen={filterPopoverOpen} closePopover={onClosePopup} anchorClassName="eui-fullWidth" - panelClassName="lnsIndexPatternDimensionEditor__filtersEditor" + panelClassName="filterQueryInput__popover" initialFocus={dataTestSubj ? `textarea[data-test-subj='${dataTestSubj}']` : undefined} button={ From 5e66b998833b59311f3c83b5291c8be303d35202 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 15 May 2023 15:43:09 -0500 Subject: [PATCH 088/202] remove superfluous hide parameter --- .../__snapshots__/visualization.test.ts.snap | 4 -- .../revert_changes_action.test.tsx.snap | 2 - .../__snapshots__/save_action.test.tsx.snap | 4 -- .../actions/revert_changes_action.test.tsx | 1 - .../actions/revert_changes_action.tsx | 1 - .../annotations/actions/save_action.test.tsx | 1 - .../annotations/actions/unlink_action.test.ts | 1 - .../xy/annotations/actions/unlink_action.tsx | 1 - .../public/visualizations/xy/state_helpers.ts | 6 +-- .../visualizations/xy/to_expression.test.ts | 1 - .../lens/public/visualizations/xy/types.ts | 3 +- .../visualizations/xy/visualization.test.ts | 48 ------------------- .../xy/visualization_helpers.tsx | 2 - .../annotations_config_panel/index.test.tsx | 10 ---- .../xy/xy_config_panel/layer_header.test.tsx | 2 - .../visualizations/xy/xy_suggestions.test.ts | 2 - 16 files changed, 2 insertions(+), 87 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap index 1496ffe97dc30..a1955c5589eea 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap @@ -34,7 +34,6 @@ Object { "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "indexPattern1", "layerId": "", @@ -46,7 +45,6 @@ Object { exports[`xy_visualization #appendLayer adding an annotation layer adds a by-value annotation layer 1`] = ` Object { "annotations": Array [], - "hide": false, "ignoreGlobalFilters": true, "indexPatternId": "indexPattern1", "layerId": "", @@ -90,7 +88,6 @@ Array [ "type": "manual", }, ], - "hide": undefined, "ignoreGlobalFilters": true, "indexPatternId": "data-view-123", "layerId": "annotation", @@ -130,7 +127,6 @@ Array [ "type": "manual", }, ], - "hide": undefined, "ignoreGlobalFilters": true, "indexPatternId": "data-view-773203", "layerId": "annotation", diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap index b9c63d726fe05..678f393b592ce 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap @@ -16,7 +16,6 @@ Object { }, ], "description": "", - "hide": false, "ignoreGlobalFilters": true, "indexPatternId": "other index pattern", "layerId": "some-id", @@ -36,7 +35,6 @@ Object { "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": true, "indexPatternId": "other index pattern", "layerId": "some-id", diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap index a947c50182fde..d317538fb4124 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -37,7 +37,6 @@ Array [ "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", @@ -92,7 +91,6 @@ Array [ "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", @@ -149,7 +147,6 @@ Array [ "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", @@ -204,7 +201,6 @@ Array [ "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": false, "indexPatternId": "some-index-pattern", "layerId": "mylayerid", diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx index 5758644a8cb1c..d01ffbc08451e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx @@ -22,7 +22,6 @@ describe('revert changes routine', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, - hide: false, simpleView: false, annotations: [ { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx index a7e52bbb00748..e733aaa5e8e49 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx @@ -89,7 +89,6 @@ export const revert = ({ layerId: layer.layerId, layerType: layer.layerType, annotationGroupId: layer.annotationGroupId, - hide: layer.hide, simpleView: layer.simpleView, indexPatternId: layer.__lastSaved.indexPatternId, diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index 7704d68227572..af78db5a05f06 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -118,7 +118,6 @@ describe('annotation group save action', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, - hide: false, simpleView: false, annotations: [ { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts index 58e70506bdc3c..29cbde77da0e5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts @@ -23,7 +23,6 @@ describe('annotation group unlink actions', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, - hide: false, simpleView: false, annotations: [ { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx index 54bc4552edf29..47d06fcc00a8b 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx @@ -32,7 +32,6 @@ export const getUnlinkLayerAction = ({ layerType: layer.layerType, indexPatternId: layer.indexPatternId, ignoreGlobalFilters: layer.ignoreGlobalFilters, - hide: layer.hide, simpleView: layer.simpleView, annotations: layer.annotations, }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index b6a8970e72ad0..7667c6052af3f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -187,7 +187,6 @@ export function getPersistableState(state: XYState) { layerId: layer.layerId, layerType: layer.layerType, annotationGroupRef: referenceName, - hide: layer.hide, simpleView: layer.simpleView, }; @@ -198,7 +197,6 @@ export function getPersistableState(state: XYState) { layerId: layer.layerId, layerType: layer.layerType, annotationGroupRef: referenceName, - hide: layer.hide, simpleView: layer.simpleView, annotations: layer.annotations, ignoreGlobalFilters: layer.ignoreGlobalFilters, @@ -258,7 +256,6 @@ export function injectReferences( if (isPersistedByValueAnnotationsLayer(persistedLayer)) { injectedLayer = { ...persistedLayer, - hide: Boolean(persistedLayer.hide), simpleView: Boolean(persistedLayer.simpleView), indexPatternId: // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? @@ -280,12 +277,11 @@ export function injectReferences( // declared as a separate variable for type checking const commonProps: Pick< XYByReferenceAnnotationLayerConfig, - 'layerId' | 'layerType' | 'annotationGroupId' | 'hide' | 'simpleView' | '__lastSaved' + 'layerId' | 'layerType' | 'annotationGroupId' | 'simpleView' | '__lastSaved' > = { layerId: persistedLayer.layerId, layerType: persistedLayer.layerType, annotationGroupId, - hide: persistedLayer.hide, simpleView: persistedLayer.simpleView, __lastSaved: annotationGroup, }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index bd83ff8fd10fa..c41021ebfd29f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -564,7 +564,6 @@ describe('#toExpression', () => { annotations: [], indexPatternId: 'my-indexPattern', ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index ecc56dc1b70f3..ab91d3394643f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -119,7 +119,6 @@ export interface XYByValueAnnotationLayerConfig { layerId: string; layerType: 'annotations'; annotations: EventAnnotationConfig[]; - hide: boolean; indexPatternId: string; simpleView: boolean; ignoreGlobalFilters: boolean; @@ -137,7 +136,7 @@ export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig export type XYPersistedByReferenceAnnotationLayerConfig = Pick< XYByValueAnnotationLayerConfig, - 'layerId' | 'layerType' | 'hide' | 'simpleView' + 'layerId' | 'layerType' | 'simpleView' > & { persistanceType: 'byReference'; annotationGroupRef: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 908bd33ea7933..92882fe63092b 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -274,7 +274,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -320,7 +319,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -435,7 +433,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName1, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - hide: false, simpleView: false, }, { @@ -445,7 +442,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName2, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - hide: false, simpleView: false, }, ]; @@ -476,7 +472,6 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId1, ignoreGlobalFilters: persistedAnnotationLayers[0].ignoreGlobalFilters, annotations: persistedAnnotationLayers[0].annotations, - hide: persistedAnnotationLayers[0].hide, simpleView: persistedAnnotationLayers[0].simpleView, indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId1], @@ -487,7 +482,6 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId2, ignoreGlobalFilters: persistedAnnotationLayers[1].ignoreGlobalFilters, annotations: persistedAnnotationLayers[1].annotations, - hide: persistedAnnotationLayers[1].hide, simpleView: persistedAnnotationLayers[1].simpleView, indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId2], @@ -546,7 +540,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName1, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - hide: false, simpleView: false, }, { @@ -556,7 +549,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName2, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - hide: false, simpleView: false, }, ]; @@ -886,7 +878,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -913,7 +904,6 @@ describe('xy_visualization', () => { label: 'Event', }, ], - hide: false, simpleView: false, }); }); @@ -1139,7 +1129,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1186,7 +1175,6 @@ describe('xy_visualization', () => { }, ], ignoreGlobalFilters: true, - hide: false, simpleView: false, }); }); @@ -1203,7 +1191,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1250,7 +1237,6 @@ describe('xy_visualization', () => { }, ], ignoreGlobalFilters: true, - hide: false, simpleView: false, }); }); @@ -1267,7 +1253,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1295,7 +1280,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], ignoreGlobalFilters: true, - hide: false, simpleView: false, }); }); @@ -1312,7 +1296,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1341,7 +1324,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }); }); @@ -1359,7 +1341,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1368,7 +1349,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1398,7 +1378,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1407,7 +1386,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [{ ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ]); @@ -1425,7 +1403,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1434,7 +1411,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1464,7 +1440,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1473,7 +1448,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ]); @@ -1491,7 +1465,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1500,7 +1473,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1529,7 +1501,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1538,7 +1509,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ]); @@ -1556,7 +1526,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, @@ -1566,7 +1535,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1596,7 +1564,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, { @@ -1605,7 +1572,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ]); @@ -1689,7 +1655,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, { ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -1711,7 +1676,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ]); @@ -2440,7 +2404,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -3076,7 +3039,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: false, - hide: false, simpleView: false, }, ], @@ -3324,7 +3286,6 @@ describe('xy_visualization', () => { indexPatternId: 'some-id', ignoreGlobalFilters: true, simpleView: false, - hide: false, annotations: [ { label: 'Event', @@ -3358,7 +3319,6 @@ describe('xy_visualization', () => { indexPatternId: 'some-id', ignoreGlobalFilters: true, simpleView: false, - hide: false, annotations: [ { label: 'Event', @@ -3380,7 +3340,6 @@ describe('xy_visualization', () => { indexPatternId: 'some-id', ignoreGlobalFilters: true, simpleView: false, - hide: false, annotations: [ { label: 'Event', @@ -3430,7 +3389,6 @@ describe('xy_visualization', () => { }, } as PointInTimeEventAnnotationConfig, ], - hide: false, simpleView: false, }; state.layers = [layer]; @@ -3451,7 +3409,6 @@ describe('xy_visualization', () => { "type": "manual", }, ], - "hide": false, "ignoreGlobalFilters": false, "layerId": "layer-id", "layerType": "annotations", @@ -3627,7 +3584,6 @@ describe('xy_visualization', () => { layerType: 'annotations', persistanceType: 'linked', annotations: layers[0].annotations, - hide: layers[0].hide, ignoreGlobalFilters: layers[0].ignoreGlobalFilters, simpleView: layers[0].simpleView, }, @@ -3637,7 +3593,6 @@ describe('xy_visualization', () => { layerType: 'annotations', persistanceType: 'linked', annotations: layers[1].annotations, - hide: layers[1].hide, ignoreGlobalFilters: layers[1].ignoreGlobalFilters, simpleView: layers[1].simpleView, }, @@ -3667,7 +3622,6 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: true, indexPatternId: 'myIndexPattern', - hide: false, simpleView: false, }, ], @@ -3702,7 +3656,6 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: true, indexPatternId: 'myIndexPattern', - hide: false, simpleView: false, }, ], @@ -3720,7 +3673,6 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: false, indexPatternId: 'myIndexPattern', - hide: false, simpleView: false, }, ]), diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index ebe262c5f78f9..ef32dce73e5f8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -322,7 +322,6 @@ const newLayerFn = { layerType: layerTypes.ANNOTATIONS, annotationGroupId, // TODO - persist these? - hide: false, simpleView: false, annotations: cloneDeep(libraryGroupConfig.annotations), @@ -340,7 +339,6 @@ const newLayerFn = { annotations: [], indexPatternId, ignoreGlobalFilters: true, - hide: false, simpleView: false, }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx index 3945f9aab5f2e..ba182f5f5fd43 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx @@ -65,7 +65,6 @@ describe('AnnotationsPanel', () => { indexPatternId: 'indexPattern1', annotations: [customLineStaticAnnotation], ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -145,7 +144,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, - hide: false, simpleView: false, }; const component = mount( @@ -232,7 +230,6 @@ describe('AnnotationsPanel', () => { layerId: 'annotation', layerType: 'annotations', ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -259,7 +256,6 @@ describe('AnnotationsPanel', () => { layerId: 'annotation', layerType: 'annotations', ignoreGlobalFilters: true, - hide: false, simpleView: false, }, ], @@ -291,7 +287,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: indexPattern.id, ignoreGlobalFilters: true, - hide: false, simpleView: false, }; const frameMock = createMockFramePublicAPI({ @@ -351,7 +346,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - hide: false, simpleView: false, }; const frameMock = createMockFramePublicAPI({ @@ -409,7 +403,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - hide: false, simpleView: false, }; const frameMock = createMockFramePublicAPI({ @@ -487,7 +480,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - hide: false, simpleView: false, }; const frameMock = createMockFramePublicAPI({ @@ -563,7 +555,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: indexPattern.id, ignoreGlobalFilters: true, - hide: false, simpleView: false, }; const frameMock = createMockFramePublicAPI({ @@ -636,7 +627,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - hide: false, simpleView: false, }; const frameMock = createMockFramePublicAPI({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx index ce4397dfaf44c..5ffa855e80442 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx @@ -29,7 +29,6 @@ describe('layer header', () => { annotations: [], indexPatternId: '', ignoreGlobalFilters: false, - hide: false, simpleView: false, __lastSaved: { title: byRefGroupTitle, @@ -47,7 +46,6 @@ describe('layer header', () => { annotations: [], indexPatternId: '', ignoreGlobalFilters: false, - hide: false, simpleView: false, }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 04f6a34e5a874..ae7c96a4573fa 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -573,7 +573,6 @@ describe('xy_suggestions', () => { layerType: LayerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, - hide: false, simpleView: false, annotations: [ { @@ -632,7 +631,6 @@ describe('xy_suggestions', () => { layerType: LayerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, - hide: false, simpleView: false, annotations: [ { From 6770eab9a047b4fd375f1d364f8691af15fe1557 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 15 May 2023 16:35:10 -0500 Subject: [PATCH 089/202] remove simpleView parameter from everywhere outside expression generation --- .../public/event_annotation_service/types.ts | 4 +- .../__snapshots__/visualization.test.ts.snap | 4 -- .../revert_changes_action.test.tsx.snap | 2 - .../__snapshots__/save_action.test.tsx.snap | 4 -- .../actions/revert_changes_action.test.tsx | 1 - .../actions/revert_changes_action.tsx | 1 - .../annotations/actions/save_action.test.tsx | 1 - .../annotations/actions/unlink_action.test.ts | 1 - .../xy/annotations/actions/unlink_action.tsx | 1 - .../public/visualizations/xy/state_helpers.ts | 6 +-- .../visualizations/xy/to_expression.test.ts | 1 - .../public/visualizations/xy/to_expression.ts | 11 ++-- .../lens/public/visualizations/xy/types.ts | 3 +- .../visualizations/xy/visualization.test.ts | 54 ------------------- .../visualizations/xy/visualization.tsx | 1 - .../xy/visualization_helpers.tsx | 3 -- .../annotations_config_panel/index.test.tsx | 10 ---- .../xy/xy_config_panel/layer_header.test.tsx | 2 - .../visualizations/xy/xy_suggestions.test.ts | 2 - 19 files changed, 12 insertions(+), 100 deletions(-) diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index 951f646866bc7..70b6c44583ca7 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -25,7 +25,9 @@ export interface EventAnnotationServiceType { toExpression: (props: EventAnnotationConfig[]) => ExpressionAstExpression[]; toFetchExpression: (props: { interval: string; - groups: EventAnnotationGroupConfig[]; + groups: Array< + Pick + >; }) => ExpressionAstExpression[]; renderEventAnnotationGroupSavedObjectFinder: (props: { fixedPageSize: number; diff --git a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap index a1955c5589eea..d64429247bb9c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/__snapshots__/visualization.test.ts.snap @@ -38,7 +38,6 @@ Object { "indexPatternId": "indexPattern1", "layerId": "", "layerType": "annotations", - "simpleView": false, } `; @@ -49,7 +48,6 @@ Object { "indexPatternId": "indexPattern1", "layerId": "", "layerType": "annotations", - "simpleView": false, } `; @@ -92,7 +90,6 @@ Array [ "indexPatternId": "data-view-123", "layerId": "annotation", "layerType": "annotations", - "simpleView": undefined, }, Object { "__lastSaved": Object { @@ -131,7 +128,6 @@ Array [ "indexPatternId": "data-view-773203", "layerId": "annotation", "layerType": "annotations", - "simpleView": undefined, }, ] `; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap index 678f393b592ce..0f25399315e8e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/revert_changes_action.test.tsx.snap @@ -20,7 +20,6 @@ Object { "indexPatternId": "other index pattern", "layerId": "some-id", "layerType": "annotations", - "simpleView": false, "tags": Array [], "title": "My library group", }, @@ -39,7 +38,6 @@ Object { "indexPatternId": "other index pattern", "layerId": "some-id", "layerType": "annotations", - "simpleView": false, }, ], } diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap index d317538fb4124..6eecf818d3f4c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/__snapshots__/save_action.test.tsx.snap @@ -41,7 +41,6 @@ Array [ "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", - "simpleView": false, }, ], "legend": Object { @@ -95,7 +94,6 @@ Array [ "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", - "simpleView": false, }, ], "legend": Object { @@ -151,7 +149,6 @@ Array [ "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", - "simpleView": false, }, ], "legend": Object { @@ -205,7 +202,6 @@ Array [ "indexPatternId": "some-index-pattern", "layerId": "mylayerid", "layerType": "annotations", - "simpleView": false, }, ], "legend": Object { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx index d01ffbc08451e..78871d913601a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.test.tsx @@ -22,7 +22,6 @@ describe('revert changes routine', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, - simpleView: false, annotations: [ { id: 'some-annotation-id', diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx index e733aaa5e8e49..98555ca466c14 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/revert_changes_action.tsx @@ -89,7 +89,6 @@ export const revert = ({ layerId: layer.layerId, layerType: layer.layerType, annotationGroupId: layer.annotationGroupId, - simpleView: layer.simpleView, indexPatternId: layer.__lastSaved.indexPatternId, ignoreGlobalFilters: layer.__lastSaved.ignoreGlobalFilters, diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx index af78db5a05f06..e5256ec49b78a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/save_action.test.tsx @@ -118,7 +118,6 @@ describe('annotation group save action', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, - simpleView: false, annotations: [ { id: 'some-annotation-id', diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts index 29cbde77da0e5..8fc27c6e5f6d3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.test.ts @@ -23,7 +23,6 @@ describe('annotation group unlink actions', () => { layerType: 'annotations', indexPatternId: 'some-index-pattern', ignoreGlobalFilters: false, - simpleView: false, annotations: [ { id: 'some-annotation-id', diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx index 47d06fcc00a8b..629bdf902be2f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/unlink_action.tsx @@ -32,7 +32,6 @@ export const getUnlinkLayerAction = ({ layerType: layer.layerType, indexPatternId: layer.indexPatternId, ignoreGlobalFilters: layer.ignoreGlobalFilters, - simpleView: layer.simpleView, annotations: layer.annotations, }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 7667c6052af3f..491ca38ee6087 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -187,7 +187,6 @@ export function getPersistableState(state: XYState) { layerId: layer.layerId, layerType: layer.layerType, annotationGroupRef: referenceName, - simpleView: layer.simpleView, }; persistableLayers.push(persistableLayer); @@ -197,7 +196,6 @@ export function getPersistableState(state: XYState) { layerId: layer.layerId, layerType: layer.layerType, annotationGroupRef: referenceName, - simpleView: layer.simpleView, annotations: layer.annotations, ignoreGlobalFilters: layer.ignoreGlobalFilters, }; @@ -256,7 +254,6 @@ export function injectReferences( if (isPersistedByValueAnnotationsLayer(persistedLayer)) { injectedLayer = { ...persistedLayer, - simpleView: Boolean(persistedLayer.simpleView), indexPatternId: // getIndexPatternIdFromInitialContext(persistedLayer, initialContext) || TODO - was this doing anything? indexPatternIdFromReferences, @@ -277,12 +274,11 @@ export function injectReferences( // declared as a separate variable for type checking const commonProps: Pick< XYByReferenceAnnotationLayerConfig, - 'layerId' | 'layerType' | 'annotationGroupId' | 'simpleView' | '__lastSaved' + 'layerId' | 'layerType' | 'annotationGroupId' | '__lastSaved' > = { layerId: persistedLayer.layerId, layerType: persistedLayer.layerType, annotationGroupId, - simpleView: persistedLayer.simpleView, __lastSaved: annotationGroup, }; diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts index c41021ebfd29f..589c0d5995784 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.test.ts @@ -564,7 +564,6 @@ describe('#toExpression', () => { annotations: [], indexPatternId: 'my-indexPattern', ignoreGlobalFilters: true, - simpleView: false, }, ], }, diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 3074fc57fd702..9ee6e10884022 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -39,13 +39,14 @@ import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { SystemPaletteExpressionFunctionDefinition } from '@kbn/charts-plugin/common'; import type { - State, + State as XYState, YConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, XYAnnotationLayerConfig, AxisConfig, ValidXYDataLayerConfig, + XYLayerConfig, } from './types'; import type { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../../types'; import { getColumnToLabelMap } from './state_helpers'; @@ -65,6 +66,10 @@ import { import type { CollapseExpressionFunction } from '../../../common/expressions'; import { hasIcon } from './xy_config_panel/shared/marker_decoration_settings'; +type XYLayerConfigWithSimpleView = XYLayerConfig & { simpleView?: boolean }; +type XYAnnotationLayerConfigWithSimpleView = XYAnnotationLayerConfig & { simpleView?: boolean }; +type State = Omit & { layers: XYLayerConfigWithSimpleView[] }; + export const getSortedAccessors = ( datasource: DatasourcePublicAPI | undefined, layer: XYDataLayerConfig | XYReferenceLineLayerConfig @@ -83,7 +88,6 @@ export const toExpression = ( state: State, datasourceLayers: DatasourceLayers, paletteService: PaletteRegistry, - attributes: Partial<{ title: string; description: string }> = {}, datasourceExpressionsByLayers: Record, eventAnnotationService: EventAnnotationServiceType ): Ast | null => { @@ -152,7 +156,6 @@ export function toPreviewExpression( }, datasourceLayers, paletteService, - {}, datasourceExpressionsByLayers, eventAnnotationService ); @@ -429,7 +432,7 @@ const referenceLineLayerToExpression = ( }; const annotationLayerToExpression = ( - layer: XYAnnotationLayerConfig, + layer: XYAnnotationLayerConfigWithSimpleView, eventAnnotationService: EventAnnotationServiceType ): Ast => { const extendedAnnotationLayerFn = buildExpressionFunction( diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index ab91d3394643f..5333d03fb12dd 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -120,7 +120,6 @@ export interface XYByValueAnnotationLayerConfig { layerType: 'annotations'; annotations: EventAnnotationConfig[]; indexPatternId: string; - simpleView: boolean; ignoreGlobalFilters: boolean; } @@ -136,7 +135,7 @@ export type XYByReferenceAnnotationLayerConfig = XYByValueAnnotationLayerConfig export type XYPersistedByReferenceAnnotationLayerConfig = Pick< XYByValueAnnotationLayerConfig, - 'layerId' | 'layerType' | 'simpleView' + 'layerId' | 'layerType' > & { persistanceType: 'byReference'; annotationGroupRef: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 92882fe63092b..407b66d91599e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -274,7 +274,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }); @@ -294,8 +293,6 @@ describe('xy_visualization', () => { layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - hide: undefined, - simpleView: undefined, }, ], }, @@ -319,7 +316,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }); @@ -433,7 +429,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName1, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - simpleView: false, }, { layerId: 'annotation', @@ -442,7 +437,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName2, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - simpleView: false, }, ]; @@ -472,7 +466,6 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId1, ignoreGlobalFilters: persistedAnnotationLayers[0].ignoreGlobalFilters, annotations: persistedAnnotationLayers[0].annotations, - simpleView: persistedAnnotationLayers[0].simpleView, indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId1], }, @@ -482,7 +475,6 @@ describe('xy_visualization', () => { annotationGroupId: annotationGroupId2, ignoreGlobalFilters: persistedAnnotationLayers[1].ignoreGlobalFilters, annotations: persistedAnnotationLayers[1].annotations, - simpleView: persistedAnnotationLayers[1].simpleView, indexPatternId: dataViewId, __lastSaved: libraryAnnotationGroups[annotationGroupId2], }, @@ -540,7 +532,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName1, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - simpleView: false, }, { layerId: 'annotation', @@ -549,7 +540,6 @@ describe('xy_visualization', () => { annotationGroupRef: refName2, ignoreGlobalFilters: false, // different from the persisted group annotations: [], // different from the persisted group - simpleView: false, }, ]; @@ -878,7 +868,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -904,7 +893,6 @@ describe('xy_visualization', () => { label: 'Event', }, ], - simpleView: false, }); }); @@ -1129,7 +1117,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1175,7 +1162,6 @@ describe('xy_visualization', () => { }, ], ignoreGlobalFilters: true, - simpleView: false, }); }); it('dragging field: should replace an existing dimension when dragged to a dimension', () => { @@ -1191,7 +1177,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1237,7 +1222,6 @@ describe('xy_visualization', () => { }, ], ignoreGlobalFilters: true, - simpleView: false, }); }); it('dragging operation: should copy previous column if passed and assign a new id', () => { @@ -1253,7 +1237,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1280,7 +1263,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], ignoreGlobalFilters: true, - simpleView: false, }); }); it('dragging operation: should reorder a dimension to a annotation layer', () => { @@ -1296,7 +1278,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1324,7 +1305,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }); }); @@ -1341,7 +1321,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1349,7 +1328,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1378,7 +1356,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1386,7 +1363,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [{ ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, - simpleView: false, }, ]); }); @@ -1403,7 +1379,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1411,7 +1386,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1440,7 +1414,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1448,7 +1421,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ]); }); @@ -1465,7 +1437,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1473,7 +1444,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1501,7 +1471,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1509,7 +1478,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ]); }); @@ -1526,7 +1494,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, { @@ -1535,7 +1502,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1564,7 +1530,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, - simpleView: false, }, { layerId: 'second', @@ -1572,7 +1537,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ]); }); @@ -1655,7 +1619,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, { ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, - simpleView: false, }, ], }, @@ -1676,7 +1639,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ]); }); @@ -2404,7 +2366,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ], }; @@ -3039,7 +3000,6 @@ describe('xy_visualization', () => { indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: false, - simpleView: false, }, ], }; @@ -3285,7 +3245,6 @@ describe('xy_visualization', () => { layerType: 'annotations', indexPatternId: 'some-id', ignoreGlobalFilters: true, - simpleView: false, annotations: [ { label: 'Event', @@ -3318,7 +3277,6 @@ describe('xy_visualization', () => { layerType: 'annotations', indexPatternId: 'some-id', ignoreGlobalFilters: true, - simpleView: false, annotations: [ { label: 'Event', @@ -3339,7 +3297,6 @@ describe('xy_visualization', () => { layerType: 'annotations', indexPatternId: 'some-id', ignoreGlobalFilters: true, - simpleView: false, annotations: [ { label: 'Event', @@ -3389,7 +3346,6 @@ describe('xy_visualization', () => { }, } as PointInTimeEventAnnotationConfig, ], - simpleView: false, }; state.layers = [layer]; @@ -3413,7 +3369,6 @@ describe('xy_visualization', () => { "layerId": "layer-id", "layerType": "annotations", "persistanceType": "byValue", - "simpleView": false, }, ] `); @@ -3488,16 +3443,12 @@ describe('xy_visualization', () => { layerId: 'layer-id', layerType: 'annotations', persistanceType: 'byReference', - hide: undefined, - simpleView: undefined, }, { annotationGroupRef: savedObjectReferences[1].name, layerId: 'layer-id2', layerType: 'annotations', persistanceType: 'byReference', - hide: undefined, - simpleView: undefined, }, ]); @@ -3585,7 +3536,6 @@ describe('xy_visualization', () => { persistanceType: 'linked', annotations: layers[0].annotations, ignoreGlobalFilters: layers[0].ignoreGlobalFilters, - simpleView: layers[0].simpleView, }, { annotationGroupRef: savedObjectReferences[1].name, @@ -3594,7 +3544,6 @@ describe('xy_visualization', () => { persistanceType: 'linked', annotations: layers[1].annotations, ignoreGlobalFilters: layers[1].ignoreGlobalFilters, - simpleView: layers[1].simpleView, }, ]); }); @@ -3622,7 +3571,6 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: true, indexPatternId: 'myIndexPattern', - simpleView: false, }, ], }, @@ -3656,7 +3604,6 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: true, indexPatternId: 'myIndexPattern', - simpleView: false, }, ], }, @@ -3673,7 +3620,6 @@ describe('xy_visualization', () => { annotations: [exampleAnnotation2], ignoreGlobalFilters: false, indexPatternId: 'myIndexPattern', - simpleView: false, }, ]), }) diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 24439146fd659..557873226471e 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -740,7 +740,6 @@ export const getXyVisualization = ({ state, layers, paletteService, - attributes, datasourceExpressionsByLayers, eventAnnotationService ), diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx index ef32dce73e5f8..a549ac42d9448 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization_helpers.tsx @@ -321,8 +321,6 @@ const newLayerFn = { layerId, layerType: layerTypes.ANNOTATIONS, annotationGroupId, - // TODO - persist these? - simpleView: false, annotations: cloneDeep(libraryGroupConfig.annotations), indexPatternId: libraryGroupConfig.indexPatternId, @@ -339,7 +337,6 @@ const newLayerFn = { annotations: [], indexPatternId, ignoreGlobalFilters: true, - simpleView: false, }; return newLayer; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx index ba182f5f5fd43..92fa248979fc3 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/index.test.tsx @@ -65,7 +65,6 @@ describe('AnnotationsPanel', () => { indexPatternId: 'indexPattern1', annotations: [customLineStaticAnnotation], ignoreGlobalFilters: true, - simpleView: false, }, ], }; @@ -144,7 +143,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, - simpleView: false, }; const component = mount( { layerId: 'annotation', layerType: 'annotations', ignoreGlobalFilters: true, - simpleView: false, }, ], }); @@ -256,7 +253,6 @@ describe('AnnotationsPanel', () => { layerId: 'annotation', layerType: 'annotations', ignoreGlobalFilters: true, - simpleView: false, }, ], }); @@ -287,7 +283,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: indexPattern.id, ignoreGlobalFilters: true, - simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -346,7 +341,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -403,7 +397,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -480,7 +473,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -555,7 +547,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', indexPatternId: indexPattern.id, ignoreGlobalFilters: true, - simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, @@ -627,7 +618,6 @@ describe('AnnotationsPanel', () => { layerType: 'annotations', ignoreGlobalFilters: true, indexPatternId: indexPattern.id, - simpleView: false, }; const frameMock = createMockFramePublicAPI({ datasourceLayers: {}, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx index 5ffa855e80442..1a2587859e390 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.test.tsx @@ -29,7 +29,6 @@ describe('layer header', () => { annotations: [], indexPatternId: '', ignoreGlobalFilters: false, - simpleView: false, __lastSaved: { title: byRefGroupTitle, description: '', @@ -46,7 +45,6 @@ describe('layer header', () => { annotations: [], indexPatternId: '', ignoreGlobalFilters: false, - simpleView: false, }; const getStateWithLayers = (layers: XYLayerConfig[]): XYState => ({ diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index ae7c96a4573fa..6dd7887b59ad1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -573,7 +573,6 @@ describe('xy_suggestions', () => { layerType: LayerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, - simpleView: false, annotations: [ { id: '1', @@ -631,7 +630,6 @@ describe('xy_suggestions', () => { layerType: LayerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, - simpleView: false, annotations: [ { id: '1', From ecb1a2fbd4ee114b94ba31b8a014331c7991cbd7 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 15 May 2023 21:46:03 +0000 Subject: [PATCH 090/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- .../core-capabilities-browser-mocks/tsconfig.json | 1 - src/plugins/event_annotation/tsconfig.json | 4 ++-- x-pack/plugins/lens/tsconfig.json | 5 +++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/core/capabilities/core-capabilities-browser-mocks/tsconfig.json b/packages/core/capabilities/core-capabilities-browser-mocks/tsconfig.json index 3ef89ddcbb465..531350d618920 100644 --- a/packages/core/capabilities/core-capabilities-browser-mocks/tsconfig.json +++ b/packages/core/capabilities/core-capabilities-browser-mocks/tsconfig.json @@ -12,7 +12,6 @@ "**/*.tsx", ], "kbn_references": [ - "@kbn/std", "@kbn/utility-types", "@kbn/core-capabilities-browser-internal" ], diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 9738e2722dabe..c06da788827a6 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -19,9 +19,9 @@ "@kbn/core-ui-settings-browser", "@kbn/datemath", "@kbn/ui-theme", - "@kbn/saved-objects-plugin", "@kbn/saved-objects-finder-plugin", - "@kbn/saved-objects-management-plugin" + "@kbn/saved-objects-management-plugin", + "@kbn/i18n-react" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index b4e2873b7356a..46a8b4dea99b2 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -72,6 +72,11 @@ "@kbn/core-saved-objects-api-server", "@kbn/object-versioning", "@kbn/config-schema", + "@kbn/core-mount-utils-browser", + "@kbn/core-notifications-browser", + "@kbn/core-lifecycle-browser", + "@kbn/core-notifications-browser-mocks", + "@kbn/saved-objects-finder-plugin", ], "exclude": [ "target/**/*", From ca19a557eebcdc42cac914e7cee1f1957d6b8c00 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 15 May 2023 16:52:30 -0500 Subject: [PATCH 091/202] put annotation groups in the analytics index --- src/plugins/event_annotation/server/saved_objects.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/event_annotation/server/saved_objects.ts b/src/plugins/event_annotation/server/saved_objects.ts index 443df50aaa0c9..94a8c20990e23 100644 --- a/src/plugins/event_annotation/server/saved_objects.ts +++ b/src/plugins/event_annotation/server/saved_objects.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { CoreSetup, mergeSavedObjectMigrationMaps, @@ -19,6 +20,7 @@ import { EventAnnotationGroupAttributes } from '../common/types'; export function setupSavedObjects(coreSetup: CoreSetup) { coreSetup.savedObjects.registerType({ name: EVENT_ANNOTATION_GROUP_TYPE, + indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, namespaceType: 'multiple', // TODO double-check type management: { From 1cb7fb3e0a4b8d0015245bb1fa926233475d1ccf Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 15 May 2023 22:02:50 +0000 Subject: [PATCH 092/202] [CI] Auto-commit changed files from 'node scripts/check_mappings_update --fix' --- .../current_mappings.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 4a80946539699..f2676c043c502 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -931,6 +931,17 @@ } } }, + "event-annotation-group": { + "dynamic": false, + "properties": { + "title": { + "type": "text" + }, + "description": { + "type": "text" + } + } + }, "visualization": { "dynamic": false, "properties": { From 8a28c3d16ef5d398a2f79fe5d2c2ba9632f9d0cd Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 16 May 2023 15:30:16 +0000 Subject: [PATCH 093/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/event_annotation/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index c06da788827a6..562bf05259c44 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -21,7 +21,8 @@ "@kbn/ui-theme", "@kbn/saved-objects-finder-plugin", "@kbn/saved-objects-management-plugin", - "@kbn/i18n-react" + "@kbn/i18n-react", + "@kbn/core-saved-objects-server" ], "exclude": [ "target/**/*", From 78f89d1ad72f02e580491e68a93037fafcea53b2 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 10:49:27 -0500 Subject: [PATCH 094/202] fix linting problem --- .../lens/public/shared_components/flyout_container.tsx | 3 --- .../visualizations/xy/load_annotation_library_flyout.tsx | 5 ----- 2 files changed, 8 deletions(-) diff --git a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx index e71bee4eb08ee..6b970a3e55f38 100644 --- a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -47,7 +47,6 @@ export function FlyoutContainer({ panelContainerRef, children, customFooter, - onClickInside, }: { isOpen: boolean; handleClose: () => boolean; @@ -57,7 +56,6 @@ export function FlyoutContainer({ panelRef?: (el: HTMLDivElement) => void; panelContainerRef?: (el: HTMLDivElement) => void; customFooter?: React.ReactElement; - onClickInside?: () => void; }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); @@ -97,7 +95,6 @@ export function FlyoutContainer({ onEscapeKey={closeFlyout} >
    onClickInside?.()} ref={panelContainerRef} role="dialog" aria-labelledby="lnsDimensionContainerTitle" diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx index 8a529d8d83976..ca9f2fabda0b0 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -52,11 +52,6 @@ export function LoadAnnotationLibraryFlyout({ return ( { - if (!hasBeenClicked.current) { - setSelectedItem(null); - } - }} panelContainerRef={(el) => (containerPanelRef.current = el)} customFooter={ From 3cad5f6d9b072127702a4684afc1c85aa8dd8467 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 12:35:19 -0500 Subject: [PATCH 095/202] fix config panel test --- .../config_panel/config_panel.test.tsx | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index 0ecfdd06bd754..c123406d160e0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -30,6 +30,7 @@ import { ReactWrapper } from 'enzyme'; import { addLayer } from '../../../state_management'; import { createIndexPatternServiceMock } from '../../../mocks/data_views_service_mock'; import { AddLayerButton } from '../../../visualizations/xy/add_layer'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; jest.mock('../../../id_generator'); @@ -117,7 +118,21 @@ describe('ConfigPanel', () => { activeVisualization: { ...visualizationMap.testVis, getLayerIds: () => Object.keys(frame.datasourceLayers), - } as unknown as Visualization, + getAddLayerButtonComponent: (props) => { + return ( + { + if (loadedGroupInfo.dataViewSpec) { + await props.addIndexPatternFromDataViewSpec(loadedGroupInfo.dataViewSpec); + } + props.addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); + }} + /> + ); + }, + } as Visualization, datasourceStates: { testDatasource: { isLoading: false, @@ -270,6 +285,7 @@ describe('ConfigPanel', () => { instance: ReactWrapper, layerType: LayerType = LayerTypes.REFERENCELINE ) { + // TODO - this test shouldn't know about the internals of a specific visualization's add layer menu act(() => { instance.find('button[data-test-subj="lnsLayerAddButton"]').first().simulate('click'); }); @@ -473,7 +489,11 @@ describe('ConfigPanel', () => { datasourceMap.testDatasource.initializeDimension = jest.fn(); const props = getDefaultProps({ visualizationMap, datasourceMap }); const { instance, lensStore } = await prepareAndMountComponent(props); - await clickToAddLayer(instance, LayerTypes.ANNOTATIONS); + + act(() => { + instance.find(AddLayerButton).prop('addLayer')(LayerTypes.ANNOTATIONS); + }); + expect(lensStore.dispatch).toHaveBeenCalledTimes(1); expect(visualizationMap.testVis.setDimension).toHaveBeenCalledWith({ From d8bade5344161fd3053898b0212c1bff9f0f2a53 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 12:37:45 -0500 Subject: [PATCH 096/202] limit --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 7896616cf76c4..78647b6890490 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -39,7 +39,7 @@ pageLoadAssetSize: embeddableEnhanced: 22107 enterpriseSearch: 35741 esUiShared: 326654 - eventAnnotation: 20500 + eventAnnotation: 22000 exploratoryView: 74673 expressionError: 22127 expressionGauge: 25000 From 95d12536cdbd3eb6f01e0d47c25c6805b5329c79 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 13:31:11 -0500 Subject: [PATCH 097/202] hide individual annotation editing --- .../group_editor_controls.tsx | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 711928e0d03f0..f6574a3c7789a 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -19,6 +19,8 @@ import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../../comm import { AnnotationEditorControls } from '../annotation_editor_controls'; import { AnnotationList } from './annotation_list'; +const ENABLE_INDIVIDUAL_ANNOTATION_EDITING = false; + export const GroupEditorControls = ({ group, update, @@ -150,17 +152,19 @@ export const GroupEditorControls = ({ } /> - - update({ ...group, annotations: newAnnotations })} - /> - + {ENABLE_INDIVIDUAL_ANNOTATION_EDITING && ( + + update({ ...group, annotations: newAnnotations })} + /> + + )} ) : ( From 49305919d033c545169d9b72e2655f83d2a937e1 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 13:33:44 -0500 Subject: [PATCH 098/202] update integration test snapshot --- .../migrations/group2/check_registered_types.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 54f9533aa81da..b9e0cd8b91db9 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -85,6 +85,7 @@ describe('checking migration metadata changes on all registered SO types', () => "enterprise_search_telemetry": "4b41830e3b28a16eb92dee0736b44ae6276ced9b", "epm-packages": "8755f947a00613f994b1bc5d5580e104043e27f6", "epm-packages-assets": "00c8b5e5bf059627ffc9fbde920e1ac75926c5f6", + "event-annotation-group": "c67d5863d7cac02d800c543724ef29b66d12e77e", "event_loop_delays_daily": "ef49e7f15649b551b458c7ea170f3ed17f89abd0", "exception-list": "38181294f64fc406c15f20d85ca306c8a4feb3c0", "exception-list-agnostic": "d527ce9d12b134cb163150057b87529043a8ec77", From fbecd3426afb78c062538669a51c7a6b5434a1e6 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 13:52:13 -0500 Subject: [PATCH 099/202] remove some commented code --- .../event_annotation_service/service.tsx | 50 ------------------- .../public/event_annotation_service/types.ts | 4 -- 2 files changed, 54 deletions(-) diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 82ea828b69186..b7bd28d255e69 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -98,18 +98,6 @@ export function getEventAnnotationService( throw savedObject.error; } - // const annotations = ( - // await client.find({ - // type: EVENT_ANNOTATION_TYPE, - // hasReference: { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // }, - // }) - // ).savedObjects - // .filter(({ error }) => !error) - // .map((annotation) => annotation.attributes); - return mapSavedObjectToGroupConfig(savedObject); }; @@ -141,15 +129,6 @@ export function getEventAnnotationService( }; const deleteAnnotationGroups = async (ids: string[]): Promise => { - // const annotationsSOs = ( - // await client.find({ - // type: EVENT_ANNOTATION_TYPE, - // hasReference: { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // }, - // }) - // ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); await client.bulkDelete([...ids.map((id) => ({ type: EVENT_ANNOTATION_GROUP_TYPE, id }))]); }; @@ -226,38 +205,9 @@ export function getEventAnnotationService( }); }; - // const updateAnnotations = async ( - // savedObjectId: string, - // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } - // ): Promise => { - // if (modifications.delete && modifications.delete.length > 0) { - // await client.bulkDelete( - // modifications.delete.map((id) => ({ type: EVENT_ANNOTATION_TYPE, id })) - // ); - // } - - // if (modifications.upsert && modifications.upsert.length > 0) { - // const annotationsToUpdate = modifications.upsert.map((a) => ({ - // type: EVENT_ANNOTATION_TYPE, - // attributes: a, - // id: a.id, - // overwrite: true, - // references: [ - // { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // name: `event-annotation-group-ref-${a.id}`, - // }, - // ], - // })); - // await client.bulkCreate(annotationsToUpdate); - // } - // }; - return { loadAnnotationGroup, findAnnotationGroupContent, - // updateAnnotations, updateAnnotationGroup, createAnnotationGroup, deleteAnnotationGroups, diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index effafff4016b0..7f06c295582cf 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -22,10 +22,6 @@ export interface EventAnnotationServiceType { ) => Promise<{ total: number; hits: EventAnnotationGroupContent[] }>; deleteAnnotationGroups: (ids: string[]) => Promise; createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; - // updateAnnotations: ( - // savedObjectId: string, - // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } - // ) => Promise; updateAnnotationGroup: ( group: EventAnnotationGroupConfig, savedObjectId: string From 4cec3322b1b20efb8f32bca50573a6f885bb788c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 13:33:44 -0500 Subject: [PATCH 100/202] update integration test snapshot --- .../migrations/group2/check_registered_types.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 54f9533aa81da..b9e0cd8b91db9 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -85,6 +85,7 @@ describe('checking migration metadata changes on all registered SO types', () => "enterprise_search_telemetry": "4b41830e3b28a16eb92dee0736b44ae6276ced9b", "epm-packages": "8755f947a00613f994b1bc5d5580e104043e27f6", "epm-packages-assets": "00c8b5e5bf059627ffc9fbde920e1ac75926c5f6", + "event-annotation-group": "c67d5863d7cac02d800c543724ef29b66d12e77e", "event_loop_delays_daily": "ef49e7f15649b551b458c7ea170f3ed17f89abd0", "exception-list": "38181294f64fc406c15f20d85ca306c8a4feb3c0", "exception-list-agnostic": "d527ce9d12b134cb163150057b87529043a8ec77", From 8be8e9246c3191bdc471ae704f1c8c4d55d8d5b4 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 16:21:25 -0500 Subject: [PATCH 101/202] remove event annotation saved object type --- .../saved_objects/migrations/group3/type_registrations.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index de5fb53c99804..cf35f218647c8 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -42,7 +42,6 @@ const previouslyRegisteredTypes = [ 'csp-rule-template', 'csp_rule', 'dashboard', - 'event-annotation', 'event-annotation-group', 'endpoint:user-artifact', 'endpoint:user-artifact-manifest', From afc0972d7f3e6e61761d0f5cd82c451e5e974957 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 16:59:56 -0500 Subject: [PATCH 102/202] fix annotation library link --- src/plugins/event_annotation/common/constants.ts | 2 +- src/plugins/event_annotation/common/index.ts | 2 +- src/plugins/event_annotation/public/plugin.ts | 4 +++- src/plugins/event_annotation/server/plugin.ts | 1 - .../public/visualizations/xy/annotations/actions/index.ts | 8 ++++++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts index 08c73c946c355..501aaea843b38 100644 --- a/src/plugins/event_annotation/common/constants.ts +++ b/src/plugins/event_annotation/common/constants.ts @@ -26,4 +26,4 @@ export const AvailableAnnotationIcons = { export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; -export const ANNOTATION_LIBRARY_APP_ID = 'annotations'; +export const ANNOTATIONS_LISTING_VIEW_ID = 'annotations'; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index c9e399aa02999..0341a9e5ed4a2 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -44,4 +44,4 @@ export type { EventAnnotationGroupAttributes, } from './types'; -export { EVENT_ANNOTATION_GROUP_TYPE, ANNOTATION_LIBRARY_APP_ID } from './constants'; +export { EVENT_ANNOTATION_GROUP_TYPE, ANNOTATIONS_LISTING_VIEW_ID } from './constants'; diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 887b698f9a41d..e58b12b0735e0 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -25,6 +25,8 @@ import { } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; import { EventAnnotationListingPageServices, getTableList } from './get_table_list'; +import { ANNOTATIONS_LISTING_VIEW_ID } from '../common/constants'; + export interface EventAnnotationStartDependencies { savedObjectsManagement: SavedObjectsManagementPluginStart; data: DataPublicPluginStart; @@ -61,7 +63,7 @@ export class EventAnnotationPlugin dependencies.visualizations.listingViewRegistry.add({ title: 'Annotation Groups', - id: 'annotations', + id: ANNOTATIONS_LISTING_VIEW_ID, getTableList: async (props) => { const [coreStart, pluginsStart] = await core.getStartServices(); diff --git a/src/plugins/event_annotation/server/plugin.ts b/src/plugins/event_annotation/server/plugin.ts index 0ae55744016e6..d5e2fee433230 100644 --- a/src/plugins/event_annotation/server/plugin.ts +++ b/src/plugins/event_annotation/server/plugin.ts @@ -16,7 +16,6 @@ import { queryPointEventAnnotation, } from '../common'; import { setupSavedObjects } from './saved_objects'; -// import { getFetchEventAnnotations } from './fetch_event_annotations'; interface SetupDependencies { expressions: ExpressionsServerSetup; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index b0958a9fde92a..9e1d762933236 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -9,7 +9,8 @@ import type { CoreStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; -import { ANNOTATION_LIBRARY_APP_ID } from '@kbn/event-annotation-plugin/common'; +import { VISUALIZE_APP_NAME } from '@kbn/visualizations-plugin/common/constants'; +import { ANNOTATIONS_LISTING_VIEW_ID } from '@kbn/event-annotation-plugin/common'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; @@ -55,7 +56,10 @@ export const createAnnotationActions = ({ toasts: core.notifications.toasts, savedObjectsTagging, dataViews, - goToAnnotationLibrary: () => core.application.navigateToApp(ANNOTATION_LIBRARY_APP_ID), + goToAnnotationLibrary: () => + core.application.navigateToApp(VISUALIZE_APP_NAME, { + path: `#/${ANNOTATIONS_LISTING_VIEW_ID}`, + }), }) ); } From 831e519d6ba81422d98325dffdc46000be78f1de Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 16 May 2023 18:58:01 -0500 Subject: [PATCH 103/202] update snapshot --- .../saved_objects/migrations/group3/dot_kibana_split.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts index a52512318fab3..790430a015e7e 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts @@ -199,6 +199,7 @@ describe('split .kibana index into multiple system indices', () => { "enterprise_search_telemetry", "epm-packages", "epm-packages-assets", + "event-annotation-group", "event_loop_delays_daily", "exception-list", "exception-list-agnostic", From 6e577cc49804240573704d951a6294cb7c92d8a0 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 17 May 2023 07:50:38 -0500 Subject: [PATCH 104/202] saved objects library link --- src/plugins/event_annotation/server/saved_objects.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/server/saved_objects.ts b/src/plugins/event_annotation/server/saved_objects.ts index 94a8c20990e23..3a60a5d9cac36 100644 --- a/src/plugins/event_annotation/server/saved_objects.ts +++ b/src/plugins/event_annotation/server/saved_objects.ts @@ -14,7 +14,8 @@ import { } from '@kbn/core/server'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; -import { EVENT_ANNOTATION_GROUP_TYPE } from '../common/constants'; +import { VISUALIZE_APP_NAME } from '@kbn/visualizations-plugin/common/constants'; +import { ANNOTATIONS_LISTING_VIEW_ID, EVENT_ANNOTATION_GROUP_TYPE } from '../common/constants'; import { EventAnnotationGroupAttributes } from '../common/types'; export function setupSavedObjects(coreSetup: CoreSetup) { @@ -28,6 +29,11 @@ export function setupSavedObjects(coreSetup: CoreSetup) { defaultSearchField: 'title', importableAndExportable: true, getTitle: (obj: { attributes: EventAnnotationGroupAttributes }) => obj.attributes.title, + getInAppUrl: (obj: { id: string }) => ({ + // TODO link to specific object + path: `/app/${VISUALIZE_APP_NAME}#/${ANNOTATIONS_LISTING_VIEW_ID}`, + uiCapabilitiesPath: 'visualize.show', + }), }, migrations: () => { const dataViewMigrations = DataViewPersistableStateService.getAllMigrations(); From 7171e7c8045e84116dd8e4724357d6c5f60a5fd5 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 17 May 2023 12:58:08 +0000 Subject: [PATCH 105/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/event_annotation/tsconfig.json | 9 ++++++++- src/plugins/visualization_ui_components/tsconfig.json | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index e0200a899909a..e2959cd56b422 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -31,7 +31,14 @@ "@kbn/unified-field-list-plugin", "@kbn/dom-drag-drop", "@kbn/i18n-react", - "@kbn/core-saved-objects-server" + "@kbn/core-saved-objects-server", + "@kbn/test-jest-helpers", + "@kbn/saved-objects-tagging-oss-plugin", + "@kbn/core-saved-objects-api-browser", + "@kbn/kibana-react-plugin", + "@kbn/core-lifecycle-browser", + "@kbn/kibana-utils-plugin", + "@kbn/unified-search-plugin" ], "exclude": [ "target/**/*", diff --git a/src/plugins/visualization_ui_components/tsconfig.json b/src/plugins/visualization_ui_components/tsconfig.json index 7175696eef5f5..cbafd595ecc62 100644 --- a/src/plugins/visualization_ui_components/tsconfig.json +++ b/src/plugins/visualization_ui_components/tsconfig.json @@ -24,7 +24,8 @@ "@kbn/core-doc-links-browser", "@kbn/core", "@kbn/ui-theme", - "@kbn/coloring" + "@kbn/coloring", + "@kbn/field-formats-plugin" ], "exclude": [ "target/**/*", From c0f7b67359d83ccb2784142300fcfe50bf1c1de7 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 18 May 2023 15:56:52 -0500 Subject: [PATCH 106/202] clean up flyout --- .../public/event_annotation_service/types.ts | 2 +- .../xy/load_annotation_library_flyout.tsx | 28 +------------------ 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/plugins/event_annotation/public/event_annotation_service/types.ts b/src/plugins/event_annotation/public/event_annotation_service/types.ts index 70b6c44583ca7..fe2c876d2168e 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/types.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/types.ts @@ -30,7 +30,7 @@ export interface EventAnnotationServiceType { >; }) => ExpressionAstExpression[]; renderEventAnnotationGroupSavedObjectFinder: (props: { - fixedPageSize: number; + fixedPageSize?: number; onChoose: (value: { id: string; type: string; diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx index ca9f2fabda0b0..e8f6a6db95776 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import React, { useRef } from 'react'; +import React from 'react'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFlyoutFooter } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import type { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { FlyoutContainer } from '../../shared_components/flyout_container'; import type { ExtraAppendLayerArg } from './visualization'; @@ -24,27 +23,6 @@ export function LoadAnnotationLibraryFlyout({ eventAnnotationService: EventAnnotationServiceType; addLayer: (argument: ExtraAppendLayerArg) => void; }) { - const containerPanelRef = useRef(null); - const otherElementsHeight = 250; - const singleEntryHeight = 40; - const numberOfElements = Math.floor( - ((containerPanelRef?.current?.clientHeight || 800) - otherElementsHeight) / singleEntryHeight - ); - - const [selectedItem, setSelectedItem] = React.useState<{ - id: string; - type: string; - fullName: string; - savedObject: SavedObjectCommon; - } | null>(null); - - // needed to clean the state when clicking not on the item on the list - const hasBeenClicked = useRef(false); - - React.useEffect(() => { - hasBeenClicked.current = false; - }, [selectedItem]); - const { renderEventAnnotationGroupSavedObjectFinder: EventAnnotationGroupSavedObjectFinder, loadAnnotationGroup, @@ -52,7 +30,6 @@ export function LoadAnnotationLibraryFlyout({ return ( (containerPanelRef.current = el)} customFooter={ { setLoadLibraryFlyoutVisible(false); - setSelectedItem(null); }} data-test-subj="lns-indexPattern-loadLibraryCancel" > @@ -86,7 +62,6 @@ export function LoadAnnotationLibraryFlyout({ })} handleClose={() => { setLoadLibraryFlyoutVisible(false); - setSelectedItem(null); return true; }} > @@ -98,7 +73,6 @@ export function LoadAnnotationLibraryFlyout({ setLoadLibraryFlyoutVisible(false); }); }} - fixedPageSize={numberOfElements} />
    From c78559e348c210b68c680887f354d708c882cf36 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 18 May 2023 15:59:00 -0500 Subject: [PATCH 107/202] logical improvement --- x-pack/plugins/lens/public/data_views_service/service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/data_views_service/service.ts b/x-pack/plugins/lens/public/data_views_service/service.ts index 5181e5741d510..f51b6804b0718 100644 --- a/x-pack/plugins/lens/public/data_views_service/service.ts +++ b/x-pack/plugins/lens/public/data_views_service/service.ts @@ -112,7 +112,9 @@ export const createIndexPatternService = ({ cache: IndexPatternMap | undefined ) => { // TODO - extract this routine into a function and unit test? - if (!spec.id) { + const dataView = await dataViews.create(spec); + + if (!dataView.id) { showLoadingDataViewError( new Error( i18n.translate('xpack.lens.indexPattern.noIdError', { @@ -122,9 +124,9 @@ export const createIndexPatternService = ({ ); return; } - const dataView = await dataViews.create(spec); + const indexPatterns = await ensureIndexPattern({ - id: dataView.id!, + id: dataView.id, onError: showLoadingDataViewError, dataViews, cache, From 59e9823c494f4950d936d51fd43b01ecb6a81d31 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 18 May 2023 21:05:19 +0000 Subject: [PATCH 108/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/lens/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 46a8b4dea99b2..5a13cc2972c27 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -76,7 +76,6 @@ "@kbn/core-notifications-browser", "@kbn/core-lifecycle-browser", "@kbn/core-notifications-browser-mocks", - "@kbn/saved-objects-finder-plugin", ], "exclude": [ "target/**/*", From 20970124bbc954558686fd3940aaefea9e050869 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 22 May 2023 14:13:53 -0500 Subject: [PATCH 109/202] removing unnecessary initialize parameter --- ...t_annotation_group_saved_object_finder.tsx | 2 +- .../event_annotation_service/service.tsx | 11 +------- .../editor_frame/state_helpers.ts | 4 +-- x-pack/plugins/lens/public/types.ts | 3 +-- .../public/visualizations/xy/state_helpers.ts | 26 +++---------------- .../visualizations/xy/visualization.tsx | 7 ++--- 6 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx index 7a56befd6b343..c0e70f6697aca 100644 --- a/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx +++ b/src/plugins/event_annotation/public/components/event_annotation_group_saved_object_finder.tsx @@ -26,7 +26,7 @@ export const EventAnnotationGroupSavedObjectFinder = ({ uiSettings: IUiSettingsClient; http: CoreStart['http']; savedObjectsManagement: SavedObjectsManagementPluginStart; - fixedPageSize: number; + fixedPageSize?: number; onChoose: (value: { id: string; type: string; diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index cb5fd016d5094..a897a9894c9e6 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -11,7 +11,6 @@ import { partition } from 'lodash'; import { queryToAst } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import { CoreStart, SavedObjectReference, SavedObjectsClientContract } from '@kbn/core/public'; -import type { SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common'; import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common'; import { @@ -198,15 +197,7 @@ export function getEventAnnotationService( updateAnnotationGroup, createAnnotationGroup, // deleteAnnotationGroup, - renderEventAnnotationGroupSavedObjectFinder: (props: { - fixedPageSize: number; - onChoose: (value: { - id: string; - type: string; - fullName: string; - savedObject: SavedObjectCommon; - }) => void; - }) => { + renderEventAnnotationGroupSavedObjectFinder: (props) => { return ( Date: Mon, 22 May 2023 14:28:55 -0500 Subject: [PATCH 110/202] clean up some comments and improve some imports --- .../event_annotation_service/service.tsx | 58 ------------------- .../public/event_annotation_service/types.ts | 5 -- .../formula/editor/math_completion.ts | 2 +- .../operations/definitions/formula/parse.ts | 2 +- .../definitions/formula/validation.ts | 2 +- 5 files changed, 3 insertions(+), 66 deletions(-) diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index a897a9894c9e6..a8c5cc1a79043 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -51,18 +51,6 @@ export function getEventAnnotationService( throw savedObject.error; } - // const annotations = ( - // await client.find({ - // type: EVENT_ANNOTATION_TYPE, - // hasReference: { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // }, - // }) - // ).savedObjects - // .filter(({ error }) => !error) - // .map((annotation) => annotation.attributes); - const adHocDataViewSpec = savedObject.attributes.dataViewSpec ? DataViewPersistableStateService.inject( savedObject.attributes.dataViewSpec, @@ -83,22 +71,6 @@ export function getEventAnnotationService( }; }; - // const deleteAnnotationGroup = async (savedObjectId: string): Promise => { - // const annotationsSOs = ( - // await client.find({ - // type: EVENT_ANNOTATION_TYPE, - // hasReference: { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // }, - // }) - // ).savedObjects.map((annotation) => ({ id: annotation.id, type: EVENT_ANNOTATION_TYPE })); - // await client.bulkDelete([ - // { type: EVENT_ANNOTATION_GROUP_TYPE, id: savedObjectId }, - // ...annotationsSOs, - // ]); - // }; - const extractDataViewInformation = (group: EventAnnotationGroupConfig) => { let { dataViewSpec = null } = group; @@ -163,40 +135,10 @@ export function getEventAnnotationService( ); }; - // const updateAnnotations = async ( - // savedObjectId: string, - // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } - // ): Promise => { - // if (modifications.delete && modifications.delete.length > 0) { - // await client.bulkDelete( - // modifications.delete.map((id) => ({ type: EVENT_ANNOTATION_TYPE, id })) - // ); - // } - - // if (modifications.upsert && modifications.upsert.length > 0) { - // const annotationsToUpdate = modifications.upsert.map((a) => ({ - // type: EVENT_ANNOTATION_TYPE, - // attributes: a, - // id: a.id, - // overwrite: true, - // references: [ - // { - // type: EVENT_ANNOTATION_GROUP_TYPE, - // id: savedObjectId, - // name: `event-annotation-group-ref-${a.id}`, - // }, - // ], - // })); - // await client.bulkCreate(annotationsToUpdate); - // } - // }; - return { loadAnnotationGroup, - // updateAnnotations, updateAnnotationGroup, createAnnotationGroup, - // deleteAnnotationGroup, renderEventAnnotationGroupSavedObjectFinder: (props) => { return ( Promise; - // deleteAnnotationGroup: (savedObjectId: string) => Promise; createAnnotationGroup: (group: EventAnnotationGroupConfig) => Promise<{ id: string }>; - // updateAnnotations: ( - // savedObjectId: string, - // modifications: { delete?: string[]; upsert?: EventAnnotationConfig[] } - // ) => Promise; updateAnnotationGroup: ( group: EventAnnotationGroupConfig, savedObjectId: string diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts index 09e6df7af48bb..773dd5c97009e 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts @@ -25,7 +25,7 @@ import { parseTimeShift } from '@kbn/data-plugin/common'; import moment from 'moment'; import { nonNullable } from '../../../../../../utils'; import { DateRange } from '../../../../../../../common/types'; -import { IndexPattern } from '../../../../../../types'; +import type { IndexPattern } from '../../../../../../types'; import { memoizedGetAvailableOperationsByMetadata } from '../../../operations'; import { tinymathFunctions, groupArgsByType, unquotedStringRegex } from '../util'; import type { GenericOperationDefinition } from '../..'; diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts index e38434090bc5e..b01bd77077842 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/parse.ts @@ -10,7 +10,7 @@ import { isObject } from 'lodash'; import type { TinymathAST, TinymathVariable, TinymathLocation } from '@kbn/tinymath'; import { nonNullable } from '../../../../../utils'; import type { DateRange } from '../../../../../../common/types'; -import { IndexPattern } from '../../../../../types'; +import type { IndexPattern } from '../../../../../types'; import { OperationDefinition, GenericOperationDefinition, diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts index 421add9dec91e..95eb8a84e6849 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/validation.ts @@ -37,7 +37,7 @@ import type { GenericOperationDefinition, } from '..'; import type { FormBasedLayer } from '../../../types'; -import { IndexPattern } from '../../../../../types'; +import type { IndexPattern } from '../../../../../types'; import type { TinymathNodeTypes } from './types'; interface ValidationErrors { From 5321a62774dfc6e72be743f1e6e51885a63c32a8 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 22 May 2023 14:42:06 -0500 Subject: [PATCH 111/202] remove ignore global filters action --- .../actions/ignore_filters_action.ts | 51 ------------------- .../xy/annotations/actions/index.ts | 3 -- 2 files changed, 54 deletions(-) delete mode 100644 x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts deleted file mode 100644 index 52b7c38db16ef..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/ignore_filters_action.ts +++ /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 { i18n } from '@kbn/i18n'; -import type { LayerAction, StateSetter } from '../../../../types'; -import type { XYState, XYAnnotationLayerConfig } from '../../types'; - -export const getIgnoreFilterAction = ({ - state, - layer, - setState, -}: { - state: XYState; - layer: XYAnnotationLayerConfig; - setState: StateSetter; -}): LayerAction => { - return { - displayName: !layer.ignoreGlobalFilters - ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersLabel', { - defaultMessage: 'Ignore global filters', - }) - : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersLabel', { - defaultMessage: 'Keep global filters', - }), - description: !layer.ignoreGlobalFilters - ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersDescription', { - defaultMessage: - 'All the dimensions configured in this layer ignore filters defined at kibana level.', - }) - : i18n.translate('xpack.lens.xyChart.annotations.keepGlobalFiltersDescription', { - defaultMessage: - 'All the dimensions configured in this layer respect filters defined at kibana level.', - }), - execute: () => { - const newLayer = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; - const newLayers = state.layers.map((layerToCheck) => - layerToCheck.layerId === layer.layerId ? newLayer : layerToCheck - ); - return setState({ ...state, layers: newLayers }); - }, - icon: !layer.ignoreGlobalFilters ? 'filterIgnore' : 'filter', - isCompatible: true, - 'data-test-subj': !layer.ignoreGlobalFilters - ? 'lnsXY_annotationLayer_ignoreFilters' - : 'lnsXY_annotationLayer_keepFilters', - }; -}; diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts index 9ed2729a51a06..9316807b12c13 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions/index.ts @@ -12,7 +12,6 @@ import { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { LayerAction, StateSetter } from '../../../../types'; import { XYState, XYAnnotationLayerConfig } from '../../types'; import { getUnlinkLayerAction } from './unlink_action'; -import { getIgnoreFilterAction } from './ignore_filters_action'; import { getSaveLayerAction } from './save_action'; import { isByReferenceAnnotationsLayer } from '../../visualization_helpers'; import { annotationLayerHasUnsavedChanges } from '../../state_helpers'; @@ -73,7 +72,5 @@ export const createAnnotationActions = ({ actions.push(getRevertChangesAction({ state, layer, setState, core })); } - actions.push(getIgnoreFilterAction({ state, layer, setState })); - return actions; }; From 8d436eb2ff238ef95a23f01d107eeee86abb8f36 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 22 May 2023 16:04:41 -0500 Subject: [PATCH 112/202] a couple of small logical changes --- .../public/editor_frame_service/editor_frame/state_helpers.ts | 3 ++- x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 51db1ed11168b..fd36fe224ecda 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -123,7 +123,8 @@ export async function initializeDataViews( }) ); - for (const group of Object.values(annotationGroups)) { + const annotationGroupValues = Object.values(annotationGroups); + for (const group of annotationGroupValues) { if (group.dataViewSpec?.id) { adHocDataViews[group.dataViewSpec.id] = group.dataViewSpec; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index 4ae7d20945732..9639c0656ab62 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -55,7 +55,7 @@ export function AddLayerButton({ ?.filter(({ canAddViaMenu: hideFromMenu }) => !hideFromMenu); }, [visualization, visualizationState, layersMeta]); - if (supportedLayers == null || !supportedLayers.length) { + if (!supportedLayers?.length) { return null; } const annotationPanel = ({ From 0b52e1d82445635d25c036168174a57729f54a0a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 22 May 2023 16:06:04 -0500 Subject: [PATCH 113/202] change icon --- x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index 9639c0656ab62..c2dfaf3e91f72 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -185,7 +185,7 @@ export function AddLayerButton({ name: i18n.translate('xpack.lens.configPanel.newAnnotation', { defaultMessage: 'New annotation', }), - icon: 'plusInCircleFilled', + icon: 'plusInCircle', onClick: () => { addLayer(LayerTypes.ANNOTATIONS); toggleLayersChoice(false); From 729b593c94c0a94a504fe06442fb1f624499ece3 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 23 May 2023 15:40:11 -0500 Subject: [PATCH 114/202] use changeIndexPattern action to load new ad-hoc data view --- .../lens/public/data_views_service/service.ts | 57 +------------------ .../config_panel/config_panel.test.tsx | 2 + .../config_panel/config_panel.tsx | 28 +++++++-- .../editor_frame/config_panel/types.ts | 6 +- .../editor_frame/editor_frame.tsx | 1 + .../visualizations/xy/visualization.test.ts | 48 +--------------- 6 files changed, 33 insertions(+), 109 deletions(-) diff --git a/x-pack/plugins/lens/public/data_views_service/service.ts b/x-pack/plugins/lens/public/data_views_service/service.ts index f51b6804b0718..217d621b0ee98 100644 --- a/x-pack/plugins/lens/public/data_views_service/service.ts +++ b/x-pack/plugins/lens/public/data_views_service/service.ts @@ -13,8 +13,8 @@ import { UPDATE_FILTER_REFERENCES_ACTION, UPDATE_FILTER_REFERENCES_TRIGGER, } from '@kbn/unified-search-plugin/public'; -import type { IndexPattern, IndexPatternMap, IndexPatternRef } from '../types'; -import { ensureIndexPattern, loadIndexPatternRefs, loadIndexPatterns } from './loader'; +import type { IndexPattern, IndexPatternMap } from '../types'; +import { ensureIndexPattern, loadIndexPatterns } from './loader'; import type { DataViewsState } from '../state_management'; import { generateId } from '../id_generator'; @@ -53,15 +53,6 @@ export interface IndexPatternServiceAPI { onIndexPatternRefresh?: () => void; }) => Promise; - /** - * Takes a data view spec and creates the both the data view and the Lens index pattern - * and adds everything to the caches. - */ - addIndexPatternFromDataViewSpec: ( - spec: DataViewSpec, - cache: IndexPatternMap | undefined - ) => Promise; - /** * Ensure an indexPattern is loaded in the cache, usually used in conjuction with a indexPattern change action. */ @@ -107,50 +98,6 @@ export const createIndexPatternService = ({ ...args, }); }, - addIndexPatternFromDataViewSpec: async ( - spec: DataViewSpec, - cache: IndexPatternMap | undefined - ) => { - // TODO - extract this routine into a function and unit test? - const dataView = await dataViews.create(spec); - - if (!dataView.id) { - showLoadingDataViewError( - new Error( - i18n.translate('xpack.lens.indexPattern.noIdError', { - defaultMessage: 'Tried to use data view spec without an ID', - }) - ) - ); - return; - } - - const indexPatterns = await ensureIndexPattern({ - id: dataView.id, - onError: showLoadingDataViewError, - dataViews, - cache, - }); - - if (!indexPatterns) { - return; - } - - const refs = await loadIndexPatternRefs(dataViews); - - const newIndexPattern = indexPatterns[dataView.id!]; - - const newRef: IndexPatternRef = { - id: newIndexPattern.id!, - name: newIndexPattern.name, - title: newIndexPattern.title, - }; - - updateIndexPatterns({ - indexPatterns, - indexPatternRefs: [...refs, newRef], - }); - }, replaceDataViewId: async (dataView: DataView) => { const newDataView = await dataViews.create({ ...dataView.toSpec(), id: generateId() }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index c123406d160e0..af716e1b1317c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -16,6 +16,7 @@ import { mockStoreDeps, MountStoreProps, } from '../../../mocks'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { Visualization } from '../../../types'; import { LayerPanels } from './config_panel'; import { LayerPanel } from './layer_panel'; @@ -150,6 +151,7 @@ describe('ConfigPanel', () => { isFullscreen: false, toggleFullscreen: jest.fn(), uiActions, + dataViews: {} as DataViewsPublicPluginStart, getUserMessages: () => [], }; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index d956424ea6951..67e9ebb77536d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -321,11 +321,29 @@ export function LayerPanels( visualizationState: visualization.state, layersMeta: props.framePublicAPI, addLayer, - addIndexPatternFromDataViewSpec: (spec: DataViewSpec) => - props.indexPatternService.addIndexPatternFromDataViewSpec( - spec, - props.framePublicAPI.dataViews.indexPatterns - ), + addIndexPatternFromDataViewSpec: async (spec: DataViewSpec) => { + const dataView = await props.dataViews.create(spec); + + if (!dataView.id) { + return; + } + + const indexPatternId = dataView.id; + + const newIndexPatterns = await indexPatternService.ensureIndexPattern({ + id: indexPatternId, + cache: props.framePublicAPI.dataViews.indexPatterns, + }); + + dispatchLens( + changeIndexPattern({ + dataViews: { indexPatterns: newIndexPatterns }, + datasourceIds: Object.keys(datasourceStates), + visualizationIds: visualization.activeId ? [visualization.activeId] : [], + indexPatternId, + }) + ); + }, })} ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index 933ed2002b832..9d2253dfb12b1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { IndexPatternServiceAPI } from '../../../data_views_service/service'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import type { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { Visualization, @@ -23,6 +24,7 @@ export interface ConfigPanelWrapperProps { datasourceMap: DatasourceMap; visualizationMap: VisualizationMap; core: DatasourceDimensionEditorProps['core']; + dataViews: DataViewsPublicPluginStart; indexPatternService: IndexPatternServiceAPI; uiActions: UiActionsStart; getUserMessages: UserMessagesGetter; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index f64ba76952722..9051a42562f95 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -142,6 +142,7 @@ export function EditorFrame(props: EditorFrameProps) { visualizationMap={visualizationMap} framePublicAPI={framePublicAPI} uiActions={props.plugins.uiActions} + dataViews={props.plugins.dataViews} indexPatternService={props.indexPatternService} getUserMessages={props.getUserMessages} /> diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 407b66d91599e..4465eae89bc1d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -3576,56 +3576,10 @@ describe('xy_visualization', () => { }, jest.fn() ) - ).toEqual([ - expect.objectContaining({ - displayName: 'Keep global filters', - description: - 'All the dimensions configured in this layer respect filters defined at kibana level.', - icon: 'filter', - isCompatible: true, - 'data-test-subj': 'lnsXY_annotationLayer_keepFilters', - }), - ]); + ).toEqual([]); }); }); - it('should return an action that performs a state update on click', () => { - const baseState = exampleState(); - const setState = jest.fn(); - const [action] = xyVisualization.getSupportedActionsForLayer?.( - 'annotation', - { - ...baseState, - layers: [ - ...baseState.layers, - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - indexPatternId: 'myIndexPattern', - }, - ], - }, - setState - )!; - action.execute(null); - - expect(setState).toHaveBeenCalledWith( - expect.objectContaining({ - layers: expect.arrayContaining([ - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: false, - indexPatternId: 'myIndexPattern', - }, - ]), - }) - ); - }); - describe('layer settings', () => { describe('hasLayerSettings', () => { it('should expose no settings for a data or reference lines layer', () => { From 3b493c633f2fd4796446bcb793817b1f2800abf2 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 23 May 2023 16:12:55 -0500 Subject: [PATCH 115/202] always ensure index pattern is loaded before adding library annotation layer --- .../config_panel/config_panel.test.tsx | 4 +++- .../config_panel/config_panel.tsx | 21 ++++++++++++------- x-pack/plugins/lens/public/types.ts | 2 +- .../visualizations/xy/visualization.tsx | 6 +++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index af716e1b1317c..377a530816628 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -120,13 +120,15 @@ describe('ConfigPanel', () => { ...visualizationMap.testVis, getLayerIds: () => Object.keys(frame.datasourceLayers), getAddLayerButtonComponent: (props) => { + // TODO this is currently a copy of the XY add layer menu. + // this test should just use some generic component to exercise the flows return ( { if (loadedGroupInfo.dataViewSpec) { - await props.addIndexPatternFromDataViewSpec(loadedGroupInfo.dataViewSpec); + await props.ensureIndexPattern(loadedGroupInfo.dataViewSpec); } props.addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); }} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 67e9ebb77536d..c59023b42ce52 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -315,20 +315,25 @@ export function LayerPanels( ); })} {!hideAddLayerButton && - activeVisualization.getAddLayerButtonComponent && - activeVisualization.getAddLayerButtonComponent({ + activeVisualization?.getAddLayerButtonComponent?.({ visualization: activeVisualization, visualizationState: visualization.state, layersMeta: props.framePublicAPI, addLayer, - addIndexPatternFromDataViewSpec: async (spec: DataViewSpec) => { - const dataView = await props.dataViews.create(spec); + ensureIndexPattern: async (specOrId: DataViewSpec | string) => { + let indexPatternId; - if (!dataView.id) { - return; - } + if (typeof specOrId === 'string') { + indexPatternId = specOrId; + } else { + const dataView = await props.dataViews.create(specOrId); + + if (!dataView.id) { + return; + } - const indexPatternId = dataView.id; + indexPatternId = dataView.id; + } const newIndexPatterns = await indexPatternService.ensureIndexPattern({ id: indexPatternId, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index e448da07c76e3..8a5648563d435 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1235,7 +1235,7 @@ export interface Visualization; - addIndexPatternFromDataViewSpec: (spec: DataViewSpec) => Promise; + ensureIndexPattern: (specOrId: DataViewSpec | string) => Promise; }) => JSX.Element | null; /** * Creates map of columns ids and unique lables. Used only for noDatasource layers diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 6e8dd398ba265..8be88fd8b6d26 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -724,9 +724,9 @@ export const getXyVisualization = ({ {...props} eventAnnotationService={eventAnnotationService} onAddLayerFromAnnotationGroup={async (loadedGroupInfo) => { - if (loadedGroupInfo.dataViewSpec) { - await props.addIndexPatternFromDataViewSpec(loadedGroupInfo.dataViewSpec); - } + await props.ensureIndexPattern( + loadedGroupInfo.dataViewSpec ?? loadedGroupInfo.indexPatternId + ); props.addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); }} /> From 302d329deea21e8bf699c6b0a192c6c18031d9fb Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 23 May 2023 18:54:17 -0500 Subject: [PATCH 116/202] save index pattern references for linked layers --- .../public/visualizations/xy/state_helpers.ts | 7 ++++- .../visualizations/xy/visualization.test.ts | 26 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts index 7517570d1fe13..80f8874cfa776 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/state_helpers.ts @@ -192,8 +192,13 @@ export function getPersistableState(state: XYState) { annotations: layer.annotations, ignoreGlobalFilters: layer.ignoreGlobalFilters, }; - persistableLayers.push(persistableLayer); + + savedObjectReferences.push({ + type: 'index-pattern', + id: layer.indexPatternId, + name: getLayerReferenceName(layer.layerId), + }); } } else { const { indexPatternId, ...persistableLayer } = layer; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 4465eae89bc1d..005d2105b1187 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -3528,9 +3528,31 @@ describe('xy_visualization', () => { const { state: persistableState, savedObjectReferences } = xyVisualization.getPersistableState!(state); + expect(savedObjectReferences).toHaveLength(4); + + expect(savedObjectReferences.filter(({ type }) => type === 'index-pattern')) + .toMatchInlineSnapshot(` + Array [ + Object { + "id": "some-index-pattern", + "name": "xy-visualization-layer-layer-id", + "type": "index-pattern", + }, + Object { + "id": "some-index-pattern", + "name": "xy-visualization-layer-layer-id2", + "type": "index-pattern", + }, + ] + `); + + const annotationGroupRefs = savedObjectReferences.filter( + ({ type }) => type === 'event-annotation-group' + ); + expect(persistableState.layers).toEqual([ { - annotationGroupRef: savedObjectReferences[0].name, + annotationGroupRef: annotationGroupRefs[0].name, layerId: 'layer-id', layerType: 'annotations', persistanceType: 'linked', @@ -3538,7 +3560,7 @@ describe('xy_visualization', () => { ignoreGlobalFilters: layers[0].ignoreGlobalFilters, }, { - annotationGroupRef: savedObjectReferences[1].name, + annotationGroupRef: annotationGroupRefs[1].name, layerId: 'layer-id2', layerType: 'annotations', persistanceType: 'linked', From 5f9f3b4f8f7e61ccd7e5a35adca86582f6a06e53 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 23 May 2023 18:57:36 -0500 Subject: [PATCH 117/202] use flag icon for saved objects management --- src/plugins/event_annotation/server/saved_objects.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/server/saved_objects.ts b/src/plugins/event_annotation/server/saved_objects.ts index 94a8c20990e23..768def6b27f79 100644 --- a/src/plugins/event_annotation/server/saved_objects.ts +++ b/src/plugins/event_annotation/server/saved_objects.ts @@ -22,9 +22,9 @@ export function setupSavedObjects(coreSetup: CoreSetup) { name: EVENT_ANNOTATION_GROUP_TYPE, indexPattern: ANALYTICS_SAVED_OBJECT_INDEX, hidden: false, - namespaceType: 'multiple', // TODO double-check type + namespaceType: 'multiple', management: { - icon: 'questionInCircle', + icon: 'flag', defaultSearchField: 'title', importableAndExportable: true, getTitle: (obj: { attributes: EventAnnotationGroupAttributes }) => obj.attributes.title, From dc1ed29c1525a222b2cd224a6c780e79cb7a295c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 24 May 2023 17:24:37 -0500 Subject: [PATCH 118/202] fix dirty state bug, clean up addLayer --- .../public/app_plugin/show_underlying_data.ts | 5 +- .../editor_frame/config_panel/add_layer.tsx | 158 ------------------ .../config_panel/config_panel.test.tsx | 64 +++---- .../config_panel/config_panel.tsx | 13 +- .../lens/public/state_management/index.ts | 1 + .../public/state_management/lens_slice.ts | 16 ++ x-pack/plugins/lens/public/types.ts | 39 +++-- .../metric/visualization.test.ts | 2 - .../visualizations/metric/visualization.tsx | 2 - ...st.ts.snap => visualization.test.tsx.snap} | 0 .../public/visualizations/xy/add_layer.tsx | 59 +------ .../visualizations/xy/annotations/helpers.tsx | 3 +- ...ization.test.ts => visualization.test.tsx} | 0 .../visualizations/xy/visualization.tsx | 17 +- 14 files changed, 88 insertions(+), 291 deletions(-) delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx rename x-pack/plugins/lens/public/visualizations/xy/__snapshots__/{visualization.test.ts.snap => visualization.test.tsx.snap} (100%) rename x-pack/plugins/lens/public/visualizations/xy/{visualization.test.ts => visualization.test.tsx} (100%) 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 a181cea794584..5b6712ed23862 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 @@ -25,7 +25,10 @@ import { showMemoizedErrorNotification } from '../lens_ui_errors'; import { TableInspectorAdapter } from '../editor_frame_service/types'; import { Datasource, DatasourcePublicAPI, IndexPatternMap } from '../types'; import { Visualization } from '..'; -import { getLayerType } from '../editor_frame_service/editor_frame/config_panel/add_layer'; + +function getLayerType(visualization: Visualization, state: unknown, layerId: string) { + return visualization.getLayerType(layerId, state) || LayerTypes.DATA; +} /** * Joins a series of queries. diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx deleted file mode 100644 index 299f2b81fc5bb..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx +++ /dev/null @@ -1,158 +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, { useState, useMemo } from 'react'; -import { - EuiToolTip, - EuiButton, - EuiPopover, - EuiIcon, - EuiContextMenu, - EuiBadge, - EuiFlexItem, - EuiFlexGroup, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import type { LayerType } from '../../../../common/types'; -import type { FramePublicAPI, Visualization } from '../../../types'; - -interface AddLayerButtonProps { - visualization: Visualization; - visualizationState: unknown; - onAddLayerClick: (layerType: LayerType) => void; - layersMeta: Pick; -} - -export function getLayerType(visualization: Visualization, state: unknown, layerId: string) { - return visualization.getLayerType(layerId, state) || LayerTypes.DATA; -} - -export function AddLayerButton({ - visualization, - visualizationState, - onAddLayerClick, - layersMeta, -}: AddLayerButtonProps) { - const [showLayersChoice, toggleLayersChoice] = useState(false); - - const supportedLayers = useMemo(() => { - if (!visualization.appendLayer || !visualizationState) { - return null; - } - return visualization - .getSupportedLayers?.(visualizationState, layersMeta) - ?.filter(({ canAddViaMenu: hideFromMenu }) => !hideFromMenu); - }, [visualization, visualizationState, layersMeta]); - - if (supportedLayers == null || !supportedLayers.length) { - return null; - } - if (supportedLayers.length === 1) { - return ( - - onAddLayerClick(supportedLayers[0].type)} - iconType="layers" - > - {i18n.translate('xpack.lens.configPanel.addLayerButton', { - defaultMessage: 'Add layer', - })} - - - ); - } - return ( - toggleLayersChoice(!showLayersChoice)} - iconType="layers" - > - {i18n.translate('xpack.lens.configPanel.addLayerButton', { - defaultMessage: 'Add layer', - })} - - } - isOpen={showLayersChoice} - closePopover={() => toggleLayersChoice(false)} - panelPaddingSize="none" - > - { - return { - toolTipContent, - disabled, - name: - type === LayerTypes.ANNOTATIONS ? ( - - - {label} - - - - - {i18n.translate('xpack.lens.configPanel.experimentalLabel', { - defaultMessage: 'Technical preview', - })} - - - - ) : ( - {label} - ), - className: 'lnsLayerAddButton', - icon: icon && , - ['data-test-subj']: `lnsLayerAddButton-${type}`, - onClick: () => { - onAddLayerClick(type); - toggleLayersChoice(false); - }, - }; - }), - }, - ]} - /> - - ); -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index 377a530816628..78f7246c52e6d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -26,12 +26,11 @@ import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { generateId } from '../../../id_generator'; import { mountWithProvider } from '../../../mocks'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import type { LayerType } from '../../../../common/types'; import { ReactWrapper } from 'enzyme'; import { addLayer } from '../../../state_management'; import { createIndexPatternServiceMock } from '../../../mocks/data_views_service_mock'; import { AddLayerButton } from '../../../visualizations/xy/add_layer'; -import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { LayerType } from '@kbn/visualizations-plugin/common'; jest.mock('../../../id_generator'); @@ -45,6 +44,11 @@ jest.mock('@kbn/kibana-utils-plugin/public', () => { }; }); +const addNewLayer = (instance: ReactWrapper, type: LayerType = LayerTypes.REFERENCELINE) => + act(() => { + instance.find(`button[data-test-subj="${type}"]`).first().simulate('click'); + }); + const waitMs = (time: number) => new Promise((r) => setTimeout(r, time)); let container: HTMLDivElement | undefined; @@ -120,19 +124,17 @@ describe('ConfigPanel', () => { ...visualizationMap.testVis, getLayerIds: () => Object.keys(frame.datasourceLayers), getAddLayerButtonComponent: (props) => { - // TODO this is currently a copy of the XY add layer menu. - // this test should just use some generic component to exercise the flows return ( - { - if (loadedGroupInfo.dataViewSpec) { - await props.ensureIndexPattern(loadedGroupInfo.dataViewSpec); - } - props.addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo); - }} - /> + <> +

    + } + body={ + +

    + +

    +
    + } + actions={ + onCreateNew()} size="s"> + + + } + /> + ) : ( ; }) => void; + onCreateNew: () => void; }) => JSX.Element; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index c79b21b65b6cc..c522badb5b96d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -24,15 +24,13 @@ import type { ExtraAppendLayerArg } from './visualization'; interface AddLayerButtonProps { supportedLayers: VisualizationLayerDescription[]; - addLayer: AddLayerFunction; - onAddLayerFromAnnotationGroup: (arg: ExtraAppendLayerArg) => void; + addLayer: AddLayerFunction; eventAnnotationService: EventAnnotationServiceType; } export function AddLayerButton({ supportedLayers, addLayer, - onAddLayerFromAnnotationGroup, eventAnnotationService, }: AddLayerButtonProps) { const [showLayersChoice, toggleLayersChoice] = useState(false); @@ -164,7 +162,9 @@ export function AddLayerButton({ isLoadLibraryVisible={isLoadLibraryVisible} setLoadLibraryFlyoutVisible={setLoadLibraryFlyoutVisible} eventAnnotationService={eventAnnotationService} - addLayer={onAddLayerFromAnnotationGroup} + addLayer={(extraArg) => { + addLayer(LayerTypes.ANNOTATIONS, extraArg); + }} /> )} diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx index e8f6a6db95776..6b203356cc7ed 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -21,7 +21,7 @@ export function LoadAnnotationLibraryFlyout({ isLoadLibraryVisible: boolean; setLoadLibraryFlyoutVisible: (visible: boolean) => void; eventAnnotationService: EventAnnotationServiceType; - addLayer: (argument: ExtraAppendLayerArg) => void; + addLayer: (argument?: ExtraAppendLayerArg) => void; }) { const { renderEventAnnotationGroupSavedObjectFinder: EventAnnotationGroupSavedObjectFinder, @@ -65,16 +65,18 @@ export function LoadAnnotationLibraryFlyout({ return true; }} > -
    - { - loadAnnotationGroup(id).then((loadedGroup) => { - addLayer({ ...loadedGroup, annotationGroupId: id }); - setLoadLibraryFlyoutVisible(false); - }); - }} - /> -
    + { + loadAnnotationGroup(id).then((loadedGroup) => { + addLayer({ ...loadedGroup, annotationGroupId: id }); + setLoadLibraryFlyoutVisible(false); + }); + }} + onCreateNew={() => { + addLayer(); + setLoadLibraryFlyoutVisible(false); + }} + /> ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index ba298e64d232f..cc8897143a63d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -723,17 +723,19 @@ export const getXyVisualization = ({ { - await props.ensureIndexPattern( - loadedGroupInfo.dataViewSpec ?? loadedGroupInfo.indexPatternId - ); + addLayer={async (type, loadedGroupInfo) => { + if (type === LayerTypes.ANNOTATIONS && loadedGroupInfo) { + await props.ensureIndexPattern( + loadedGroupInfo.dataViewSpec ?? loadedGroupInfo.indexPatternId + ); - props.registerLibraryAnnotationGroup({ - id: loadedGroupInfo.annotationGroupId, - group: loadedGroupInfo, - }); + props.registerLibraryAnnotationGroup({ + id: loadedGroupInfo.annotationGroupId, + group: loadedGroupInfo, + }); + } - props.addLayer(LayerTypes.ANNOTATIONS, loadedGroupInfo, true); + props.addLayer(type, loadedGroupInfo, !!loadedGroupInfo); }} /> ); From 7e53a7132467db9d846feadc104a7bfade9782bd Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 31 May 2023 09:02:43 -0500 Subject: [PATCH 146/202] make unsaved changes indicator accessible --- .../shared_components/static_header.tsx | 1 - .../xy/load_annotation_library_flyout.tsx | 30 ++++++++++++------- .../xy/xy_config_panel/layer_header.tsx | 17 ++++------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/lens/public/shared_components/static_header.tsx b/x-pack/plugins/lens/public/shared_components/static_header.tsx index 48939342e92a5..8069a2d2c9849 100644 --- a/x-pack/plugins/lens/public/shared_components/static_header.tsx +++ b/x-pack/plugins/lens/public/shared_components/static_header.tsx @@ -43,7 +43,6 @@ export const StaticHeader = ({ {indicator && (
    diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx index 6b203356cc7ed..ba22b00ccd362 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -9,6 +9,8 @@ import React from 'react'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFlyoutFooter } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { css } from '@emotion/react'; import { FlyoutContainer } from '../../shared_components/flyout_container'; import type { ExtraAppendLayerArg } from './visualization'; @@ -65,18 +67,24 @@ export function LoadAnnotationLibraryFlyout({ return true; }} > - { - loadAnnotationGroup(id).then((loadedGroup) => { - addLayer({ ...loadedGroup, annotationGroupId: id }); +
    + { + loadAnnotationGroup(id).then((loadedGroup) => { + addLayer({ ...loadedGroup, annotationGroupId: id }); + setLoadLibraryFlyoutVisible(false); + }); + }} + onCreateNew={() => { + addLayer(); setLoadLibraryFlyoutVisible(false); - }); - }} - onCreateNew={() => { - addLayer(); - setLoadLibraryFlyoutVisible(false); - }} - /> + }} + /> +
    ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx index 349d897d2a619..3f6c0a6f81754 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx @@ -14,7 +14,7 @@ import { EuiText, EuiPopoverTitle, useEuiTheme, - EuiToolTip, + EuiIconTip, } from '@elastic/eui'; import { ToolbarButton } from '@kbn/kibana-react-plugin/public'; import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons'; @@ -96,20 +96,13 @@ function AnnotationsLayerHeader({ } indicator={ hasUnsavedChanges && ( - -
    - + type="dot" + color={euiThemeVars.euiColorSuccess} + /> ) } /> From fb6f3ef5a6f7498d03132b30487bb047c2cecdbb Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 31 May 2023 15:32:28 -0500 Subject: [PATCH 147/202] update library annotation strings --- .../annotation_editor_controls.tsx | 28 +++++++-------- .../annotation_editor_controls/helpers.ts | 11 +++--- .../annotation_editor_controls/icon_set.ts | 34 +++++++++++-------- .../manual_annotation_panel.tsx | 8 ++--- .../query_annotation_panel.tsx | 4 +-- .../range_annotation_panel.tsx | 2 +- .../tooltip_annotation_panel.tsx | 6 ++-- 7 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index c5031d55fae62..f180f6c49ce55 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -81,19 +81,19 @@ export const AnnotationEditorControls = ({ return ( <> @@ -268,14 +268,14 @@ export const AnnotationEditorControls = ({ )} {isRange && ( {isQueryBased && currentAnnotation && ( @@ -339,7 +339,7 @@ export const AnnotationEditorControls = ({ display="rowCompressed" className="lnsRowCompressedMargin" fullWidth - label={i18n.translate('xpack.lens.xyChart.annotation.tooltip', { + label={i18n.translate('eventAnnotation.xyChart.annotation.tooltip', { defaultMessage: 'Show additional fields', })} > diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/helpers.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/helpers.ts index 3555c4b016eee..f823dab7e7357 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/helpers.ts +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/helpers.ts @@ -23,12 +23,15 @@ export const defaultAnnotationColor = euiLightVars.euiColorAccent; // Do not compute it live as dependencies will add tens of Kbs to the plugin export const defaultAnnotationRangeColor = `#F04E981A`; // defaultAnnotationColor with opacity 0.1 -export const defaultAnnotationLabel = i18n.translate('xpack.lens.xyChart.defaultAnnotationLabel', { - defaultMessage: 'Event', -}); +export const defaultAnnotationLabel = i18n.translate( + 'eventAnnotation.xyChart.defaultAnnotationLabel', + { + defaultMessage: 'Event', + } +); export const defaultRangeAnnotationLabel = i18n.translate( - 'xpack.lens.xyChart.defaultRangeAnnotationLabel', + 'eventAnnotation.xyChart.defaultRangeAnnotationLabel', { defaultMessage: 'Event range', } diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/icon_set.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/icon_set.ts index c50d7c0418f24..e4ea40acb48ed 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/icon_set.ts +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/icon_set.ts @@ -14,37 +14,37 @@ import { AvailableAnnotationIcon } from '../../../common'; export const annotationsIconSet: IconSet = [ { value: 'asterisk', - label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.asteriskIconLabel', { defaultMessage: 'Asterisk', }), }, { value: 'alert', - label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.alertIconLabel', { defaultMessage: 'Alert', }), }, { value: 'bell', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.bellIconLabel', { defaultMessage: 'Bell', }), }, { value: 'bolt', - label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.boltIconLabel', { defaultMessage: 'Bolt', }), }, { value: 'bug', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.bugIconLabel', { defaultMessage: 'Bug', }), }, { value: 'circle', - label: i18n.translate('xpack.lens.xyChart.iconSelect.circleIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.circleIconLabel', { defaultMessage: 'Circle', }), icon: IconCircle, @@ -53,51 +53,55 @@ export const annotationsIconSet: IconSet = [ { value: 'editorComment', - label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.commentIconLabel', { defaultMessage: 'Comment', }), }, { value: 'flag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.flagIconLabel', { defaultMessage: 'Flag', }), }, { value: 'heart', - label: i18n.translate('xpack.lens.xyChart.iconSelect.heartLabel', { defaultMessage: 'Heart' }), + label: i18n.translate('eventAnnotation.xyChart.iconSelect.heartLabel', { + defaultMessage: 'Heart', + }), }, { value: 'mapMarker', - label: i18n.translate('xpack.lens.xyChart.iconSelect.mapMarkerLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.mapMarkerLabel', { defaultMessage: 'Map Marker', }), }, { value: 'pinFilled', - label: i18n.translate('xpack.lens.xyChart.iconSelect.mapPinLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.mapPinLabel', { defaultMessage: 'Map Pin', }), }, { value: 'starEmpty', - label: i18n.translate('xpack.lens.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), + label: i18n.translate('eventAnnotation.xyChart.iconSelect.starLabel', { + defaultMessage: 'Star', + }), }, { value: 'starFilled', - label: i18n.translate('xpack.lens.xyChart.iconSelect.starFilledLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.starFilledLabel', { defaultMessage: 'Star filled', }), }, { value: 'tag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.tagIconLabel', { defaultMessage: 'Tag', }), }, { value: 'triangle', - label: i18n.translate('xpack.lens.xyChart.iconSelect.triangleIconLabel', { + label: i18n.translate('eventAnnotation.xyChart.iconSelect.triangleIconLabel', { defaultMessage: 'Triangle', }), icon: IconTriangle, diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx index ec0081df10800..431bdbc3e16f7 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx @@ -36,7 +36,7 @@ export const ConfigPanelManualAnnotation = ({ diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/range_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/range_annotation_panel.tsx index 1f42d056b18f1..0418994677947 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/range_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/range_annotation_panel.tsx @@ -39,7 +39,7 @@ export const ConfigPanelApplyAsRangeSwitch = ({ data-test-subj="lns-xyAnnotation-rangeSwitch" label={ - {i18n.translate('xpack.lens.xyChart.applyAsRange', { + {i18n.translate('eventAnnotation.xyChart.applyAsRange', { defaultMessage: 'Apply as range', })} diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx index 6088ab4c2a9aa..fad49f3314d13 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx @@ -98,7 +98,7 @@ export function TooltipSection({ onClick={() => { handleInputChange([...localValues, { id: generateId(), value: undefined, isNew: true }]); }} - label={i18n.translate('xpack.lens.xyChart.annotation.tooltip.addField', { + label={i18n.translate('eventAnnotation.xyChart.annotation.tooltip.addField', { defaultMessage: 'Add field', })} isDisabled={localValues.length > MAX_TOOLTIP_FIELDS_SIZE} @@ -115,7 +115,7 @@ export function TooltipSection({ className="lnsConfigPanelAnnotations__noFieldsPrompt" > - {i18n.translate('xpack.lens.xyChart.annotation.tooltip.noFields', { + {i18n.translate('eventAnnotation.xyChart.annotation.tooltip.noFields', { defaultMessage: 'None selected', })} @@ -170,7 +170,7 @@ export function TooltipSection({ handleInputChange(arrayRef.filter((_, i) => i !== index)); }} removeTitle={i18n.translate( - 'xpack.lens.xyChart.annotation.tooltip.deleteButtonLabel', + 'eventAnnotation.xyChart.annotation.tooltip.deleteButtonLabel', { defaultMessage: 'Delete', } From e3a08ad8f86c60049a6f988dc31586ebeaf5f316 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 31 May 2023 15:41:57 -0500 Subject: [PATCH 148/202] skip annotation editing tests --- .../group_editor_controls.test.tsx | 85 ++++++++++--------- .../group_editor_controls.tsx | 2 +- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index dfe83f6cbfb22..d46895f54814e 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -15,7 +15,7 @@ import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { act } from 'react-dom/test-utils'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; -import { AnnotationEditorControls } from '..'; +import { AnnotationEditorControls, ENABLE_INDIVIDUAL_ANNOTATION_EDITING } from '..'; jest.mock('@elastic/eui', () => { return { @@ -194,54 +194,63 @@ describe('event annotation group editor', () => { `); }); - it('adds a new annotation group', () => { - act(() => { - wrapper.find('button[data-test-subj="addAnnotation"]').simulate('click'); + if (ENABLE_INDIVIDUAL_ANNOTATION_EDITING) { + it('adds a new annotation group', () => { + act(() => { + wrapper.find('button[data-test-subj="addAnnotation"]').simulate('click'); + }); + + expect(updateMock).toHaveBeenCalledTimes(2); + const newAnnotations = (updateMock.mock.calls[0][0] as EventAnnotationGroupConfig) + .annotations; + expect(newAnnotations.length).toBe(group.annotations.length + 1); + expect(wrapper.exists(AnnotationEditorControls)); // annotation controls opened }); - expect(updateMock).toHaveBeenCalledTimes(2); - const newAnnotations = (updateMock.mock.calls[0][0] as EventAnnotationGroupConfig).annotations; - expect(newAnnotations.length).toBe(group.annotations.length + 1); - expect(wrapper.exists(AnnotationEditorControls)); // annotation controls opened - }); + it('incorporates annotation updates into group', () => { + const annotations = [ + getDefaultManualAnnotation('1', ''), + getDefaultManualAnnotation('2', ''), + ]; - it('incorporates annotation updates into group', () => { - const annotations = [getDefaultManualAnnotation('1', ''), getDefaultManualAnnotation('2', '')]; + act(() => { + wrapper.setProps({ + selectedAnnotation: annotations[0], + group: { ...group, annotations }, + }); + }); - act(() => { - wrapper.setProps({ - selectedAnnotation: annotations[0], - group: { ...group, annotations }, + wrapper.find(AnnotationEditorControls).prop('onAnnotationChange')({ + ...annotations[0], + color: 'newColor', }); - }); - wrapper.find(AnnotationEditorControls).prop('onAnnotationChange')({ - ...annotations[0], - color: 'newColor', + expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock.mock.calls[0][0].annotations[0].color).toBe('newColor'); + expect(setSelectedAnnotationMock).toHaveBeenCalledTimes(1); }); - expect(updateMock).toHaveBeenCalledTimes(1); - expect(updateMock.mock.calls[0][0].annotations[0].color).toBe('newColor'); - expect(setSelectedAnnotationMock).toHaveBeenCalledTimes(1); - }); + it('removes an annotation from a group', () => { + const annotations = [ + getDefaultManualAnnotation('1', ''), + getDefaultManualAnnotation('2', ''), + ]; - it('removes an annotation from a group', () => { - const annotations = [getDefaultManualAnnotation('1', ''), getDefaultManualAnnotation('2', '')]; + act(() => { + wrapper.setProps({ + group: { ...group, annotations }, + }); + }); - act(() => { - wrapper.setProps({ - group: { ...group, annotations }, + act(() => { + wrapper + .find('button[data-test-subj="indexPattern-dimension-remove"]') + .last() + .simulate('click'); }); - }); - act(() => { - wrapper - .find('button[data-test-subj="indexPattern-dimension-remove"]') - .last() - .simulate('click'); + expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock.mock.calls[0][0].annotations).toEqual(annotations.slice(0, 1)); }); - - expect(updateMock).toHaveBeenCalledTimes(1); - expect(updateMock.mock.calls[0][0].annotations).toEqual(annotations.slice(0, 1)); - }); + } }); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index f6574a3c7789a..b08fa84380f4b 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -19,7 +19,7 @@ import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../../comm import { AnnotationEditorControls } from '../annotation_editor_controls'; import { AnnotationList } from './annotation_list'; -const ENABLE_INDIVIDUAL_ANNOTATION_EDITING = false; +export const ENABLE_INDIVIDUAL_ANNOTATION_EDITING = false; export const GroupEditorControls = ({ group, From a8f9773ca34bfc1e48e19f429c8df7f7a4ed0d61 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 31 May 2023 21:25:08 +0000 Subject: [PATCH 149/202] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../server/options_list/options_list_cluster_settings_route.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts index 51e2b94b600b0..aa1c839ffe778 100644 --- a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts +++ b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts @@ -8,7 +8,6 @@ import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; import { CoreSetup } from '@kbn/core/server'; -import { errors } from '@elastic/elasticsearch'; export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { const router = http.createRouter(); From 7036e0c4fbbf35579c16258f39390f8a1f66657d Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 10:44:43 -0500 Subject: [PATCH 150/202] clean up inadvertant changes --- .../core_versioned_route.test.ts | 2 +- .../analytics_collection_overview.test.tsx | 1 - .../endpoint/lib/policy/license_watch.test.ts | 26 +++---------------- .../security_solution/server/plugin.ts | 1 - 4 files changed, 4 insertions(+), 26 deletions(-) diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts index b42c1868ae87b..032914981ceb4 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts @@ -194,7 +194,7 @@ describe('Versioned route', () => { ); const kibanaResponse = await handler!( - { core: { env: { mode: { dev: true } } } } as any, + {} as any, createRequest({ version: '1', body: { foo: 1 }, diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx index fdce4918d6aa5..30cc657a22c2f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_overview.test.tsx @@ -41,7 +41,6 @@ const mockActions = { analyticsEventsExist: jest.fn(), fetchAnalyticsCollection: jest.fn(), fetchAnalyticsCollectionDataViewId: jest.fn(), - analyticsEventsExist: jest.fn(), setTimeRange: jest.fn(), }; diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts index 9c57b8156e3e3..9d962bc0e64ce 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts @@ -16,7 +16,6 @@ import { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks'; import { PolicyWatcher } from './license_watch'; import type { ILicense } from '@kbn/licensing-plugin/common/types'; import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock'; -import { cloudMock } from '@kbn/cloud-plugin/server/mocks'; import type { PackagePolicyClient } from '@kbn/fleet-plugin/server'; import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; @@ -36,7 +35,6 @@ const MockPPWithEndpointPolicy = (cb?: (p: PolicyConfig) => PolicyConfig): Packa describe('Policy-Changing license watcher', () => { const logger = loggingSystemMock.create().get('license_watch.test'); - const cloudServiceMock = cloudMock.createSetup(); const soStartMock = savedObjectsServiceMock.createStartContract(); const esStartMock = elasticsearchServiceMock.createStart(); let packagePolicySvcMock: jest.Mocked; @@ -53,13 +51,7 @@ describe('Policy-Changing license watcher', () => { // mock a license-changing service to test reactivity const licenseEmitter: Subject = new Subject(); const licenseService = new LicenseService(); - const pw = new PolicyWatcher( - packagePolicySvcMock, - soStartMock, - esStartMock, - cloudServiceMock, - logger - ); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger); // swap out watch function, just to ensure it gets called when a license change happens const mockWatch = jest.fn(); @@ -104,13 +96,7 @@ describe('Policy-Changing license watcher', () => { perPage: 100, }); - const pw = new PolicyWatcher( - packagePolicySvcMock, - soStartMock, - esStartMock, - cloudServiceMock, - logger - ); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger); await pw.watch(Gold); // just manually trigger with a given license expect(packagePolicySvcMock.list.mock.calls.length).toBe(3); // should have asked for 3 pages of resuts @@ -137,13 +123,7 @@ describe('Policy-Changing license watcher', () => { perPage: 100, }); - const pw = new PolicyWatcher( - packagePolicySvcMock, - soStartMock, - esStartMock, - cloudServiceMock, - logger - ); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger); // emulate a license change below paid tier await pw.watch(Basic); diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 5ffc0bb11017e..3661caeca6d5d 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -480,7 +480,6 @@ export class Plugin implements ISecuritySolutionPlugin { plugins.fleet.packagePolicyService, core.savedObjects, core.elasticsearch, - plugins.cloud, logger ); this.policyWatcher.start(licenseService); From 9e84b4dbaae53ed072b0b70a140d67aae3a8dc79 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 10:47:38 -0500 Subject: [PATCH 151/202] update table list view invocations --- .../public/examples/msearch/msearch_table.tsx | 2 +- src/plugins/files_management/public/app.tsx | 4 ++-- x-pack/plugins/graph/public/apps/listing_route.tsx | 2 +- .../plugins/maps/public/routes/list_page/maps_list_view.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx index 47eaab4ba602b..71f2844e4fd29 100644 --- a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx +++ b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx @@ -51,7 +51,7 @@ export const MSearchTable = () => { initialPageSize={50} entityName={`ContentItem`} entityNamePlural={`ContentItems`} - tableListTitle={`MSearch Demo`} + title={`MSearch Demo`} urlStateEnabled={false} emptyPrompt={<>No data found. Try to install some sample data first.} onClickTitle={(item) => { diff --git a/src/plugins/files_management/public/app.tsx b/src/plugins/files_management/public/app.tsx index 3ee4e5f52720c..4fc2e8fff45f8 100644 --- a/src/plugins/files_management/public/app.tsx +++ b/src/plugins/files_management/public/app.tsx @@ -42,8 +42,8 @@ export const App: FunctionComponent = () => { return (
    - tableListTitle={i18nTexts.tableListTitle} - tableListDescription={i18nTexts.tableListDescription} + title={i18nTexts.tableListTitle} + description={i18nTexts.tableListDescription} titleColumnName={i18nTexts.titleColumnName} emptyPrompt={} entityName={i18nTexts.entityName} diff --git a/x-pack/plugins/graph/public/apps/listing_route.tsx b/x-pack/plugins/graph/public/apps/listing_route.tsx index a34a4bc5b591b..27da1a51b8f4b 100644 --- a/x-pack/plugins/graph/public/apps/listing_route.tsx +++ b/x-pack/plugins/graph/public/apps/listing_route.tsx @@ -111,7 +111,7 @@ export function ListingRoute({ entityNamePlural={i18n.translate('xpack.graph.listing.table.entityNamePlural', { defaultMessage: 'graphs', })} - tableListTitle={i18n.translate('xpack.graph.listing.graphsTitle', { + title={i18n.translate('xpack.graph.listing.graphsTitle', { defaultMessage: 'Graphs', })} getDetailViewLink={({ id }) => getEditUrl(addBasePath, { id })} diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index d98444057097d..75cc9de21049c 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -133,7 +133,7 @@ function MapsListViewComp({ history }: Props) { entityNamePlural={i18n.translate('xpack.maps.mapListing.entityNamePlural', { defaultMessage: 'maps', })} - tableListTitle={APP_NAME} + title={APP_NAME} onClickTitle={({ id }) => history.push(getEditPath(id))} /> ); From a7cd08a40cc579e27a6a01014dc91d284f9c8641 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 10:49:25 -0500 Subject: [PATCH 152/202] update i18n string --- .../event_annotation/public/components/group_editor_flyout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index 3798a41396996..be9629a2467d3 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -76,7 +76,7 @@ export const GroupEditorFlyout = ({

    From a417b01af6153b48cbc0b7b01a59a6e984b2bbbe Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 11:16:11 -0500 Subject: [PATCH 153/202] update more translations --- .../components/dimension_buttons/trigger.tsx | 4 +-- .../public/components/line_style_settings.tsx | 10 +++--- .../components/text_decoration_setting.tsx | 10 +++--- .../translations/translations/fr-FR.json | 35 ------------------- .../translations/translations/ja-JP.json | 35 ------------------- .../translations/translations/zh-CN.json | 35 ------------------- 6 files changed, 12 insertions(+), 117 deletions(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx index 944945d56e1c6..c050582332b05 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/trigger.tsx @@ -15,11 +15,11 @@ import { euiThemeVars } from '@kbn/ui-theme'; export const defaultDimensionTriggerTooltip = (

    - {i18n.translate('xpack.lens.configure.invalidConfigTooltip', { + {i18n.translate('visualizationUiComponents.configure.invalidConfigTooltip', { defaultMessage: 'Invalid configuration.', })}
    - {i18n.translate('xpack.lens.configure.invalidConfigTooltipClick', { + {i18n.translate('visualizationUiComponents.configure.invalidConfigTooltipClick', { defaultMessage: 'Click for more details.', })}

    diff --git a/src/plugins/visualization_ui_components/public/components/line_style_settings.tsx b/src/plugins/visualization_ui_components/public/components/line_style_settings.tsx index 3df2837971f9f..0b7f09b6a7444 100644 --- a/src/plugins/visualization_ui_components/public/components/line_style_settings.tsx +++ b/src/plugins/visualization_ui_components/public/components/line_style_settings.tsx @@ -36,7 +36,7 @@ export const LineStyleSettings = ({ @@ -51,7 +51,7 @@ export const LineStyleSettings = ({
    Date: Thu, 1 Jun 2023 11:56:46 -0500 Subject: [PATCH 154/202] fix tag saving --- src/plugins/event_annotation/common/types.ts | 1 - .../public/event_annotation_service/service.test.ts | 3 +-- .../public/event_annotation_service/service.tsx | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index eaec066edf36a..a3af72ea8c120 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -86,7 +86,6 @@ export type EventAnnotationConfig = export interface EventAnnotationGroupAttributes { title: string; description: string; - tags: string[]; ignoreGlobalFilters: boolean; annotations: EventAnnotationConfig[]; dataViewSpec?: DataViewSpec | null; diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index cce0fbe562d50..905435bc4f4d0 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -618,11 +618,10 @@ describe('Event Annotation Service', () => { { title: 'newTitle', description: '', - tags: [], annotations: [], dataViewSpec: null, ignoreGlobalFilters: false, - }, + } as EventAnnotationGroupAttributes, { references: [ { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index c6c7e2aac396e..7a467c4bec9fa 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -174,7 +174,7 @@ export function getEventAnnotationService( ); return { - attributes: { title, description, ignoreGlobalFilters, annotations, dataViewSpec, tags }, + attributes: { title, description, ignoreGlobalFilters, annotations, dataViewSpec }, references, }; }; From 4f810dc8fd385b175af5a0b8f21e1e21639babc5 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 12:38:10 -0500 Subject: [PATCH 155/202] remove superfluous service --- x-pack/test/functional/apps/lens/group6/annotations.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/group6/annotations.ts b/x-pack/test/functional/apps/lens/group6/annotations.ts index c3e7e660a3754..685d19bf02633 100644 --- a/x-pack/test/functional/apps/lens/group6/annotations.ts +++ b/x-pack/test/functional/apps/lens/group6/annotations.ts @@ -20,7 +20,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]); const find = getService('find'); const retry = getService('retry'); - const browser = getService('browser'); const toastsService = getService('toasts'); const testSubjects = getService('testSubjects'); const from = 'Sep 19, 2015 @ 06:31:44.000'; From f432a9e2d9eb2cd82ab052b235762ff923e84a1d Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 15:19:07 -0500 Subject: [PATCH 156/202] maybe fix some functional tests --- .../public/components/dimension_buttons/empty_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx index 23a9427236cc5..e66d512e80f73 100644 --- a/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx +++ b/src/plugins/visualization_ui_components/public/components/dimension_buttons/empty_button.tsx @@ -58,7 +58,7 @@ export const EmptyDimensionButton = ({ onClick(); }} > - + ); }; From e729a93966aec9f02c066b2f31def73012f296bb Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 1 Jun 2023 15:49:55 -0500 Subject: [PATCH 157/202] add placeholder test --- .../components/annotation_editor_controls/index.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx index 5900c19a4941a..f16f96ce8ff8c 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx @@ -7,7 +7,10 @@ */ // TODO - translate these tests to work with AnnotationEditorControls component -export {}; + +describe('AnnotationEditorControls', () => { + it('passes', () => expect(true).toBeTruthy()); +}); // import React from 'react'; // import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; From 6edd24086a18dfe75c9beed5045a5145fc0eaa11 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 10:07:08 -0500 Subject: [PATCH 158/202] fix layer panel unit tests --- .../editor_frame/config_panel/layer_panel.test.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index 18b5acd160aaf..eae473567922d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -21,6 +21,7 @@ import { mountWithProvider, } from '../../../mocks'; import { createIndexPatternServiceMock } from '../../../mocks/data_views_service_mock'; +import { DimensionButton } from '@kbn/visualization-ui-components/public'; jest.mock('../../../id_generator'); @@ -714,9 +715,7 @@ describe('LayerPanel', () => { expect(instance.exists('[data-test-subj="lns-fakeDimension"]')).toBeTruthy(); expect( - instance - .find('[data-test-subj="lns-fakeDimension"] .lnsLayerPanel__triggerTextLabel') - .text() + instance.find('[data-test-subj="lns-fakeDimension"] .dimensionTrigger__textLabel').text() ).toBe(fakeAccessorLabel); }); @@ -826,7 +825,7 @@ describe('LayerPanel', () => { const dragDropElement = instance .find('[data-test-subj="lnsGroup"] DragDrop') .first() - .find('.lnsLayerPanel__dimension') + .find(DimensionButton) .first(); dragDropElement.simulate('dragOver'); From 5bed6f476f4b5a6b86c0d15aecdb8938bc936180 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 10:54:48 -0500 Subject: [PATCH 159/202] fix some test subjects --- .../table_list/src/tabbed_table_list_view.tsx | 6 +++-- .../table_list/src/table_list_view.tsx | 23 +++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.tsx b/packages/content-management/table_list/src/tabbed_table_list_view.tsx index c6b3524347660..2b8301e0bff63 100644 --- a/packages/content-management/table_list/src/tabbed_table_list_view.tsx +++ b/packages/content-management/table_list/src/tabbed_table_list_view.tsx @@ -17,7 +17,7 @@ import type { } from './table_list_view'; export type TableListTabParentProps = - Pick, 'onFetchSuccess'>; + Pick, 'onFetchSuccess' | 'setPageDataTestSubject'>; export interface TableListTab { title: string; @@ -42,6 +42,7 @@ export const TabbedTableListView = ({ changeActiveTab, }: TabbedTableListViewProps) => { const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); + const [pageDataTestSubject, setPageDataTestSubject] = useState(); const getActiveTab = useCallback( () => tabs.find((tab) => tab.id === activeTabId) ?? tabs[0], @@ -58,6 +59,7 @@ export const TabbedTableListView = ({ setHasInitialFetchReturned(true); } }, + setPageDataTestSubject, }); setTableList(newTableList); } @@ -66,7 +68,7 @@ export const TabbedTableListView = ({ }, [hasInitialFetchReturned, activeTabId, tabs, getActiveTab]); return ( - + {title}} description={description} diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index e3ad0204baefa..3b1b54c231856 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -144,7 +144,12 @@ export type TableListProps & { onFetchSuccess: () => void; tableCaption: string; refreshListBouncer?: boolean }; +> & { + tableCaption: string; + refreshListBouncer?: boolean; + onFetchSuccess: () => void; + setPageDataTestSubject: (subject: string) => void; +}; export interface State { items: T[]; @@ -298,9 +303,12 @@ function TableListComp({ contentEditor = { enabled: false }, titleColumnName, withoutPageTemplateWrapper, - onFetchSuccess: onInitialFetchReturned, + onFetchSuccess, refreshListBouncer, + setPageDataTestSubject, }: TableListProps) { + setPageDataTestSubject(`${entityName}LandingPage`); + if (!getDetailViewLink && !onClickTitle) { throw new Error( `[TableListView] One o["getDetailViewLink" or "onClickTitle"] prop must be provided.` @@ -398,7 +406,6 @@ function TableListComp({ const hasQuery = searchQuery.text !== ''; const hasNoItems = !isFetchingItems && items.length === 0 && !hasQuery; - const pageDataTestSubject = `${entityName}LandingPage`; const showFetchError = Boolean(fetchError); const showLimitError = !showFetchError && totalItems > listingLimit; @@ -429,6 +436,8 @@ function TableListComp({ response, }, }); + + onFetchSuccess(); } } catch (err) { dispatch({ @@ -436,7 +445,7 @@ function TableListComp({ data: err, }); } - }, [searchQueryParser, findItems, searchQuery.text]); + }, [searchQueryParser, searchQuery.text, findItems, onFetchSuccess]); useEffect(() => { fetchItems(); @@ -939,7 +948,7 @@ function TableListComp({ if (!showFetchError && hasNoItems) { return ( - + @@ -1046,9 +1055,8 @@ export const TableListView = ({ ? (React.Fragment as unknown as typeof KibanaPageTemplate) : KibanaPageTemplate; - const pageDataTestSubject = `${entityName}LandingPage`; - const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); + const [pageDataTestSubject, setPageDataTestSubject] = useState(); return ( @@ -1089,6 +1097,7 @@ export const TableListView = ({ setHasInitialFetchReturned(true); } }} + setPageDataTestSubject={setPageDataTestSubject} /> From 594a34244706b2014da86bc34c47daa7a95334e4 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 11:30:54 -0500 Subject: [PATCH 160/202] update unit tests --- .../table_list/src/table_list_view.test.tsx | 22 +++++++++++++++++++ .../public/components/table_list.test.tsx | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index c441305fd227c..59c77caa2cef0 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -1185,6 +1185,7 @@ describe('TableList', () => { onFetchSuccess: jest.fn(), tableCaption: 'test title', getDetailViewLink: () => '', + setPageDataTestSubject: () => {}, }; const setup = registerTestBed(WithServices(TableList), { @@ -1237,4 +1238,25 @@ describe('TableList', () => { expect(metadata.tableCellsValues[0][0]).toBe('Updated title'); }); + + it('reports successful fetches', async () => { + const onFetchSuccess = jest.fn(); + + act(() => { + setup({ onFetchSuccess }); + }); + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(onFetchSuccess).toHaveBeenCalled(); + }); + + it('reports the page data test subject', async () => { + const setPageDataTestSubject = jest.fn(); + + act(() => { + setup({ setPageDataTestSubject }); + }); + + expect(setPageDataTestSubject).toHaveBeenCalledWith('testLandingPage'); + }); }); diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 8e8e7fc3bca4a..7542027ce96d7 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -71,7 +71,7 @@ describe('annotation list view', () => { save: true, }} parentProps={{ - onFetchSuccess: () => {}, + onInitialFetchSuccess: () => {}, }} dataViews={[ { From 1c780eb368eaae8750dade4ffec2d2dc87e0f658 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 11:49:16 -0500 Subject: [PATCH 161/202] update test --- .../event_annotation/public/components/table_list.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 7542027ce96d7..8a1362c688668 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -71,7 +71,8 @@ describe('annotation list view', () => { save: true, }} parentProps={{ - onInitialFetchSuccess: () => {}, + onFetchSuccess: () => {}, + setPageDataTestSubject: () => {}, }} dataViews={[ { From 9999542c5b9cac065634361f991f0d6b04953398 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 11:50:12 -0500 Subject: [PATCH 162/202] fix export --- .../public/components/annotation_editor_controls/index.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx index f16f96ce8ff8c..20703e2c0803e 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx @@ -8,6 +8,8 @@ // TODO - translate these tests to work with AnnotationEditorControls component +export {}; + describe('AnnotationEditorControls', () => { it('passes', () => expect(true).toBeTruthy()); }); From c9928740334fd5f0e908967b06275a9f10ae809b Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 13:50:24 -0500 Subject: [PATCH 163/202] update snapshot --- .../event_annotation/public/components/table_list.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 8a1362c688668..8739fc9035257 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -89,7 +89,7 @@ describe('annotation list view', () => { it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot(` " - + " `); }); From f239ebe0bb3c4ffe632a228eb379676718520c0c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 13:55:48 -0500 Subject: [PATCH 164/202] reinstate test selector --- .../annotation_editor_controls/query_annotation_panel.tsx | 2 +- x-pack/test/functional/page_objects/lens_page.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx index 6039367f34216..ff0238c7980f1 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/query_annotation_panel.tsx @@ -72,9 +72,9 @@ export const ConfigPanelQueryAnnotation = ({ label={i18n.translate('eventAnnotation.xyChart.annotation.queryInput', { defaultMessage: 'Annotation query', })} - data-test-subj="annotation-query-based-query-input" > Date: Fri, 2 Jun 2023 16:40:30 -0500 Subject: [PATCH 165/202] move module location to resolve jest error --- .../{group_editor_controls => }/get_annotation_accessor.ts | 6 +++--- .../public/components/group_editor_controls/index.ts | 1 - src/plugins/event_annotation/public/components/index.ts | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) rename src/plugins/event_annotation/public/components/{group_editor_controls => }/get_annotation_accessor.ts (87%) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts b/src/plugins/event_annotation/public/components/get_annotation_accessor.ts similarity index 87% rename from src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts rename to src/plugins/event_annotation/public/components/get_annotation_accessor.ts index 59cdfe15d0814..1ece7164f2557 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts +++ b/src/plugins/event_annotation/public/components/get_annotation_accessor.ts @@ -7,13 +7,13 @@ */ import type { AccessorConfig } from '@kbn/visualization-ui-components/public'; -import type { EventAnnotationConfig } from '../../../common'; +import { EventAnnotationConfig } from '../../common'; import { defaultAnnotationColor, defaultAnnotationRangeColor, isRangeAnnotationConfig, -} from '../../event_annotation_service/helpers'; -import { annotationsIconSet } from '../annotation_editor_controls'; +} from '../event_annotation_service/helpers'; +import { annotationsIconSet } from './annotation_editor_controls/icon_set'; export const getAnnotationAccessor = (annotation: EventAnnotationConfig): AccessorConfig => { const annotationIcon = !isRangeAnnotationConfig(annotation) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/index.ts b/src/plugins/event_annotation/public/components/group_editor_controls/index.ts index 016048f7f667f..b9db18b4682f4 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/index.ts +++ b/src/plugins/event_annotation/public/components/group_editor_controls/index.ts @@ -7,4 +7,3 @@ */ export * from './group_editor_controls'; -export * from './get_annotation_accessor'; diff --git a/src/plugins/event_annotation/public/components/index.ts b/src/plugins/event_annotation/public/components/index.ts index 983a30d85db6c..3f1b16113c6bd 100644 --- a/src/plugins/event_annotation/public/components/index.ts +++ b/src/plugins/event_annotation/public/components/index.ts @@ -9,3 +9,5 @@ export * from './annotation_editor_controls'; export * from './group_editor_controls'; + +export * from './get_annotation_accessor'; From 6e27746e9bb7d0c97a8135995cb582fc2628e113 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 2 Jun 2023 16:47:56 -0500 Subject: [PATCH 166/202] make annotation editor controls lazy --- .../annotation_editor_controls.tsx | 5 ++++- .../public/components/annotation_editor_controls/index.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index f180f6c49ce55..8546b1670749d 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -46,7 +46,7 @@ import { TooltipSection } from './tooltip_annotation_panel'; import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; -export const AnnotationEditorControls = ({ +const AnnotationEditorControls = ({ annotation: currentAnnotation, onAnnotationChange, dataView, @@ -377,3 +377,6 @@ const ConfigPanelGenericSwitch = ({ /> ); + +// eslint-disable-next-line import/no-default-export +export default AnnotationEditorControls; diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts index 021339e9d6683..1ccf4675a2252 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts @@ -6,5 +6,8 @@ * Side Public License, v 1. */ -export { AnnotationEditorControls } from './annotation_editor_controls'; +import { lazy } from 'react'; + +export const AnnotationEditorControls = lazy(() => import('./annotation_editor_controls')); + export { annotationsIconSet } from './icon_set'; From 4fb329171dcd3a4c7154da36aa104d05f221281a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 5 Jun 2023 09:38:59 -0500 Subject: [PATCH 167/202] reduce bundle size --- packages/kbn-optimizer/limits.yml | 2 +- .../public/components/annotation_editor_controls/index.ts | 6 +++++- src/plugins/event_annotation/public/components/index.ts | 2 +- src/plugins/event_annotation/public/get_table_list.tsx | 2 +- src/plugins/event_annotation/public/index.ts | 6 +++++- src/plugins/event_annotation/public/plugin.ts | 7 ++++--- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index cad710f8b64ee..6ea789843f27e 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -39,7 +39,7 @@ pageLoadAssetSize: embeddableEnhanced: 22107 enterpriseSearch: 35741 esUiShared: 326654 - eventAnnotation: 22000 + eventAnnotation: 33432 exploratoryView: 74673 expressionError: 22127 expressionGauge: 25000 diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts index 1ccf4675a2252..97ce3999a74b4 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts @@ -6,8 +6,12 @@ * Side Public License, v 1. */ +import { withSuspense } from '@kbn/shared-ux-utility'; import { lazy } from 'react'; -export const AnnotationEditorControls = lazy(() => import('./annotation_editor_controls')); +export const AnnotationEditorControls = withSuspense( + lazy(() => import('./annotation_editor_controls')), + null +); export { annotationsIconSet } from './icon_set'; diff --git a/src/plugins/event_annotation/public/components/index.ts b/src/plugins/event_annotation/public/components/index.ts index 3f1b16113c6bd..5433625c6344b 100644 --- a/src/plugins/event_annotation/public/components/index.ts +++ b/src/plugins/event_annotation/public/components/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export * from './annotation_editor_controls'; +export { AnnotationEditorControls, annotationsIconSet } from './annotation_editor_controls'; export * from './group_editor_controls'; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index e0ed6080f4d6a..16ab550dfce81 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -18,8 +18,8 @@ import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plug import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; -import { EventAnnotationGroupTableList } from './components/table_list'; import { EventAnnotationServiceType } from './event_annotation_service/types'; +import { EventAnnotationGroupTableList } from './components/table_list'; export interface EventAnnotationListingPageServices { core: CoreStart; diff --git a/src/plugins/event_annotation/public/index.ts b/src/plugins/event_annotation/public/index.ts index d9a3f94298aad..1b96e39546bcb 100644 --- a/src/plugins/event_annotation/public/index.ts +++ b/src/plugins/event_annotation/public/index.ts @@ -21,4 +21,8 @@ export { isManualPointAnnotationConfig, isQueryAnnotationConfig, } from './event_annotation_service/helpers'; -export { AnnotationEditorControls, annotationsIconSet, getAnnotationAccessor } from './components'; +export { + AnnotationEditorControls, + annotationsIconSet, +} from './components/annotation_editor_controls'; +export { getAnnotationAccessor } from './components/get_annotation_accessor'; diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 4542dceda15da..358827f35f2a0 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -9,11 +9,11 @@ import type { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; -import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; +import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public/types'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { EventAnnotationService } from './event_annotation_service'; @@ -24,7 +24,7 @@ import { eventAnnotationGroup, } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; -import { EventAnnotationListingPageServices, getTableList } from './get_table_list'; +import type { EventAnnotationListingPageServices } from './get_table_list'; import { ANNOTATIONS_LISTING_VIEW_ID } from '../common/constants'; export interface EventAnnotationStartDependencies { @@ -94,6 +94,7 @@ export class EventAnnotationPlugin }, }; + const { getTableList } = await import('./get_table_list'); return getTableList(props, services); }, }); From 25d02639100b880443628b2e638fc21d2ed50ea8 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:44:57 +0000 Subject: [PATCH 168/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/event_annotation/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index e2959cd56b422..07376c4938139 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -38,7 +38,8 @@ "@kbn/kibana-react-plugin", "@kbn/core-lifecycle-browser", "@kbn/kibana-utils-plugin", - "@kbn/unified-search-plugin" + "@kbn/unified-search-plugin", + "@kbn/shared-ux-utility" ], "exclude": [ "target/**/*", From 7ad9adaa3020183724c94d83aff75770f0b763d9 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 5 Jun 2023 11:16:54 -0500 Subject: [PATCH 169/202] fix lazy loading --- .../annotation_editor_controls.tsx | 18 ++++++----- .../{index.ts => index.tsx} | 13 +++++--- .../get_annotation_accessor.ts | 32 +++++++++++++++++++ 3 files changed, 50 insertions(+), 13 deletions(-) rename src/plugins/event_annotation/public/components/annotation_editor_controls/{index.ts => index.tsx} (54%) create mode 100644 src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index 8546b1670749d..d36d7a310b1b1 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -46,6 +46,15 @@ import { TooltipSection } from './tooltip_annotation_panel'; import { ConfigPanelManualAnnotation } from './manual_annotation_panel'; import { ConfigPanelQueryAnnotation } from './query_annotation_panel'; +export interface Props { + annotation: EventAnnotationConfig; + onAnnotationChange: (annotation: EventAnnotationConfig) => void; + dataView: DataView; + getDefaultRangeEnd: (rangeStart: string) => string; + calendarClassName?: string; + queryInputServices: QueryInputServices; +} + const AnnotationEditorControls = ({ annotation: currentAnnotation, onAnnotationChange, @@ -53,14 +62,7 @@ const AnnotationEditorControls = ({ getDefaultRangeEnd, calendarClassName, queryInputServices, -}: { - annotation: EventAnnotationConfig; - onAnnotationChange: (annotation: EventAnnotationConfig) => void; - dataView: DataView; - getDefaultRangeEnd: (rangeStart: string) => string; - calendarClassName?: string; - queryInputServices: QueryInputServices; -}) => { +}: Props) => { const { hasFieldData } = useExistingFieldsReader(); const isQueryBased = isQueryAnnotationConfig(currentAnnotation); diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx similarity index 54% rename from src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts rename to src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx index 97ce3999a74b4..9da375cc584ca 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.ts +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.tsx @@ -6,12 +6,15 @@ * Side Public License, v 1. */ -import { withSuspense } from '@kbn/shared-ux-utility'; -import { lazy } from 'react'; +import React, { Suspense, lazy } from 'react'; +import type { Props } from './annotation_editor_controls'; -export const AnnotationEditorControls = withSuspense( - lazy(() => import('./annotation_editor_controls')), - null +const AnnotationEditorControlsLazy = lazy(() => import('./annotation_editor_controls')); + +export const AnnotationEditorControls = (props: Props) => ( + + + ); export { annotationsIconSet } from './icon_set'; diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts b/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.ts new file mode 100644 index 0000000000000..59cdfe15d0814 --- /dev/null +++ b/src/plugins/event_annotation/public/components/group_editor_controls/get_annotation_accessor.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 type { AccessorConfig } from '@kbn/visualization-ui-components/public'; +import type { EventAnnotationConfig } from '../../../common'; +import { + defaultAnnotationColor, + defaultAnnotationRangeColor, + isRangeAnnotationConfig, +} from '../../event_annotation_service/helpers'; +import { annotationsIconSet } from '../annotation_editor_controls'; + +export const getAnnotationAccessor = (annotation: EventAnnotationConfig): AccessorConfig => { + const annotationIcon = !isRangeAnnotationConfig(annotation) + ? annotationsIconSet.find((option) => option.value === annotation?.icon) || + annotationsIconSet.find((option) => option.value === 'triangle') + : undefined; + const icon = annotationIcon?.icon ?? annotationIcon?.value; + return { + columnId: annotation.id, + triggerIconType: annotation.isHidden ? 'invisible' : icon ? 'custom' : 'color', + customIcon: icon, + color: + annotation?.color || + (isRangeAnnotationConfig(annotation) ? defaultAnnotationRangeColor : defaultAnnotationColor), + }; +}; From 2264aacc040ccf6acda3ee53dcfa2e7f6bb27281 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 5 Jun 2023 16:25:26 +0000 Subject: [PATCH 170/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/event_annotation/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 07376c4938139..570b3b934bc7a 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -39,7 +39,6 @@ "@kbn/core-lifecycle-browser", "@kbn/kibana-utils-plugin", "@kbn/unified-search-plugin", - "@kbn/shared-ux-utility" ], "exclude": [ "target/**/*", From f368da4f9acc26d5cc38de0da699b559fe4a9786 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 09:42:07 -0500 Subject: [PATCH 171/202] translate string --- src/plugins/event_annotation/public/plugin.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index 358827f35f2a0..7fdb907c2db10 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -16,6 +16,7 @@ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public/t import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { i18n } from '@kbn/i18n'; import { EventAnnotationService } from './event_annotation_service'; import { manualPointEventAnnotation, @@ -62,7 +63,9 @@ export class EventAnnotationPlugin ); dependencies.visualizations.listingViewRegistry.add({ - title: 'Annotation Groups', + title: i18n.translate('eventAnnotation.listingViewTitle', { + defaultMessage: 'Annotation Groups', + }), id: ANNOTATIONS_LISTING_VIEW_ID, getTableList: async (props) => { const [coreStart, pluginsStart] = await core.getStartServices(); From f1187a74f8e0176c6db3f8a71934d104e670765b Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 09:43:55 -0500 Subject: [PATCH 172/202] clean up comment --- .../annotation_editor_controls/manual_annotation_panel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx index 431bdbc3e16f7..8ddac6c6173c4 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/manual_annotation_panel.tsx @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React from 'react'; From a69f659ac240ead057e856ba104dc8c283499d85 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 09:44:20 -0500 Subject: [PATCH 173/202] hide data view picker --- .../group_editor_controls.test.tsx | 45 +++---------- .../group_editor_controls.tsx | 67 ++++++++++--------- 2 files changed, 46 insertions(+), 66 deletions(-) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index d46895f54814e..d887d03d2bceb 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -11,7 +11,7 @@ import { EventAnnotationGroupConfig, getDefaultManualAnnotation } from '../../.. import { ReactWrapper } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { GroupEditorControls } from './group_editor_controls'; -import { EuiSelectProps, EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; +import { EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { act } from 'react-dom/test-utils'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; @@ -109,15 +109,17 @@ describe('event annotation group editor', () => { wrapper.find(TagSelector).prop('onTagsSelected')(['im a new tag!']); }); - const setDataViewId = (id: string) => - ( - wrapper.find( - "EuiSelect[data-test-subj='annotationDataViewSelection']" - ) as ReactWrapper - ).prop('onChange')!({ target: { value: id } } as React.ChangeEvent); + // TODO - reenable data view selection tests when ENABLE_INDIVIDUAL_ANNOTATION_EDITING is set to true! - setDataViewId(dataViewId); - setDataViewId(adHocDataViewId); + // const setDataViewId = (id: string) => + // ( + // wrapper.find( + // "EuiSelect[data-test-subj='annotationDataViewSelection']" + // ) as ReactWrapper + // ).prop('onChange')!({ target: { value: id } } as React.ChangeEvent); + + // setDataViewId(dataViewId); + // setDataViewId(adHocDataViewId); expect(updateMock.mock.calls).toMatchInlineSnapshot(` Array [ @@ -165,31 +167,6 @@ describe('event annotation group editor', () => { "title": "My group", }, ], - Array [ - Object { - "annotations": Array [], - "dataViewSpec": undefined, - "description": "", - "ignoreGlobalFilters": false, - "indexPatternId": "my-index-pattern", - "tags": Array [], - "title": "My group", - }, - ], - Array [ - Object { - "annotations": Array [], - "dataViewSpec": Object { - "id": "ad-hoc", - "title": "Ad Hoc Data View", - }, - "description": "", - "ignoreGlobalFilters": false, - "indexPatternId": "ad-hoc", - "tags": Array [], - "title": "My group", - }, - ], ] `); }); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index b08fa84380f4b..ca2615391e8e9 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -131,39 +131,42 @@ export const GroupEditorControls = ({ } /> - - ({ - value, - text: name ?? title, - }))} - value={group.indexPatternId} - onChange={({ target: { value } }) => - update({ - ...group, - indexPatternId: value, - dataViewSpec: value === adHocDataView?.id ? adHocDataView.toSpec(false) : undefined, - }) - } - /> - {ENABLE_INDIVIDUAL_ANNOTATION_EDITING && ( - - update({ ...group, annotations: newAnnotations })} - /> - + <> + + ({ + value, + text: name ?? title, + }))} + value={group.indexPatternId} + onChange={({ target: { value } }) => + update({ + ...group, + indexPatternId: value, + dataViewSpec: + value === adHocDataView?.id ? adHocDataView.toSpec(false) : undefined, + }) + } + /> + + + update({ ...group, annotations: newAnnotations })} + /> + + )} From b5471b32d0d1ce036606f549701a72b52ffe47e3 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 09:44:44 -0500 Subject: [PATCH 174/202] mark type imports --- src/plugins/event_annotation/public/get_table_list.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index 16ab550dfce81..24d0ab96979e9 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -10,15 +10,15 @@ import React, { FC } from 'react'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { FormattedRelative } from '@kbn/i18n-react'; import { - TableListTabParentProps, + type TableListTabParentProps, TableListViewKibanaProvider, } from '@kbn/content-management-table-list'; import { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; -import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; -import { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; -import { EventAnnotationServiceType } from './event_annotation_service/types'; +import type { EventAnnotationServiceType } from './event_annotation_service/types'; import { EventAnnotationGroupTableList } from './components/table_list'; export interface EventAnnotationListingPageServices { From 3feea0a4d62df274473225199a2da67357b62b33 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 10:46:38 -0500 Subject: [PATCH 175/202] use app name --- src/plugins/event_annotation/common/constants.ts | 2 ++ .../annotation_editor_controls.tsx | 10 ++++++++-- .../query_annotation_panel.tsx | 4 +++- .../tooltip_annotation_panel.tsx | 2 +- .../group_editor_controls.test.tsx | 1 + .../group_editor_controls/group_editor_controls.tsx | 2 ++ x-pack/plugins/lens/common/constants.ts | 6 +----- x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx | 4 ++-- .../annotations_config_panel/annotations_panel.tsx | 2 ++ 9 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts index 501aaea843b38..08398d53119cc 100644 --- a/src/plugins/event_annotation/common/constants.ts +++ b/src/plugins/event_annotation/common/constants.ts @@ -27,3 +27,5 @@ export const AvailableAnnotationIcons = { export const EVENT_ANNOTATION_GROUP_TYPE = 'event-annotation-group'; export const ANNOTATIONS_LISTING_VIEW_ID = 'annotations'; + +export const EVENT_ANNOTATION_APP_NAME = 'event-annotations'; diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index d36d7a310b1b1..00052f3bdf20a 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -26,6 +26,7 @@ import type { FieldOptionValue } from '@kbn/visualization-ui-components/public'; import { DataView } from '@kbn/data-views-plugin/common'; import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; import moment from 'moment'; +import { htmlIdGenerator } from '@elastic/eui/src/services/accessibility/html_id_generator'; import { isQueryAnnotationConfig, isRangeAnnotationConfig } from '../..'; import { AvailableAnnotationIcon, @@ -53,8 +54,11 @@ export interface Props { getDefaultRangeEnd: (rangeStart: string) => string; calendarClassName?: string; queryInputServices: QueryInputServices; + appName: string; } +export const idPrefix = htmlIdGenerator()(); + const AnnotationEditorControls = ({ annotation: currentAnnotation, onAnnotationChange, @@ -62,6 +66,7 @@ const AnnotationEditorControls = ({ getDefaultRangeEnd, calendarClassName, queryInputServices, + appName, }: Props) => { const { hasFieldData } = useExistingFieldsReader(); @@ -166,6 +171,7 @@ const AnnotationEditorControls = ({ dataView={dataView} queryInputShouldOpen={queryInputShouldOpen} queryInputServices={queryInputServices} + appName={appName} /> ) : ( | undefined) => void; dataView: DataView; queryInputShouldOpen?: boolean; queryInputServices: QueryInputServices; + appName: string; }) => { const { hasFieldData } = useExistingFieldsReader(); // list only date fields @@ -82,7 +84,7 @@ export const ConfigPanelQueryAnnotation = ({ onChange({ filter: { type: 'kibana_query', ...query } }); }} dataView={dataView} - appName="TODO" + appName={appName} queryInputServices={queryInputServices} /> diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx index fad49f3314d13..e0e290e611a01 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/tooltip_annotation_panel.tsx @@ -141,7 +141,7 @@ export function TooltipSection({ field: field.name, dataType: field.type, }, - exists: dataView.id ? hasFieldData(dataView.id, field.name) : false, // TODO check this logic + exists: dataView.id ? hasFieldData(dataView.id, field.name) : false, compatible: true, 'data-test-subj': `lnsXY-annotation-tooltip-fieldOption-${field.name}`, } as FieldOption) diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index d887d03d2bceb..80e9a9008ff69 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -110,6 +110,7 @@ describe('event annotation group editor', () => { }); // TODO - reenable data view selection tests when ENABLE_INDIVIDUAL_ANNOTATION_EDITING is set to true! + // this will happen in https://github.com/elastic/kibana/issues/158774 // const setDataViewId = (id: string) => // ( diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index ca2615391e8e9..39f62b4a5fc99 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -15,6 +15,7 @@ import { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-os import { euiThemeVars } from '@kbn/ui-theme'; import { QueryInputServices } from '@kbn/visualization-ui-components/public'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { EVENT_ANNOTATION_APP_NAME } from '../../../common/constants'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../../common'; import { AnnotationEditorControls } from '../annotation_editor_controls'; import { AnnotationList } from './annotation_list'; @@ -177,6 +178,7 @@ export const GroupEditorControls = ({ dataView={currentDataView} getDefaultRangeEnd={(rangeStart) => rangeStart} queryInputServices={queryInputServices} + appName={EVENT_ANNOTATION_APP_NAME} /> ); }; diff --git a/x-pack/plugins/lens/common/constants.ts b/x-pack/plugins/lens/common/constants.ts index 8838b974ac4dc..5909dbcb48f4b 100644 --- a/x-pack/plugins/lens/common/constants.ts +++ b/x-pack/plugins/lens/common/constants.ts @@ -8,10 +8,10 @@ import rison from '@kbn/rison'; import type { RefreshInterval, TimeRange } from '@kbn/data-plugin/common/query'; import type { Filter } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; export const PLUGIN_ID = 'lens'; export const APP_ID = 'lens'; +export const LENS_APP_NAME = 'lens'; export const LENS_EMBEDDABLE_TYPE = 'lens'; export const DOC_TYPE = 'lens'; export const NOT_INTERNATIONALIZED_PRODUCT_NAME = 'Lens Visualizations'; @@ -89,7 +89,3 @@ export function getEditPath( export function getFullPath(id?: string) { return `/app/${PLUGIN_ID}${id ? getEditPath(id) : getBasePath()}`; } - -export const LENS_APP_NAME = i18n.translate('xpack.lens.queryInput.appName', { - defaultMessage: 'Lens', -}); 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 41b0fa567d567..ca5efceaf48bd 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 @@ -17,7 +17,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; import moment from 'moment'; import { LENS_APP_LOCATOR } from '../../common/locator/locator'; -import { ENABLE_SQL } from '../../common/constants'; +import { ENABLE_SQL, LENS_APP_NAME } from '../../common/constants'; import { LensAppServices, LensTopNavActions, LensTopNavMenuProps } from './types'; import { toggleSettingsMenuOpen } from './settings_menu'; import { @@ -1096,7 +1096,7 @@ export const LensTopNavMenu = ({ showFilterBar={true} data-test-subj="lnsApp_topNav" screenTitle={'lens'} - appName={'lens'} + appName={LENS_APP_NAME} displayStyle="detached" className="hide-for-sharing" /> diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx index 35fa11957bf37..19bbcc7f4bfc8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -14,6 +14,7 @@ import { useDebouncedValue } from '@kbn/visualization-ui-components/public'; import { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public'; import moment from 'moment'; import { search } from '@kbn/data-plugin/public'; +import { LENS_APP_NAME } from '../../../../../common/constants'; import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../../utils'; import { LensAppServices } from '../../../../app_plugin/types'; import { updateLayer } from '..'; @@ -100,6 +101,7 @@ export const AnnotationsPanel = ( } queryInputServices={queryInputServices} calendarClassName={DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS} + appName={LENS_APP_NAME} /> ) : null; }; From 8a2d3cd95bd273a9ae14c64a9a0b265d9248f2fa Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 11:13:16 -0500 Subject: [PATCH 176/202] fix import --- .../annotation_editor_controls/annotation_editor_controls.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx index 00052f3bdf20a..e88aa0d72558a 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -26,7 +26,7 @@ import type { FieldOptionValue } from '@kbn/visualization-ui-components/public'; import { DataView } from '@kbn/data-views-plugin/common'; import { useExistingFieldsReader } from '@kbn/unified-field-list-plugin/public'; import moment from 'moment'; -import { htmlIdGenerator } from '@elastic/eui/src/services/accessibility/html_id_generator'; +import { htmlIdGenerator } from '@elastic/eui'; import { isQueryAnnotationConfig, isRangeAnnotationConfig } from '../..'; import { AvailableAnnotationIcon, From 3e98134d90669cb14e324bc8de83fcc26e923545 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 11:20:58 -0500 Subject: [PATCH 177/202] update limit --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 7bcb88951b8fd..96509459186b3 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -40,7 +40,7 @@ pageLoadAssetSize: enterpriseSearch: 35741 essSecurity: 16573 esUiShared: 326654 - eventAnnotation: 33432 + eventAnnotation: 48565 exploratoryView: 74673 expressionError: 22127 expressionGauge: 25000 From 03787d79a3b7b876e67afd5dc88f3559b9045d53 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 6 Jun 2023 18:22:40 -0500 Subject: [PATCH 178/202] remove unused translations --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 3 files changed, 3 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index da3a1332b194d..b5d5bd3c1af82 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21061,7 +21061,6 @@ "xpack.lens.pieChart.visualOptionsLabel": "Options visuelles", "xpack.lens.primaryMetric.headingLabel": "Valeur", "xpack.lens.primaryMetric.label": "Indicateur principal", - "xpack.lens.queryInput.appName": "Lens", "xpack.lens.reporting.shareContextMenu.csvReportsButtonLabel": "Téléchargement CSV", "xpack.lens.resetLayerAriaLabel": "Effacer le calque", "xpack.lens.saveDuplicateRejectedDescription": "La confirmation d'enregistrement avec un doublon de titre a été rejetée.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ed7df4d357a20..2fb123642a20b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21061,7 +21061,6 @@ "xpack.lens.pieChart.visualOptionsLabel": "視覚オプション", "xpack.lens.primaryMetric.headingLabel": "値", "xpack.lens.primaryMetric.label": "主メトリック", - "xpack.lens.queryInput.appName": "レンズ", "xpack.lens.reporting.shareContextMenu.csvReportsButtonLabel": "CSVダウンロード", "xpack.lens.resetLayerAriaLabel": "レイヤーをクリア", "xpack.lens.saveDuplicateRejectedDescription": "重複ファイルの保存確認が拒否されました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 97af6530bb89b..2fd1e189fa962 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21061,7 +21061,6 @@ "xpack.lens.pieChart.visualOptionsLabel": "视觉选项", "xpack.lens.primaryMetric.headingLabel": "值", "xpack.lens.primaryMetric.label": "主要指标", - "xpack.lens.queryInput.appName": "Lens", "xpack.lens.reporting.shareContextMenu.csvReportsButtonLabel": "CSV 下载", "xpack.lens.resetLayerAriaLabel": "清除图层", "xpack.lens.saveDuplicateRejectedDescription": "已拒绝使用重复标题保存确认", From c6f0e6c9fb4c5f3f62de843efa60fd58bf1bbaac Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 7 Jun 2023 09:16:58 -0500 Subject: [PATCH 179/202] change to type import --- src/plugins/event_annotation/public/get_table_list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index 24d0ab96979e9..f2265c3c0bad1 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -13,7 +13,7 @@ import { type TableListTabParentProps, TableListViewKibanaProvider, } from '@kbn/content-management-table-list'; -import { CoreStart } from '@kbn/core-lifecycle-browser'; +import type { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; From b89454b0543f90bf407ca2682f9b672523eb2b0a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 7 Jun 2023 12:05:37 -0500 Subject: [PATCH 180/202] move TableListView to own package --- .../public/examples/msearch/msearch_app.tsx | 2 +- .../public/examples/msearch/msearch_table.tsx | 2 +- package.json | 3 +- .../README.mdx | 0 .../table_list_view/index.ts | 11 + .../jest.config.js | 2 +- .../kibana.jsonc | 2 +- .../package.json | 4 +- .../src/table_list_view.stories.tsx | 14 +- .../table_list_view/src/table_list_view.tsx | 131 +++ .../tsconfig.json | 0 .../table_list_view_table/README.mdx | 20 + .../index.ts | 4 +- .../table_list_view_table/jest.config.js | 13 + .../table_list_view_table/kibana.jsonc | 5 + .../table_list_view_table/package.json | 6 + .../src/__jest__/index.ts | 0 .../src/__jest__/tests.helpers.tsx | 0 .../src/actions.ts | 2 +- .../src/components/confirm_delete_modal.tsx | 0 .../src/components/index.ts | 0 .../src/components/item_details.tsx | 2 +- .../src/components/listing_limit_warning.tsx | 0 .../src/components/table.tsx | 2 +- .../src/components/table_sort_select.tsx | 2 +- .../src/components/tag_badge.tsx | 0 .../src/components/tag_filter_panel.tsx | 0 .../src/components/updated_at_field.tsx | 0 .../src/components/use_tag_filter_panel.tsx | 0 .../src/constants.ts | 0 .../src/index.ts | 6 +- .../src/mocks.tsx | 0 .../src/reducer.tsx | 2 +- .../src/services.tsx | 0 .../src/tabbed_table_list_view.test.tsx | 0 .../src/tabbed_table_list_view.tsx | 2 +- .../src/table_list_view.test.tsx | 2 +- .../src/table_list_view_table.tsx} | 141 +--- .../src/types.ts | 0 .../src/use_tags.ts | 2 +- .../src/use_url_state.ts | 0 .../table_list_view_table/tsconfig.json | 32 + .../dashboard_listing.test.tsx | 8 +- .../dashboard_listing/dashboard_listing.tsx | 6 +- src/plugins/event_annotation/common/types.ts | 2 +- .../public/components/table_list.test.tsx | 29 +- .../public/components/table_list.tsx | 7 +- .../public/get_table_list.tsx | 2 +- src/plugins/files_management/public/app.tsx | 2 +- .../public/mount_management_section.tsx | 2 +- src/plugins/files_management/tsconfig.json | 3 +- src/plugins/visualizations/public/types.ts | 2 +- .../components/visualize_listing.tsx | 795 +++++++++--------- .../public/visualize_app/index.tsx | 2 +- tsconfig.base.json | 6 +- x-pack/plugins/graph/public/application.tsx | 2 +- .../graph/public/apps/listing_route.tsx | 4 +- x-pack/plugins/maps/public/render_app.tsx | 2 +- .../routes/list_page/maps_list_view.tsx | 4 +- yarn.lock | 6 +- 60 files changed, 714 insertions(+), 584 deletions(-) rename packages/content-management/{table_list => table_list_view}/README.mdx (100%) create mode 100644 packages/content-management/table_list_view/index.ts rename packages/content-management/{table_list => table_list_view}/jest.config.js (86%) rename packages/content-management/{table_list => table_list_view}/kibana.jsonc (57%) rename packages/content-management/{table_list => table_list_view}/package.json (62%) rename packages/content-management/{table_list => table_list_view}/src/table_list_view.stories.tsx (90%) create mode 100644 packages/content-management/table_list_view/src/table_list_view.tsx rename packages/content-management/{table_list => table_list_view}/tsconfig.json (100%) create mode 100644 packages/content-management/table_list_view_table/README.mdx rename packages/content-management/{table_list => table_list_view_table}/index.ts (92%) create mode 100644 packages/content-management/table_list_view_table/jest.config.js create mode 100644 packages/content-management/table_list_view_table/kibana.jsonc create mode 100644 packages/content-management/table_list_view_table/package.json rename packages/content-management/{table_list => table_list_view_table}/src/__jest__/index.ts (100%) rename packages/content-management/{table_list => table_list_view_table}/src/__jest__/tests.helpers.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/actions.ts (99%) rename packages/content-management/{table_list => table_list_view_table}/src/components/confirm_delete_modal.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/components/index.ts (100%) rename packages/content-management/{table_list => table_list_view_table}/src/components/item_details.tsx (99%) rename packages/content-management/{table_list => table_list_view_table}/src/components/listing_limit_warning.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/components/table.tsx (99%) rename packages/content-management/{table_list => table_list_view_table}/src/components/table_sort_select.tsx (98%) rename packages/content-management/{table_list => table_list_view_table}/src/components/tag_badge.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/components/tag_filter_panel.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/components/updated_at_field.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/components/use_tag_filter_panel.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/constants.ts (100%) rename packages/content-management/{table_list => table_list_view_table}/src/index.ts (84%) rename packages/content-management/{table_list => table_list_view_table}/src/mocks.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/reducer.tsx (99%) rename packages/content-management/{table_list => table_list_view_table}/src/services.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/tabbed_table_list_view.test.tsx (100%) rename packages/content-management/{table_list => table_list_view_table}/src/tabbed_table_list_view.tsx (98%) rename packages/content-management/{table_list => table_list_view_table}/src/table_list_view.test.tsx (99%) rename packages/content-management/{table_list/src/table_list_view.tsx => table_list_view_table/src/table_list_view_table.tsx} (88%) rename packages/content-management/{table_list => table_list_view_table}/src/types.ts (100%) rename packages/content-management/{table_list => table_list_view_table}/src/use_tags.ts (98%) rename packages/content-management/{table_list => table_list_view_table}/src/use_url_state.ts (100%) create mode 100644 packages/content-management/table_list_view_table/tsconfig.json diff --git a/examples/content_management_examples/public/examples/msearch/msearch_app.tsx b/examples/content_management_examples/public/examples/msearch/msearch_app.tsx index aa2bc6c1a8b7b..ae05e15db91d0 100644 --- a/examples/content_management_examples/public/examples/msearch/msearch_app.tsx +++ b/examples/content_management_examples/public/examples/msearch/msearch_app.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ContentClientProvider, type ContentClient } from '@kbn/content-management-plugin/public'; -import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view-table'; import type { CoreStart } from '@kbn/core/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; diff --git a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx index 71f2844e4fd29..a30c652cf0f79 100644 --- a/examples/content_management_examples/public/examples/msearch/msearch_table.tsx +++ b/examples/content_management_examples/public/examples/msearch/msearch_table.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list-view'; import { useContentClient } from '@kbn/content-management-plugin/public'; import React from 'react'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; diff --git a/package.json b/package.json index c04ae6738f64d..55fb532280a23 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,8 @@ "@kbn/content-management-content-editor": "link:packages/content-management/content_editor", "@kbn/content-management-examples-plugin": "link:examples/content_management_examples", "@kbn/content-management-plugin": "link:src/plugins/content_management", - "@kbn/content-management-table-list": "link:packages/content-management/table_list", + "@kbn/content-management-table-list-view": "link:packages/content-management/table_list_view", + "@kbn/content-management-table-list-view-table": "link:packages/content-management/table_list_view_table", "@kbn/content-management-utils": "link:packages/kbn-content-management-utils", "@kbn/controls-example-plugin": "link:examples/controls_example", "@kbn/controls-plugin": "link:src/plugins/controls", diff --git a/packages/content-management/table_list/README.mdx b/packages/content-management/table_list_view/README.mdx similarity index 100% rename from packages/content-management/table_list/README.mdx rename to packages/content-management/table_list_view/README.mdx diff --git a/packages/content-management/table_list_view/index.ts b/packages/content-management/table_list_view/index.ts new file mode 100644 index 0000000000000..e40f9356e84b0 --- /dev/null +++ b/packages/content-management/table_list_view/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. + */ + +export { TableListView } from './src/table_list_view'; + +export type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-table'; diff --git a/packages/content-management/table_list/jest.config.js b/packages/content-management/table_list_view/jest.config.js similarity index 86% rename from packages/content-management/table_list/jest.config.js rename to packages/content-management/table_list_view/jest.config.js index 546d16dd86cf0..aab014b44dcb8 100644 --- a/packages/content-management/table_list/jest.config.js +++ b/packages/content-management/table_list_view/jest.config.js @@ -9,5 +9,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../../..', - roots: ['/packages/content-management/table_list'], + roots: ['/packages/content-management/table_list_view'], }; diff --git a/packages/content-management/table_list/kibana.jsonc b/packages/content-management/table_list_view/kibana.jsonc similarity index 57% rename from packages/content-management/table_list/kibana.jsonc rename to packages/content-management/table_list_view/kibana.jsonc index 2e4dd9548f604..a9c3a12553ff4 100644 --- a/packages/content-management/table_list/kibana.jsonc +++ b/packages/content-management/table_list_view/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", - "id": "@kbn/content-management-table-list", + "id": "@kbn/content-management-table-list-view", "owner": "@elastic/appex-sharedux" } diff --git a/packages/content-management/table_list/package.json b/packages/content-management/table_list_view/package.json similarity index 62% rename from packages/content-management/table_list/package.json rename to packages/content-management/table_list_view/package.json index b387c8a466b5e..1a3dbce9e195c 100644 --- a/packages/content-management/table_list/package.json +++ b/packages/content-management/table_list_view/package.json @@ -1,6 +1,6 @@ { - "name": "@kbn/content-management-table-list", + "name": "@kbn/content-management-table-list-view", "private": true, "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0" -} \ No newline at end of file +} diff --git a/packages/content-management/table_list/src/table_list_view.stories.tsx b/packages/content-management/table_list_view/src/table_list_view.stories.tsx similarity index 90% rename from packages/content-management/table_list/src/table_list_view.stories.tsx rename to packages/content-management/table_list_view/src/table_list_view.stories.tsx index 4943c9d0be657..878e5413b6774 100644 --- a/packages/content-management/table_list/src/table_list_view.stories.tsx +++ b/packages/content-management/table_list_view/src/table_list_view.stories.tsx @@ -11,10 +11,16 @@ import Chance from 'chance'; import moment from 'moment'; import { action } from '@storybook/addon-actions'; -import { Params, getStoryArgTypes, getStoryServices } from './mocks'; - -import { TableListView as Component, UserContentCommonSchema } from './table_list_view'; -import { TableListViewProvider } from './services'; +import { + TableListViewProvider, + UserContentCommonSchema, +} from '@kbn/content-management-table-list-view-table'; +import { + Params, + getStoryArgTypes, + getStoryServices, +} from '@kbn/content-management-table-list-view-table/src/mocks'; +import { TableListView as Component } from './table_list_view'; import mdx from '../README.mdx'; diff --git a/packages/content-management/table_list_view/src/table_list_view.tsx b/packages/content-management/table_list_view/src/table_list_view.tsx new file mode 100644 index 0000000000000..59fbe67a4a4c5 --- /dev/null +++ b/packages/content-management/table_list_view/src/table_list_view.tsx @@ -0,0 +1,131 @@ +/* + * 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 { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import React, { ReactNode, useState } from 'react'; +import { + TableListViewTable, + type TableListViewTableProps, + type UserContentCommonSchema, +} from '@kbn/content-management-table-list-view-table'; + +export type TableListViewProps = Pick< + TableListViewTableProps, + | 'entityName' + | 'entityNamePlural' + | 'initialFilter' + | 'headingId' + | 'initialPageSize' + | 'listingLimit' + | 'urlStateEnabled' + | 'customTableColumn' + | 'emptyPrompt' + | 'findItems' + | 'createItem' + | 'editItem' + | 'deleteItems' + | 'getDetailViewLink' + | 'onClickTitle' + | 'id' + | 'rowItemActions' + | 'contentEditor' + | 'titleColumnName' + | 'withoutPageTemplateWrapper' + | 'showEditActionForItem' +> & { + title: string; + description?: string; + /** + * Additional actions (buttons) to be placed in the page header. + * @note only the first two values will be used. + */ + additionalRightSideActions?: ReactNode[]; + children?: ReactNode | undefined; +}; + +export const TableListView = ({ + title, + description, + entityName, + entityNamePlural, + initialFilter, + headingId, + initialPageSize, + listingLimit, + urlStateEnabled = true, + customTableColumn, + emptyPrompt, + findItems, + createItem, + editItem, + deleteItems, + getDetailViewLink, + onClickTitle, + rowItemActions, + id: listingId, + contentEditor, + children, + titleColumnName, + additionalRightSideActions, + withoutPageTemplateWrapper, +}: TableListViewProps) => { + const PageTemplate = withoutPageTemplateWrapper + ? (React.Fragment as unknown as typeof KibanaPageTemplate) + : KibanaPageTemplate; + + const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); + const [pageDataTestSubject, setPageDataTestSubject] = useState(); + + return ( + + {title}} + description={description} + rightSideItems={additionalRightSideActions?.slice(0, 2)} + data-test-subj="top-nav" + /> + + {/* Any children passed to the component */} + {children} + + { + if (!hasInitialFetchReturned) { + setHasInitialFetchReturned(true); + } + }} + setPageDataTestSubject={setPageDataTestSubject} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default TableListView; diff --git a/packages/content-management/table_list/tsconfig.json b/packages/content-management/table_list_view/tsconfig.json similarity index 100% rename from packages/content-management/table_list/tsconfig.json rename to packages/content-management/table_list_view/tsconfig.json diff --git a/packages/content-management/table_list_view_table/README.mdx b/packages/content-management/table_list_view_table/README.mdx new file mode 100644 index 0000000000000..df3fdc9db53e9 --- /dev/null +++ b/packages/content-management/table_list_view_table/README.mdx @@ -0,0 +1,20 @@ +--- +id: sharedUX/contentManagement/TableListViewTable +slug: /shared-ux/content-management/table-list-view-table +title: Table list view +summary: A table to render user generated saved objects. +tags: ['shared-ux', 'content-management'] +date: 2022-08-09 +--- + +The `` renders a list of user content saved object. + +**Uncomplete documentation**. Will be updated. + +## API + +TODO: https://github.com/elastic/kibana/issues/144402 + +## EUI Promotion Status + +This component is not currently considered for promotion to EUI. diff --git a/packages/content-management/table_list/index.ts b/packages/content-management/table_list_view_table/index.ts similarity index 92% rename from packages/content-management/table_list/index.ts rename to packages/content-management/table_list_view_table/index.ts index da29e61111535..66abaeb022bf3 100644 --- a/packages/content-management/table_list/index.ts +++ b/packages/content-management/table_list_view_table/index.ts @@ -7,15 +7,15 @@ */ export { - TableListView, TabbedTableListView, - TableList, + TableListViewTable, TableListViewProvider, TableListViewKibanaProvider, } from './src'; export type { UserContentCommonSchema, + TableListViewTableProps, TableListTab, TableListTabParentProps, RowActions, diff --git a/packages/content-management/table_list_view_table/jest.config.js b/packages/content-management/table_list_view_table/jest.config.js new file mode 100644 index 0000000000000..6f81c7abbaf3c --- /dev/null +++ b/packages/content-management/table_list_view_table/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/content-management/table_list_view_table'], +}; diff --git a/packages/content-management/table_list_view_table/kibana.jsonc b/packages/content-management/table_list_view_table/kibana.jsonc new file mode 100644 index 0000000000000..85861c453bffb --- /dev/null +++ b/packages/content-management/table_list_view_table/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/content-management-table-list-view-table", + "owner": "@elastic/appex-sharedux" +} diff --git a/packages/content-management/table_list_view_table/package.json b/packages/content-management/table_list_view_table/package.json new file mode 100644 index 0000000000000..b9d5dcf7a03f2 --- /dev/null +++ b/packages/content-management/table_list_view_table/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/content-management-table-list-view-table", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/content-management/table_list/src/__jest__/index.ts b/packages/content-management/table_list_view_table/src/__jest__/index.ts similarity index 100% rename from packages/content-management/table_list/src/__jest__/index.ts rename to packages/content-management/table_list_view_table/src/__jest__/index.ts diff --git a/packages/content-management/table_list/src/__jest__/tests.helpers.tsx b/packages/content-management/table_list_view_table/src/__jest__/tests.helpers.tsx similarity index 100% rename from packages/content-management/table_list/src/__jest__/tests.helpers.tsx rename to packages/content-management/table_list_view_table/src/__jest__/tests.helpers.tsx diff --git a/packages/content-management/table_list/src/actions.ts b/packages/content-management/table_list_view_table/src/actions.ts similarity index 99% rename from packages/content-management/table_list/src/actions.ts rename to packages/content-management/table_list_view_table/src/actions.ts index 6ae2740381fcc..0a18c74a571aa 100644 --- a/packages/content-management/table_list/src/actions.ts +++ b/packages/content-management/table_list_view_table/src/actions.ts @@ -8,7 +8,7 @@ import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { Query } from '@elastic/eui'; -import type { State, UserContentCommonSchema } from './table_list_view'; +import type { State, UserContentCommonSchema } from './table_list_view_table'; /** Action to trigger a fetch of the table items */ export interface OnFetchItemsAction { diff --git a/packages/content-management/table_list/src/components/confirm_delete_modal.tsx b/packages/content-management/table_list_view_table/src/components/confirm_delete_modal.tsx similarity index 100% rename from packages/content-management/table_list/src/components/confirm_delete_modal.tsx rename to packages/content-management/table_list_view_table/src/components/confirm_delete_modal.tsx diff --git a/packages/content-management/table_list/src/components/index.ts b/packages/content-management/table_list_view_table/src/components/index.ts similarity index 100% rename from packages/content-management/table_list/src/components/index.ts rename to packages/content-management/table_list_view_table/src/components/index.ts diff --git a/packages/content-management/table_list/src/components/item_details.tsx b/packages/content-management/table_list_view_table/src/components/item_details.tsx similarity index 99% rename from packages/content-management/table_list/src/components/item_details.tsx rename to packages/content-management/table_list_view_table/src/components/item_details.tsx index e0e595e1a2d70..653e2b32af6b7 100644 --- a/packages/content-management/table_list/src/components/item_details.tsx +++ b/packages/content-management/table_list_view_table/src/components/item_details.tsx @@ -12,7 +12,7 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import type { Tag } from '../types'; import { useServices } from '../services'; -import type { UserContentCommonSchema, TableListViewProps } from '../table_list_view'; +import type { UserContentCommonSchema, TableListViewProps } from '../table_list_view_table'; import { TagBadge } from './tag_badge'; type InheritedProps = Pick< diff --git a/packages/content-management/table_list/src/components/listing_limit_warning.tsx b/packages/content-management/table_list_view_table/src/components/listing_limit_warning.tsx similarity index 100% rename from packages/content-management/table_list/src/components/listing_limit_warning.tsx rename to packages/content-management/table_list_view_table/src/components/listing_limit_warning.tsx diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list_view_table/src/components/table.tsx similarity index 99% rename from packages/content-management/table_list/src/components/table.tsx rename to packages/content-management/table_list_view_table/src/components/table.tsx index 596c09cad34c2..84f9d94dde4a0 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list_view_table/src/components/table.tsx @@ -28,7 +28,7 @@ import type { State as TableListViewState, TableListProps, UserContentCommonSchema, -} from '../table_list_view'; +} from '../table_list_view_table'; import type { TableItemsRowActions } from '../types'; import { TableSortSelect } from './table_sort_select'; import { TagFilterPanel } from './tag_filter_panel'; diff --git a/packages/content-management/table_list/src/components/table_sort_select.tsx b/packages/content-management/table_list_view_table/src/components/table_sort_select.tsx similarity index 98% rename from packages/content-management/table_list/src/components/table_sort_select.tsx rename to packages/content-management/table_list_view_table/src/components/table_sort_select.tsx index ec2c5c5db1f6d..37cc6fc9eea80 100644 --- a/packages/content-management/table_list/src/components/table_sort_select.tsx +++ b/packages/content-management/table_list_view_table/src/components/table_sort_select.tsx @@ -19,7 +19,7 @@ import { } from '@elastic/eui'; import { css } from '@emotion/react'; -import { State } from '../table_list_view'; +import { State } from '../table_list_view_table'; type SortItem = EuiSelectableOption & { column: SortColumnField; diff --git a/packages/content-management/table_list/src/components/tag_badge.tsx b/packages/content-management/table_list_view_table/src/components/tag_badge.tsx similarity index 100% rename from packages/content-management/table_list/src/components/tag_badge.tsx rename to packages/content-management/table_list_view_table/src/components/tag_badge.tsx diff --git a/packages/content-management/table_list/src/components/tag_filter_panel.tsx b/packages/content-management/table_list_view_table/src/components/tag_filter_panel.tsx similarity index 100% rename from packages/content-management/table_list/src/components/tag_filter_panel.tsx rename to packages/content-management/table_list_view_table/src/components/tag_filter_panel.tsx diff --git a/packages/content-management/table_list/src/components/updated_at_field.tsx b/packages/content-management/table_list_view_table/src/components/updated_at_field.tsx similarity index 100% rename from packages/content-management/table_list/src/components/updated_at_field.tsx rename to packages/content-management/table_list_view_table/src/components/updated_at_field.tsx diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list_view_table/src/components/use_tag_filter_panel.tsx similarity index 100% rename from packages/content-management/table_list/src/components/use_tag_filter_panel.tsx rename to packages/content-management/table_list_view_table/src/components/use_tag_filter_panel.tsx diff --git a/packages/content-management/table_list/src/constants.ts b/packages/content-management/table_list_view_table/src/constants.ts similarity index 100% rename from packages/content-management/table_list/src/constants.ts rename to packages/content-management/table_list_view_table/src/constants.ts diff --git a/packages/content-management/table_list/src/index.ts b/packages/content-management/table_list_view_table/src/index.ts similarity index 84% rename from packages/content-management/table_list/src/index.ts rename to packages/content-management/table_list_view_table/src/index.ts index 65674db8a5037..f8327982eb76e 100644 --- a/packages/content-management/table_list/src/index.ts +++ b/packages/content-management/table_list_view_table/src/index.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -export { TableListView, TableList } from './table_list_view'; +export { TableListViewTable } from './table_list_view_table'; export type { - TableListViewProps, + TableListViewTableProps, State as TableListViewState, UserContentCommonSchema, -} from './table_list_view'; +} from './table_list_view_table'; export { TabbedTableListView, diff --git a/packages/content-management/table_list/src/mocks.tsx b/packages/content-management/table_list_view_table/src/mocks.tsx similarity index 100% rename from packages/content-management/table_list/src/mocks.tsx rename to packages/content-management/table_list_view_table/src/mocks.tsx diff --git a/packages/content-management/table_list/src/reducer.tsx b/packages/content-management/table_list_view_table/src/reducer.tsx similarity index 99% rename from packages/content-management/table_list/src/reducer.tsx rename to packages/content-management/table_list_view_table/src/reducer.tsx index 84ff8c0308f96..c8486d92caced 100644 --- a/packages/content-management/table_list/src/reducer.tsx +++ b/packages/content-management/table_list_view_table/src/reducer.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { State, UserContentCommonSchema } from './table_list_view'; +import type { State, UserContentCommonSchema } from './table_list_view_table'; import type { Action } from './actions'; export function getReducer() { diff --git a/packages/content-management/table_list/src/services.tsx b/packages/content-management/table_list_view_table/src/services.tsx similarity index 100% rename from packages/content-management/table_list/src/services.tsx rename to packages/content-management/table_list_view_table/src/services.tsx diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/tabbed_table_list_view.test.tsx similarity index 100% rename from packages/content-management/table_list/src/tabbed_table_list_view.test.tsx rename to packages/content-management/table_list_view_table/src/tabbed_table_list_view.test.tsx diff --git a/packages/content-management/table_list/src/tabbed_table_list_view.tsx b/packages/content-management/table_list_view_table/src/tabbed_table_list_view.tsx similarity index 98% rename from packages/content-management/table_list/src/tabbed_table_list_view.tsx rename to packages/content-management/table_list_view_table/src/tabbed_table_list_view.tsx index 2b8301e0bff63..f9d00d3186e22 100644 --- a/packages/content-management/table_list/src/tabbed_table_list_view.tsx +++ b/packages/content-management/table_list_view_table/src/tabbed_table_list_view.tsx @@ -14,7 +14,7 @@ import type { TableListProps, TableListViewProps, UserContentCommonSchema, -} from './table_list_view'; +} from './table_list_view_table'; export type TableListTabParentProps = Pick, 'onFetchSuccess' | 'setPageDataTestSubject'>; diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx similarity index 99% rename from packages/content-management/table_list/src/table_list_view.test.tsx rename to packages/content-management/table_list_view_table/src/table_list_view.test.tsx index 59c77caa2cef0..52ccc46e2eb48 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx @@ -22,7 +22,7 @@ import TableListView, { TableListProps, UserContentCommonSchema, type TableListViewProps, -} from './table_list_view'; +} from './table_list_view_table'; const mockUseEffect = useEffect; diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx similarity index 88% rename from packages/content-management/table_list/src/table_list_view.tsx rename to packages/content-management/table_list_view_table/src/table_list_view_table.tsx index 3b1b54c231856..8a84f380ab664 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -6,15 +6,7 @@ * Side Public License, v 1. */ -import React, { - useReducer, - useCallback, - useEffect, - useRef, - useMemo, - ReactNode, - useState, -} from 'react'; +import React, { useReducer, useCallback, useEffect, useRef, useMemo } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { EuiBasicTableColumn, @@ -57,11 +49,11 @@ interface ContentEditorConfig enabled?: boolean; } -export interface TableListViewProps { +export interface TableListViewTableProps< + T extends UserContentCommonSchema = UserContentCommonSchema +> { entityName: string; entityNamePlural: string; - title: string; - description?: string; listingLimit: number; initialFilter?: string; initialPageSize: number; @@ -81,7 +73,6 @@ export interface TableListViewProps RowActions | undefined; - children?: ReactNode | undefined; findItems( searchQuery: string, refs?: { @@ -107,11 +98,7 @@ export interface TableListViewProps = Pick< - TableListViewProps, - | 'entityName' - | 'entityNamePlural' - | 'initialFilter' - | 'headingId' - | 'initialPageSize' - | 'listingLimit' - | 'urlStateEnabled' - | 'customTableColumn' - | 'emptyPrompt' - | 'findItems' - | 'createItem' - | 'editItem' - | 'deleteItems' - | 'getDetailViewLink' - | 'onClickTitle' - | 'id' - | 'rowItemActions' - | 'contentEditor' - | 'titleColumnName' - | 'withoutPageTemplateWrapper' - | 'showEditActionForItem' -> & { + // TODO are these used? tableCaption: string; refreshListBouncer?: boolean; onFetchSuccess: () => void; setPageDataTestSubject: (subject: string) => void; -}; +} export interface State { items: T[]; @@ -280,7 +243,7 @@ const tableColumnMetadata = { }, } as const; -function TableListComp({ +function TableListViewTableComp({ tableCaption, entityName, entityNamePlural, @@ -306,7 +269,7 @@ function TableListComp({ onFetchSuccess, refreshListBouncer, setPageDataTestSubject, -}: TableListProps) { +}: TableListViewTableProps) { setPageDataTestSubject(`${entityName}LandingPage`); if (!getDetailViewLink && !onClickTitle) { @@ -1023,86 +986,6 @@ function TableListComp({ ); } -export const TableList = React.memo(TableListComp) as typeof TableListComp; - -export const TableListView = ({ - title, - description, - entityName, - entityNamePlural, - initialFilter, - headingId, - initialPageSize, - listingLimit, - urlStateEnabled = true, - customTableColumn, - emptyPrompt, - findItems, - createItem, - editItem, - deleteItems, - getDetailViewLink, - onClickTitle, - rowItemActions, - id: listingId, - contentEditor, - children, - titleColumnName, - additionalRightSideActions, - withoutPageTemplateWrapper, -}: TableListViewProps) => { - const PageTemplate = withoutPageTemplateWrapper - ? (React.Fragment as unknown as typeof KibanaPageTemplate) - : KibanaPageTemplate; - - const [hasInitialFetchReturned, setHasInitialFetchReturned] = useState(false); - const [pageDataTestSubject, setPageDataTestSubject] = useState(); - - return ( - - {title}} - description={description} - rightSideItems={additionalRightSideActions?.slice(0, 2)} - data-test-subj="top-nav" - /> - - {/* Any children passed to the component */} - {children} - - { - if (!hasInitialFetchReturned) { - setHasInitialFetchReturned(true); - } - }} - setPageDataTestSubject={setPageDataTestSubject} - /> - - - ); -}; - -// eslint-disable-next-line import/no-default-export -export default TableListView; +export const TableListViewTable = React.memo( + TableListViewTableComp +) as typeof TableListViewTableComp; diff --git a/packages/content-management/table_list/src/types.ts b/packages/content-management/table_list_view_table/src/types.ts similarity index 100% rename from packages/content-management/table_list/src/types.ts rename to packages/content-management/table_list_view_table/src/types.ts diff --git a/packages/content-management/table_list/src/use_tags.ts b/packages/content-management/table_list_view_table/src/use_tags.ts similarity index 98% rename from packages/content-management/table_list/src/use_tags.ts rename to packages/content-management/table_list_view_table/src/use_tags.ts index 345a3484306ff..207304564a829 100644 --- a/packages/content-management/table_list/src/use_tags.ts +++ b/packages/content-management/table_list_view_table/src/use_tags.ts @@ -9,7 +9,7 @@ import { useCallback, useMemo } from 'react'; import { Query } from '@elastic/eui'; import type { Tag } from './types'; -import type { UserContentCommonSchema } from './table_list_view'; +import type { UserContentCommonSchema } from './table_list_view_table'; type QueryUpdater = (query: Query, tag: Tag) => Query; diff --git a/packages/content-management/table_list/src/use_url_state.ts b/packages/content-management/table_list_view_table/src/use_url_state.ts similarity index 100% rename from packages/content-management/table_list/src/use_url_state.ts rename to packages/content-management/table_list_view_table/src/use_url_state.ts diff --git a/packages/content-management/table_list_view_table/tsconfig.json b/packages/content-management/table_list_view_table/tsconfig.json new file mode 100644 index 0000000000000..16a8a6b1a6de1 --- /dev/null +++ b/packages/content-management/table_list_view_table/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types", + "@kbn/ambient-storybook-types", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/i18n-react", + "@kbn/content-management-content-editor", + "@kbn/core-http-browser", + "@kbn/core-mount-utils-browser", + "@kbn/core-overlays-browser", + "@kbn/shared-ux-page-kibana-template", + "@kbn/shared-ux-link-redirect-app", + "@kbn/test-jest-helpers", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx index 383b51fbd65fa..6af5c1d23fca5 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx @@ -19,10 +19,10 @@ import { DashboardListing, DashboardListingProps } from './dashboard_listing'; * need to ensure we're passing down the correct props, but the table list view itself doesn't need to be rendered * in our tests because it is covered in its package. */ -import { TableListView } from '@kbn/content-management-table-list'; -// import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; -jest.mock('@kbn/content-management-table-list', () => { - const originalModule = jest.requireActual('@kbn/content-management-table-list'); +import { TableListView } from '@kbn/content-management-table-list-view'; +// import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view'; +jest.mock('@kbn/content-management-table-list-view-table', () => { + const originalModule = jest.requireActual('@kbn/content-management-table-list-view-table'); return { __esModule: true, ...originalModule, diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 9d55fafd94e5b..aec52783685fa 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -10,11 +10,11 @@ import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; import React, { PropsWithChildren, useCallback, useState } from 'react'; import { - TableListView, - TableListViewKibanaDependencies, + type TableListViewKibanaDependencies, TableListViewKibanaProvider, type UserContentCommonSchema, -} from '@kbn/content-management-table-list'; +} from '@kbn/content-management-table-list-view-table'; +import { TableListView } from '@kbn/content-management-table-list-view'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { toMountPoint, useExecutionContext } from '@kbn/kibana-react-plugin/public'; diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index a3af72ea8c120..df24958b91b30 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; +import type { UserContentCommonSchema } from '@kbn/content-management-table-list-view'; import { DataViewSpec, KibanaQueryOutput } from '@kbn/data-plugin/common'; import { DatatableColumn } from '@kbn/expressions-plugin/common'; import { $Values } from '@kbn/utility-types'; diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 8739fc9035257..48843b5f213b2 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -12,7 +12,10 @@ import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING, } from './table_list'; -import { TableList, UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { + TableListViewTable, + type UserContentCommonSchema, +} from '@kbn/content-management-table-list-view-table'; import { EventAnnotationServiceType } from '../event_annotation_service/types'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { shallow, ShallowWrapper } from 'enzyme'; @@ -98,7 +101,7 @@ describe('annotation list view', () => { const searchQuery = 'My Search Query'; const references = [{ id: 'first_id', type: 'sometype' }]; const referencesToExclude = [{ id: 'second_id', type: 'sometype' }]; - wrapper.find(TableList).prop('findItems')(searchQuery, { + wrapper.find(TableListViewTable).prop('findItems')(searchQuery, { references, referencesToExclude, }); @@ -115,13 +118,13 @@ describe('annotation list view', () => { it('prevent deleting when user is missing perms', () => { wrapper.setProps({ visualizeCapabilities: { delete: false } }); - expect(wrapper.find(TableList).prop('deleteItems')).toBeUndefined(); + expect(wrapper.find(TableListViewTable).prop('deleteItems')).toBeUndefined(); }); it('deletes groups using the service', () => { - expect(wrapper.find(TableList).prop('deleteItems')).toBeDefined(); + expect(wrapper.find(TableListViewTable).prop('deleteItems')).toBeDefined(); - wrapper.find(TableList).prop('deleteItems')!([ + wrapper.find(TableListViewTable).prop('deleteItems')!([ { id: 'some-id-1', references: [ @@ -166,15 +169,17 @@ describe('annotation list view', () => { it('prevents editing when user is missing perms', () => { wrapper.setProps({ visualizeCapabilities: { save: false } }); - expect(wrapper.find(TableList).prop('deleteItems')).toBeUndefined(); + expect(wrapper.find(TableListViewTable).prop('deleteItems')).toBeUndefined(); }); it('edits existing group', async () => { expect(wrapper.find(GroupEditorFlyout).exists()).toBeFalsy(); - const initialBouncerValue = wrapper.find(TableList).prop('refreshListBouncer'); + const initialBouncerValue = wrapper.find(TableListViewTable).prop('refreshListBouncer'); act(() => { - wrapper.find(TableList).prop('editItem')!({ id: '1234' } as UserContentCommonSchema); + wrapper.find(TableListViewTable).prop('editItem')!({ + id: '1234', + } as UserContentCommonSchema); }); // wait one tick to give promise time to settle @@ -198,12 +203,16 @@ describe('annotation list view', () => { ); expect(wrapper.find(GroupEditorFlyout).exists()).toBeFalsy(); - expect(wrapper.find(TableList).prop('refreshListBouncer')).not.toBe(initialBouncerValue); // (should refresh list) + expect(wrapper.find(TableListViewTable).prop('refreshListBouncer')).not.toBe( + initialBouncerValue + ); // (should refresh list) }); it('opens editor when title is clicked', async () => { act(() => { - wrapper.find(TableList).prop('onClickTitle')!({ id: '1234' } as UserContentCommonSchema); + wrapper.find(TableListViewTable).prop('onClickTitle')!({ + id: '1234', + } as UserContentCommonSchema); }); await new Promise((resolve) => setTimeout(resolve, 0)); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index 1eed240ecfc97..d4db655691c3b 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -7,7 +7,10 @@ */ import React, { useCallback, useState } from 'react'; -import { TableList, TableListTabParentProps } from '@kbn/content-management-table-list'; +import { + TableListViewTable, + TableListTabParentProps, +} from '@kbn/content-management-table-list-view-table'; import { i18n } from '@kbn/i18n'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; @@ -111,7 +114,7 @@ export const EventAnnotationGroupTableList = ({ return ( <> - + refreshListBouncer={refreshListBouncer} tableCaption={i18n.translate('eventAnnotation.tableList.listTitle', { defaultMessage: 'Annotation Library', diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index f2265c3c0bad1..bfcca626b0aed 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -12,7 +12,7 @@ import { FormattedRelative } from '@kbn/i18n-react'; import { type TableListTabParentProps, TableListViewKibanaProvider, -} from '@kbn/content-management-table-list'; +} from '@kbn/content-management-table-list-view-table'; import type { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; diff --git a/src/plugins/files_management/public/app.tsx b/src/plugins/files_management/public/app.tsx index 4fc2e8fff45f8..08f942c644789 100644 --- a/src/plugins/files_management/public/app.tsx +++ b/src/plugins/files_management/public/app.tsx @@ -9,7 +9,7 @@ import type { FunctionComponent } from 'react'; import React, { useState } from 'react'; import { EuiButtonEmpty } from '@elastic/eui'; -import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list-view'; import numeral from '@elastic/numeral'; import type { FileJSON } from '@kbn/files-plugin/common'; diff --git a/src/plugins/files_management/public/mount_management_section.tsx b/src/plugins/files_management/public/mount_management_section.tsx index 9c7091516d46e..229e1d2b306f6 100755 --- a/src/plugins/files_management/public/mount_management_section.tsx +++ b/src/plugins/files_management/public/mount_management_section.tsx @@ -17,7 +17,7 @@ import type { ManagementAppMountParams } from '@kbn/management-plugin/public'; import { TableListViewKibanaProvider, TableListViewKibanaDependencies, -} from '@kbn/content-management-table-list'; +} from '@kbn/content-management-table-list-view-table'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import type { StartDependencies } from './types'; import { App } from './app'; diff --git a/src/plugins/files_management/tsconfig.json b/src/plugins/files_management/tsconfig.json index d15175fae0470..28986030e75f8 100644 --- a/src/plugins/files_management/tsconfig.json +++ b/src/plugins/files_management/tsconfig.json @@ -9,7 +9,8 @@ "@kbn/files-plugin", "@kbn/management-plugin", "@kbn/i18n", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-table-list-view", "@kbn/kibana-react-plugin", "@kbn/i18n-react", "@kbn/shared-ux-file-image", diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index b78dcbaf71921..523fefaf9de5b 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -16,7 +16,7 @@ import { import type { ISearchSource } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/public'; -import { TableListTab } from '@kbn/content-management-table-list'; +import type { TableListTab } from '@kbn/content-management-table-list-view-table'; import type { Vis } from './vis'; import type { PersistedState } from './persisted_state'; import type { VisParams, SerializedVis } from '../common'; diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index 73f8a42015104..d805b4139d3bc 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -8,402 +8,405 @@ import './visualize_listing.scss'; -import React, { - useCallback, - useRef, - useMemo, - useEffect, - MouseEvent, - MutableRefObject, -} from 'react'; -import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import useUnmount from 'react-use/lib/useUnmount'; -import useMount from 'react-use/lib/useMount'; - -import { useLocation, useParams } from 'react-router-dom'; - -import type { SavedObjectReference } from '@kbn/core/public'; -import { useKibana, useExecutionContext } from '@kbn/kibana-react-plugin/public'; -import { - TableList, - TabbedTableListView, - type TableListTab, -} from '@kbn/content-management-table-list'; -import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; -import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; -import { TableListViewProps } from '@kbn/content-management-table-list/src'; -import { findListItems } from '../../utils/saved_visualize_utils'; -import { updateBasicSoAttributes } from '../../utils/saved_objects_utils/update_basic_attributes'; -import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title'; -import { showNewVisModal } from '../../wizard'; -import { getTypes } from '../../services'; -import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING } from '../..'; -import type { VisualizationListItem } from '../..'; -import type { VisualizeServices } from '../types'; -import { VisualizeConstants } from '../../../common/constants'; -import { getNoItemsMessage, getCustomColumn } from '../utils'; -import { getVisualizeListItemLink } from '../utils/get_visualize_list_item_link'; -import type { VisualizationStage } from '../../vis_types/vis_type_alias_registry'; - -interface VisualizeUserContent extends VisualizationListItem, UserContentCommonSchema { - type: string; - attributes: { - title: string; - description?: string; - editApp: string; - editUrl: string; - readOnly: boolean; - error?: string; - }; -} - -const toTableListViewSavedObject = (savedObject: Record): VisualizeUserContent => { - return { - id: savedObject.id as string, - updatedAt: savedObject.updatedAt as string, - references: savedObject.references as Array<{ id: string; type: string; name: string }>, - type: savedObject.savedObjectType as string, - editUrl: savedObject.editUrl as string, - editApp: savedObject.editApp as string, - icon: savedObject.icon as string, - stage: savedObject.stage as VisualizationStage, - savedObjectType: savedObject.savedObjectType as string, - typeTitle: savedObject.typeTitle as string, - title: (savedObject.title as string) ?? '', - error: (savedObject.error as string) ?? '', - attributes: { - title: (savedObject.title as string) ?? '', - description: savedObject.description as string, - editApp: savedObject.editApp as string, - editUrl: savedObject.editUrl as string, - readOnly: savedObject.readOnly as boolean, - error: savedObject.error as string, - }, - }; -}; -type CustomTableViewProps = Pick< - TableListViewProps, - | 'createItem' - | 'findItems' - | 'deleteItems' - | 'editItem' - | 'contentEditor' - | 'emptyPrompt' - | 'showEditActionForItem' ->; - -const useTableListViewProps = ( - closeNewVisModal: MutableRefObject<() => void>, - listingLimit: number -): CustomTableViewProps => { - const { - services: { - application, - history, - savedObjects, - savedObjectsTagging, - overlays, - toastNotifications, - visualizeCapabilities, - }, - } = useKibana(); - - const visualizedUserContent = useRef(); - - const createNewVis = useCallback(() => { - closeNewVisModal.current = showNewVisModal(); - }, [closeNewVisModal]); - - const editItem = useCallback( - ({ attributes: { editUrl, editApp } }: VisualizeUserContent) => { - if (editApp) { - application.navigateToApp(editApp, { path: editUrl }); - return; - } - // for visualizations the edit and view URLs are the same - history.push(editUrl); - }, - [application, history] - ); - - const noItemsFragment = useMemo(() => getNoItemsMessage(createNewVis), [createNewVis]); - - const fetchItems = useCallback( - ( - searchTerm: string, - { - references, - referencesToExclude, - }: { - references?: SavedObjectReference[]; - referencesToExclude?: SavedObjectReference[]; - } = {} - ) => { - return findListItems( - getTypes(), - searchTerm, - listingLimit, - references, - referencesToExclude - ).then(({ total, hits }: { total: number; hits: Array> }) => { - const content = hits.map(toTableListViewSavedObject); - - visualizedUserContent.current = content; - - return { - total, - hits: content, - }; - }); - }, - [listingLimit] - ); - - const onContentEditorSave = useCallback( - async (args: { id: string; title: string; description?: string; tags: string[] }) => { - const content = visualizedUserContent.current?.find(({ id }) => id === args.id); - - if (content) { - await updateBasicSoAttributes( - content.id, - content.type, - { - title: args.title, - description: args.description ?? '', - tags: args.tags, - }, - { overlays, savedObjectsTagging } - ); - } - }, - [overlays, savedObjectsTagging] - ); - - const contentEditorValidators: OpenContentEditorParams['customValidators'] = useMemo( - () => ({ - title: [ - { - type: 'warning', - async fn(value, id) { - if (id) { - const content = visualizedUserContent.current?.find((c) => c.id === id); - if (content) { - try { - await checkForDuplicateTitle( - { - id, - title: value, - lastSavedTitle: content.title, - getEsType: () => content.type, - }, - false, - false, - () => {}, - { overlays } - ); - } catch (e) { - return i18n.translate( - 'visualizations.visualizeListingDeleteErrorTitle.duplicateWarning', - { - defaultMessage: 'Saving "{value}" creates a duplicate title.', - values: { - value, - }, - } - ); - } - } - } - }, - }, - ], - }), - [overlays] - ); - - const deleteItems = useCallback( - async (selectedItems: object[]) => { - await Promise.all( - selectedItems.map((item: any) => savedObjects.client.delete(item.savedObjectType, item.id)) - ).catch((error) => { - toastNotifications.addError(error, { - title: i18n.translate('visualizations.visualizeListingDeleteErrorTitle', { - defaultMessage: 'Error deleting visualization', - }), - }); - }); - }, - [savedObjects.client, toastNotifications] - ); - - const props: CustomTableViewProps = { - findItems: fetchItems, - deleteItems, - contentEditor: { - isReadonly: !visualizeCapabilities.save, - onSave: onContentEditorSave, - customValidators: contentEditorValidators, - }, - editItem, - emptyPrompt: noItemsFragment, - createItem: createNewVis, - showEditActionForItem: ({ attributes: { readOnly } }) => - visualizeCapabilities.save && !readOnly, - }; - - return props; -}; +// import React, { +// useCallback, +// useRef, +// useMemo, +// useEffect, +// MouseEvent, +// MutableRefObject, +// } from 'react'; +// import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; +// import { i18n } from '@kbn/i18n'; +// import { FormattedMessage } from '@kbn/i18n-react'; +// import useUnmount from 'react-use/lib/useUnmount'; +// import useMount from 'react-use/lib/useMount'; + +// import { useLocation, useParams } from 'react-router-dom'; + +// import type { SavedObjectReference } from '@kbn/core/public'; +// import { useKibana, useExecutionContext } from '@kbn/kibana-react-plugin/public'; +// // import { +// // TableList, +// // TabbedTableListView, +// // type TableListTab, +// // } from 'packages/content-management/table_list_table'; +// import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; +// // import { +// // type UserContentCommonSchema, +// // TableListViewProps, +// // } from '@kbn/content-management-table-list-view'; +// import { findListItems } from '../../utils/saved_visualize_utils'; +// import { updateBasicSoAttributes } from '../../utils/saved_objects_utils/update_basic_attributes'; +// import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title'; +// import { showNewVisModal } from '../../wizard'; +// import { getTypes } from '../../services'; +// import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING } from '../..'; +// import type { VisualizationListItem } from '../..'; +// import type { VisualizeServices } from '../types'; +// import { VisualizeConstants } from '../../../common/constants'; +// import { getNoItemsMessage, getCustomColumn } from '../utils'; +// import { getVisualizeListItemLink } from '../utils/get_visualize_list_item_link'; +// import type { VisualizationStage } from '../../vis_types/vis_type_alias_registry'; + +// interface VisualizeUserContent extends VisualizationListItem, UserContentCommonSchema { +// type: string; +// attributes: { +// title: string; +// description?: string; +// editApp: string; +// editUrl: string; +// readOnly: boolean; +// error?: string; +// }; +// } + +// const toTableListViewSavedObject = (savedObject: Record): VisualizeUserContent => { +// return { +// id: savedObject.id as string, +// updatedAt: savedObject.updatedAt as string, +// references: savedObject.references as Array<{ id: string; type: string; name: string }>, +// type: savedObject.savedObjectType as string, +// editUrl: savedObject.editUrl as string, +// editApp: savedObject.editApp as string, +// icon: savedObject.icon as string, +// stage: savedObject.stage as VisualizationStage, +// savedObjectType: savedObject.savedObjectType as string, +// typeTitle: savedObject.typeTitle as string, +// title: (savedObject.title as string) ?? '', +// error: (savedObject.error as string) ?? '', +// attributes: { +// title: (savedObject.title as string) ?? '', +// description: savedObject.description as string, +// editApp: savedObject.editApp as string, +// editUrl: savedObject.editUrl as string, +// readOnly: savedObject.readOnly as boolean, +// error: savedObject.error as string, +// }, +// }; +// }; +// type CustomTableViewProps = Pick< +// TableListViewProps, +// | 'createItem' +// | 'findItems' +// | 'deleteItems' +// | 'editItem' +// | 'contentEditor' +// | 'emptyPrompt' +// | 'showEditActionForItem' +// >; + +// const useTableListViewProps = ( +// closeNewVisModal: MutableRefObject<() => void>, +// listingLimit: number +// ): CustomTableViewProps => { +// const { +// services: { +// application, +// history, +// savedObjects, +// savedObjectsTagging, +// overlays, +// toastNotifications, +// visualizeCapabilities, +// }, +// } = useKibana(); + +// const visualizedUserContent = useRef(); + +// const createNewVis = useCallback(() => { +// closeNewVisModal.current = showNewVisModal(); +// }, [closeNewVisModal]); + +// const editItem = useCallback( +// ({ attributes: { editUrl, editApp } }: VisualizeUserContent) => { +// if (editApp) { +// application.navigateToApp(editApp, { path: editUrl }); +// return; +// } +// // for visualizations the edit and view URLs are the same +// history.push(editUrl); +// }, +// [application, history] +// ); + +// const noItemsFragment = useMemo(() => getNoItemsMessage(createNewVis), [createNewVis]); + +// const fetchItems = useCallback( +// ( +// searchTerm: string, +// { +// references, +// referencesToExclude, +// }: { +// references?: SavedObjectReference[]; +// referencesToExclude?: SavedObjectReference[]; +// } = {} +// ) => { +// return findListItems( +// getTypes(), +// searchTerm, +// listingLimit, +// references, +// referencesToExclude +// ).then(({ total, hits }: { total: number; hits: Array> }) => { +// const content = hits.map(toTableListViewSavedObject); + +// visualizedUserContent.current = content; + +// return { +// total, +// hits: content, +// }; +// }); +// }, +// [listingLimit] +// ); + +// const onContentEditorSave = useCallback( +// async (args: { id: string; title: string; description?: string; tags: string[] }) => { +// const content = visualizedUserContent.current?.find(({ id }) => id === args.id); + +// if (content) { +// await updateBasicSoAttributes( +// content.id, +// content.type, +// { +// title: args.title, +// description: args.description ?? '', +// tags: args.tags, +// }, +// { overlays, savedObjectsTagging } +// ); +// } +// }, +// [overlays, savedObjectsTagging] +// ); + +// const contentEditorValidators: OpenContentEditorParams['customValidators'] = useMemo( +// () => ({ +// title: [ +// { +// type: 'warning', +// async fn(value, id) { +// if (id) { +// const content = visualizedUserContent.current?.find((c) => c.id === id); +// if (content) { +// try { +// await checkForDuplicateTitle( +// { +// id, +// title: value, +// lastSavedTitle: content.title, +// getEsType: () => content.type, +// }, +// false, +// false, +// () => {}, +// { overlays } +// ); +// } catch (e) { +// return i18n.translate( +// 'visualizations.visualizeListingDeleteErrorTitle.duplicateWarning', +// { +// defaultMessage: 'Saving "{value}" creates a duplicate title.', +// values: { +// value, +// }, +// } +// ); +// } +// } +// } +// }, +// }, +// ], +// }), +// [overlays] +// ); + +// const deleteItems = useCallback( +// async (selectedItems: object[]) => { +// await Promise.all( +// selectedItems.map((item: any) => savedObjects.client.delete(item.savedObjectType, item.id)) +// ).catch((error) => { +// toastNotifications.addError(error, { +// title: i18n.translate('visualizations.visualizeListingDeleteErrorTitle', { +// defaultMessage: 'Error deleting visualization', +// }), +// }); +// }); +// }, +// [savedObjects.client, toastNotifications] +// ); + +// const props: CustomTableViewProps = { +// findItems: fetchItems, +// deleteItems, +// contentEditor: { +// isReadonly: !visualizeCapabilities.save, +// onSave: onContentEditorSave, +// customValidators: contentEditorValidators, +// }, +// editItem, +// emptyPrompt: noItemsFragment, +// createItem: createNewVis, +// showEditActionForItem: ({ attributes: { readOnly } }) => +// visualizeCapabilities.save && !readOnly, +// }; + +// return props; +// }; export const VisualizeListing = () => { - const { - services: { - application, - executionContext, - chrome, - history, - stateTransferService, - dashboardCapabilities, - uiSettings, - kbnUrlStateStorage, - listingViewRegistry, - }, - } = useKibana(); - const { pathname } = useLocation(); - const closeNewVisModal = useRef(() => {}); - - useExecutionContext(executionContext, { - type: 'application', - page: 'list', - }); - - useEffect(() => { - if (pathname === '/new') { - // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately - closeNewVisModal.current = showNewVisModal({ - onClose: () => { - // In case the user came via a URL to this page, change the URL to the regular landing page URL after closing the modal - history.push(VisualizeConstants.LANDING_PAGE_PATH); - }, - }); - } else { - // close modal window if exists - closeNewVisModal.current(); - } - }, [history, pathname]); - - useMount(() => { - // Reset editor state for all apps if the visualize listing page is loaded. - stateTransferService.clearEditorState(); - chrome.setBreadcrumbs([ - { - text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', { - defaultMessage: 'Visualize Library', - }), - }, - ]); - chrome.docTitle.change( - i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' }) - ); - }); - useUnmount(() => closeNewVisModal.current()); - - const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); - const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); - - const tableViewProps = useTableListViewProps(closeNewVisModal, listingLimit); - - const visualizeLibraryTitle = i18n.translate('visualizations.listing.table.listTitle', { - defaultMessage: 'Visualize Library', - }); - - const visualizeTab: TableListTab = useMemo(() => { - const calloutMessage = ( - { - event.preventDefault(); - application.navigateToUrl(application.getUrlForApp('dashboards')); - }} - > - - - ), - }} - /> - ); - - return { - title: 'Visualizations', - id: 'visualizations', - getTableList: (propsFromParent) => ( - <> - {dashboardCapabilities.createNew && ( - <> - - - - )} - - id="vis" - // we allow users to create visualizations even if they can't save them - // for data exploration purposes - customTableColumn={getCustomColumn()} - listingLimit={listingLimit} - initialPageSize={initialPageSize} - initialFilter={''} - entityName={i18n.translate('visualizations.listing.table.entityName', { - defaultMessage: 'visualization', - })} - entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { - defaultMessage: 'visualizations', - })} - getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => - getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) - } - tableCaption={visualizeLibraryTitle} - {...tableViewProps} - {...propsFromParent} - /> - - ), - }; - }, [ - application, - dashboardCapabilities.createNew, - initialPageSize, - kbnUrlStateStorage, - listingLimit, - tableViewProps, - visualizeLibraryTitle, - ]); - - const tabs = useMemo( - () => [visualizeTab, ...Array.from(listingViewRegistry as Set)], - [listingViewRegistry, visualizeTab] - ); - - const { activeTab } = useParams<{ activeTab: string }>(); - - return ( - { - application.navigateToUrl(`#/${id}`); - }} - /> - ); + return null; + // const { + // services: { + // application, + // executionContext, + // chrome, + // history, + // stateTransferService, + // dashboardCapabilities, + // uiSettings, + // kbnUrlStateStorage, + // listingViewRegistry, + // }, + // } = useKibana(); + // const { pathname } = useLocation(); + // const closeNewVisModal = useRef(() => {}); + + // useExecutionContext(executionContext, { + // type: 'application', + // page: 'list', + // }); + + // useEffect(() => { + // if (pathname === '/new') { + // // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately + // closeNewVisModal.current = showNewVisModal({ + // onClose: () => { + // // In case the user came via a URL to this page, change the URL to the regular landing page URL after closing the modal + // history.push(VisualizeConstants.LANDING_PAGE_PATH); + // }, + // }); + // } else { + // // close modal window if exists + // closeNewVisModal.current(); + // } + // }, [history, pathname]); + + // useMount(() => { + // // Reset editor state for all apps if the visualize listing page is loaded. + // stateTransferService.clearEditorState(); + // chrome.setBreadcrumbs([ + // { + // text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', { + // defaultMessage: 'Visualize Library', + // }), + // }, + // ]); + // chrome.docTitle.change( + // i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' }) + // ); + // }); + // useUnmount(() => closeNewVisModal.current()); + + // const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); + // const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + + // const tableViewProps = useTableListViewProps(closeNewVisModal, listingLimit); + + // const visualizeLibraryTitle = i18n.translate('visualizations.listing.table.listTitle', { + // defaultMessage: 'Visualize Library', + // }); + + // const visualizeTab: TableListTab = useMemo(() => { + // const calloutMessage = ( + // { + // event.preventDefault(); + // application.navigateToUrl(application.getUrlForApp('dashboards')); + // }} + // > + // + // + // ), + // }} + // /> + // ); + + // return { + // title: 'Visualizations', + // id: 'visualizations', + // getTableList: (propsFromParent) => ( + // <> + // {dashboardCapabilities.createNew && ( + // <> + // + // + // + // )} + // + // id="vis" + // // we allow users to create visualizations even if they can't save them + // // for data exploration purposes + // customTableColumn={getCustomColumn()} + // listingLimit={listingLimit} + // initialPageSize={initialPageSize} + // initialFilter={''} + // entityName={i18n.translate('visualizations.listing.table.entityName', { + // defaultMessage: 'visualization', + // })} + // entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { + // defaultMessage: 'visualizations', + // })} + // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => + // getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) + // } + // tableCaption={visualizeLibraryTitle} + // {...tableViewProps} + // {...propsFromParent} + // /> + // + // ), + // }; + // }, [ + // application, + // dashboardCapabilities.createNew, + // initialPageSize, + // kbnUrlStateStorage, + // listingLimit, + // tableViewProps, + // visualizeLibraryTitle, + // ]); + + // const tabs = useMemo( + // () => [visualizeTab, ...Array.from(listingViewRegistry as Set)], + // [listingViewRegistry, visualizeTab] + // ); + + // const { activeTab } = useParams<{ activeTab: string }>(); + + // return ( + // { + // application.navigateToUrl(`#/${id}`); + // }} + // /> + // ); }; diff --git a/src/plugins/visualizations/public/visualize_app/index.tsx b/src/plugins/visualizations/public/visualize_app/index.tsx index e432275c755e6..0dc41f8f35d07 100644 --- a/src/plugins/visualizations/public/visualize_app/index.tsx +++ b/src/plugins/visualizations/public/visualize_app/index.tsx @@ -17,7 +17,7 @@ import { toMountPoint, } from '@kbn/kibana-react-plugin/public'; import { FormattedRelative } from '@kbn/i18n-react'; -import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view-table'; import { VisualizeApp } from './app'; import { VisualizeServices } from './types'; import { addHelpMenuToAppChrome, addBadgeToAppChrome } from './utils'; diff --git a/tsconfig.base.json b/tsconfig.base.json index f8ed16064cc8f..43839afa60282 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -168,8 +168,10 @@ "@kbn/content-management-examples-plugin/*": ["examples/content_management_examples/*"], "@kbn/content-management-plugin": ["src/plugins/content_management"], "@kbn/content-management-plugin/*": ["src/plugins/content_management/*"], - "@kbn/content-management-table-list": ["packages/content-management/table_list"], - "@kbn/content-management-table-list/*": ["packages/content-management/table_list/*"], + "@kbn/content-management-table-list-view": ["packages/content-management/table_list_view"], + "@kbn/content-management-table-list-view/*": ["packages/content-management/table_list_view/*"], + "@kbn/content-management-table-list-view-table": ["packages/content-management/table_list_view_table"], + "@kbn/content-management-table-list-view-table/*": ["packages/content-management/table_list_view_table/*"], "@kbn/content-management-utils": ["packages/kbn-content-management-utils"], "@kbn/content-management-utils/*": ["packages/kbn-content-management-utils/*"], "@kbn/controls-example-plugin": ["examples/controls_example"], diff --git a/x-pack/plugins/graph/public/application.tsx b/x-pack/plugins/graph/public/application.tsx index 28b44b804373a..82e1a061c67c8 100644 --- a/x-pack/plugins/graph/public/application.tsx +++ b/x-pack/plugins/graph/public/application.tsx @@ -27,7 +27,7 @@ import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation- import { Storage } from '@kbn/kibana-utils-plugin/public'; import { FormattedRelative } from '@kbn/i18n-react'; import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public'; -import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view-table'; import './index.scss'; import('./font_awesome'); diff --git a/x-pack/plugins/graph/public/apps/listing_route.tsx b/x-pack/plugins/graph/public/apps/listing_route.tsx index 27da1a51b8f4b..d5e7d2be00967 100644 --- a/x-pack/plugins/graph/public/apps/listing_route.tsx +++ b/x-pack/plugins/graph/public/apps/listing_route.tsx @@ -11,8 +11,8 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui'; import { ApplicationStart } from '@kbn/core/public'; import { useHistory, useLocation } from 'react-router-dom'; -import { TableListView } from '@kbn/content-management-table-list'; -import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { TableListView } from '@kbn/content-management-table-list-view'; +import type { UserContentCommonSchema } from '@kbn/content-management-table-list-view'; import { deleteSavedWorkspace, findSavedWorkspace } from '../helpers/saved_workspace_utils'; import { getEditPath, getEditUrl, getNewPath, setBreadcrumbs } from '../services/url'; import { GraphWorkspaceSavedObject } from '../types'; diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx index c4bddf5a71541..3897e38222eef 100644 --- a/x-pack/plugins/maps/public/render_app.tsx +++ b/x-pack/plugins/maps/public/render_app.tsx @@ -15,7 +15,7 @@ import { ExitFullScreenButtonKibanaProvider } from '@kbn/shared-ux-button-exit-f import { KibanaThemeProvider, toMountPoint } from '@kbn/kibana-react-plugin/public'; import { FormattedRelative } from '@kbn/i18n-react'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; -import { TableListViewKibanaProvider } from '@kbn/content-management-table-list'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view-table'; import { getCoreChrome, getCoreI18n, diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index 75cc9de21049c..93fc858f1c9d8 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -9,8 +9,8 @@ import React, { useCallback, memo, useEffect } from 'react'; import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; -import { TableListView } from '@kbn/content-management-table-list'; -import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; +import { TableListView } from '@kbn/content-management-table-list-view'; +import type { UserContentCommonSchema } from '@kbn/content-management-table-list-view'; import type { MapItem } from '../../../common/content_management'; import { APP_ID, APP_NAME, getEditPath, MAP_PATH } from '../../../common/constants'; diff --git a/yarn.lock b/yarn.lock index b0be00120d65d..60a7a817b59dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3171,7 +3171,11 @@ version "0.0.0" uid "" -"@kbn/content-management-table-list@link:packages/content-management/table_list": +"@kbn/content-management-table-list-view-table@link:packages/content-management/table_list_view_table": + version "0.0.0" + uid "" + +"@kbn/content-management-table-list-view@link:packages/content-management/table_list_view": version "0.0.0" uid "" From 080b20004dbc5fd5afd1a38424700222d3f6346a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 7 Jun 2023 13:40:20 -0500 Subject: [PATCH 181/202] move TabbedTableListView to own package --- package.json | 1 + .../tabbed_table_list_view/README.mdx | 20 + .../tabbed_table_list_view/index.ts | 11 + .../tabbed_table_list_view/jest.config.js | 13 + .../tabbed_table_list_view/kibana.jsonc | 5 + .../tabbed_table_list_view/package.json | 6 + .../tabbed_table_list_view/src/index.ts | 9 + .../src/tabbed_table_list_view.test.tsx | 0 .../src/tabbed_table_list_view.tsx | 8 +- .../tabbed_table_list_view/tsconfig.json | 32 + .../table_list_view/index.ts | 1 + .../table_list_view_table/src/index.ts | 6 - .../components/visualize_listing.tsx | 797 +++++++++--------- tsconfig.base.json | 2 + yarn.lock | 4 + 15 files changed, 506 insertions(+), 409 deletions(-) create mode 100644 packages/content-management/tabbed_table_list_view/README.mdx create mode 100644 packages/content-management/tabbed_table_list_view/index.ts create mode 100644 packages/content-management/tabbed_table_list_view/jest.config.js create mode 100644 packages/content-management/tabbed_table_list_view/kibana.jsonc create mode 100644 packages/content-management/tabbed_table_list_view/package.json create mode 100644 packages/content-management/tabbed_table_list_view/src/index.ts rename packages/content-management/{table_list_view_table => tabbed_table_list_view}/src/tabbed_table_list_view.test.tsx (100%) rename packages/content-management/{table_list_view_table => tabbed_table_list_view}/src/tabbed_table_list_view.tsx (92%) create mode 100644 packages/content-management/tabbed_table_list_view/tsconfig.json diff --git a/package.json b/package.json index 55fb532280a23..7cb0748c05ed5 100644 --- a/package.json +++ b/package.json @@ -187,6 +187,7 @@ "@kbn/content-management-content-editor": "link:packages/content-management/content_editor", "@kbn/content-management-examples-plugin": "link:examples/content_management_examples", "@kbn/content-management-plugin": "link:src/plugins/content_management", + "@kbn/content-management-tabbed-table-list-view": "link:packages/content-management/tabbed_table_list_view", "@kbn/content-management-table-list-view": "link:packages/content-management/table_list_view", "@kbn/content-management-table-list-view-table": "link:packages/content-management/table_list_view_table", "@kbn/content-management-utils": "link:packages/kbn-content-management-utils", diff --git a/packages/content-management/tabbed_table_list_view/README.mdx b/packages/content-management/tabbed_table_list_view/README.mdx new file mode 100644 index 0000000000000..357a07f8fc1c3 --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/README.mdx @@ -0,0 +1,20 @@ +--- +id: sharedUX/contentManagement/TabbedTableListView +slug: /shared-ux/content-management/tabbed-table-list-view +title: Tabbed table list view +summary: A table to render user generated saved objects. +tags: ['shared-ux', 'content-management'] +date: 2022-08-09 +--- + +The `` renders an eui page to display a list of user content saved object. + +**Uncomplete documentation**. Will be updated. + +## API + +TODO: https://github.com/elastic/kibana/issues/144402 + +## EUI Promotion Status + +This component is not currently considered for promotion to EUI. diff --git a/packages/content-management/tabbed_table_list_view/index.ts b/packages/content-management/tabbed_table_list_view/index.ts new file mode 100644 index 0000000000000..d18ef6ac4c75c --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/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. + */ + +export { TabbedTableListView, type TableListTab } from './src'; + +export type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-table'; diff --git a/packages/content-management/tabbed_table_list_view/jest.config.js b/packages/content-management/tabbed_table_list_view/jest.config.js new file mode 100644 index 0000000000000..dcb469837dcbc --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/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/content-management/tabbed_table_list_view'], +}; diff --git a/packages/content-management/tabbed_table_list_view/kibana.jsonc b/packages/content-management/tabbed_table_list_view/kibana.jsonc new file mode 100644 index 0000000000000..335fdd602037a --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/content-management-tabbed-table-list-view", + "owner": "@elastic/appex-sharedux" +} diff --git a/packages/content-management/tabbed_table_list_view/package.json b/packages/content-management/tabbed_table_list_view/package.json new file mode 100644 index 0000000000000..42c4ca6b19c0d --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/content-management-tabbed-table-list-view", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/content-management/tabbed_table_list_view/src/index.ts b/packages/content-management/tabbed_table_list_view/src/index.ts new file mode 100644 index 0000000000000..2eb0f905c21a4 --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/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 { TabbedTableListView, type TableListTab } from './tabbed_table_list_view'; diff --git a/packages/content-management/table_list_view_table/src/tabbed_table_list_view.test.tsx b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx similarity index 100% rename from packages/content-management/table_list_view_table/src/tabbed_table_list_view.test.tsx rename to packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx diff --git a/packages/content-management/table_list_view_table/src/tabbed_table_list_view.tsx b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx similarity index 92% rename from packages/content-management/table_list_view_table/src/tabbed_table_list_view.tsx rename to packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx index f9d00d3186e22..40c0288aafd40 100644 --- a/packages/content-management/table_list_view_table/src/tabbed_table_list_view.tsx +++ b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx @@ -11,13 +11,13 @@ import { css } from '@emotion/react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import React, { useCallback, useEffect, useState } from 'react'; import type { - TableListProps, - TableListViewProps, + TableListViewTableProps, UserContentCommonSchema, -} from './table_list_view_table'; +} from '@kbn/content-management-table-list-view-table'; +import type { TableListViewProps } from '@kbn/content-management-table-list-view'; export type TableListTabParentProps = - Pick, 'onFetchSuccess' | 'setPageDataTestSubject'>; + Pick, 'onFetchSuccess' | 'setPageDataTestSubject'>; export interface TableListTab { title: string; diff --git a/packages/content-management/tabbed_table_list_view/tsconfig.json b/packages/content-management/tabbed_table_list_view/tsconfig.json new file mode 100644 index 0000000000000..16a8a6b1a6de1 --- /dev/null +++ b/packages/content-management/tabbed_table_list_view/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types", + "@kbn/ambient-storybook-types", + "@emotion/react/types/css-prop" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/i18n-react", + "@kbn/content-management-content-editor", + "@kbn/core-http-browser", + "@kbn/core-mount-utils-browser", + "@kbn/core-overlays-browser", + "@kbn/shared-ux-page-kibana-template", + "@kbn/shared-ux-link-redirect-app", + "@kbn/test-jest-helpers", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/content-management/table_list_view/index.ts b/packages/content-management/table_list_view/index.ts index e40f9356e84b0..cb075152947ed 100644 --- a/packages/content-management/table_list_view/index.ts +++ b/packages/content-management/table_list_view/index.ts @@ -7,5 +7,6 @@ */ export { TableListView } from './src/table_list_view'; +export type { TableListViewProps } from './src/table_list_view'; export type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-table'; diff --git a/packages/content-management/table_list_view_table/src/index.ts b/packages/content-management/table_list_view_table/src/index.ts index f8327982eb76e..4f060ea25b9f1 100644 --- a/packages/content-management/table_list_view_table/src/index.ts +++ b/packages/content-management/table_list_view_table/src/index.ts @@ -14,12 +14,6 @@ export type { UserContentCommonSchema, } from './table_list_view_table'; -export { - TabbedTableListView, - type TableListTab, - type TableListTabParentProps, -} from './tabbed_table_list_view'; - export { TableListViewProvider, TableListViewKibanaProvider } from './services'; export type { RowActions } from './types'; diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index d805b4139d3bc..e169a7ebaa034 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -8,405 +8,404 @@ import './visualize_listing.scss'; -// import React, { -// useCallback, -// useRef, -// useMemo, -// useEffect, -// MouseEvent, -// MutableRefObject, -// } from 'react'; -// import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; -// import { i18n } from '@kbn/i18n'; -// import { FormattedMessage } from '@kbn/i18n-react'; -// import useUnmount from 'react-use/lib/useUnmount'; -// import useMount from 'react-use/lib/useMount'; - -// import { useLocation, useParams } from 'react-router-dom'; - -// import type { SavedObjectReference } from '@kbn/core/public'; -// import { useKibana, useExecutionContext } from '@kbn/kibana-react-plugin/public'; -// // import { -// // TableList, -// // TabbedTableListView, -// // type TableListTab, -// // } from 'packages/content-management/table_list_table'; -// import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; -// // import { -// // type UserContentCommonSchema, -// // TableListViewProps, -// // } from '@kbn/content-management-table-list-view'; -// import { findListItems } from '../../utils/saved_visualize_utils'; -// import { updateBasicSoAttributes } from '../../utils/saved_objects_utils/update_basic_attributes'; -// import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title'; -// import { showNewVisModal } from '../../wizard'; -// import { getTypes } from '../../services'; -// import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING } from '../..'; -// import type { VisualizationListItem } from '../..'; -// import type { VisualizeServices } from '../types'; -// import { VisualizeConstants } from '../../../common/constants'; -// import { getNoItemsMessage, getCustomColumn } from '../utils'; -// import { getVisualizeListItemLink } from '../utils/get_visualize_list_item_link'; -// import type { VisualizationStage } from '../../vis_types/vis_type_alias_registry'; - -// interface VisualizeUserContent extends VisualizationListItem, UserContentCommonSchema { -// type: string; -// attributes: { -// title: string; -// description?: string; -// editApp: string; -// editUrl: string; -// readOnly: boolean; -// error?: string; -// }; -// } - -// const toTableListViewSavedObject = (savedObject: Record): VisualizeUserContent => { -// return { -// id: savedObject.id as string, -// updatedAt: savedObject.updatedAt as string, -// references: savedObject.references as Array<{ id: string; type: string; name: string }>, -// type: savedObject.savedObjectType as string, -// editUrl: savedObject.editUrl as string, -// editApp: savedObject.editApp as string, -// icon: savedObject.icon as string, -// stage: savedObject.stage as VisualizationStage, -// savedObjectType: savedObject.savedObjectType as string, -// typeTitle: savedObject.typeTitle as string, -// title: (savedObject.title as string) ?? '', -// error: (savedObject.error as string) ?? '', -// attributes: { -// title: (savedObject.title as string) ?? '', -// description: savedObject.description as string, -// editApp: savedObject.editApp as string, -// editUrl: savedObject.editUrl as string, -// readOnly: savedObject.readOnly as boolean, -// error: savedObject.error as string, -// }, -// }; -// }; -// type CustomTableViewProps = Pick< -// TableListViewProps, -// | 'createItem' -// | 'findItems' -// | 'deleteItems' -// | 'editItem' -// | 'contentEditor' -// | 'emptyPrompt' -// | 'showEditActionForItem' -// >; - -// const useTableListViewProps = ( -// closeNewVisModal: MutableRefObject<() => void>, -// listingLimit: number -// ): CustomTableViewProps => { -// const { -// services: { -// application, -// history, -// savedObjects, -// savedObjectsTagging, -// overlays, -// toastNotifications, -// visualizeCapabilities, -// }, -// } = useKibana(); - -// const visualizedUserContent = useRef(); - -// const createNewVis = useCallback(() => { -// closeNewVisModal.current = showNewVisModal(); -// }, [closeNewVisModal]); - -// const editItem = useCallback( -// ({ attributes: { editUrl, editApp } }: VisualizeUserContent) => { -// if (editApp) { -// application.navigateToApp(editApp, { path: editUrl }); -// return; -// } -// // for visualizations the edit and view URLs are the same -// history.push(editUrl); -// }, -// [application, history] -// ); - -// const noItemsFragment = useMemo(() => getNoItemsMessage(createNewVis), [createNewVis]); - -// const fetchItems = useCallback( -// ( -// searchTerm: string, -// { -// references, -// referencesToExclude, -// }: { -// references?: SavedObjectReference[]; -// referencesToExclude?: SavedObjectReference[]; -// } = {} -// ) => { -// return findListItems( -// getTypes(), -// searchTerm, -// listingLimit, -// references, -// referencesToExclude -// ).then(({ total, hits }: { total: number; hits: Array> }) => { -// const content = hits.map(toTableListViewSavedObject); - -// visualizedUserContent.current = content; - -// return { -// total, -// hits: content, -// }; -// }); -// }, -// [listingLimit] -// ); - -// const onContentEditorSave = useCallback( -// async (args: { id: string; title: string; description?: string; tags: string[] }) => { -// const content = visualizedUserContent.current?.find(({ id }) => id === args.id); - -// if (content) { -// await updateBasicSoAttributes( -// content.id, -// content.type, -// { -// title: args.title, -// description: args.description ?? '', -// tags: args.tags, -// }, -// { overlays, savedObjectsTagging } -// ); -// } -// }, -// [overlays, savedObjectsTagging] -// ); - -// const contentEditorValidators: OpenContentEditorParams['customValidators'] = useMemo( -// () => ({ -// title: [ -// { -// type: 'warning', -// async fn(value, id) { -// if (id) { -// const content = visualizedUserContent.current?.find((c) => c.id === id); -// if (content) { -// try { -// await checkForDuplicateTitle( -// { -// id, -// title: value, -// lastSavedTitle: content.title, -// getEsType: () => content.type, -// }, -// false, -// false, -// () => {}, -// { overlays } -// ); -// } catch (e) { -// return i18n.translate( -// 'visualizations.visualizeListingDeleteErrorTitle.duplicateWarning', -// { -// defaultMessage: 'Saving "{value}" creates a duplicate title.', -// values: { -// value, -// }, -// } -// ); -// } -// } -// } -// }, -// }, -// ], -// }), -// [overlays] -// ); - -// const deleteItems = useCallback( -// async (selectedItems: object[]) => { -// await Promise.all( -// selectedItems.map((item: any) => savedObjects.client.delete(item.savedObjectType, item.id)) -// ).catch((error) => { -// toastNotifications.addError(error, { -// title: i18n.translate('visualizations.visualizeListingDeleteErrorTitle', { -// defaultMessage: 'Error deleting visualization', -// }), -// }); -// }); -// }, -// [savedObjects.client, toastNotifications] -// ); - -// const props: CustomTableViewProps = { -// findItems: fetchItems, -// deleteItems, -// contentEditor: { -// isReadonly: !visualizeCapabilities.save, -// onSave: onContentEditorSave, -// customValidators: contentEditorValidators, -// }, -// editItem, -// emptyPrompt: noItemsFragment, -// createItem: createNewVis, -// showEditActionForItem: ({ attributes: { readOnly } }) => -// visualizeCapabilities.save && !readOnly, -// }; - -// return props; -// }; +import React, { + useCallback, + useRef, + useMemo, + useEffect, + MouseEvent, + MutableRefObject, +} from 'react'; +import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import useUnmount from 'react-use/lib/useUnmount'; +import useMount from 'react-use/lib/useMount'; + +import { useLocation, useParams } from 'react-router-dom'; + +import type { SavedObjectReference } from '@kbn/core/public'; +import { useKibana, useExecutionContext } from '@kbn/kibana-react-plugin/public'; +import { + TabbedTableListView, + type TableListTab, +} from '@kbn/content-management-tabbed-table-list-view'; +import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; +import { + type UserContentCommonSchema, + TableListViewProps, +} from '@kbn/content-management-table-list-view'; +import { TableListViewTable } from '@kbn/content-management-table-list-view-table'; +import { findListItems } from '../../utils/saved_visualize_utils'; +import { updateBasicSoAttributes } from '../../utils/saved_objects_utils/update_basic_attributes'; +import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title'; +import { showNewVisModal } from '../../wizard'; +import { getTypes } from '../../services'; +import { SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING } from '../..'; +import type { VisualizationListItem } from '../..'; +import type { VisualizeServices } from '../types'; +import { VisualizeConstants } from '../../../common/constants'; +import { getNoItemsMessage, getCustomColumn } from '../utils'; +import { getVisualizeListItemLink } from '../utils/get_visualize_list_item_link'; +import type { VisualizationStage } from '../../vis_types/vis_type_alias_registry'; + +interface VisualizeUserContent extends VisualizationListItem, UserContentCommonSchema { + type: string; + attributes: { + title: string; + description?: string; + editApp: string; + editUrl: string; + readOnly: boolean; + error?: string; + }; +} + +const toTableListViewSavedObject = (savedObject: Record): VisualizeUserContent => { + return { + id: savedObject.id as string, + updatedAt: savedObject.updatedAt as string, + references: savedObject.references as Array<{ id: string; type: string; name: string }>, + type: savedObject.savedObjectType as string, + editUrl: savedObject.editUrl as string, + editApp: savedObject.editApp as string, + icon: savedObject.icon as string, + stage: savedObject.stage as VisualizationStage, + savedObjectType: savedObject.savedObjectType as string, + typeTitle: savedObject.typeTitle as string, + title: (savedObject.title as string) ?? '', + error: (savedObject.error as string) ?? '', + attributes: { + title: (savedObject.title as string) ?? '', + description: savedObject.description as string, + editApp: savedObject.editApp as string, + editUrl: savedObject.editUrl as string, + readOnly: savedObject.readOnly as boolean, + error: savedObject.error as string, + }, + }; +}; +type CustomTableViewProps = Pick< + TableListViewProps, + | 'createItem' + | 'findItems' + | 'deleteItems' + | 'editItem' + | 'contentEditor' + | 'emptyPrompt' + | 'showEditActionForItem' +>; + +const useTableListViewProps = ( + closeNewVisModal: MutableRefObject<() => void>, + listingLimit: number +): CustomTableViewProps => { + const { + services: { + application, + history, + savedObjects, + savedObjectsTagging, + overlays, + toastNotifications, + visualizeCapabilities, + }, + } = useKibana(); + + const visualizedUserContent = useRef(); + + const createNewVis = useCallback(() => { + closeNewVisModal.current = showNewVisModal(); + }, [closeNewVisModal]); + + const editItem = useCallback( + ({ attributes: { editUrl, editApp } }: VisualizeUserContent) => { + if (editApp) { + application.navigateToApp(editApp, { path: editUrl }); + return; + } + // for visualizations the edit and view URLs are the same + history.push(editUrl); + }, + [application, history] + ); + + const noItemsFragment = useMemo(() => getNoItemsMessage(createNewVis), [createNewVis]); + + const fetchItems = useCallback( + ( + searchTerm: string, + { + references, + referencesToExclude, + }: { + references?: SavedObjectReference[]; + referencesToExclude?: SavedObjectReference[]; + } = {} + ) => { + return findListItems( + getTypes(), + searchTerm, + listingLimit, + references, + referencesToExclude + ).then(({ total, hits }: { total: number; hits: Array> }) => { + const content = hits.map(toTableListViewSavedObject); + + visualizedUserContent.current = content; + + return { + total, + hits: content, + }; + }); + }, + [listingLimit] + ); + + const onContentEditorSave = useCallback( + async (args: { id: string; title: string; description?: string; tags: string[] }) => { + const content = visualizedUserContent.current?.find(({ id }) => id === args.id); + + if (content) { + await updateBasicSoAttributes( + content.id, + content.type, + { + title: args.title, + description: args.description ?? '', + tags: args.tags, + }, + { overlays, savedObjectsTagging } + ); + } + }, + [overlays, savedObjectsTagging] + ); + + const contentEditorValidators: OpenContentEditorParams['customValidators'] = useMemo( + () => ({ + title: [ + { + type: 'warning', + async fn(value, id) { + if (id) { + const content = visualizedUserContent.current?.find((c) => c.id === id); + if (content) { + try { + await checkForDuplicateTitle( + { + id, + title: value, + lastSavedTitle: content.title, + getEsType: () => content.type, + }, + false, + false, + () => {}, + { overlays } + ); + } catch (e) { + return i18n.translate( + 'visualizations.visualizeListingDeleteErrorTitle.duplicateWarning', + { + defaultMessage: 'Saving "{value}" creates a duplicate title.', + values: { + value, + }, + } + ); + } + } + } + }, + }, + ], + }), + [overlays] + ); + + const deleteItems = useCallback( + async (selectedItems: object[]) => { + await Promise.all( + selectedItems.map((item: any) => savedObjects.client.delete(item.savedObjectType, item.id)) + ).catch((error) => { + toastNotifications.addError(error, { + title: i18n.translate('visualizations.visualizeListingDeleteErrorTitle', { + defaultMessage: 'Error deleting visualization', + }), + }); + }); + }, + [savedObjects.client, toastNotifications] + ); + + const props: CustomTableViewProps = { + findItems: fetchItems, + deleteItems, + contentEditor: { + isReadonly: !visualizeCapabilities.save, + onSave: onContentEditorSave, + customValidators: contentEditorValidators, + }, + editItem, + emptyPrompt: noItemsFragment, + createItem: createNewVis, + showEditActionForItem: ({ attributes: { readOnly } }) => + visualizeCapabilities.save && !readOnly, + }; + + return props; +}; export const VisualizeListing = () => { - return null; - // const { - // services: { - // application, - // executionContext, - // chrome, - // history, - // stateTransferService, - // dashboardCapabilities, - // uiSettings, - // kbnUrlStateStorage, - // listingViewRegistry, - // }, - // } = useKibana(); - // const { pathname } = useLocation(); - // const closeNewVisModal = useRef(() => {}); - - // useExecutionContext(executionContext, { - // type: 'application', - // page: 'list', - // }); - - // useEffect(() => { - // if (pathname === '/new') { - // // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately - // closeNewVisModal.current = showNewVisModal({ - // onClose: () => { - // // In case the user came via a URL to this page, change the URL to the regular landing page URL after closing the modal - // history.push(VisualizeConstants.LANDING_PAGE_PATH); - // }, - // }); - // } else { - // // close modal window if exists - // closeNewVisModal.current(); - // } - // }, [history, pathname]); - - // useMount(() => { - // // Reset editor state for all apps if the visualize listing page is loaded. - // stateTransferService.clearEditorState(); - // chrome.setBreadcrumbs([ - // { - // text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', { - // defaultMessage: 'Visualize Library', - // }), - // }, - // ]); - // chrome.docTitle.change( - // i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' }) - // ); - // }); - // useUnmount(() => closeNewVisModal.current()); - - // const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); - // const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); - - // const tableViewProps = useTableListViewProps(closeNewVisModal, listingLimit); - - // const visualizeLibraryTitle = i18n.translate('visualizations.listing.table.listTitle', { - // defaultMessage: 'Visualize Library', - // }); - - // const visualizeTab: TableListTab = useMemo(() => { - // const calloutMessage = ( - // { - // event.preventDefault(); - // application.navigateToUrl(application.getUrlForApp('dashboards')); - // }} - // > - // - // - // ), - // }} - // /> - // ); - - // return { - // title: 'Visualizations', - // id: 'visualizations', - // getTableList: (propsFromParent) => ( - // <> - // {dashboardCapabilities.createNew && ( - // <> - // - // - // - // )} - // - // id="vis" - // // we allow users to create visualizations even if they can't save them - // // for data exploration purposes - // customTableColumn={getCustomColumn()} - // listingLimit={listingLimit} - // initialPageSize={initialPageSize} - // initialFilter={''} - // entityName={i18n.translate('visualizations.listing.table.entityName', { - // defaultMessage: 'visualization', - // })} - // entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { - // defaultMessage: 'visualizations', - // })} - // getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => - // getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) - // } - // tableCaption={visualizeLibraryTitle} - // {...tableViewProps} - // {...propsFromParent} - // /> - // - // ), - // }; - // }, [ - // application, - // dashboardCapabilities.createNew, - // initialPageSize, - // kbnUrlStateStorage, - // listingLimit, - // tableViewProps, - // visualizeLibraryTitle, - // ]); - - // const tabs = useMemo( - // () => [visualizeTab, ...Array.from(listingViewRegistry as Set)], - // [listingViewRegistry, visualizeTab] - // ); - - // const { activeTab } = useParams<{ activeTab: string }>(); - - // return ( - // { - // application.navigateToUrl(`#/${id}`); - // }} - // /> - // ); + const { + services: { + application, + executionContext, + chrome, + history, + stateTransferService, + dashboardCapabilities, + uiSettings, + kbnUrlStateStorage, + listingViewRegistry, + }, + } = useKibana(); + const { pathname } = useLocation(); + const closeNewVisModal = useRef(() => {}); + + useExecutionContext(executionContext, { + type: 'application', + page: 'list', + }); + + useEffect(() => { + if (pathname === '/new') { + // In case the user navigated to the page via the /visualize/new URL we start the dialog immediately + closeNewVisModal.current = showNewVisModal({ + onClose: () => { + // In case the user came via a URL to this page, change the URL to the regular landing page URL after closing the modal + history.push(VisualizeConstants.LANDING_PAGE_PATH); + }, + }); + } else { + // close modal window if exists + closeNewVisModal.current(); + } + }, [history, pathname]); + + useMount(() => { + // Reset editor state for all apps if the visualize listing page is loaded. + stateTransferService.clearEditorState(); + chrome.setBreadcrumbs([ + { + text: i18n.translate('visualizations.visualizeListingBreadcrumbsTitle', { + defaultMessage: 'Visualize Library', + }), + }, + ]); + chrome.docTitle.change( + i18n.translate('visualizations.listingPageTitle', { defaultMessage: 'Visualize Library' }) + ); + }); + useUnmount(() => closeNewVisModal.current()); + + const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); + const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + + const tableViewProps = useTableListViewProps(closeNewVisModal, listingLimit); + + const visualizeLibraryTitle = i18n.translate('visualizations.listing.table.listTitle', { + defaultMessage: 'Visualize Library', + }); + + const visualizeTab: TableListTab = useMemo(() => { + const calloutMessage = ( + { + event.preventDefault(); + application.navigateToUrl(application.getUrlForApp('dashboards')); + }} + > + + + ), + }} + /> + ); + + return { + title: 'Visualizations', + id: 'visualizations', + getTableList: (propsFromParent) => ( + <> + {dashboardCapabilities.createNew && ( + <> + + + + )} + + id="vis" + // we allow users to create visualizations even if they can't save them + // for data exploration purposes + customTableColumn={getCustomColumn()} + listingLimit={listingLimit} + initialPageSize={initialPageSize} + initialFilter={''} + entityName={i18n.translate('visualizations.listing.table.entityName', { + defaultMessage: 'visualization', + })} + entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { + defaultMessage: 'visualizations', + })} + getDetailViewLink={({ attributes: { editApp, editUrl, error } }) => + getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) + } + tableCaption={visualizeLibraryTitle} + {...tableViewProps} + {...propsFromParent} + /> + + ), + }; + }, [ + application, + dashboardCapabilities.createNew, + initialPageSize, + kbnUrlStateStorage, + listingLimit, + tableViewProps, + visualizeLibraryTitle, + ]); + + const tabs = useMemo( + () => [visualizeTab, ...Array.from(listingViewRegistry as Set)], + [listingViewRegistry, visualizeTab] + ); + + const { activeTab } = useParams<{ activeTab: string }>(); + + return ( + { + application.navigateToUrl(`#/${id}`); + }} + /> + ); }; diff --git a/tsconfig.base.json b/tsconfig.base.json index 43839afa60282..5751e88d3054e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -168,6 +168,8 @@ "@kbn/content-management-examples-plugin/*": ["examples/content_management_examples/*"], "@kbn/content-management-plugin": ["src/plugins/content_management"], "@kbn/content-management-plugin/*": ["src/plugins/content_management/*"], + "@kbn/content-management-tabbed-table-list-view": ["packages/content-management/tabbed_table_list_view"], + "@kbn/content-management-tabbed-table-list-view/*": ["packages/content-management/tabbed_table_list_view/*"], "@kbn/content-management-table-list-view": ["packages/content-management/table_list_view"], "@kbn/content-management-table-list-view/*": ["packages/content-management/table_list_view/*"], "@kbn/content-management-table-list-view-table": ["packages/content-management/table_list_view_table"], diff --git a/yarn.lock b/yarn.lock index 60a7a817b59dc..1532a4f43a809 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3171,6 +3171,10 @@ version "0.0.0" uid "" +"@kbn/content-management-tabbed-table-list-view@link:packages/content-management/tabbed_table_list_view": + version "0.0.0" + uid "" + "@kbn/content-management-table-list-view-table@link:packages/content-management/table_list_view_table": version "0.0.0" uid "" From 21116fcac49faca216e5f78802ecca5b4ae13bb2 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 7 Jun 2023 18:53:57 +0000 Subject: [PATCH 182/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- examples/content_management_examples/tsconfig.json | 3 ++- .../tabbed_table_list_view/tsconfig.json | 10 ++-------- .../content-management/table_list_view/tsconfig.json | 9 +-------- src/plugins/dashboard/tsconfig.json | 3 ++- src/plugins/event_annotation/tsconfig.json | 3 ++- src/plugins/visualizations/tsconfig.json | 6 ++++-- x-pack/plugins/graph/tsconfig.json | 3 ++- x-pack/plugins/maps/tsconfig.json | 3 ++- 8 files changed, 17 insertions(+), 23 deletions(-) diff --git a/examples/content_management_examples/tsconfig.json b/examples/content_management_examples/tsconfig.json index 7f07213ce82b6..410a1fb667f9d 100644 --- a/examples/content_management_examples/tsconfig.json +++ b/examples/content_management_examples/tsconfig.json @@ -20,10 +20,11 @@ "@kbn/content-management-plugin", "@kbn/core-application-browser", "@kbn/shared-ux-link-redirect-app", - "@kbn/content-management-table-list", "@kbn/kibana-react-plugin", "@kbn/i18n-react", "@kbn/saved-objects-tagging-oss-plugin", "@kbn/core-saved-objects-api-browser", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-table-list-view", ] } diff --git a/packages/content-management/tabbed_table_list_view/tsconfig.json b/packages/content-management/tabbed_table_list_view/tsconfig.json index 16a8a6b1a6de1..2e5d3974b6bd2 100644 --- a/packages/content-management/tabbed_table_list_view/tsconfig.json +++ b/packages/content-management/tabbed_table_list_view/tsconfig.json @@ -16,15 +16,9 @@ "**/*.tsx", ], "kbn_references": [ - "@kbn/i18n", - "@kbn/i18n-react", - "@kbn/content-management-content-editor", - "@kbn/core-http-browser", - "@kbn/core-mount-utils-browser", - "@kbn/core-overlays-browser", "@kbn/shared-ux-page-kibana-template", - "@kbn/shared-ux-link-redirect-app", - "@kbn/test-jest-helpers", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-table-list-view", ], "exclude": [ "target/**/*", diff --git a/packages/content-management/table_list_view/tsconfig.json b/packages/content-management/table_list_view/tsconfig.json index 16a8a6b1a6de1..abd7b992b9c25 100644 --- a/packages/content-management/table_list_view/tsconfig.json +++ b/packages/content-management/table_list_view/tsconfig.json @@ -16,15 +16,8 @@ "**/*.tsx", ], "kbn_references": [ - "@kbn/i18n", - "@kbn/i18n-react", - "@kbn/content-management-content-editor", - "@kbn/core-http-browser", - "@kbn/core-mount-utils-browser", - "@kbn/core-overlays-browser", "@kbn/shared-ux-page-kibana-template", - "@kbn/shared-ux-link-redirect-app", - "@kbn/test-jest-helpers", + "@kbn/content-management-table-list-view-table", ], "exclude": [ "target/**/*", diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 4ecdedf6268da..59ac25d38bc1d 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -32,7 +32,6 @@ "@kbn/data-view-editor-plugin", "@kbn/unified-search-plugin", "@kbn/shared-ux-page-analytics-no-data", - "@kbn/content-management-table-list", "@kbn/i18n-react", "@kbn/expressions-plugin", "@kbn/field-formats-plugin", @@ -58,6 +57,8 @@ "@kbn/shared-ux-button-toolbar", "@kbn/core-saved-objects-server", "@kbn/core-saved-objects-utils-server", + "@kbn/content-management-table-list-view", + "@kbn/content-management-table-list-view-table", ], "exclude": [ "target/**/*", diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 570b3b934bc7a..c4db61d896308 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -23,7 +23,6 @@ "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-tagging-plugin", "@kbn/presentation-util-plugin", - "@kbn/content-management-table-list", "@kbn/visualizations-plugin", "@kbn/data-views-plugin", "@kbn/visualization-ui-components", @@ -39,6 +38,8 @@ "@kbn/core-lifecycle-browser", "@kbn/kibana-utils-plugin", "@kbn/unified-search-plugin", + "@kbn/content-management-table-list-view", + "@kbn/content-management-table-list-view-table", ], "exclude": [ "target/**/*", diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index dd5edc8961361..0d40c09d35620 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -42,7 +42,6 @@ "@kbn/i18n-react", "@kbn/safer-lodash-set", "@kbn/shared-ux-page-analytics-no-data", - "@kbn/content-management-table-list", "@kbn/test-jest-helpers", "@kbn/analytics", "@kbn/content-management-content-editor", @@ -57,7 +56,10 @@ "@kbn/core-saved-objects-api-server", "@kbn/object-versioning", "@kbn/core-saved-objects-server", - "@kbn/core-saved-objects-utils-server" + "@kbn/core-saved-objects-utils-server", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-tabbed-table-list-view", + "@kbn/content-management-table-list-view" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/graph/tsconfig.json b/x-pack/plugins/graph/tsconfig.json index b91a6913f07ae..4ee1639a2d4f8 100644 --- a/x-pack/plugins/graph/tsconfig.json +++ b/x-pack/plugins/graph/tsconfig.json @@ -28,7 +28,6 @@ "@kbn/config-schema", "@kbn/i18n-react", "@kbn/inspector-plugin", - "@kbn/content-management-table-list", "@kbn/test-jest-helpers", "@kbn/data-views-plugin", "@kbn/es-query", @@ -44,6 +43,8 @@ "@kbn/content-management-plugin", "@kbn/core-saved-objects-api-server", "@kbn/object-versioning", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-table-list-view", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 6f24d4d7d3c5e..b0e27ee323dba 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -50,7 +50,6 @@ "@kbn/field-formats-plugin", "@kbn/shared-ux-button-exit-full-screen", "@kbn/i18n-react", - "@kbn/content-management-table-list", "@kbn/react-field", "@kbn/analytics", "@kbn/mapbox-gl", @@ -71,6 +70,8 @@ "@kbn/core-saved-objects-server", "@kbn/maps-vector-tile-utils", "@kbn/core-http-common", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-table-list-view", ], "exclude": [ "target/**/*", From 2831feea9c97385668031fab5abcc513e85cf44a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:02:47 +0000 Subject: [PATCH 183/202] [CI] Auto-commit changed files from 'node scripts/generate codeowners' --- .github/CODEOWNERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dc6e697fc2259..858f6bd7bad40 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -87,7 +87,9 @@ src/plugins/console @elastic/platform-deployment-management packages/content-management/content_editor @elastic/appex-sharedux examples/content_management_examples @elastic/appex-sharedux src/plugins/content_management @elastic/appex-sharedux -packages/content-management/table_list @elastic/appex-sharedux +packages/content-management/tabbed_table_list_view @elastic/appex-sharedux +packages/content-management/table_list_view @elastic/appex-sharedux +packages/content-management/table_list_view_table @elastic/appex-sharedux packages/kbn-content-management-utils @elastic/kibana-data-discovery examples/controls_example @elastic/kibana-presentation src/plugins/controls @elastic/kibana-presentation From 1a41890d6c180cde234f7272220f579220d2d847 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 8 Jun 2023 15:24:23 -0500 Subject: [PATCH 184/202] fix types --- .../content_management_examples/tsconfig.json | 3 +- .../tabbed_table_list_view/index.ts | 2 +- .../tabbed_table_list_view/src/index.ts | 6 ++- .../tabbed_table_list_view/tsconfig.json | 1 + .../table_list_view/tsconfig.json | 1 + .../table_list_view_table/index.ts | 15 +----- .../src/components/item_details.tsx | 4 +- .../src/components/table.tsx | 4 +- .../src/table_list_view.test.tsx | 50 ++++++++++--------- .../src/table_list_view_table.tsx | 4 +- src/plugins/dashboard/tsconfig.json | 2 +- .../public/components/table_list.tsx | 6 +-- .../public/get_table_list.tsx | 6 +-- src/plugins/event_annotation/tsconfig.json | 2 +- src/plugins/visualizations/public/types.ts | 2 +- src/plugins/visualizations/tsconfig.json | 3 +- x-pack/plugins/graph/tsconfig.json | 2 +- x-pack/plugins/maps/tsconfig.json | 2 +- 18 files changed, 57 insertions(+), 58 deletions(-) diff --git a/examples/content_management_examples/tsconfig.json b/examples/content_management_examples/tsconfig.json index 7f07213ce82b6..81233b3d7eb45 100644 --- a/examples/content_management_examples/tsconfig.json +++ b/examples/content_management_examples/tsconfig.json @@ -20,7 +20,8 @@ "@kbn/content-management-plugin", "@kbn/core-application-browser", "@kbn/shared-ux-link-redirect-app", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view", + "@kbn/content-management-table-list-view-table", "@kbn/kibana-react-plugin", "@kbn/i18n-react", "@kbn/saved-objects-tagging-oss-plugin", diff --git a/packages/content-management/tabbed_table_list_view/index.ts b/packages/content-management/tabbed_table_list_view/index.ts index d18ef6ac4c75c..cf228d45a10e3 100644 --- a/packages/content-management/tabbed_table_list_view/index.ts +++ b/packages/content-management/tabbed_table_list_view/index.ts @@ -6,6 +6,6 @@ * Side Public License, v 1. */ -export { TabbedTableListView, type TableListTab } from './src'; +export { TabbedTableListView, type TableListTab, type TableListTabParentProps } from './src'; export type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-table'; diff --git a/packages/content-management/tabbed_table_list_view/src/index.ts b/packages/content-management/tabbed_table_list_view/src/index.ts index 2eb0f905c21a4..515598b765bcd 100644 --- a/packages/content-management/tabbed_table_list_view/src/index.ts +++ b/packages/content-management/tabbed_table_list_view/src/index.ts @@ -6,4 +6,8 @@ * Side Public License, v 1. */ -export { TabbedTableListView, type TableListTab } from './tabbed_table_list_view'; +export { + TabbedTableListView, + type TableListTab, + type TableListTabParentProps, +} from './tabbed_table_list_view'; diff --git a/packages/content-management/tabbed_table_list_view/tsconfig.json b/packages/content-management/tabbed_table_list_view/tsconfig.json index 16a8a6b1a6de1..28a8a9ffd8ec2 100644 --- a/packages/content-management/tabbed_table_list_view/tsconfig.json +++ b/packages/content-management/tabbed_table_list_view/tsconfig.json @@ -19,6 +19,7 @@ "@kbn/i18n", "@kbn/i18n-react", "@kbn/content-management-content-editor", + "@kbn/content-management-table-list-view", "@kbn/core-http-browser", "@kbn/core-mount-utils-browser", "@kbn/core-overlays-browser", diff --git a/packages/content-management/table_list_view/tsconfig.json b/packages/content-management/table_list_view/tsconfig.json index 16a8a6b1a6de1..7d2b905f43d26 100644 --- a/packages/content-management/table_list_view/tsconfig.json +++ b/packages/content-management/table_list_view/tsconfig.json @@ -25,6 +25,7 @@ "@kbn/shared-ux-page-kibana-template", "@kbn/shared-ux-link-redirect-app", "@kbn/test-jest-helpers", + "@kbn/content-management-table-list-view-table" ], "exclude": [ "target/**/*", diff --git a/packages/content-management/table_list_view_table/index.ts b/packages/content-management/table_list_view_table/index.ts index 66abaeb022bf3..28acf44221f89 100644 --- a/packages/content-management/table_list_view_table/index.ts +++ b/packages/content-management/table_list_view_table/index.ts @@ -6,19 +6,8 @@ * Side Public License, v 1. */ -export { - TabbedTableListView, - TableListViewTable, - TableListViewProvider, - TableListViewKibanaProvider, -} from './src'; +export { TableListViewTable, TableListViewProvider, TableListViewKibanaProvider } from './src'; -export type { - UserContentCommonSchema, - TableListViewTableProps, - TableListTab, - TableListTabParentProps, - RowActions, -} from './src'; +export type { UserContentCommonSchema, TableListViewTableProps, RowActions } from './src'; export type { TableListViewKibanaDependencies } from './src/services'; diff --git a/packages/content-management/table_list_view_table/src/components/item_details.tsx b/packages/content-management/table_list_view_table/src/components/item_details.tsx index 653e2b32af6b7..0b2f52b216905 100644 --- a/packages/content-management/table_list_view_table/src/components/item_details.tsx +++ b/packages/content-management/table_list_view_table/src/components/item_details.tsx @@ -12,11 +12,11 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import type { Tag } from '../types'; import { useServices } from '../services'; -import type { UserContentCommonSchema, TableListViewProps } from '../table_list_view_table'; +import type { UserContentCommonSchema, TableListViewTableProps } from '../table_list_view_table'; import { TagBadge } from './tag_badge'; type InheritedProps = Pick< - TableListViewProps, + TableListViewTableProps, 'onClickTitle' | 'getDetailViewLink' | 'id' >; interface Props extends InheritedProps { diff --git a/packages/content-management/table_list_view_table/src/components/table.tsx b/packages/content-management/table_list_view_table/src/components/table.tsx index 84f9d94dde4a0..875d1ddedfa41 100644 --- a/packages/content-management/table_list_view_table/src/components/table.tsx +++ b/packages/content-management/table_list_view_table/src/components/table.tsx @@ -26,7 +26,7 @@ import { useServices } from '../services'; import type { Action } from '../actions'; import type { State as TableListViewState, - TableListProps, + TableListViewTableProps, UserContentCommonSchema, } from '../table_list_view_table'; import type { TableItemsRowActions } from '../types'; @@ -54,7 +54,7 @@ interface Props extends State, TagManageme tableCaption: string; tableColumns: Array>; hasUpdatedAtMetadata: boolean; - deleteItems: TableListProps['deleteItems']; + deleteItems: TableListViewTableProps['deleteItems']; tableItemsRowActions: TableItemsRowActions; renderCreateButton: () => React.ReactElement | undefined; onSortChange: (column: SortColumnField, direction: Direction) => void; diff --git a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx index 52ccc46e2eb48..281e677223c60 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx @@ -17,11 +17,10 @@ import type { LocationDescriptor, History } from 'history'; import { WithServices } from './__jest__'; import { getTagList } from './mocks'; -import TableListView, { - TableList, - TableListProps, - UserContentCommonSchema, - type TableListViewProps, +import { + TableListViewTable, + type TableListViewTableProps, + type UserContentCommonSchema, } from './table_list_view_table'; const mockUseEffect = useEffect; @@ -62,16 +61,18 @@ const getActions = (testBed: TestBed) => ({ }); describe('TableListView', () => { - const requiredProps: TableListViewProps = { + const requiredProps: TableListViewTableProps = { entityName: 'test', entityNamePlural: 'tests', listingLimit: 500, initialFilter: '', initialPageSize: 20, - title: 'test title', findItems: jest.fn().mockResolvedValue({ total: 0, hits: [] }), getDetailViewLink: () => 'http://elastic.co', urlStateEnabled: false, + onFetchSuccess: () => {}, + tableCaption: 'my caption', + setPageDataTestSubject: () => {}, }; beforeAll(() => { @@ -82,8 +83,8 @@ describe('TableListView', () => { jest.useRealTimers(); }); - const setup = registerTestBed( - WithServices(TableListView), + const setup = registerTestBed( + WithServices(TableListViewTable), { defaultProps: { ...requiredProps }, memoryRouter: { wrapComponent: true }, @@ -377,8 +378,8 @@ describe('TableListView', () => { }); describe('column sorting', () => { - const setupColumnSorting = registerTestBed( - WithServices(TableListView, { + const setupColumnSorting = registerTestBed( + WithServices(TableListViewTable, { TagList: getTagList({ references: [] }), }), { @@ -580,8 +581,8 @@ describe('TableListView', () => { }); describe('content editor', () => { - const setupInspector = registerTestBed( - WithServices(TableListView), + const setupInspector = registerTestBed( + WithServices(TableListViewTable), { defaultProps: { ...requiredProps }, memoryRouter: { wrapComponent: true }, @@ -631,8 +632,8 @@ describe('TableListView', () => { }); describe('tag filtering', () => { - const setupTagFiltering = registerTestBed( - WithServices(TableListView, { + const setupTagFiltering = registerTestBed( + WithServices(TableListViewTable, { getTagList: () => [ { id: 'id-tag-1', name: 'tag-1', type: 'tag', description: '', color: '' }, { id: 'id-tag-2', name: 'tag-2', type: 'tag', description: '', color: '' }, @@ -783,8 +784,8 @@ describe('TableListView', () => { describe('url state', () => { let router: Router | undefined; - const setupTagFiltering = registerTestBed( - WithServices(TableListView, { + const setupTagFiltering = registerTestBed( + WithServices(TableListViewTable, { getTagList: () => [ { id: 'id-tag-1', name: 'tag-1', type: 'tag', description: '', color: '' }, { id: 'id-tag-2', name: 'tag-2', type: 'tag', description: '', color: '' }, @@ -1093,7 +1094,7 @@ describe('TableListView', () => { }, ]; - const setupTest = async (props?: Partial) => { + const setupTest = async (props?: Partial) => { let testBed: TestBed | undefined; const deleteItems = jest.fn(); await act(async () => { @@ -1176,7 +1177,7 @@ describe('TableListView', () => { }); describe('TableList', () => { - const requiredProps: TableListProps = { + const requiredProps: TableListViewTableProps = { entityName: 'test', entityNamePlural: 'tests', initialPageSize: 20, @@ -1188,10 +1189,13 @@ describe('TableList', () => { setPageDataTestSubject: () => {}, }; - const setup = registerTestBed(WithServices(TableList), { - defaultProps: { ...requiredProps, refreshListBouncer: false }, - memoryRouter: { wrapComponent: true }, - }); + const setup = registerTestBed( + WithServices(TableListViewTable), + { + defaultProps: { ...requiredProps, refreshListBouncer: false }, + memoryRouter: { wrapComponent: true }, + } + ); it('refreshes the list when the bouncer changes', async () => { let testBed: TestBed; diff --git a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx index 8a84f380ab664..8125e1e088be7 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -270,7 +270,9 @@ function TableListViewTableComp({ refreshListBouncer, setPageDataTestSubject, }: TableListViewTableProps) { - setPageDataTestSubject(`${entityName}LandingPage`); + useEffect(() => { + setPageDataTestSubject(`${entityName}LandingPage`); + }, [entityName, setPageDataTestSubject]); if (!getDetailViewLink && !onClickTitle) { throw new Error( diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 4ecdedf6268da..8e47c8a7f11a2 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -32,7 +32,7 @@ "@kbn/data-view-editor-plugin", "@kbn/unified-search-plugin", "@kbn/shared-ux-page-analytics-no-data", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view", "@kbn/i18n-react", "@kbn/expressions-plugin", "@kbn/field-formats-plugin", diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index d4db655691c3b..a46c6a78bd679 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -7,10 +7,8 @@ */ import React, { useCallback, useState } from 'react'; -import { - TableListViewTable, - TableListTabParentProps, -} from '@kbn/content-management-table-list-view-table'; +import { TableListViewTable } from '@kbn/content-management-table-list-view-table'; +import type { TableListTabParentProps } from '@kbn/content-management-tabbed-table-list-view'; import { i18n } from '@kbn/i18n'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-browser'; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index bfcca626b0aed..cd438f30c7f3e 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -9,10 +9,8 @@ import React, { FC } from 'react'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { FormattedRelative } from '@kbn/i18n-react'; -import { - type TableListTabParentProps, - TableListViewKibanaProvider, -} from '@kbn/content-management-table-list-view-table'; +import { TableListViewKibanaProvider } from '@kbn/content-management-table-list-view-table'; +import { type TableListTabParentProps } from '@kbn/content-management-tabbed-table-list-view'; import type { CoreStart } from '@kbn/core-lifecycle-browser'; import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 570b3b934bc7a..dcea1179ffc09 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -23,7 +23,7 @@ "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-tagging-plugin", "@kbn/presentation-util-plugin", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view", "@kbn/visualizations-plugin", "@kbn/data-views-plugin", "@kbn/visualization-ui-components", diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index 523fefaf9de5b..c23f40dc85bae 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -16,7 +16,7 @@ import { import type { ISearchSource } from '@kbn/data-plugin/common'; import { ExpressionAstExpression } from '@kbn/expressions-plugin/public'; -import type { TableListTab } from '@kbn/content-management-table-list-view-table'; +import type { TableListTab } from '@kbn/content-management-tabbed-table-list-view'; import type { Vis } from './vis'; import type { PersistedState } from './persisted_state'; import type { VisParams, SerializedVis } from '../common'; diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index dd5edc8961361..e0b5c0db70b9a 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -42,7 +42,8 @@ "@kbn/i18n-react", "@kbn/safer-lodash-set", "@kbn/shared-ux-page-analytics-no-data", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view", + "@kbn/content-management-tabbed-table-list-view", "@kbn/test-jest-helpers", "@kbn/analytics", "@kbn/content-management-content-editor", diff --git a/x-pack/plugins/graph/tsconfig.json b/x-pack/plugins/graph/tsconfig.json index b91a6913f07ae..3696c9764243f 100644 --- a/x-pack/plugins/graph/tsconfig.json +++ b/x-pack/plugins/graph/tsconfig.json @@ -28,7 +28,7 @@ "@kbn/config-schema", "@kbn/i18n-react", "@kbn/inspector-plugin", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view", "@kbn/test-jest-helpers", "@kbn/data-views-plugin", "@kbn/es-query", diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 6f24d4d7d3c5e..cdefa8f12f5ac 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -50,7 +50,7 @@ "@kbn/field-formats-plugin", "@kbn/shared-ux-button-exit-full-screen", "@kbn/i18n-react", - "@kbn/content-management-table-list", + "@kbn/content-management-table-list-view", "@kbn/react-field", "@kbn/analytics", "@kbn/mapbox-gl", From 261ec576c43e631bcd76aaa5331284a40978e3ea Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 8 Jun 2023 15:31:56 -0500 Subject: [PATCH 185/202] fix dashboard listing tests --- .../public/dashboard_listing/dashboard_listing.test.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx index 6af5c1d23fca5..99b164433fe41 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx @@ -29,6 +29,13 @@ jest.mock('@kbn/content-management-table-list-view-table', () => { TableListViewKibanaProvider: jest.fn().mockImplementation(({ children }) => { return <>{children}; }), + }; +}); +jest.mock('@kbn/content-management-table-list-view', () => { + const originalModule = jest.requireActual('@kbn/content-management-table-list-view-table'); + return { + __esModule: true, + ...originalModule, TableListView: jest.fn().mockReturnValue(null), }; }); From ecee63a6414728a61ab021b49ff6c5d49178fba1 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 8 Jun 2023 20:39:25 +0000 Subject: [PATCH 186/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- .../content-management/tabbed_table_list_view/tsconfig.json | 6 ------ packages/content-management/table_list_view/tsconfig.json | 2 -- src/plugins/event_annotation/tsconfig.json | 1 + 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/content-management/tabbed_table_list_view/tsconfig.json b/packages/content-management/tabbed_table_list_view/tsconfig.json index 8c0ad89c9e333..7d0e24d7aff69 100644 --- a/packages/content-management/tabbed_table_list_view/tsconfig.json +++ b/packages/content-management/tabbed_table_list_view/tsconfig.json @@ -16,13 +16,7 @@ "**/*.tsx", ], "kbn_references": [ - "@kbn/i18n", - "@kbn/i18n-react", - "@kbn/content-management-content-editor", "@kbn/content-management-table-list-view", - "@kbn/core-http-browser", - "@kbn/core-mount-utils-browser", - "@kbn/core-overlays-browser", "@kbn/shared-ux-page-kibana-template", "@kbn/content-management-table-list-view-table", "@kbn/content-management-table-list-view", diff --git a/packages/content-management/table_list_view/tsconfig.json b/packages/content-management/table_list_view/tsconfig.json index 99cf7d128d09f..8b09db8f78c5a 100644 --- a/packages/content-management/table_list_view/tsconfig.json +++ b/packages/content-management/table_list_view/tsconfig.json @@ -17,8 +17,6 @@ ], "kbn_references": [ "@kbn/shared-ux-page-kibana-template", - "@kbn/shared-ux-link-redirect-app", - "@kbn/test-jest-helpers", "@kbn/content-management-table-list-view-table" ], "exclude": [ diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index bda663434260e..99a994688da36 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -41,6 +41,7 @@ "@kbn/unified-search-plugin", "@kbn/content-management-table-list-view", "@kbn/content-management-table-list-view-table", + "@kbn/content-management-tabbed-table-list-view", ], "exclude": [ "target/**/*", From b9472f0ed106aab61cbcdaf187001137f8669639 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 9 Jun 2023 08:50:06 -0500 Subject: [PATCH 187/202] update snapshot --- .../event_annotation/public/components/table_list.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 48843b5f213b2..613401fc61d13 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -92,7 +92,7 @@ describe('annotation list view', () => { it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot(` " - + " `); }); From f95e8d46267746e25acba0c348beaf681f7e269a Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 9 Jun 2023 11:06:21 -0500 Subject: [PATCH 188/202] some design feedback --- .../src/tabbed_table_list_view.test.tsx | 35 +++++++++---------- .../src/tabbed_table_list_view.tsx | 26 ++++---------- .../group_editor_controls.tsx | 25 ++++++++++--- .../public/components/group_editor_flyout.tsx | 2 +- src/plugins/event_annotation/public/plugin.ts | 2 +- 5 files changed, 46 insertions(+), 44 deletions(-) diff --git a/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx index ef17abbf82f10..ee8351487bc92 100644 --- a/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx +++ b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx @@ -7,14 +7,14 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { TabbedTableListView, TableListTabParentProps, TableListTab, } from './tabbed_table_list_view'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import { EuiTab } from '@elastic/eui'; +import { EuiPageTemplate } from '@elastic/eui'; import { act } from 'react-dom/test-utils'; // Mock the necessary props for the component @@ -22,16 +22,20 @@ const title = 'Test Title'; const description = 'Test Description'; const headingId = 'test-heading-id'; const children =
    Test Children
    ; + +const tableList1 = 'Test Table List 1'; +const tableList2 = 'Test Table List 2'; + const tabs: TableListTab[] = [ { title: 'Tab 1', id: 'tab-1', - getTableList: async (props: TableListTabParentProps) =>
    Test Table List 1
    , + getTableList: async (props: TableListTabParentProps) =>
    {tableList1}
    , }, { title: 'Tab 2', id: 'tab-2', - getTableList: async (props: TableListTabParentProps) =>
    Test Table List 2
    , + getTableList: async (props: TableListTabParentProps) =>
    {tableList2}
    , }, ]; @@ -85,13 +89,14 @@ describe('TabbedTableListView', () => { changeActiveTab={() => {}} /> ); - expect(wrapper.find(EuiTab).length).toEqual(tabs.length); + + expect(wrapper.find(EuiPageTemplate.Header).prop('tabs')).toHaveLength(2); }); - it('should switch tabs when a tab is clicked', () => { + it('should switch tabs when props change', async () => { const changeActiveTab = jest.fn(); - const wrapper = shallow( + const wrapper = mount( { /> ); - const getTab = (index: number) => wrapper.find(EuiTab).at(index); + await new Promise((resolve) => setTimeout(resolve, 0)); - expect(getTab(0).prop('isSelected')).toBeTruthy(); - expect(getTab(1).prop('isSelected')).toBeFalsy(); - - act(() => { - getTab(1).simulate('click'); - }); - - expect(changeActiveTab).toHaveBeenCalledWith('tab-2'); + expect(wrapper.find(EuiPageTemplate.Section).text()).toContain(tableList1); act(() => { wrapper.setProps({ @@ -120,7 +118,8 @@ describe('TabbedTableListView', () => { }); }); - expect(getTab(0).prop('isSelected')).toBeFalsy(); - expect(getTab(1).prop('isSelected')).toBeTruthy(); + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(wrapper.find(EuiPageTemplate.Section).text()).toContain(tableList2); }); }); diff --git a/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx index 40c0288aafd40..9872e35d90c88 100644 --- a/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx +++ b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.tsx @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import { EuiTab, EuiTabs } from '@elastic/eui'; -import { css } from '@emotion/react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import React, { useCallback, useEffect, useState } from 'react'; import type { @@ -73,24 +71,12 @@ export const TabbedTableListView = ({ pageTitle={{title}} description={description} data-test-subj="top-nav" - css={css` - .euiPageHeaderContent { - padding-bottom: 0; - } - `} - > - - {tabs.map((tab, index) => ( - changeActiveTab(tab.id)} - isSelected={tab.id === getActiveTab().id} - > - {tab.title} - - ))} - -
    + tabs={tabs.map((tab) => ({ + onClick: () => changeActiveTab(tab.id), + isSelected: tab.id === getActiveTab().id, + label: tab.title, + }))} + /> {/* Any children passed to the component */} {children} diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 39f62b4a5fc99..43bdf565df822 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -6,7 +6,15 @@ * Side Public License, v 1. */ -import { EuiFieldText, EuiForm, EuiFormRow, EuiSelect, EuiText, EuiTextArea } from '@elastic/eui'; +import { + EuiFieldText, + EuiForm, + EuiFormRow, + EuiSelect, + EuiText, + EuiTextArea, + EuiTitle, +} from '@elastic/eui'; import { css } from '@emotion/react'; import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; @@ -78,8 +86,8 @@ export const GroupEditorControls = ({ return !selectedAnnotation ? ( <> - - + + + + } > update({ ...group, diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index be9629a2467d3..3e07e97176d25 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -73,7 +73,7 @@ export const GroupEditorFlyout = ({ return ( - +

    { From 19052083055ef4b1ff4a53199dc3d25a2cfe2b2d Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 9 Jun 2023 14:24:56 -0500 Subject: [PATCH 189/202] show success toast when group is saved --- src/plugins/event_annotation/public/components/table_list.tsx | 4 ++++ src/plugins/event_annotation/public/get_table_list.tsx | 1 + 2 files changed, 5 insertions(+) diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index a46c6a78bd679..f033c6098e880 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -15,6 +15,7 @@ import { SavedObjectsFindOptionsReference } from '@kbn/core-saved-objects-api-br import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import { IToasts } from '@kbn/core-notifications-browser'; import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; import { EventAnnotationGroupContent } from '../../common/types'; @@ -32,6 +33,7 @@ export const EventAnnotationGroupTableList = ({ dataViews, createDataView, queryInputServices, + toasts, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; @@ -41,6 +43,7 @@ export const EventAnnotationGroupTableList = ({ dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; queryInputServices: QueryInputServices; + toasts: IToasts; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -100,6 +103,7 @@ export const EventAnnotationGroupTableList = ({ : eventAnnotationService.createAnnotationGroup(groupToEditInfo.group) ).then(() => { setGroupToEditInfo(undefined); + toasts.addSuccess(`Saved "${groupToEditInfo.group.title}"`); refreshList(); }) } diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index cd438f30c7f3e..58a657cd2593e 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -44,6 +44,7 @@ export const getTableList = ( }} > Date: Fri, 9 Jun 2023 14:44:17 -0500 Subject: [PATCH 190/202] prevent saving group without title --- .../__snapshots__/group_editor_flyout.test.tsx.snap | 1 + .../group_editor_controls.test.tsx | 1 + .../group_editor_controls/group_editor_controls.tsx | 11 +++++++++++ .../public/components/group_editor_flyout.test.tsx | 9 +++++++++ .../public/components/group_editor_flyout.tsx | 13 +++++++++++-- 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap index 915a6b4dc32fd..49def88aecb70 100644 --- a/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap +++ b/src/plugins/event_annotation/public/components/__snapshots__/group_editor_flyout.test.tsx.snap @@ -32,6 +32,7 @@ Object { "queryInputServices": Object {}, "selectedAnnotation": undefined, "setSelectedAnnotation": [Function], + "showValidation": false, "update": [MockFunction], } `; diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx index 80e9a9008ff69..d3b9f384a185f 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.test.tsx @@ -75,6 +75,7 @@ describe('event annotation group editor', () => { } as unknown as DataView) } queryInputServices={{} as QueryInputServices} + showValidation={false} /> ); diff --git a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx index 43bdf565df822..c0a07a6fcb72b 100644 --- a/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_controls/group_editor_controls.tsx @@ -30,6 +30,10 @@ import { AnnotationList } from './annotation_list'; export const ENABLE_INDIVIDUAL_ANNOTATION_EDITING = false; +const isTitleValid = (title: string) => Boolean(title.length); + +export const isGroupValid = (group: EventAnnotationGroupConfig) => isTitleValid(group.title); + export const GroupEditorControls = ({ group, update, @@ -39,6 +43,7 @@ export const GroupEditorControls = ({ dataViews: globalDataViews, createDataView, queryInputServices, + showValidation, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; @@ -48,6 +53,7 @@ export const GroupEditorControls = ({ dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; queryInputServices: QueryInputServices; + showValidation: boolean; }) => { // save the spec for the life of the component since the user might change their mind after selecting another data view const [adHocDataView, setAdHocDataView] = useState(); @@ -101,10 +107,15 @@ export const GroupEditorControls = ({ label={i18n.translate('eventAnnotation.groupEditor.title', { defaultMessage: 'Title', })} + isInvalid={showValidation && !isTitleValid(group.title)} + error={i18n.translate('eventAnnotation.groupEditor.titleRequired', { + defaultMessage: 'A title is required.', + })} > update({ ...group, diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx index 2acf54c2c84dd..7f732aa882ffb 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.test.tsx @@ -96,6 +96,15 @@ describe('group editor flyout', () => { expect(onSave).toHaveBeenCalledTimes(1); }); + it("doesn't save invalid group config", () => { + component.setProps({ + group: { ...group, title: '' }, + }); + + simulateButtonClick(component, SELECTORS.SAVE_BUTTON); + + expect(onSave).not.toHaveBeenCalled(); + }); it('reports group updates', () => { const newGroup = { ...group, description: 'new description' }; component.find(GroupEditorControls).prop('update')(newGroup); diff --git a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx index 3e07e97176d25..3e296951542a8 100644 --- a/src/plugins/event_annotation/public/components/group_editor_flyout.tsx +++ b/src/plugins/event_annotation/public/components/group_editor_flyout.tsx @@ -24,7 +24,7 @@ import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/pu import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { EventAnnotationConfig, EventAnnotationGroupConfig } from '../../common'; -import { GroupEditorControls } from './group_editor_controls'; +import { GroupEditorControls, isGroupValid } from './group_editor_controls'; export const GroupEditorFlyout = ({ group, @@ -53,6 +53,8 @@ export const GroupEditorFlyout = ({ } }, []); + const [hasAttemptedSave, setHasAttemptedSave] = useState(false); + const resetContentScroll = useCallback( () => flyoutBodyOverflowRef.current && flyoutBodyOverflowRef.current.scroll(0, 0), [] @@ -93,6 +95,7 @@ export const GroupEditorFlyout = ({ dataViews={dataViews} createDataView={createDataView} queryInputServices={queryInputServices} + showValidation={hasAttemptedSave} /> @@ -120,7 +123,13 @@ export const GroupEditorFlyout = ({ iconType="save" data-test-subj="saveAnnotationGroup" fill - onClick={onSave} + onClick={() => { + setHasAttemptedSave(true); + + if (isGroupValid(group)) { + onSave(); + } + }} > Date: Fri, 9 Jun 2023 19:51:29 +0000 Subject: [PATCH 191/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/event_annotation/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 99a994688da36..fd98957d39ae5 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -42,6 +42,7 @@ "@kbn/content-management-table-list-view", "@kbn/content-management-table-list-view-table", "@kbn/content-management-tabbed-table-list-view", + "@kbn/core-notifications-browser", ], "exclude": [ "target/**/*", From bc5c7f10aaaedba2444a47e19cbedb3a535ce590 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 9 Jun 2023 16:50:18 -0500 Subject: [PATCH 192/202] data views column --- src/plugins/event_annotation/common/types.ts | 4 +++- .../public/components/table_list.test.tsx | 8 ++++++- .../public/components/table_list.tsx | 23 +++++++++++++++++++ .../event_annotation_service/service.tsx | 3 +-- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index df24958b91b30..9bdd4cd283523 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -101,7 +101,9 @@ export interface EventAnnotationGroupConfig { dataViewSpec?: DataViewSpec; } -export type EventAnnotationGroupContent = UserContentCommonSchema; +export type EventAnnotationGroupContent = UserContentCommonSchema & { + attributes: { indexPatternId: string; dataViewSpec?: DataViewSpec }; +}; export type EventAnnotationArgs = | ManualPointEventAnnotationArgs diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 613401fc61d13..d1a97ad2ad7fb 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -26,6 +26,8 @@ import { act } from 'react-dom/test-utils'; import { GroupEditorFlyout } from './group_editor_flyout'; import { DataView } from '@kbn/data-views-plugin/common'; import { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; +import { IToasts } from '@kbn/core-notifications-browser'; describe('annotation list view', () => { const adHocDVId = 'ad-hoc'; @@ -45,6 +47,7 @@ describe('annotation list view', () => { let wrapper: ShallowWrapper; let mockEventAnnotationService: EventAnnotationServiceType; + let mockToasts: IToasts; beforeEach(() => { mockEventAnnotationService = { @@ -64,6 +67,8 @@ describe('annotation list view', () => { ), } as Partial as IUiSettingsClient; + mockToasts = toastsServiceMock.createStartContract(); + wrapper = shallow( { ]} createDataView={() => Promise.resolve({} as DataView)} queryInputServices={{} as QueryInputServices} + toasts={mockToasts} /> ); }); @@ -92,7 +98,7 @@ describe('annotation list view', () => { it('renders a table list view', () => { expect(wrapper.debug()).toMatchInlineSnapshot(` " - + " `); }); diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index f033c6098e880..e9aadb9382c40 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -24,6 +24,28 @@ import { GroupEditorFlyout } from './group_editor_flyout'; export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; export const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; +const getCustomColumn = (dataViews: DataView[]) => { + const dataViewNameMap = Object.fromEntries( + dataViews.map((dataView) => [dataView.id, dataView.name ?? dataView.title]) + ); + + return { + field: 'dataView', + name: i18n.translate('eventAnnotation.tableList.dataView', { + defaultMessage: 'Data view', + }), + sortable: false, + width: '150px', + render: (_field: string, record: EventAnnotationGroupContent) => ( +
    + {record.attributes.dataViewSpec + ? record.attributes.dataViewSpec.name + : dataViewNameMap[record.attributes.indexPatternId]} +
    + ), + }; +}; + export const EventAnnotationGroupTableList = ({ uiSettings, eventAnnotationService, @@ -131,6 +153,7 @@ export const EventAnnotationGroupTableList = ({ listingLimit={listingLimit} initialPageSize={initialPageSize} initialFilter={''} + customTableColumn={getCustomColumn(dataViews)} // emptyPrompt={noItemsFragment} TODO entityName={i18n.translate('eventAnnotation.tableList.entityName', { defaultMessage: 'annotation group', diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index 7a467c4bec9fa..a841fa4fd31c0 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -79,8 +79,7 @@ export function getEventAnnotationService( type: savedObject.type, updatedAt: savedObject.updatedAt ? savedObject.updatedAt : '', attributes: { - title: savedObject.attributes.title, - description: savedObject.attributes.description, + ...mapSavedObjectToGroupConfig(savedObject), }, }; }; From 08603c26943372c96e955bcc6c9ff14216ca3fb9 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 9 Jun 2023 21:56:58 +0000 Subject: [PATCH 193/202] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/dashboard/tsconfig.json | 5 +++-- src/plugins/event_annotation/tsconfig.json | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 78c7ba0dccd52..a4ca23893c48c 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -32,7 +32,6 @@ "@kbn/data-view-editor-plugin", "@kbn/unified-search-plugin", "@kbn/shared-ux-page-analytics-no-data", - "@kbn/content-management-table-list", "@kbn/content-management-plugin", "@kbn/content-management-utils", "@kbn/i18n-react", @@ -60,7 +59,9 @@ "@kbn/core-saved-objects-server", "@kbn/core-saved-objects-utils-server", "@kbn/object-versioning", - "@kbn/core-saved-objects-api-server" + "@kbn/core-saved-objects-api-server", + "@kbn/content-management-table-list-view", + "@kbn/content-management-table-list-view-table" ], "exclude": ["target/**/*"] } diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index fd98957d39ae5..d8d9d61af2ac3 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -43,6 +43,7 @@ "@kbn/content-management-table-list-view-table", "@kbn/content-management-tabbed-table-list-view", "@kbn/core-notifications-browser", + "@kbn/core-notifications-browser-mocks", ], "exclude": [ "target/**/*", From b642b302d7a05f04f7f1567eb385bf463b60fbed Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 09:11:20 -0500 Subject: [PATCH 194/202] scope down content attributes --- .../__snapshots__/service.test.ts.snap | 10 ++++++++++ .../public/event_annotation_service/service.tsx | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap index d59cc3acc0d6d..0bd643095dcdc 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap +++ b/src/plugins/event_annotation/public/event_annotation_service/__snapshots__/service.test.ts.snap @@ -5,7 +5,9 @@ Object { "hits": Array [ Object { "attributes": Object { + "dataViewSpec": undefined, "description": undefined, + "indexPatternId": undefined, "title": undefined, }, "id": "nonExistingGroup", @@ -15,7 +17,9 @@ Object { }, Object { "attributes": Object { + "dataViewSpec": undefined, "description": "", + "indexPatternId": "ipid", "title": "groupTitle", }, "id": undefined, @@ -36,7 +40,9 @@ Object { }, Object { "attributes": Object { + "dataViewSpec": undefined, "description": undefined, + "indexPatternId": "ipid", "title": "groupTitle", }, "id": "multiAnnotations", @@ -52,7 +58,11 @@ Object { }, Object { "attributes": Object { + "dataViewSpec": Object { + "id": "my-id", + }, "description": undefined, + "indexPatternId": "my-id", "title": "groupTitle", }, "id": "multiAnnotations", diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index a841fa4fd31c0..65c2b9146df1c 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -73,13 +73,18 @@ export function getEventAnnotationService( const mapSavedObjectToGroupContent = ( savedObject: SimpleSavedObject ): EventAnnotationGroupContent => { + const groupConfig = mapSavedObjectToGroupConfig(savedObject); + return { id: savedObject.id, references: savedObject.references, type: savedObject.type, updatedAt: savedObject.updatedAt ? savedObject.updatedAt : '', attributes: { - ...mapSavedObjectToGroupConfig(savedObject), + title: groupConfig.title, + description: groupConfig.description, + indexPatternId: groupConfig.indexPatternId, + dataViewSpec: groupConfig.dataViewSpec, }, }; }; From 4fc045ccd9490530217964b1bd152803bf947646 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 09:57:38 -0500 Subject: [PATCH 195/202] better empty state --- .../public/components/table_list.tsx | 37 ++++++++++++++++++- .../public/get_table_list.tsx | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/plugins/event_annotation/public/components/table_list.tsx b/src/plugins/event_annotation/public/components/table_list.tsx index e9aadb9382c40..84535f2ffefc5 100644 --- a/src/plugins/event_annotation/public/components/table_list.tsx +++ b/src/plugins/event_annotation/public/components/table_list.tsx @@ -16,6 +16,8 @@ import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/pu import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components/public'; import { IToasts } from '@kbn/core-notifications-browser'; +import { EuiButton, EuiEmptyPrompt, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { EventAnnotationGroupConfig } from '../../common'; import type { EventAnnotationServiceType } from '../event_annotation_service/types'; import { EventAnnotationGroupContent } from '../../common/types'; @@ -56,6 +58,7 @@ export const EventAnnotationGroupTableList = ({ createDataView, queryInputServices, toasts, + navigateToLens, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; @@ -66,6 +69,7 @@ export const EventAnnotationGroupTableList = ({ createDataView: (spec: DataViewSpec) => Promise; queryInputServices: QueryInputServices; toasts: IToasts; + navigateToLens: () => void; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); @@ -154,7 +158,38 @@ export const EventAnnotationGroupTableList = ({ initialPageSize={initialPageSize} initialFilter={''} customTableColumn={getCustomColumn(dataViews)} - // emptyPrompt={noItemsFragment} TODO + emptyPrompt={ + +

    + +

    + + } + body={ +

    + +

    + } + actions={ + + + + } + iconType="flag" + /> + } entityName={i18n.translate('eventAnnotation.tableList.entityName', { defaultMessage: 'annotation group', })} diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation/public/get_table_list.tsx index 58a657cd2593e..5226d31894d15 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation/public/get_table_list.tsx @@ -53,6 +53,7 @@ export const getTableList = ( dataViews={services.dataViews} createDataView={services.createDataView} queryInputServices={services.queryInputServices} + navigateToLens={() => services.core.application.navigateToApp('lens')} /> From 2e7b014ca4d4137c5ddf6b9f6b6f544357531fe6 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 11:50:42 -0500 Subject: [PATCH 196/202] improve tests --- .../src/tabbed_table_list_view.test.tsx | 37 ++++++++++--------- .../src/table_list_view.test.tsx | 11 ++---- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx index ee8351487bc92..cc7ced86951a4 100644 --- a/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx +++ b/packages/content-management/tabbed_table_list_view/src/tabbed_table_list_view.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { mount, shallow } from 'enzyme'; +import { ReactWrapper, mount, shallow } from 'enzyme'; import { TabbedTableListView, TableListTabParentProps, @@ -96,30 +96,33 @@ describe('TabbedTableListView', () => { it('should switch tabs when props change', async () => { const changeActiveTab = jest.fn(); - const wrapper = mount( - - ); + let wrapper: ReactWrapper | undefined; + await act(async () => { + wrapper = mount( + + ); + }); - await new Promise((resolve) => setTimeout(resolve, 0)); + if (!wrapper) { + throw new Error("enzyme wrapper didn't initialize"); + } expect(wrapper.find(EuiPageTemplate.Section).text()).toContain(tableList1); - act(() => { - wrapper.setProps({ + await act(async () => { + wrapper?.setProps({ activeTabId: 'tab-2', }); }); - await new Promise((resolve) => setTimeout(resolve, 0)); - expect(wrapper.find(EuiPageTemplate.Section).text()).toContain(tableList2); }); }); diff --git a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx index 281e677223c60..f03ab50c5234b 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx @@ -1202,13 +1202,11 @@ describe('TableList', () => { const findItems = jest.fn().mockResolvedValue({ total: 0, hits: [] }); - act(() => { + await act(async () => { testBed = setup({ findItems }); }); - await new Promise((resolve) => setTimeout(resolve, 0)); const { component, table } = testBed!; - component.update(); findItems.mockClear(); expect(findItems).not.toHaveBeenCalled(); @@ -1226,14 +1224,12 @@ describe('TableList', () => { ]; findItems.mockResolvedValue({ total: hits.length, hits }); - act(() => { + await act(async () => { component.setProps({ refreshListBouncer: true, }); }); - await new Promise((resolve) => setTimeout(resolve, 0)); - component.update(); expect(findItems).toHaveBeenCalledTimes(1); @@ -1246,10 +1242,9 @@ describe('TableList', () => { it('reports successful fetches', async () => { const onFetchSuccess = jest.fn(); - act(() => { + await act(async () => { setup({ onFetchSuccess }); }); - await new Promise((resolve) => setTimeout(resolve, 0)); expect(onFetchSuccess).toHaveBeenCalled(); }); From 1ec1a839fbdc46bc0808a6ba2ab1657e4c3409f6 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 11:51:40 -0500 Subject: [PATCH 197/202] remove comment --- .../table_list_view_table/src/table_list_view_table.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx index 8125e1e088be7..a0d544de5687e 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -107,7 +107,6 @@ export interface TableListViewTableProps< withoutPageTemplateWrapper?: boolean; contentEditor?: ContentEditorConfig; - // TODO are these used? tableCaption: string; refreshListBouncer?: boolean; onFetchSuccess: () => void; From e1491888ee012027dab462a2d9b9fcbff920ad34 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 13:30:47 -0500 Subject: [PATCH 198/202] remove snapshot test --- .../public/components/table_list.test.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index d1a97ad2ad7fb..57e9f00d43ef6 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -95,14 +95,6 @@ describe('annotation list view', () => { ); }); - it('renders a table list view', () => { - expect(wrapper.debug()).toMatchInlineSnapshot(` - " - - " - `); - }); - it('searches for groups', () => { const searchQuery = 'My Search Query'; const references = [{ id: 'first_id', type: 'sometype' }]; From d87ebb5a27b3c4afdbc9ba78544806c77d3342ff Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 13:31:29 -0500 Subject: [PATCH 199/202] fix type --- .../event_annotation/public/components/table_list.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/event_annotation/public/components/table_list.test.tsx b/src/plugins/event_annotation/public/components/table_list.test.tsx index 57e9f00d43ef6..b6d20e64dc8d4 100644 --- a/src/plugins/event_annotation/public/components/table_list.test.tsx +++ b/src/plugins/event_annotation/public/components/table_list.test.tsx @@ -91,6 +91,7 @@ describe('annotation list view', () => { createDataView={() => Promise.resolve({} as DataView)} queryInputServices={{} as QueryInputServices} toasts={mockToasts} + navigateToLens={() => {}} /> ); }); From 4259be3999f883f2eb372625dcf5b56692ef50f4 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Mon, 12 Jun 2023 15:38:05 -0500 Subject: [PATCH 200/202] delete through library in functional tests --- test/functional/page_objects/visualize_page.ts | 8 ++++++++ test/functional/services/listing_table.ts | 16 ++++++++++++++-- .../functional/apps/lens/group6/annotations.ts | 12 ++++-------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index bcfbc8caa9ce1..a08b950ce9853 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -69,6 +69,14 @@ export class VisualizePageObject extends FtrService { await this.common.navigateToApp('visualize'); } + public async selectVisualizationsTab() { + await this.listingTable.selectTab(1); + } + + public async selectAnnotationsTab() { + await this.listingTable.selectTab(2); + } + public async clickNewVisualization() { await this.listingTable.clickNewButton(); } diff --git a/test/functional/services/listing_table.ts b/test/functional/services/listing_table.ts index 96e9ba2e49d34..9bd2eb80132a8 100644 --- a/test/functional/services/listing_table.ts +++ b/test/functional/services/listing_table.ts @@ -204,6 +204,10 @@ export class ListingTableService extends FtrService { await this.testSubjects.click('deleteSelectedItems'); } + public async selectFirstItemInList() { + await this.find.clickByCssSelector('.euiTableCellContent .euiCheckbox__input'); + } + public async clickItemCheckbox(id: string) { await this.testSubjects.click(`checkboxSelectRow-${id}`); } @@ -213,9 +217,13 @@ export class ListingTableService extends FtrService { * @param name item name * @param id row id */ - public async deleteItem(name: string, id: string) { + public async deleteItem(name: string, id?: string) { await this.searchForItemWithName(name); - await this.clickItemCheckbox(id); + if (id) { + await this.clickItemCheckbox(id); + } else { + await this.selectFirstItemInList(); + } await this.clickDeleteSelected(); await this.common.clickConfirmOnModal(); } @@ -253,4 +261,8 @@ export class ListingTableService extends FtrService { timeout: 5000, }); } + + public async selectTab(which: number) { + await this.find.clickByCssSelector(`.euiTab:nth-child(${which})`); + } } diff --git a/x-pack/test/functional/apps/lens/group6/annotations.ts b/x-pack/test/functional/apps/lens/group6/annotations.ts index 685d19bf02633..aa3b409d21fb0 100644 --- a/x-pack/test/functional/apps/lens/group6/annotations.ts +++ b/x-pack/test/functional/apps/lens/group6/annotations.ts @@ -22,6 +22,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const toastsService = getService('toasts'); const testSubjects = getService('testSubjects'); + const listingTable = getService('listingTable'); const from = 'Sep 19, 2015 @ 06:31:44.000'; const to = 'Sep 23, 2015 @ 18:31:44.000'; @@ -181,15 +182,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should remove layer for deleted annotation group', async () => { - // TODO - delete from listing page instead - - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaSavedObjects(); - await PageObjects.savedObjects.searchForObject(ANNOTATION_GROUP_TITLE); - await PageObjects.savedObjects.clickCheckboxByTitle(ANNOTATION_GROUP_TITLE); - await PageObjects.savedObjects.clickDelete({ confirmDelete: true }); - await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.visualize.selectAnnotationsTab(); + await listingTable.deleteItem(ANNOTATION_GROUP_TITLE); + await PageObjects.visualize.selectVisualizationsTab(); await PageObjects.visualize.loadSavedVisualization(FIRST_VIS_TITLE, { navigateToVisualize: false, }); From 6d11a38673c84316d445bf86299af445587d6ff5 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 13 Jun 2023 10:55:33 -0500 Subject: [PATCH 201/202] reenable annotation controls unit test --- .../annotation_editor_controls/index.test.tsx | 1114 +++++++---------- .../public/components/name_input.tsx | 2 +- .../dimension_panel/dimension_panel.test.tsx | 4 +- 3 files changed, 429 insertions(+), 691 deletions(-) diff --git a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx index 20703e2c0803e..bfbd8a5d65b44 100644 --- a/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx +++ b/src/plugins/event_annotation/public/components/annotation_editor_controls/index.test.tsx @@ -6,692 +6,430 @@ * Side Public License, v 1. */ -// TODO - translate these tests to work with AnnotationEditorControls component - -export {}; - -describe('AnnotationEditorControls', () => { - it('passes', () => expect(true).toBeTruthy()); +import { DataView, DataViewField, IIndexPatternFieldList } from '@kbn/data-views-plugin/common'; +import AnnotationEditorControls from './annotation_editor_controls'; + +import React from 'react'; +import { mount } from 'enzyme'; +import { EventAnnotationConfig, RangeEventAnnotationConfig } from '../../../common'; +import { QueryInputServices } from '@kbn/visualization-ui-components/public'; +import moment from 'moment'; +import { act } from 'react-dom/test-utils'; +import { EuiButtonGroup } from '@elastic/eui'; + +jest.mock('@kbn/unified-search-plugin/public', () => ({ + QueryStringInput: () => { + return 'QueryStringInput'; + }, +})); + +const customLineStaticAnnotation: EventAnnotationConfig = { + id: 'ann1', + type: 'manual', + key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, + label: 'Event', + icon: 'triangle' as const, + color: 'red', + lineStyle: 'dashed' as const, + lineWidth: 3, +}; + +describe('AnnotationsPanel', () => { + const mockDataView: DataView = { + fields: [ + new DataViewField({ + type: 'date', + name: 'field1', + searchable: true, + aggregatable: true, + }), + new DataViewField({ + type: 'date', + name: '@timestamp', + searchable: true, + aggregatable: true, + }), + ] as unknown as IIndexPatternFieldList, + getFieldByName: (name) => + new DataViewField({ type: 'some-type', name, searchable: true, aggregatable: true }), + timeFieldName: '@timestamp', + } as Partial as DataView; + + const mockQueryInputServices = { + http: {}, + uiSettings: {}, + storage: {}, + dataViews: {}, + unifiedSearch: {}, + docLinks: {}, + notifications: {}, + data: {}, + } as QueryInputServices; + + describe('Dimension Editor', () => { + test('shows correct options for line annotations', () => { + const component = mount( + {}} + dataView={{} as DataView} + getDefaultRangeEnd={() => ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').prop('selected') + ).toEqual(moment('2022-03-18T08:25:00.000Z')); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').exists() + ).toBeFalsy(); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').exists() + ).toBeFalsy(); + expect( + component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') + ).toEqual(false); + expect(component.find('EuiFieldText[data-test-subj="name-input"]').prop('value')).toEqual( + 'Event' + ); + expect( + component.find('EuiComboBox[data-test-subj="lns-icon-select"]').prop('selectedOptions') + ).toEqual([{ label: 'Triangle', value: 'triangle' }]); + expect(component.find('TextDecorationSetting').exists()).toBeTruthy(); + expect(component.find('LineStyleSettings').exists()).toBeTruthy(); + expect( + component.find('EuiButtonGroup[data-test-subj="lns-xyAnnotation-fillStyle"]').exists() + ).toBeFalsy(); + }); + test('shows correct options for range annotations', () => { + const rangeAnnotation: EventAnnotationConfig = { + color: 'red', + icon: 'triangle', + id: 'ann1', + type: 'manual', + isHidden: undefined, + key: { + endTimestamp: '2022-03-21T10:49:00.000Z', + timestamp: '2022-03-18T08:25:00.000Z', + type: 'range', + }, + label: 'Event range', + lineStyle: 'dashed', + lineWidth: 3, + }; + + const component = mount( + {}} + dataView={{} as DataView} + getDefaultRangeEnd={() => ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').prop('selected') + ).toEqual(moment('2022-03-18T08:25:00.000Z')); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').prop('selected') + ).toEqual(moment('2022-03-21T10:49:00.000Z')); + expect( + component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').exists() + ).toBeFalsy(); + expect( + component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') + ).toEqual(true); + expect(component.find('EuiFieldText[data-test-subj="name-input"]').prop('value')).toEqual( + 'Event range' + ); + expect(component.find('EuiComboBox[data-test-subj="lns-icon-select"]').exists()).toBeFalsy(); + expect(component.find('TextDecorationSetting').exists()).toBeFalsy(); + expect(component.find('LineStyleSettings').exists()).toBeFalsy(); + expect(component.find('[data-test-subj="lns-xyAnnotation-fillStyle"]').exists()).toBeTruthy(); + }); + + test('calculates correct endTimstamp and transparent color when switching for range annotation and back', async () => { + const onAnnotationChange = jest.fn(); + const rangeEndTimestamp = new Date().toISOString(); + const component = mount( + rangeEndTimestamp} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); + + const expectedRangeAnnotation: RangeEventAnnotationConfig = { + color: '#FF00001A', + id: 'ann1', + isHidden: undefined, + label: 'Event range', + type: 'manual', + key: { + endTimestamp: rangeEndTimestamp, + timestamp: '2022-03-18T08:25:00.000Z', + type: 'range', + }, + }; + + expect(onAnnotationChange).toBeCalledWith(expectedRangeAnnotation); + + act(() => { + component.setProps({ annotation: expectedRangeAnnotation }); + }); + + expect( + component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') + ).toEqual(true); + + component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); + + expect(onAnnotationChange).toBeCalledWith({ + color: '#FF0000', + id: 'ann1', + isHidden: undefined, + key: { + timestamp: '2022-03-18T08:25:00.000Z', + type: 'point_in_time', + }, + label: 'Event', + type: 'manual', + }); + }); + + test('shows correct options for query based', () => { + const annotation: EventAnnotationConfig = { + color: 'red', + icon: 'triangle', + id: 'ann1', + type: 'query', + isHidden: undefined, + timeField: 'timestamp', + key: { + type: 'point_in_time', + }, + label: 'Query based event', + lineStyle: 'dashed', + lineWidth: 3, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }; + + const component = mount( + {}} + dataView={mockDataView} + getDefaultRangeEnd={() => ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + expect( + component.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]').exists() + ).toBeTruthy(); + expect( + component.find('[data-test-subj="annotation-query-based-query-input"]').exists() + ).toBeTruthy(); + + // The provided indexPattern has 2 date fields + expect( + component + .find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]') + .at(0) + .prop('options') + ).toHaveLength(2); + // When in query mode a new "field" option is added to the previous 2 ones + expect( + component.find('[data-test-subj="lns-lineMarker-text-visibility"]').at(0).prop('options') + ).toHaveLength(3); + expect( + component.find('[data-test-subj="lnsXY-annotation-tooltip-add_field"]').exists() + ).toBeTruthy(); + }); + + test('should prefill timeField with the default time field when switching to query based annotations', () => { + const onAnnotationChange = jest.fn(); + + const component = mount( + ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + act(() => { + component + .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) + .find(EuiButtonGroup) + .prop('onChange')!('lens_xyChart_annotation_query'); + }); + component.update(); + + expect(onAnnotationChange).toHaveBeenCalledWith( + expect.objectContaining({ timeField: '@timestamp' }) + ); + }); + + test('should avoid to retain specific manual configurations when switching to query based annotations', () => { + const onAnnotationChange = jest.fn(); + + const component = mount( + ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + act(() => { + component + .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) + .find(EuiButtonGroup) + .prop('onChange')!('lens_xyChart_annotation_query'); + }); + component.update(); + + expect(onAnnotationChange).toHaveBeenCalledWith( + expect.objectContaining({ + key: expect.not.objectContaining({ timestamp: expect.any('string') }), + }) + ); + }); + + test('should avoid to retain range manual configurations when switching to query based annotations', () => { + const annotation: EventAnnotationConfig = { + color: 'red', + icon: 'triangle', + id: 'ann1', + type: 'manual', + isHidden: undefined, + key: { + endTimestamp: '2022-03-21T10:49:00.000Z', + timestamp: '2022-03-18T08:25:00.000Z', + type: 'range', + }, + label: 'Event range', + lineStyle: 'dashed', + lineWidth: 3, + }; + + const onAnnotationChange = jest.fn(); + + const component = mount( + ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + act(() => { + component + .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) + .find(EuiButtonGroup) + .prop('onChange')!('lens_xyChart_annotation_query'); + }); + component.update(); + + expect(onAnnotationChange).toHaveBeenCalledWith( + expect.objectContaining({ label: expect.not.stringContaining('Event range') }) + ); + }); + + test('should set a default tiemstamp when switching from query based to manual annotations', () => { + const annotation: EventAnnotationConfig = { + color: 'red', + icon: 'triangle', + id: 'ann1', + type: 'query', + isHidden: undefined, + timeField: 'timestamp', + key: { + type: 'point_in_time', + }, + label: 'Query based event', + lineStyle: 'dashed', + lineWidth: 3, + filter: { type: 'kibana_query', query: '', language: 'kuery' }, + }; + + const onAnnotationChange = jest.fn(); + + const component = mount( + ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + act(() => { + component + .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) + .find(EuiButtonGroup) + .prop('onChange')!('lens_xyChart_annotation_manual'); + }); + component.update(); + + expect(onAnnotationChange).toHaveBeenCalledWith( + expect.objectContaining({ + key: { type: 'point_in_time', timestamp: expect.any(String) }, + }) + ); + + // also check query specific props are not carried over + expect(onAnnotationChange).toHaveBeenCalledWith( + expect.not.objectContaining({ timeField: 'timestamp' }) + ); + }); + + test('should fallback to the first date field available in the dataView if not time-based', () => { + const onAnnotationChange = jest.fn(); + const component = mount( + ''} + queryInputServices={mockQueryInputServices} + appName="myApp" + /> + ); + + act(() => { + component + .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) + .find(EuiButtonGroup) + .prop('onChange')!('lens_xyChart_annotation_query'); + }); + component.update(); + + expect(onAnnotationChange).toHaveBeenCalledWith( + expect.objectContaining({ timeField: 'field1' }) + ); + }); + }); }); - -// import React from 'react'; -// import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; -// import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; -// import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -// import { AnnotationsPanel } from '.'; -// import { FramePublicAPI } from '../../../../types'; -// import { DatasourcePublicAPI } from '../../../..'; -// import { createMockFramePublicAPI } from '../../../../mocks'; -// import { State, XYState } from '../../types'; -// import { Position } from '@elastic/charts'; -// import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -// import moment from 'moment'; -// import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; -// import { createMockDataViewsState } from '../../../../data_views_service/mocks'; -// import { createMockedIndexPattern } from '../../../../datasources/form_based/mocks'; -// import { act } from 'react-dom/test-utils'; -// import { EuiButtonGroup } from '@elastic/eui'; - -// jest.mock('lodash', () => { -// const original = jest.requireActual('lodash'); - -// return { -// ...original, -// debounce: (fn: unknown) => fn, -// }; -// }); - -// jest.mock('@kbn/unified-search-plugin/public', () => ({ -// QueryStringInput: () => { -// return 'QueryStringInput'; -// }, -// })); - -// const customLineStaticAnnotation: EventAnnotationConfig = { -// id: 'ann1', -// type: 'manual', -// key: { type: 'point_in_time' as const, timestamp: '2022-03-18T08:25:00.000Z' }, -// label: 'Event', -// icon: 'triangle' as const, -// color: 'red', -// lineStyle: 'dashed' as const, -// lineWidth: 3, -// }; - -// describe('AnnotationsPanel', () => { -// const datatableUtilities = createDatatableUtilitiesMock(); -// let frame: FramePublicAPI; - -// function testState(): State { -// return { -// legend: { isVisible: true, position: Position.Right }, -// valueLabels: 'hide', -// preferredSeriesType: 'bar', -// layers: [ -// { -// layerType: LayerTypes.ANNOTATIONS, -// layerId: 'annotation', -// indexPatternId: 'indexPattern1', -// annotations: [customLineStaticAnnotation], -// ignoreGlobalFilters: true, -// hide: false, -// simpleView: false, -// }, -// ], -// }; -// } - -// beforeEach(() => { -// frame = createMockFramePublicAPI({ datasourceLayers: {} }); -// }); - -// describe('Dimension Editor', () => { -// test('shows correct options for line annotations', () => { -// const state = testState(); -// const component = mount( -// -// ); - -// expect( -// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').prop('selected') -// ).toEqual(moment('2022-03-18T08:25:00.000Z')); -// expect( -// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').exists() -// ).toBeFalsy(); -// expect( -// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').exists() -// ).toBeFalsy(); -// expect( -// component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') -// ).toEqual(false); -// expect( -// component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') -// ).toEqual('Event'); -// expect( -// component.find('EuiComboBox[data-test-subj="lns-icon-select"]').prop('selectedOptions') -// ).toEqual([{ label: 'Triangle', value: 'triangle' }]); -// expect(component.find('TextDecorationSetting').exists()).toBeTruthy(); -// expect(component.find('LineStyleSettings').exists()).toBeTruthy(); -// expect( -// component.find('EuiButtonGroup[data-test-subj="lns-xyAnnotation-fillStyle"]').exists() -// ).toBeFalsy(); -// }); -// test('shows correct options for range annotations', () => { -// const state = testState(); -// state.layers[0] = { -// annotations: [ -// { -// color: 'red', -// icon: 'triangle', -// id: 'ann1', -// type: 'manual', -// isHidden: undefined, -// key: { -// endTimestamp: '2022-03-21T10:49:00.000Z', -// timestamp: '2022-03-18T08:25:00.000Z', -// type: 'range', -// }, -// label: 'Event range', -// lineStyle: 'dashed', -// lineWidth: 3, -// }, -// ], -// layerId: 'annotation', -// layerType: 'annotations', -// indexPatternId: 'indexPattern1', -// ignoreGlobalFilters: true, -// hide: false, -// simpleView: false, -// }; -// const component = mount( -// -// ); - -// expect( -// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-fromTime"]').prop('selected') -// ).toEqual(moment('2022-03-18T08:25:00.000Z')); -// expect( -// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-toTime"]').prop('selected') -// ).toEqual(moment('2022-03-21T10:49:00.000Z')); -// expect( -// component.find('EuiDatePicker[data-test-subj="lns-xyAnnotation-time"]').exists() -// ).toBeFalsy(); -// expect( -// component.find('EuiSwitch[data-test-subj="lns-xyAnnotation-rangeSwitch"]').prop('checked') -// ).toEqual(true); -// expect( -// component.find('EuiFieldText[data-test-subj="column-label-edit"]').prop('value') -// ).toEqual('Event range'); -// expect(component.find('EuiComboBox[data-test-subj="lns-icon-select"]').exists()).toBeFalsy(); -// expect(component.find('TextDecorationSetting').exists()).toBeFalsy(); -// expect(component.find('LineStyleSettings').exists()).toBeFalsy(); -// expect(component.find('[data-test-subj="lns-xyAnnotation-fillStyle"]').exists()).toBeTruthy(); -// }); - -// test('calculates correct endTimstamp and transparent color when switching for range annotation and back', () => { -// const state = testState(); -// const setState = jest.fn(); -// const component = mount( -// -// ); - -// component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); - -// expect(setState).toBeCalledWith({ -// ...state, -// layers: [ -// { -// annotations: [ -// { -// color: '#FF00001A', -// id: 'ann1', -// isHidden: undefined, -// label: 'Event range', -// type: 'manual', -// key: { -// endTimestamp: '2022-03-21T10:49:00.000Z', -// timestamp: '2022-03-18T08:25:00.000Z', -// type: 'range', -// }, -// }, -// ], -// indexPatternId: 'indexPattern1', -// layerId: 'annotation', -// layerType: 'annotations', -// ignoreGlobalFilters: true, -// hide: false, -// simpleView: false, -// }, -// ], -// }); -// component.find('button[data-test-subj="lns-xyAnnotation-rangeSwitch"]').simulate('click'); -// expect(setState).toBeCalledWith({ -// ...state, -// layers: [ -// { -// annotations: [ -// { -// color: '#FF0000', -// id: 'ann1', -// isHidden: undefined, -// key: { -// timestamp: '2022-03-18T08:25:00.000Z', -// type: 'point_in_time', -// }, -// label: 'Event', -// type: 'manual', -// }, -// ], -// indexPatternId: 'indexPattern1', -// layerId: 'annotation', -// layerType: 'annotations', -// ignoreGlobalFilters: true, -// hide: false, -// simpleView: false, -// }, -// ], -// }); -// }); - -// test('shows correct options for query based', () => { -// const state = testState(); -// const indexPattern = createMockedIndexPattern(); -// state.layers[0] = { -// annotations: [ -// { -// color: 'red', -// icon: 'triangle', -// id: 'ann1', -// type: 'query', -// isHidden: undefined, -// timeField: 'timestamp', -// key: { -// type: 'point_in_time', -// }, -// label: 'Query based event', -// lineStyle: 'dashed', -// lineWidth: 3, -// filter: { type: 'kibana_query', query: '', language: 'kuery' }, -// }, -// ], -// layerId: 'annotation', -// layerType: 'annotations', -// indexPatternId: indexPattern.id, -// ignoreGlobalFilters: true, -// hide: false, -// simpleView: false, -// }; -// const frameMock = createMockFramePublicAPI({ -// datasourceLayers: {}, -// dataViews: createMockDataViewsState({ -// indexPatterns: { [indexPattern.id]: indexPattern }, -// }), -// }); - -// const component = mount( -// -// ); - -// expect( -// component.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]').exists() -// ).toBeTruthy(); -// expect( -// component.find('[data-test-subj="lnsXY-annotation-query-based-query-input"]').exists() -// ).toBeTruthy(); - -// // The provided indexPattern has 2 date fields -// expect( -// component -// .find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]') -// .at(0) -// .prop('options') -// ).toHaveLength(2); -// // When in query mode a new "field" option is added to the previous 2 ones -// expect( -// component.find('[data-test-subj="lns-lineMarker-text-visibility"]').at(0).prop('options') -// ).toHaveLength(3); -// expect( -// component.find('[data-test-subj="lnsXY-annotation-tooltip-add_field"]').exists() -// ).toBeTruthy(); -// }); - -// test('should prefill timeField with the default time field when switching to query based annotations', () => { -// const state = testState(); -// const indexPattern = createMockedIndexPattern(); -// state.layers[0] = { -// annotations: [customLineStaticAnnotation], -// layerId: 'annotation', -// layerType: 'annotations', -// ignoreGlobalFilters: true, -// indexPatternId: indexPattern.id, -// hide: false, -// simpleView: false, -// }; -// const frameMock = createMockFramePublicAPI({ -// datasourceLayers: {}, -// dataViews: createMockDataViewsState({ -// indexPatterns: { [indexPattern.id]: indexPattern }, -// }), -// }); - -// const setState = jest.fn(); - -// const component = mount( -// -// ); - -// act(() => { -// component -// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) -// .find(EuiButtonGroup) -// .prop('onChange')!('lens_xyChart_annotation_query'); -// }); -// component.update(); - -// expect(setState).toHaveBeenCalledWith( -// expect.objectContaining({ -// layers: [ -// expect.objectContaining({ -// annotations: [expect.objectContaining({ timeField: 'timestamp' })], -// }), -// ], -// }) -// ); -// }); - -// test('should avoid to retain specific manual configurations when switching to query based annotations', () => { -// const state = testState(); -// const indexPattern = createMockedIndexPattern(); -// state.layers[0] = { -// annotations: [customLineStaticAnnotation], -// layerId: 'annotation', -// layerType: 'annotations', -// ignoreGlobalFilters: true, -// indexPatternId: indexPattern.id, -// hide: false, -// simpleView: false, -// }; -// const frameMock = createMockFramePublicAPI({ -// datasourceLayers: {}, -// dataViews: createMockDataViewsState({ -// indexPatterns: { [indexPattern.id]: indexPattern }, -// }), -// }); - -// const setState = jest.fn(); - -// const component = mount( -// -// ); - -// act(() => { -// component -// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) -// .find(EuiButtonGroup) -// .prop('onChange')!('lens_xyChart_annotation_query'); -// }); -// component.update(); - -// expect(setState).toHaveBeenCalledWith( -// expect.objectContaining({ -// layers: [ -// expect.objectContaining({ -// annotations: [ -// expect.objectContaining({ -// key: expect.not.objectContaining({ timestamp: expect.any('string') }), -// }), -// ], -// }), -// ], -// }) -// ); -// }); - -// test('should avoid to retain range manual configurations when switching to query based annotations', () => { -// const state = testState(); -// const indexPattern = createMockedIndexPattern(); -// state.layers[0] = { -// annotations: [ -// { -// color: 'red', -// icon: 'triangle', -// id: 'ann1', -// type: 'manual', -// isHidden: undefined, -// key: { -// endTimestamp: '2022-03-21T10:49:00.000Z', -// timestamp: '2022-03-18T08:25:00.000Z', -// type: 'range', -// }, -// label: 'Event range', -// lineStyle: 'dashed', -// lineWidth: 3, -// }, -// ], -// layerId: 'annotation', -// layerType: 'annotations', -// ignoreGlobalFilters: true, -// indexPatternId: indexPattern.id, -// hide: false, -// simpleView: false, -// }; -// const frameMock = createMockFramePublicAPI({ -// datasourceLayers: {}, -// dataViews: createMockDataViewsState({ -// indexPatterns: { [indexPattern.id]: indexPattern }, -// }), -// }); - -// const setState = jest.fn(); - -// const component = mount( -// -// ); - -// act(() => { -// component -// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) -// .find(EuiButtonGroup) -// .prop('onChange')!('lens_xyChart_annotation_query'); -// }); -// component.update(); - -// expect(setState).toHaveBeenCalledWith( -// expect.objectContaining({ -// layers: [ -// expect.objectContaining({ -// annotations: [ -// expect.objectContaining({ label: expect.not.stringContaining('Event range') }), -// ], -// }), -// ], -// }) -// ); -// }); - -// test('should set a default tiemstamp when switching from query based to manual annotations', () => { -// const state = testState(); -// const indexPattern = createMockedIndexPattern(); -// state.layers[0] = { -// annotations: [ -// { -// color: 'red', -// icon: 'triangle', -// id: 'ann1', -// type: 'query', -// isHidden: undefined, -// timeField: 'timestamp', -// key: { -// type: 'point_in_time', -// }, -// label: 'Query based event', -// lineStyle: 'dashed', -// lineWidth: 3, -// filter: { type: 'kibana_query', query: '', language: 'kuery' }, -// }, -// ], -// layerId: 'annotation', -// layerType: 'annotations', -// indexPatternId: indexPattern.id, -// ignoreGlobalFilters: true, -// hide: false, -// simpleView: false, -// }; -// const frameMock = createMockFramePublicAPI({ -// datasourceLayers: {}, -// dataViews: createMockDataViewsState({ -// indexPatterns: { [indexPattern.id]: indexPattern }, -// }), -// }); - -// const setState = jest.fn(); - -// const component = mount( -// -// ); - -// act(() => { -// component -// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) -// .find(EuiButtonGroup) -// .prop('onChange')!('lens_xyChart_annotation_manual'); -// }); -// component.update(); - -// expect(setState).toHaveBeenCalledWith( -// expect.objectContaining({ -// layers: [ -// expect.objectContaining({ -// annotations: [ -// expect.objectContaining({ -// key: { type: 'point_in_time', timestamp: expect.any(String) }, -// }), -// ], -// }), -// ], -// }) -// ); - -// // also check query specific props are not carried over -// expect(setState).toHaveBeenCalledWith( -// expect.objectContaining({ -// layers: [ -// expect.objectContaining({ -// annotations: [expect.not.objectContaining({ timeField: 'timestamp' })], -// }), -// ], -// }) -// ); -// }); - -// test('should fallback to the first date field available in the dataView if not time-based', () => { -// const state = testState(); -// const indexPattern = createMockedIndexPattern({ timeFieldName: '' }); -// state.layers[0] = { -// annotations: [customLineStaticAnnotation], -// layerId: 'annotation', -// layerType: 'annotations', -// ignoreGlobalFilters: true, -// indexPatternId: indexPattern.id, -// hide: false, -// simpleView: false, -// }; -// const frameMock = createMockFramePublicAPI({ -// datasourceLayers: {}, -// dataViews: createMockDataViewsState({ -// indexPatterns: { [indexPattern.id]: indexPattern }, -// }), -// }); - -// const setState = jest.fn(); - -// const component = mount( -// -// ); - -// act(() => { -// component -// .find(`[data-test-subj="lns-xyAnnotation-placementType"]`) -// .find(EuiButtonGroup) -// .prop('onChange')!('lens_xyChart_annotation_query'); -// }); -// component.update(); - -// expect(setState).toHaveBeenCalledWith( -// expect.objectContaining({ -// layers: [ -// expect.objectContaining({ -// annotations: [expect.objectContaining({ timeField: 'timestampLabel' })], -// }), -// ], -// }) -// ); -// }); -// }); -// }); diff --git a/src/plugins/visualization_ui_components/public/components/name_input.tsx b/src/plugins/visualization_ui_components/public/components/name_input.tsx index 1a3f3e836e98d..89f303e57156e 100644 --- a/src/plugins/visualization_ui_components/public/components/name_input.tsx +++ b/src/plugins/visualization_ui_components/public/components/name_input.tsx @@ -32,7 +32,7 @@ export const NameInput = ({ { act(() => { wrapper - .find('input[data-test-subj="column-label-edit"]') + .find('input[data-test-subj="name-input"]') .simulate('change', { target: { value: 'New Label' } }); }); @@ -757,7 +757,7 @@ describe('FormBasedDimensionEditor', () => { act(() => { wrapper - .find('input[data-test-subj="column-label-edit"]') + .find('input[data-test-subj="name-input"]') .simulate('change', { target: { value: 'Sum of bytes' } }); }); From 42ec1075d9a4d755f3811d2eb01059b814a4b691 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 13 Jun 2023 15:33:42 -0500 Subject: [PATCH 202/202] fix test selector --- x-pack/test/functional/page_objects/lens_page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 544dfd981d52f..c404e85f1ce07 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -773,7 +773,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, async editDimensionLabel(label: string) { - await testSubjects.setValue('column-label-edit', label, { clearWithKeyboard: true }); + await testSubjects.setValue('name-input', label, { clearWithKeyboard: true }); }, async editDimensionFormat(format: string) { await this.selectOptionFromComboBox('indexPattern-dimension-format', format);